diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,11 @@
+## 1.1.0.0
+* `Eq` and `Ord` instaces for `ServerID`.
+* Support unassigned primary IPs.
+* Export `singlePort`.
+* Force EOF on `FromJSON LabelKey`.
+* Fix `getSSHKeys` return type.
+* Fix typo on `getPrimaryIP` request path.
+
 ## 1.0.0.0
 * Require `base >= 4.18`.
 * Remove module `Hetzner.DNS`. Instead, the DNS API now lives under
diff --git a/hetzner.cabal b/hetzner.cabal
--- a/hetzner.cabal
+++ b/hetzner.cabal
@@ -2,7 +2,7 @@
 category: Cloud
 synopsis: Hetzner Cloud and DNS library.
 description: Hetzner Cloud and DNS library. Check the readme and documentation for more details.
-version: 1.0.0.0
+version: 1.1.0.0
 cabal-version: 1.18
 build-type: Simple
 author: Daniel Casanueva (coding `at` danielcasanueva.eu)
diff --git a/src/Hetzner/Cloud.hs b/src/Hetzner/Cloud.hs
--- a/src/Hetzner/Cloud.hs
+++ b/src/Hetzner/Cloud.hs
@@ -80,6 +80,7 @@
   , FirewallID (..)
   , TrafficDirection (..)
   , PortRange (..)
+  , singlePort
   , FirewallRuleProtocol (..)
   , FirewallRule (..)
   , anyIPv4
@@ -364,7 +365,7 @@
 instance FromJSON LabelKey where
   parseJSON = JSON.withText "LabelKey" $ \t ->
     either (fail . Parser.errorBundlePretty) pure $
-      Parser.runParser labelKeyParser "JSON" t
+      Parser.runParser (labelKeyParser <* Parser.eof) "JSON" t
 
 instance ToJSON LabelKey where
    toJSON = JSON.String . labelKeyRender
@@ -1611,8 +1612,8 @@
 
 -- | Primary IP.
 data PrimaryIP = PrimaryIP
-  { -- | Resource the primary IP is assigned to.
-    primaryIPAssignee :: ResourceID
+  { -- | Resource the primary IP is assigned to, if assigned.
+    primaryIPAssignee :: Maybe ResourceID
     -- | This primary IP is deleted when the resource it is assigned to is deleted.
   , primaryIPAutoDelete :: Bool
   , primaryIPIsBlocked :: Bool
@@ -1658,7 +1659,7 @@
 -- | Get a single primary IP.
 getPrimaryIP :: Token -> PrimaryIPID -> IO PrimaryIP
 getPrimaryIP token (PrimaryIPID i) = withoutKey @"primary_ip" <$>
-  cloudQuery "GET" ("/primary_ips" <> fromString (show i)) noBody token Nothing
+  cloudQuery "GET" ("/primary_ips/" <> fromString (show i)) noBody token Nothing
 
 -- | Set reverse DNS for a primary IP.
 --
@@ -1916,7 +1917,7 @@
     _ -> fail $ "Invalid server status: " ++ Text.unpack t
 
 -- | Server identifier.
-newtype ServerID = ServerID Int deriving (Show, FromJSON, ToJSON)
+newtype ServerID = ServerID Int deriving (Show, Eq, Ord, FromJSON, ToJSON)
 
 -- | A server.
 data Server = Server
@@ -2251,9 +2252,8 @@
     <*> o .: "public_key"
 
 -- | Get all uploaded SSH keys.
-getSSHKeys :: Token -> IO [SSHKey]
-getSSHKeys token = withoutKey @"ssh_keys" <$>
-  cloudQuery "GET" "/ssh_keys" noBody token Nothing
+getSSHKeys :: Token -> IO (WithMeta "ssh_keys" [SSHKey])
+getSSHKeys token = cloudQuery "GET" "/ssh_keys" noBody token Nothing
 
 -- | Get a single SSH key.
 getSSHKey :: Token -> SSHKeyID -> IO SSHKey
