diff --git a/api-builder.cabal b/api-builder.cabal
--- a/api-builder.cabal
+++ b/api-builder.cabal
@@ -1,90 +1,95 @@
-name: api-builder
-version: 0.11.0.0
-synopsis: Library for easily building REST API wrappers in Haskell
-license: BSD3
-license-file: LICENSE
-author: Fraser Murray
-maintainer: fraser.m.murray@gmail.com
-homepage: https://github.com/intolerable/api-builder
-copyright: (c) Fraser Murray 2014
-category: Network
-build-type: Simple
-cabal-version: >= 1.10
+name:           api-builder
+version:        0.17.0.0
+synopsis:       Library for easily building REST API wrappers in Haskell
+category:       Network
+homepage:       https://github.com/intolerable/api-builder
+author:         Fraser Murray
+maintainer:     fraser.m.murray@gmail.com
+copyright:      (c) Fraser Murray 2014
+license:        BSD3
+license-file:   LICENSE
+build-type:     Simple
+cabal-version:  >= 1.10
 
 source-repository head
   type: git
   location: git://github.com/intolerable/api-builder.git
 
-source-repository this
-  type: git
-  location: git://github.com/intolerable/api-builder.git
-  tag: v0.11.0.0
-
 library
-  exposed-modules:
-    Network.API.Builder
-    Network.API.Builder.API
-    Network.API.Builder.Builder
-    Network.API.Builder.Error
-    Network.API.Builder.Examples.StackOverflow
-    Network.API.Builder.Query
-    Network.API.Builder.Receive
-    Network.API.Builder.Routes
-    Network.API.Builder.Send
-    Network.API.Builder.Send.Multipart
-  build-depends:
-    base >= 4.6 && < 4.9,
-    aeson >= 0.9 && < 0.10,
-    bifunctors >= 4.0 && < 6.0,
-    bytestring == 0.10.*,
-    HTTP == 4000.*,
-    http-client >= 0.4.11 && < 0.4.21,
-    http-client-tls >= 0.2 && < 0.2.3,
-    http-types == 0.8.*,
-    text == 1.*,
-    transformers >= 0.4 && < 0.5
   hs-source-dirs: src/
-  default-language: Haskell2010
   default-extensions:
-    FlexibleInstances
     OverloadedStrings
+    FlexibleInstances
   ghc-options: -Wall
+  build-depends:
+    HTTP >=4000,
+    aeson >=0.9,
+    base >=4.6,
+    bifunctors >=4.0,
+    bytestring >=0.10,
+    http-client >=0.4.30,
+    http-client-tls >=0.2,
+    http-types >=0.8,
+    text >=1.0,
+    tls >=1.3,
+    transformers >=0.4
+  exposed-modules:
+      Network.API.Builder
+      Network.API.Builder.API
+      Network.API.Builder.Builder
+      Network.API.Builder.Error
+      Network.API.Builder.Examples.StackOverflow
+      Network.API.Builder.Query
+      Network.API.Builder.Receive
+      Network.API.Builder.Routes
+      Network.API.Builder.Send
+      Network.API.Builder.Send.Multipart
+  other-modules:
+      Paths_api_builder
+  default-language: Haskell2010
 
 test-suite test
-  hs-source-dirs: test
-  main-is: Spec.hs
-  default-extensions:
-    OverloadedStrings
-    ScopedTypeVariables
-  default-language: Haskell2010
   type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  hs-source-dirs: test/
+  default-extensions: OverloadedStrings FlexibleInstances ScopedTypeVariables
+  ghc-options: -Wall
   build-depends:
-    base == 4.*,
-    Cabal >= 1.16.0,
-    api-builder,
+    Cabal >=1.16.0,
     aeson,
+    api-builder,
+    base ==4.*,
     bytestring,
     hspec,
     http-client,
+    QuickCheck,
     text,
     transformers
-  GHC-options: -Wall
+  other-modules:
+    Network.API.Builder.ErrorSpec
+    Network.API.Builder.QuerySpec
+    Network.API.Builder.RoutesSpec
+    Network.API.Builder.SendSpec
+    Paths_api_builder
+  default-language: Haskell2010
 
 test-suite test-io
