diff --git a/apps/Docs.hs b/apps/Docs.hs
new file mode 100644
--- /dev/null
+++ b/apps/Docs.hs
@@ -0,0 +1,68 @@
+
+module Main (main) where
+
+-- base
+import Data.Foldable (forM_)
+import Data.List (sortOn)
+-- hetzner
+import Hetzner.Cloud qualified as Hetzner
+-- bytestring
+import qualified Data.ByteString.Lazy as ByteString
+-- directory
+import System.Directory (createDirectoryIfMissing)
+-- blaze-html
+import Text.Blaze.Html (toHtml, preEscapedToHtml, (!))
+import Text.Blaze.Html.Renderer.Utf8 (renderHtml)
+import Text.Blaze.Html5 qualified as H
+import Text.Blaze.Html5.Attributes qualified as A
+-- time
+import Data.Time.Clock.POSIX (getPOSIXTime)
+
+main :: IO ()
+main = do
+  mtoken <- Hetzner.getTokenFromEnv
+  token <- maybe (fail "HETZNER_API_TOKEN is not set") pure mtoken
+  now <- round <$> getPOSIXTime :: IO Int
+  -- Server types
+  let dir = "public/server-types"
+  createDirectoryIfMissing True dir
+  serverTypes0 <- Hetzner.streamToList $ Hetzner.streamPages $ Hetzner.getServerTypes token
+  let maxPrice = maximum . (:) 0
+               . fmap (Hetzner.grossPrice . Hetzner.monthlyPrice)
+               . Hetzner.serverPricing
+  let serverTypes = sortOn maxPrice $ filter (not . Hetzner.serverDeprecated) serverTypes0
+  ByteString.writeFile (dir ++ "/index.html") $ renderHtml $ H.docTypeHtml $ do
+    H.head $ do
+      H.title "Hetzner Server Type List"
+      H.link ! A.rel "stylesheet" ! A.href "https://use.typekit.net/yxf7kgr.css"
+      H.link ! A.rel "stylesheet" ! A.href "../common.css"
+    H.body $ do
+      H.h1 "Hetzner Server Type List"
+      H.hr
+      H.table $ do
+        H.tr ! A.class_ "hrow" $ do
+          H.th "Server ID"
+          H.th "Name"
+          H.th "Architecture"
+          H.th "Cores"
+          H.th "Memory"
+          H.th "Disk"
+          H.th "CPU Type"
+          H.th "Max Monthly Price"
+        forM_ serverTypes $ \serverType -> H.tr $ do
+          H.td $ (\(Hetzner.ServerTypeID i) -> toHtml i) $ Hetzner.serverTypeID serverType
+          H.td $ toHtml $ Hetzner.serverTypeName serverType
+          H.td $ toHtml $ show $ Hetzner.serverArchitecture serverType
+          H.td $ toHtml $ Hetzner.serverCores serverType
+          H.td $ toHtml $ show (Hetzner.serverMemory serverType) ++ " GB"
+          H.td $ toHtml $ show (Hetzner.serverDisk serverType) ++ " GB"
+          H.td $ toHtml $ case Hetzner.serverCPUType serverType of
+            Hetzner.SharedCPU -> "Shared" :: String
+            Hetzner.DedicatedCPU -> "Dedicated"
+          H.td $ toHtml $ show (maxPrice serverType) ++ " €"
+    H.p $ H.em $ do
+      "Updated "
+      H.script $ preEscapedToHtml $
+        "const date = new Date(" ++ show (now * 1000) ++ "); "
+        ++ "document.write(date.toLocaleString());"
+      "."
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,7 +1,16 @@
+## 0.4.0.0
+* Fix `getServerTypes`. It wasn't returning them all.
+  Unfortunately, the type signature had to change.
+* Add `hetzner-docs` executable.
+
 ## 0.3.0.0
 * Added support for the following server actions:
-  `setServerReverseDNS`, `powerOnServer`, `powerOffServer`, `shutdownServer`,
-  `rebootServer`, `changeServerType`.
+  - `setServerReverseDNS`
+  - `powerOnServer`
+  - `powerOffServer`
+  - `shutdownServer`
+  - `rebootServer`
+  - `changeServerType`
 * Removed `getActions`, as it has been discontinued by Hetzner.
 
 ## 0.2.1.1
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: 0.3.0.0
+version: 0.4.0.0
 cabal-version: 1.18
 build-type: Simple
 author: Daniel Casanueva (daniel.casanueva `at` proton.me)
@@ -43,3 +43,11 @@
   ghc-options: -Wall -Wunused-packages
   default-extensions: ImportQualifiedPost, OverloadedStrings
   build-depends: base, hetzner
+
+executable hetzner-docs
+  default-language: Haskell2010
+  hs-source-dirs: apps
+  main-is: Docs.hs
+  ghc-options: -Wall -Wunused-packages
+  default-extensions: ImportQualifiedPost, OverloadedStrings
+  build-depends: base, bytestring, directory, blaze-html, hetzner, time
diff --git a/src/Hetzner/Cloud.hs b/src/Hetzner/Cloud.hs
--- a/src/Hetzner/Cloud.hs
+++ b/src/Hetzner/Cloud.hs
@@ -1512,9 +1512,11 @@
     <*> o .: "storage_type"
 
 -- | Get all server types.
-getServerTypes :: Token -> IO [ServerType]
-getServerTypes token = withoutKey @"server_types" <$>
-  cloudQuery "GET" "/server_types" noBody token Nothing
+--
+--   A regularly updated list of server types can be browsed
+--   [here](https://daniel-casanueva.gitlab.io/hetzner/server-types).
+getServerTypes :: Token -> Maybe Int -> IO (WithMeta "server_types" [ServerType])
+getServerTypes = cloudQuery "GET" "/server_types" noBody
 
 ----------------------------------------------------------------------------------------------------
 -- SSH Keys