-  hs-source-dirs: test-io
-  main-is: Spec.hs
-  default-extensions:
-    LambdaCase
-  default-language: Haskell2010
   type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  hs-source-dirs: test-io/
+  default-extensions: OverloadedStrings FlexibleInstances LambdaCase
+  ghc-options: -Wall
   build-depends:
-    base == 4.*,
-    Cabal >= 1.16.0,
-    api-builder,
-    aeson,
-    bytestring,
-    containers,
-    hspec,
-    text,
-    transformers
-  GHC-options: -Wall
+      Cabal >=1.16.0
+    , aeson
+    , api-builder
+    , base ==4.*
+    , bytestring
+    , containers
+    , hspec
+    , text
+    , transformers
+  other-modules:
+    Network.API.Builder.Examples.StackOverflowSpec
+    Paths_api_builder
+  default-language: Haskell2010
diff --git a/src/Network/API/Builder/API.hs b/src/Network/API/Builder/API.hs
--- a/src/Network/API/Builder/API.hs
+++ b/src/Network/API/Builder/API.hs
@@ -81,7 +81,6 @@
 execAPI b s api = do
   m <- liftIO $ newManager tlsManagerSettings
   (res, _, _) <- runAPI b m s api
-  liftIO $ closeManager m
   return res
 
 -- | Runs an @API@ by executing its transformer stack and dumping it all into @IO@.
diff --git a/src/Network/API/Builder/Error.hs b/src/Network/API/Builder/Error.hs
--- a/src/Network/API/Builder/Error.hs
+++ b/src/Network/API/Builder/Error.hs
@@ -1,7 +1,7 @@
 module Network.API.Builder.Error
   ( APIError(..) ) where
 
-import Data.Monoid
+import Data.Semigroup
 import Network.HTTP.Client (HttpException)
 import Prelude
 
@@ -23,9 +23,13 @@
   (APIError a) == (APIError b) = a == b
   InvalidURLError == InvalidURLError = True
   (ParseError a) == (ParseError b) = a == b
+  EmptyError == EmptyError = True
   _ == _ = False
 
+instance Semigroup (APIError a) where
+  EmptyError <> x = x
+  x <> _ = x
+
 instance Monoid (APIError a) where
   mempty = EmptyError
-  EmptyError `mappend` x = x
-  x `mappend` _ = x
+  mappend = (<>)
diff --git a/src/Network/API/Builder/Examples/StackOverflow.hs b/src/Network/API/Builder/Examples/StackOverflow.hs
--- a/src/Network/API/Builder/Examples/StackOverflow.hs
+++ b/src/Network/API/Builder/Examples/StackOverflow.hs
@@ -3,7 +3,7 @@
 module Network.API.Builder.Examples.StackOverflow where
 
 import Network.API.Builder
-import Control.Applicative
+import Control.Applicative ((<$>), (<*>))
 import Data.Aeson
 import Data.Monoid (mempty)
 import Data.Text (Text)
@@ -35,6 +35,9 @@
 stackOverflow :: Builder
 stackOverflow = basicBuilder "StackOverflow API" "http://api.stackexchange.com"
 
+stackOverflowSSL :: Builder
+stackOverflowSSL = basicBuilder "StackOverflow API" "https://api.stackexchange.com"
+
 answersRoute :: Route
 answersRoute = Route { urlPieces = [ "2.2", "questions" ]
                      , urlParams = [ "order" =. ("desc" :: Text)
@@ -44,3 +47,6 @@
 
 getAnswers :: IO (Either (APIError ()) Questions)
 getAnswers = execAPI stackOverflow () $ runRoute answersRoute
+
+getAnswersSSL :: IO (Either (APIError ()) Questions)
+getAnswersSSL = execAPI stackOverflowSSL () $ runRoute answersRoute
diff --git a/src/Network/API/Builder/Send.hs b/src/Network/API/Builder/Send.hs
--- a/src/Network/API/Builder/Send.hs
+++ b/src/Network/API/Builder/Send.hs
@@ -18,7 +18,7 @@
   send builder r () =
     case httpMethod r of
       "POST" -> do
-        req <- parseUrl $ Text.unpack $ routeURL (_baseURL builder) (_customizeRoute builder r)
+        req <- parseUrlThrow $ Text.unpack $ routeURL (_baseURL builder) (_customizeRoute builder r)
         return $ _customizeRequest builder $
           req { requestHeaders = ("Content-Type", "application/x-www-form-urlencoded") : requestHeaders req
               , requestBody = RequestBodyBS (dropQuestion $ queryString req)
@@ -29,19 +29,22 @@
 
 basicSend :: Builder -> Route -> Maybe Request
 basicSend builder r = do
-  req <- parseUrl $ Text.unpack $ routeURL (_baseURL builder) (_customizeRoute builder r)
+  req <- parseUrlThrow $ Text.unpack $ routeURL (_baseURL builder) (_customizeRoute builder r)
   return $ _customizeRequest builder $ req { method = httpMethod r }
 
 instance Sendable Value where
   send builder r value =
     case httpMethod r of
       "POST" -> do
-        req <- parseUrl $ Text.unpack $ routeURL (_baseURL builder) (_customizeRoute builder r)
+        req <- parseUrlThrow $ Text.unpack $ routeURL (_baseURL builder) (_customizeRoute builder r)
         return $ _customizeRequest builder $
           req { requestBody = RequestBodyLBS (encode value)
               , requestHeaders = ("Content-Type", "application/json") : requestHeaders req
               , method = httpMethod r }
       _ -> Nothing
+
+useToJSON :: ToJSON a => Builder -> Route -> a -> Maybe Request
+useToJSON b r v = send b r (toJSON v)
 
 instance Sendable ByteString where
   send builder r bs =
diff --git a/test-io/Network/API/Builder/Examples/StackOverflowSpec.hs b/test-io/Network/API/Builder/Examples/StackOverflowSpec.hs
new file mode 100644
--- /dev/null
+++ b/test-io/Network/API/Builder/Examples/StackOverflowSpec.hs
@@ -0,0 +1,40 @@
+module Network.API.Builder.Examples.StackOverflowSpec where
+
+import Network.API.Builder.Examples.StackOverflow
+
+import Control.Monad
+import Data.List (nub)
+import Test.Hspec
+import qualified Data.Set as Set
+import qualified Data.Text as Text
+
+main :: IO ()
+main = hspec spec
+
+spec :: Spec
+spec =
+  describe "getAnswers" $ do
+
+    it "can get answers" $
+      getAnswers >>= \case
+        Left _ -> expectationFailure "getAnswers failed"
+        Right (Questions qs) -> do
+          nub qs `shouldMatchList` qs
+          forM_ qs $ \q -> do
+            tags q `shouldSatisfy` isNubbed
+            title q `shouldSatisfy` (not . Text.null)
+
+    it "can get answers via https" $
+      getAnswersSSL >>= \case
+        Left _ -> expectationFailure "getAnswers failed"
+        Right (Questions qs) -> do
+          nub qs `shouldMatchList` qs
+          forM_ qs $ \q -> do
+            tags q `shouldSatisfy` isNubbed
+            title q `shouldSatisfy` (not . Text.null)
+
+isNubbed :: Ord a => [a] -> Bool
+isNubbed = f Set.empty
+  where
+    f _ [] = True
+    f s (x:xs) = not (Set.member x s) && f (Set.insert x s) xs
diff --git a/test/Network/API/Builder/ErrorSpec.hs b/test/Network/API/Builder/ErrorSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/API/Builder/ErrorSpec.hs
@@ -0,0 +1,40 @@
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Network.API.Builder.ErrorSpec where
+
+import Data.Semigroup ((<>))
+import Network.API.Builder.Error
+
+import Test.Hspec
+import Test.Hspec.QuickCheck
+import Test.QuickCheck
+
+main :: IO ()
+main = hspec spec
+
+instance Arbitrary a => Arbitrary (APIError a) where
+  arbitrary = oneof
+    [ APIError <$> arbitrary
+    , pure InvalidURLError
+    , ParseError <$> arbitrary
+    , pure EmptyError
+    ]
+
+spec :: Spec
+spec =
+  describe "APIError" $ do
+    describe "Eq" $ do
+      it "should check for equality properly" $ do
+        HTTPError undefined == (HTTPError undefined :: APIError ())
+          `shouldBe` False
+        APIError () `shouldBe` APIError ()
+        InvalidURLError `shouldBe` (InvalidURLError :: APIError ())
+
+      prop "ParseError s == ParseError s" $ \s ->
+        ParseError s == (ParseError s :: APIError ())
+
+      prop "APIError x == APIError x" $ \(x :: String) ->
+        APIError x == APIError x
+
+    describe "Semigroup" $ do
+      prop "(x <> y) <> z == x <> (y <> z)" $ \(x :: APIError ()) y z ->
+        (x <> y) <> z == x <> (y <> z)
diff --git a/test/Network/API/Builder/QuerySpec.hs b/test/Network/API/Builder/QuerySpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/API/Builder/QuerySpec.hs
@@ -0,0 +1,33 @@
+module Network.API.Builder.QuerySpec where
+
+import Network.API.Builder.Query
+
+import Test.Hspec
+import Test.Hspec.QuickCheck
+
+main :: IO ()
+main = hspec spec
+
+spec :: Spec
+spec =
+  describe "ToQuery" $ do
+
+    it "can convert an Integer" $ do
+      toQuery "integer" (5 :: Integer) `shouldBe`
+        [("integer", "5")]
+
+    it "can convert a Boolean" $ do
+      toQuery "bool" False `shouldBe` [("bool", "false")]
+
+    it "can convert a list of Ints" $ do
+      toQuery "list" ([1,2,3,4,5] :: [Int]) `shouldBe` [("list", "1,2,3,4,5")]
+
+    it "can convert Maybe values" $ do
+      toQuery "maybe" (Just False) `shouldBe` [("maybe", "false")]
+      toQuery "maybe" (Nothing :: Maybe Bool) `shouldBe` []
+
+    prop "toQuery x y == toQuery x (Just y)" $ \(y :: Int) ->
+      toQuery "x" y == toQuery "x" (Just y)
+
+skip :: String -> Expectation -> Expectation
+skip _ _ = return ()
diff --git a/test/Network/API/Builder/RoutesSpec.hs b/test/Network/API/Builder/RoutesSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/API/Builder/RoutesSpec.hs
@@ -0,0 +1,24 @@
+module Network.API.Builder.RoutesSpec where
+
+import Network.API.Builder.Routes
+
+import Test.Hspec
+
+main :: IO ()
+main = hspec spec
+
+spec :: Spec
+spec = do
+  describe "routeURL" $ do
+    it "should be able to create a basic url" $ do
+      routeURL "" (Route [ "api", "index" ] [ ] "GET")
+        `shouldBe` "/api/index"
+      routeURL "" (Route [ "api", "index.json" ] [ ] "GET")
+        `shouldBe` "/api/index.json"
+      routeURL "" (Route [ ] [ "test" =. False ] "GET")
+        `shouldBe` "?test=false"
+      routeURL "" (Route [ "about.json" ] [ "test" =. True ] "GET")
+        `shouldBe` "/about.json?test=true"
+      routeURL "" (Route [ "about.json" ] [ "test" =. True ] "GET")
+        `shouldBe` "/about.json?test=true"
+
diff --git a/test/Network/API/Builder/SendSpec.hs b/test/Network/API/Builder/SendSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/API/Builder/SendSpec.hs
@@ -0,0 +1,100 @@
+module Network.API.Builder.SendSpec where
+
+import Network.API.Builder.Builder
+import Network.API.Builder.Routes
+import Network.API.Builder.Send
+
+import Data.Aeson
+import Data.ByteString.Lazy (ByteString)
+import qualified Network.HTTP.Client as HTTP
+import Test.Hspec
+
+main :: IO ()
+main = hspec spec
+
+spec :: Spec
+spec = do
+  describe "Sendable" $ do
+
+    describe "()" $ do
+      it "should be able to construct a basic request" $ do
+        case send exampleBuilder (Route ["api.json"] [] "GET") () of
+          Just req -> do
+            HTTP.secure req `shouldBe` True
+            HTTP.host req `shouldBe` "example.com"
+            HTTP.method req `shouldBe` "GET"
+            HTTP.port req `shouldBe` 443
+            HTTP.queryString req `shouldBe` ""
+            HTTP.path req `shouldBe` "/api.json"
+          Nothing -> expectationFailure "req construction failed"
+      it "should be able to construct request with query params" $ do
+        case send exampleBuilder (Route ["api.json"] ["a_query" =. True] "GET") () of
+          Just req -> do
+            HTTP.secure req `shouldBe` True
+            HTTP.host req `shouldBe` "example.com"
+            HTTP.method req `shouldBe` "GET"
+            HTTP.port req `shouldBe` 443
+            HTTP.queryString req `shouldBe` "?a_query=true"
+            HTTP.path req `shouldBe` "/api.json"
+          Nothing -> expectationFailure "req construction failed"
+      it "should be able to construct a POST request" $ do
+        case send exampleBuilder (Route ["api.json"] ["query" =. False] "POST") () of
+          Just req -> do
+            HTTP.secure req `shouldBe` True
+            HTTP.host req `shouldBe` "example.com"
+            HTTP.method req `shouldBe` "POST"
+            HTTP.port req `shouldBe` 443
+            HTTP.queryString req `shouldBe` ""
+            HTTP.path req `shouldBe` "/api.json"
+            case HTTP.requestBody req of
+              HTTP.RequestBodyBS "query=false" -> return ()
+              _ -> expectationFailure "incorrect POST body"
+          Nothing -> expectationFailure "req construction failed"
+
+    describe "PostQuery" $ do
+      it "should be able to construct a POST request with no body" $ do
+        case send exampleBuilder (Route ["api.json"] ["query" =. False] "POST") PostQuery of
+          Just req -> do
+            HTTP.secure req `shouldBe` True
+            HTTP.host req `shouldBe` "example.com"
+            HTTP.method req `shouldBe` "POST"
+            HTTP.port req `shouldBe` 443
+            HTTP.queryString req `shouldBe` "?query=false"
+            HTTP.path req `shouldBe` "/api.json"
+            case HTTP.requestBody req of
+              HTTP.RequestBodyLBS "" -> return ()
+              _ -> expectationFailure "incorrect POST body"
+          Nothing -> expectationFailure "req construction failed"
+
+    describe "Value" $ do
+      it "should be able to construct a POST request with the correct JSON body" $ do
+        case send exampleBuilder (Route ["api.json"] ["query" =. False] "POST") $ object ["hello" .= Null] of
+          Just req -> do
+            HTTP.secure req `shouldBe` True
+            HTTP.host req `shouldBe` "example.com"
+            HTTP.method req `shouldBe` "POST"
+            HTTP.port req `shouldBe` 443
+            HTTP.queryString req `shouldBe` "?query=false"
+            HTTP.path req `shouldBe` "/api.json"
+            case HTTP.requestBody req of
+              HTTP.RequestBodyLBS "{\"hello\":null}" -> return ()
+              _ -> expectationFailure "incorrect POST body"
+          Nothing -> expectationFailure "req construction failed"
+
+    describe "ByteString" $ do
+      it "should be able to construct a POST request with the correct ByteString body" $ do
+        case send exampleBuilder (Route ["api.json"] ["query" =. False] "POST") ("hello world" :: ByteString) of
+          Just req -> do
+            HTTP.secure req `shouldBe` True
+            HTTP.host req `shouldBe` "example.com"
+            HTTP.method req `shouldBe` "POST"
+            HTTP.port req `shouldBe` 443
+            HTTP.queryString req `shouldBe` "?query=false"
+            HTTP.path req `shouldBe` "/api.json"
+            case HTTP.requestBody req of
+              HTTP.RequestBodyLBS "hello world" -> return ()
+              _ -> expectationFailure "incorrect POST body"
+          Nothing -> expectationFailure "req construction failed"
+
+exampleBuilder :: Builder
+exampleBuilder = basicBuilder "example" "https://example.com"
