diff --git a/Data/Factual/Query/CrosswalkQuery.hs b/Data/Factual/Query/CrosswalkQuery.hs
--- a/Data/Factual/Query/CrosswalkQuery.hs
+++ b/Data/Factual/Query/CrosswalkQuery.hs
@@ -7,6 +7,7 @@
 
 import Data.Factual.Query
 import Data.Factual.Utils
+import Network.HTTP.Base (urlEncode)
 
 -- | A crosswalk query can be formed by specifying a factual id and
 --   (optionally) a list of namespaces to only include when attempting to find
@@ -32,17 +33,17 @@
 -- The following helper functions are used to generate the separate parts of
 -- the query path.
 factualIdString :: Maybe String -> String
-factualIdString (Just id) = "factual_id=" ++ id
+factualIdString (Just id) = "factual_id=" ++ urlEncode id
 factualIdString Nothing   = ""
 
 namespaceString :: Maybe String -> String
-namespaceString (Just namespace) = "namespace=" ++ namespace
+namespaceString (Just namespace) = "namespace=" ++ urlEncode namespace
 namespaceString Nothing          = ""
 
 namespaceIdString :: Maybe String -> String
-namespaceIdString (Just id) = "namespace_id=" ++ id
+namespaceIdString (Just id) = "namespace_id=" ++ urlEncode id
 namespaceIdString Nothing   = ""
 
 onlyString :: [String] -> String
 onlyString []   = ""
-onlyString strs = "only=" ++ (join "," strs)
+onlyString strs = "only=" ++ urlEncode (join "," strs)
diff --git a/Data/Factual/Query/FacetsQuery.hs b/Data/Factual/Query/FacetsQuery.hs
--- a/Data/Factual/Query/FacetsQuery.hs
+++ b/Data/Factual/Query/FacetsQuery.hs
@@ -16,6 +16,7 @@
 import Data.Factual.Shared.Filter
 import Data.Factual.Shared.Geo
 import Data.Factual.Utils
+import Network.HTTP.Base (urlEncode)
 
 -- | The FacetsQuery type is used to construct facets queries. A table and search
 --   should be specified, but the rest of the query options are essentially
@@ -45,5 +46,5 @@
 
 -- Helper functions
 minCountString :: Maybe Int -> String
-minCountString (Just x) = "min_count=" ++ show x
+minCountString (Just x) = "min_count=" ++ (urlEncode $ show x)
 minCountString Nothing  = ""
diff --git a/Data/Factual/Query/ReadQuery.hs b/Data/Factual/Query/ReadQuery.hs
--- a/Data/Factual/Query/ReadQuery.hs
+++ b/Data/Factual/Query/ReadQuery.hs
@@ -16,6 +16,7 @@
 import Data.Factual.Shared.Filter
 import Data.Factual.Shared.Geo
 import Data.Factual.Utils
+import Network.HTTP.Base (urlEncode)
 
 -- | The ReadQuery type is used to construct read queries. A table should be
 --   specified, but the rest of the query options are essentially optional
@@ -48,5 +49,5 @@
 
 -- The following helper functions are used in generating query Strings.
 offsetString :: Maybe Int -> String
-offsetString (Just x) = "offset=" ++ show x
+offsetString (Just x) = "offset=" ++ (urlEncode $ show x)
 offsetString Nothing = ""
diff --git a/Data/Factual/Query/ResolveQuery.hs b/Data/Factual/Query/ResolveQuery.hs
--- a/Data/Factual/Query/ResolveQuery.hs
+++ b/Data/Factual/Query/ResolveQuery.hs
@@ -9,6 +9,7 @@
 
 import Data.Factual.Query
 import Data.Factual.Utils
+import Network.HTTP.Base (urlEncode)
 
 -- | A resolve value can either be a String or a Number (Double). The first
 --   argument is the name of the field and the second argument is the input
@@ -30,6 +31,5 @@
 -- ResolveQuery is a member of the Query typeclass so that it can be used to
 -- make requests.
 instance Query ResolveQuery where
-  toPath (ResolveQuery values) = "/places/resolve?values={"
-                               ++ (join "," $ map show values)
-                               ++ "}"
+  toPath (ResolveQuery values) = "/places/resolve?values="
+                               ++ urlEncode ("{" ++ (join "," $ map show values) ++ "}")
diff --git a/Data/Factual/Response.hs b/Data/Factual/Response.hs
--- a/Data/Factual/Response.hs
+++ b/Data/Factual/Response.hs
@@ -26,17 +26,20 @@
 -- | A response object has a status (that will be ok if the query was successful
 --   and error if the query failed), a version (which should always be 3.0) and
 --   the actual response data which is an Aeson value.
-data Response = Response { status   :: String
-                         , version  :: Double
-                         , response :: Value
+data Response = Response { status       :: String
+                         , version      :: Double
+                         , response     :: Value
+                         , errorMessage :: Maybe String
+                         , errorType    :: Maybe String
                          } deriving (Eq, Show)
 
 -- | This function is used by the API module to turn the Aeson value returned by
 --   the API into a Response value.
 fromValue :: Value -> Response
-fromValue value = Response { status = lookupString "status" value
-                           , version = lookupNumber "version" value
-                           , response = lookupValue "response" value }
+fromValue value
+  | respStatus == "error" = formErrorResponse value
+  | otherwise             = formValidResponse value
+  where respStatus = lookupString "status" value
 
 -- | This function can be used to convert an Aeson Array value into a vanilla
 --   list.
@@ -65,6 +68,19 @@
 lookupValueSafe key (Object x) = L.lookup (T.pack key) x
 
 -- The following helper functions aid the lookup functions.
+formErrorResponse :: Value -> Response
+formErrorResponse value = Response { status = "error"
+                                   , version = lookupNumber "version" value
+                                   , response = Null
+                                   , errorMessage = Just $ lookupString "message" value
+                                   , errorType = Just $ lookupString "error_type" value }
+
+formValidResponse :: Value -> Response
+formValidResponse value = Response { status = "ok"
+                                   , version = lookupNumber "version" value
+                                   , response = lookupValue "response" value
+                                   , errorMessage = Nothing
+                                   , errorType = Nothing }
 extractString :: Value -> String
 extractString (String x) = T.unpack x
 
diff --git a/Data/Factual/Shared/Filter.hs b/Data/Factual/Shared/Filter.hs
--- a/Data/Factual/Shared/Filter.hs
+++ b/Data/Factual/Shared/Filter.hs
@@ -9,6 +9,7 @@
   ) where
 
 import Data.Factual.Utils
+import Network.HTTP.Base (urlEncode)
 
 -- | A Field is a String representation of the field name.
 type Field = String
@@ -57,4 +58,4 @@
 
 filtersString :: [Filter] -> String
 filtersString [] = ""
-filtersString fs = "filters={" ++ (join "," $ map show fs) ++ "}"
+filtersString fs = "filters=" ++ urlEncode ("{" ++ (join "," $ map show fs) ++ "}")
diff --git a/Data/Factual/Shared/Geo.hs b/Data/Factual/Shared/Geo.hs
--- a/Data/Factual/Shared/Geo.hs
+++ b/Data/Factual/Shared/Geo.hs
@@ -10,6 +10,8 @@
   , geoString
   ) where
 
+import Network.HTTP.Base (urlEncode)
+
 -- | A Lat is the latitude represented as a Double.
 type Lat = Double
 -- | A Long is the longitude represented as a Double.
@@ -41,5 +43,5 @@
 
 -- Helper functions
 geoString :: Maybe Geo -> String
-geoString (Just c) = "geo=" ++ show c
+geoString (Just g) = "geo=" ++ (urlEncode $ show g)
 geoString Nothing = ""
diff --git a/Data/Factual/Shared/Search.hs b/Data/Factual/Shared/Search.hs
--- a/Data/Factual/Shared/Search.hs
+++ b/Data/Factual/Shared/Search.hs
@@ -8,6 +8,7 @@
   ) where
 
 import Data.Factual.Utils
+import Network.HTTP.Base (urlEncode)
 
 -- | This type is used to construct an ANDed or ORed search in a query.
 data Search = AndSearch [String] | OrSearch [String] | NoSearch deriving Eq
@@ -23,4 +24,4 @@
 searchString :: Search -> String
 searchString (AndSearch []) = ""
 searchString (OrSearch []) = ""
-searchString search = "q=" ++ show search
+searchString search = "q=" ++ (urlEncode $ show search)
diff --git a/Data/Factual/Shared/Table.hs b/Data/Factual/Shared/Table.hs
--- a/Data/Factual/Shared/Table.hs
+++ b/Data/Factual/Shared/Table.hs
@@ -6,16 +6,25 @@
      Table(..)
   ) where
 
--- | Three Table values are currently available for the Factual API.
+-- | This type defines the available tables. Use the Custom table option for
+--   tables that are not listed you.
 data Table = Places
            | USRestaurants
            | Global
+           | HealthCareProviders
+           | WorldGeographies
+           | ProductsCPG
+           | ProductsCrosswalk
            | Custom String
            deriving Eq
 
 -- Table is a member of the Show typeclass to generate the beginning of the path.
 instance Show Table where
-  show Places        = "/t/places/"
-  show USRestaurants = "/t/restaurants-us/"
-  show Global        = "/t/global/"
-  show (Custom name) = "/t/" ++ name ++ "/"
+  show Places              = "/t/places/"
+  show USRestaurants       = "/t/restaurants-us/"
+  show Global              = "/t/global/"
+  show HealthCareProviders = "/t/health-care-providers-us"
+  show WorldGeographies    = "/t/world-geographies"
+  show ProductsCPG         = "/t/products-cpg"
+  show ProductsCrosswalk   = "/t/products-crosswalk"
+  show (Custom name)       = "/t/" ++ name ++ "/"
diff --git a/Data/Factual/Utils.hs b/Data/Factual/Utils.hs
--- a/Data/Factual/Utils.hs
+++ b/Data/Factual/Utils.hs
@@ -10,6 +10,7 @@
   ) where
 
 import Data.List (intersperse)
+import Network.HTTP.Base (urlEncode)
 
 -- | The join function joins a list of lists into a list using a separator list.
 --   The most common use case is for joining Strings with a common separator
@@ -25,10 +26,10 @@
 -- The following helper functions are used in generating query Strings.
 selectString :: [String] -> String
 selectString []      = ""
-selectString selects = "select=" ++ (join "," selects)
+selectString selects = "select=" ++ (urlEncode $ (join "," selects))
 
 limitString :: Maybe Int -> String
-limitString (Just x) = "limit=" ++ show x
+limitString (Just x) = "limit=" ++ (urlEncode $ show x)
 limitString Nothing = ""
 
 includeCountString :: Bool -> String
diff --git a/Network/Factual/API.hs b/Network/Factual/API.hs
--- a/Network/Factual/API.hs
+++ b/Network/Factual/API.hs
@@ -7,13 +7,16 @@
     -- * Read functions
   , makeRequest
   , makeRawRequest
+  , makeMultiRequest
     -- * Write functions
   , sendWrite
     -- * The hoauth Token type
   , Token(..)
   ) where
 
+import Network.HTTP.Base (urlEncode)
 import Data.Maybe (fromJust)
+import Data.List (intersperse)
 import Network.OAuth.Consumer
 import Network.OAuth.Http.Request (Request(..), Method(..), parseURL, fromList)
 import Network.OAuth.Http.Response (Response(..))
@@ -21,6 +24,8 @@
 import Data.Aeson (Value, decode)
 import Data.Factual.Query
 import Data.Factual.Write
+import Data.Factual.Utils
+import qualified Data.Map as M
 import qualified Data.ByteString.Lazy.Char8 as B
 import qualified Data.Factual.Response as F
 
@@ -44,24 +49,45 @@
 --   in your Token and the path of your request (e.g. \"\/t\/places?q=starbucks\")
 makeRawRequest :: Token -> String -> IO F.Response
 makeRawRequest token queryString = do
-  let fullpath = basePath ++ queryString
-  let request = generateRequest fullpath
-  makeRequest' token request
+  response <- makeRawRequest' token queryString
+  return $ F.fromValue $ extractJSON response
 
+-- | This function can be used to make multi queries. You pass in a Map of Strings
+--   to queries and a single query is made to the API. The result is a Map of the
+--   same keys to regular response values.
+makeMultiRequest :: (Query query) => Token -> M.Map String query -> IO (M.Map String F.Response)
+makeMultiRequest token mult = do
+  let queryString = multiQueryString $ M.toList mult
+  response <- makeRawRequest' token queryString
+  return $ formMultiResponse response $ M.keys mult
+
 -- | This function takes an OAuth token and a Write and retunrs and IO action
 --   which sends the Write to API and returns a Response.
-sendWrite :: (Write write) => Token -> write -> IO F.Response
+sendWrite :: (Write write) => Token -> write -> IO Response
 sendWrite token write = do
   let fullpath = basePath ++ path write
   let request = generatePostRequest fullpath (body write)
   makeRequest' token request
 
 -- The following helper functions aid the exported API functions
-makeRequest' :: Token -> Request -> IO F.Response
-makeRequest' token request = do
-  response <- runOAuthM token $ setupOAuth request
-  return $ F.fromValue $ extractJSON response
+makeRawRequest' :: Token -> String -> IO Response
+makeRawRequest' token queryString = do
+  let fullpath = basePath ++ queryString
+  let request = generateRequest fullpath
+  makeRequest' token request
 
+makeRequest' :: Token -> Request -> IO Response
+makeRequest' token request = runOAuthM token $ setupOAuth request
+
+multiQueryString :: (Query query) => [(String, query)] -> String
+multiQueryString ps = "/multi?queries=" ++ (urlEncode $ "{" ++ (join "," $ map queryPair ps) ++ "}")
+  where queryPair (n,q) = "\"" ++ n ++ "\":\"" ++ toPath q ++ "\""
+
+formMultiResponse :: Response -> [String] -> M.Map String F.Response
+formMultiResponse res ks = M.fromList $ map formPair ks
+  where formPair k = (k, F.fromValue $ F.lookupValue k json)
+        json       = extractJSON res
+
 generateRequest :: String -> Request
 generateRequest url = (fromJust $ parseURL url) { reqHeaders = (fromList headersList) }
 
@@ -80,7 +106,7 @@
 extractJSON = fromJust . decode . rspPayload
 
 headersList :: [(String, String)]
-headersList = [("X-Factual-Lib", "factual-haskell-driver-0.2.0")]
+headersList = [("X-Factual-Lib", "factual-haskell-driver-0.3.0")]
 
 basePath :: String
 basePath = "http://api.v3.factual.com"
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -6,25 +6,21 @@
 
 ## Prerequisites
 
-This driver was developed on [ghc 7.0.4](http://www.haskell.org/ghc/)
-and [The Haskell Platform 2011.4.0.0](http://hackage.haskell.org/platform/).
+This driver targets [ghc 7.4.1](http://www.haskell.org/ghc/)
+and [The Haskell Platform 2012.2.0.0](http://hackage.haskell.org/platform/).
 Please follow the installation instructions for your specific
 platform to install ghc and The Haskell Platform.
 
+## Installation from cabal
+
+    $ cabal install factual-api
+
 ## Installation from git
 
     $ git clone git@github.com:rudyl313/factual-haskell-driver.git
     $ cabal install hoauth
     $ cabal install aeson
 
-Note you may require libcurl to install hoauth. On lucid you can run:
-
-    $ sudo apt-get install libcurl4-openssl-dev
-
-## Installation from cabal
-
-    $ cabal install factual-api
-
 # Tests
 
 Load the tests file into ghci to run the tests:
@@ -38,13 +34,13 @@
     Cases: 41  Tried: 41  Errors: 0  Failures: 0
     Counts {cases = 41, tried = 41, errors = 0, failures = 0}
     *Main> runIntegrationTests "mykey" "mysecret"
-    Cases: 8  Tried: 8  Errors: 0  Failures: 0
-    Counts {cases = 8, tried = 8, errors = 0, failures = 0}
+    Cases: 10  Tried: 10  Errors: 0  Failures: 0
+    Counts {cases = 10, tried = 10, errors = 0, failures = 0}
 
 # Documentation
 
 You can read library documentation by opening docs/index.html in
-your browser.
+your browser or by visiting the [Hackage page](http://hackage.haskell.org/package/factual-api).
 
 # Examples
 
@@ -57,3 +53,10 @@
 In this example replace mykey with your key and mysecret with your
 secret. Note that compiling the source code generates .o and .hi
 files in the source directories.
+
+# Writes
+
+Although there is code in the current version to support API writes,
+these features have not yet been implemented in the Factual API. Please
+refrain from attempting to use these features until they have been
+officially announced.
diff --git a/examples/CrosswalkExample.hs b/examples/CrosswalkExample.hs
--- a/examples/CrosswalkExample.hs
+++ b/examples/CrosswalkExample.hs
@@ -1,6 +1,6 @@
 module Main where
 
-import System (getArgs)
+import System.Environment (getArgs)
 import Network.Factual.API
 import Data.Factual.Query.CrosswalkQuery
 import Data.Factual.Response
diff --git a/examples/FacetsExample.hs b/examples/FacetsExample.hs
--- a/examples/FacetsExample.hs
+++ b/examples/FacetsExample.hs
@@ -1,6 +1,6 @@
 module Main where
 
-import System (getArgs)
+import System.Environment (getArgs)
 import Network.Factual.API
 import Data.Factual.Query.FacetsQuery
 import Data.Factual.Response
diff --git a/examples/GeocodeExample.hs b/examples/GeocodeExample.hs
--- a/examples/GeocodeExample.hs
+++ b/examples/GeocodeExample.hs
@@ -1,6 +1,6 @@
 module Main where
 
-import System (getArgs)
+import System.Environment (getArgs)
 import Network.Factual.API
 import Data.Factual.Query.GeocodeQuery
 import Data.Factual.Response
diff --git a/examples/GeopulseExample.hs b/examples/GeopulseExample.hs
--- a/examples/GeopulseExample.hs
+++ b/examples/GeopulseExample.hs
@@ -1,6 +1,6 @@
 module Main where
 
-import System (getArgs)
+import System.Environment (getArgs)
 import Network.Factual.API
 import Data.Factual.Query.GeopulseQuery
 import Data.Factual.Response
diff --git a/examples/MultiExample.hs b/examples/MultiExample.hs
new file mode 100644
--- /dev/null
+++ b/examples/MultiExample.hs
@@ -0,0 +1,42 @@
+module Main where
+
+import System.Environment (getArgs)
+import Network.Factual.API
+import Data.Factual.Query.ReadQuery
+import Data.Factual.Response
+import qualified Data.Map as M
+
+main :: IO()
+main = do
+  args <- getArgs
+  let oauthKey = head args
+  let oauthSecret = last args
+  let token = generateToken oauthKey oauthSecret
+  let query1 = ReadQuery { table = Places
+                         , search = AndSearch []
+                         , select = ["name"]
+                         , limit = Just 50
+                         , offset = Nothing
+                         , includeCount = True
+                         , geo = Just (Circle 34.06021 (-118.41828) 5000.0)
+                         , filters = [EqualStr "name" "Stand"] }
+  let query2 = ReadQuery { table = Places
+                         , search = AndSearch []
+                         , select = ["name"]
+                         , limit = Just 50
+                         , offset = Nothing
+                         , includeCount = True
+                         , geo = Just (Circle 34.06021 (-118.41828) 5000.0)
+                         , filters = [EqualStr "name" "Xerox"] }
+  let multiQuery = M.fromList [("query1", query1), ("query2", query2)]
+  multiResult <- makeMultiRequest token multiQuery
+  let result1 = multiResult M.! "query1"
+  let result2 = multiResult M.! "query2"
+  putStrLn "query1:"
+  putStrLn $ "Status: " ++ status result1
+  putStrLn $ "Version: " ++ show (version result1)
+  putStrLn $ "Data: " ++ show (response result1)
+  putStrLn "query2:"
+  putStrLn $ "Status: " ++ status result2
+  putStrLn $ "Version: " ++ show (version result2)
+  putStrLn $ "Data: " ++ show (response result2)
diff --git a/examples/RawReadExample.hs b/examples/RawReadExample.hs
--- a/examples/RawReadExample.hs
+++ b/examples/RawReadExample.hs
@@ -1,6 +1,6 @@
 module Main where
 
-import System (getArgs)
+import System.Environment (getArgs)
 import Network.Factual.API
 import Data.Factual.Response
 
diff --git a/examples/ReadExample.hs b/examples/ReadExample.hs
--- a/examples/ReadExample.hs
+++ b/examples/ReadExample.hs
@@ -1,6 +1,6 @@
 module Main where
 
-import System (getArgs)
+import System.Environment (getArgs)
 import Network.Factual.API
 import Data.Factual.Query.ReadQuery
 import Data.Factual.Response
diff --git a/examples/ResolveExample.hs b/examples/ResolveExample.hs
--- a/examples/ResolveExample.hs
+++ b/examples/ResolveExample.hs
@@ -1,6 +1,6 @@
 module Main where
 
-import System (getArgs)
+import System.Environment (getArgs)
 import Network.Factual.API
 import Data.Factual.Query.ResolveQuery
 import Data.Factual.Response
diff --git a/examples/SchemaExample.hs b/examples/SchemaExample.hs
--- a/examples/SchemaExample.hs
+++ b/examples/SchemaExample.hs
@@ -1,6 +1,6 @@
 module Main where
 
-import System (getArgs)
+import System.Environment (getArgs)
 import Network.Factual.API
 import Data.Factual.Query.SchemaQuery
 import Data.Factual.Response
diff --git a/factual-api.cabal b/factual-api.cabal
--- a/factual-api.cabal
+++ b/factual-api.cabal
@@ -1,5 +1,5 @@
 name:            factual-api
-version:         0.2.0
+version:         0.3.0
 license:         BSD3
 license-file:    LICENSE.txt
 category:        API, Web
@@ -7,7 +7,7 @@
 author:          Rudiger Lippert <rudy@factual.com>
 maintainer:      Rudiger Lippert <rudy@factual.com>
 stability:       stable
-tested-with:     GHC == 7.0.4
+tested-with:     GHC == 7.4.1
 synopsis:        A driver for the Factual API
 cabal-version:   >= 1.8
 homepage:        https://github.com/rudyl313/factual-haskell-driver
@@ -50,7 +50,10 @@
     aeson >= 0.6.0.0,
     attoparsec >= 0.10.1.0,
     base == 4.*,
+    bytestring,
+    containers,
     hoauth >= 0.3.0,
+    HTTP,
     text >= 0.11.1.5,
     unordered-containers >= 0.1.4.6,
     vector >= 0.9.1
diff --git a/test/Tests.hs b/test/Tests.hs
--- a/test/Tests.hs
+++ b/test/Tests.hs
@@ -44,7 +44,7 @@
                      , TestLabel "Is blank filter test" isBlankFilterTest
                      , TestLabel "Is not blank filter test" isNotBlankFilterTest
                      , TestLabel "And filter test" andFilterTest
-                     , TestLabel "Or filter test" andFilterTest
+                     , TestLabel "Or filter test" orFilterTest
                      , TestLabel "Geo test" geoTest
                      , TestLabel "Include count test" includeCountTest
                      , TestLabel "Schema query test" schemaQueryTest
@@ -67,7 +67,9 @@
                                        , TestLabel "Raw read test" (rawIntegrationTest token)
                                        , TestLabel "Facets test" (facetsIntegrationTest token)
                                        , TestLabel "Geopulse test" (geopulseIntegrationTest token)
-                                       , TestLabel "Geocode test" (geocodeIntegrationTest token) ]
+                                       , TestLabel "Geocode test" (geocodeIntegrationTest token)
+                                       , TestLabel "Multi test" (multiIntegrationTest token)
+                                       , TestLabel "Error test" (errorIntegrationTest token) ]
                             where token = generateToken key secret
 
 placeTablePathTest = TestCase (do
@@ -91,27 +93,27 @@
   assertEqual "Correct path for custom table" expected path)
 
 geopulsePathTest = TestCase (do
-  let expected = "/places/geopulse?geo={\"$point\":[34.06021,-118.41828]}&select=commercial_density"
+  let expected = "/places/geopulse?geo=%7B%22%24point%22%3A%5B34.06021%2C-118.41828%5D%7D&select=commercial_density"
   let path = toPath $ G.GeopulseQuery { G.geo = Point 34.06021 (-118.41828) , G.select = ["commercial_density"] }
   assertEqual "Correct path for geopulse" expected path)
 
 geocodePathTest = TestCase (do
-  let expected = "/places/geocode?geo={\"$point\":[34.06021,-118.41828]}"
+  let expected = "/places/geocode?geo=%7B%22%24point%22%3A%5B34.06021%2C-118.41828%5D%7D"
   let path = toPath $ GeocodeQuery $ Point 34.06021 (-118.41828)
   assertEqual "Correct path for geocode" expected path)
 
 andSearchPathTest = TestCase (do
-  let expected = "/t/places/read?q=foo bar&include_count=false"
+  let expected = "/t/places/read?q=foo%20bar&include_count=false"
   let path = toPath $ blankReadQuery { search = AndSearch ["foo", "bar"] }
   assertEqual "Correct path for ANDed search" expected path)
 
 orSearchPathTest = TestCase (do
-  let expected = "/t/places/read?q=foo,bar&include_count=false"
+  let expected = "/t/places/read?q=foo%2Cbar&include_count=false"
   let path = toPath $ blankReadQuery { search = OrSearch ["foo", "bar"] }
   assertEqual "Correct path for ANDed search" expected path)
 
 selectPathTest = TestCase (do
-  let expected = "/t/places/read?select=foo,bar&include_count=false"
+  let expected = "/t/places/read?select=foo%2Cbar&include_count=false"
   let path = toPath $ blankReadQuery { select = ["foo", "bar"] }
   assertEqual "Correct path for select terms" expected path)
 
@@ -126,87 +128,87 @@
   assertEqual "Correct path for offset" expected path)
 
 equalNumFilterTest = TestCase (do
-  let expected = "/t/places/read?filters={\"field\":123.4}&include_count=false"
+  let expected = "/t/places/read?filters=%7B%22field%22%3A123.4%7D&include_count=false"
   let path = toPath $ blankReadQuery { filters = [EqualNum "field" 123.4] }
   assertEqual "Correct path for equal number filter" expected path)
 
 equalStrFilterTest = TestCase (do
-  let expected = "/t/places/read?filters={\"field\":\"value\"}&include_count=false"
+  let expected = "/t/places/read?filters=%7B%22field%22%3A%22value%22%7D&include_count=false"
   let path = toPath $ blankReadQuery { filters = [EqualStr "field" "value"] }
   assertEqual "Correct path for equal string filter" expected path)
 
 notEqualNumFilterTest = TestCase (do
-  let expected = "/t/places/read?filters={\"field\":{\"$neq\":123.4}}&include_count=false"
+  let expected = "/t/places/read?filters=%7B%22field%22%3A%7B%22%24neq%22%3A123.4%7D%7D&include_count=false"
   let path = toPath $ blankReadQuery { filters = [NotEqualNum "field" 123.4] }
   assertEqual "Correct path for not equal number filter" expected path)
 
 notEqualStrFilterTest = TestCase (do
-  let expected = "/t/places/read?filters={\"field\":{\"$neq\":\"value\"}}&include_count=false"
+  let expected = "/t/places/read?filters=%7B%22field%22%3A%7B%22%24neq%22%3A%22value%22%7D%7D&include_count=false"
   let path = toPath $ blankReadQuery { filters = [NotEqualStr "field" "value"] }
   assertEqual "Correct path for not equal string filter" expected path)
 
 inNumListFilterTest = TestCase (do
-  let expected = "/t/places/read?filters={\"field\":{\"$in\":[123.4,5432.1]}}&include_count=false"
+  let expected = "/t/places/read?filters=%7B%22field%22%3A%7B%22%24in%22%3A%5B123.4%2C5432.1%5D%7D%7D&include_count=false"
   let path = toPath $ blankReadQuery { filters = [InNumList "field" [123.4, 5432.1]] }
   assertEqual "Correct path for in number list filter" expected path)
 
 inStrListFilterTest = TestCase (do
-  let expected = "/t/places/read?filters={\"field\":{\"$in\":[\"value\",\"other\"]}}&include_count=false"
+  let expected = "/t/places/read?filters=%7B%22field%22%3A%7B%22%24in%22%3A%5B%22value%22%2C%22other%22%5D%7D%7D&include_count=false"
   let path = toPath $ blankReadQuery { filters = [InStrList "field" ["value","other"]] }
   assertEqual "Correct path for in string list filter" expected path)
 
 notInNumListFilterTest = TestCase (do
-  let expected = "/t/places/read?filters={\"field\":{\"$nin\":[123.4,5432.1]}}&include_count=false"
+  let expected = "/t/places/read?filters=%7B%22field%22%3A%7B%22%24nin%22%3A%5B123.4%2C5432.1%5D%7D%7D&include_count=false"
   let path = toPath $ blankReadQuery { filters = [NotInNumList "field" [123.4, 5432.1]] }
   assertEqual "Correct path for not in number list filter" expected path)
 
 notInStrListFilterTest = TestCase (do
-  let expected = "/t/places/read?filters={\"field\":{\"$nin\":[\"value\",\"other\"]}}&include_count=false"
+  let expected = "/t/places/read?filters=%7B%22field%22%3A%7B%22%24nin%22%3A%5B%22value%22%2C%22other%22%5D%7D%7D&include_count=false"
   let path = toPath $ blankReadQuery { filters = [NotInStrList "field" ["value","other"]] }
   assertEqual "Correct path for not in string list filter" expected path)
 
 beginsWithFilterTest = TestCase (do
-  let expected = "/t/places/read?filters={\"field\":{\"$bw\":\"val\"}}&include_count=false"
+  let expected = "/t/places/read?filters=%7B%22field%22%3A%7B%22%24bw%22%3A%22val%22%7D%7D&include_count=false"
   let path = toPath $ blankReadQuery { filters = [BeginsWith "field" "val"] }
   assertEqual "Correct path for begins with filter" expected path)
 
 notBeginsWithFilterTest = TestCase (do
-  let expected = "/t/places/read?filters={\"field\":{\"$nbw\":\"val\"}}&include_count=false"
+  let expected = "/t/places/read?filters=%7B%22field%22%3A%7B%22%24nbw%22%3A%22val%22%7D%7D&include_count=false"
   let path = toPath $ blankReadQuery { filters = [NotBeginsWith "field" "val"] }
   assertEqual "Correct path for not begins with filter" expected path)
 
 beginsWithAnyFilterTest = TestCase (do
-  let expected = "/t/places/read?filters={\"field\":{\"$bwin\":[\"val\",\"ot\"]}}&include_count=false"
+  let expected = "/t/places/read?filters=%7B%22field%22%3A%7B%22%24bwin%22%3A%5B%22val%22%2C%22ot%22%5D%7D%7D&include_count=false"
   let path = toPath $ blankReadQuery { filters = [BeginsWithAny "field" ["val","ot"]] }
   assertEqual "Correct path for begins with any filter" expected path)
 
 notBeginsWithAnyFilterTest = TestCase (do
-  let expected = "/t/places/read?filters={\"field\":{\"$nbwin\":[\"val\",\"ot\"]}}&include_count=false"
+  let expected = "/t/places/read?filters=%7B%22field%22%3A%7B%22%24nbwin%22%3A%5B%22val%22%2C%22ot%22%5D%7D%7D&include_count=false"
   let path = toPath $ blankReadQuery { filters = [NotBeginsWithAny "field" ["val","ot"]] }
   assertEqual "Correct path for not begins with any filter" expected path)
 
 isBlankFilterTest = TestCase (do
-  let expected = "/t/places/read?filters={\"field\":{\"$blank\":true}}&include_count=false"
+  let expected = "/t/places/read?filters=%7B%22field%22%3A%7B%22%24blank%22%3Atrue%7D%7D&include_count=false"
   let path = toPath $ blankReadQuery { filters = [IsBlank "field"] }
   assertEqual "Correct path for is blank filter" expected path)
 
 isNotBlankFilterTest = TestCase (do
-  let expected = "/t/places/read?filters={\"field\":{\"$blank\":false}}&include_count=false"
+  let expected = "/t/places/read?filters=%7B%22field%22%3A%7B%22%24blank%22%3Afalse%7D%7D&include_count=false"
   let path = toPath $ blankReadQuery { filters = [IsNotBlank "field"] }
   assertEqual "Correct path for is not blank filter" expected path)
 
 andFilterTest = TestCase (do
-  let expected = "/t/places/read?filters={\"$and\":[{\"field1\":{\"$blank\":true}},{\"field2\":{\"$blank\":false}}]}&include_count=false"
+  let expected = "/t/places/read?filters=%7B%22%24and%22%3A%5B%7B%22field1%22%3A%7B%22%24blank%22%3Atrue%7D%7D%2C%7B%22field2%22%3A%7B%22%24blank%22%3Afalse%7D%7D%5D%7D&include_count=false"
   let path = toPath $ blankReadQuery { filters = [And [IsBlank "field1", IsNotBlank "field2"]] }
   assertEqual "Correct path for and filter" expected path)
 
 orFilterTest = TestCase (do
-  let expected = "/t/places/read?filters={\"$or\":[{\"field1\":{\"$blank\":true}},{\"field2\":{\"$blank\":false}}]}&include_count=false"
+  let expected = "/t/places/read?filters=%7B%22%24or%22%3A%5B%7B%22field1%22%3A%7B%22%24blank%22%3Atrue%7D%7D%2C%7B%22field2%22%3A%7B%22%24blank%22%3Afalse%7D%7D%5D%7D&include_count=false"
   let path = toPath $ blankReadQuery { filters = [Or [IsBlank "field1", IsNotBlank "field2"]] }
   assertEqual "Correct path for or filter" expected path)
 
 geoTest = TestCase (do
-  let expected = "/t/places/read?geo={\"$circle\":{\"$center\":[300.1, 200.3],\"$meters\":100.5}}&include_count=false"
+  let expected = "/t/places/read?geo=%7B%22%24circle%22%3A%7B%22%24center%22%3A%5B300.1%2C%20200.3%5D%2C%22%24meters%22%3A100.5%7D%7D&include_count=false"
   let path = toPath $ blankReadQuery { geo = Just (Circle 300.1 200.3 100.5) }
   assertEqual "Correct path for geo" expected path)
 
@@ -221,7 +223,7 @@
   assertEqual "Correct path for a schema query" expected path)
 
 resolveQueryTest = TestCase (do
-  let expected = "/places/resolve?values={\"field1\":\"value1\",\"field2\":32.1}"
+  let expected = "/places/resolve?values=%7B%22field1%22%3A%22value1%22%2C%22field2%22%3A32.1%7D"
   let path = toPath $ ResolveQuery [ResolveStr "field1" "value1", ResolveNum "field2" 32.1]
   assertEqual "Correct path for a resolve query" expected path)
 
@@ -246,14 +248,12 @@
   assertEqual "Correct path for a namespace id" expected path)
 
 onlyTest = TestCase (do
-  let expected = "/places/crosswalk?only=yelp,loopd"
+  let expected = "/places/crosswalk?only=yelp%2Cloopd"
   let path = toPath $ blankCrosswalkQuery { C.only = ["yelp", "loopd"] }
   assertEqual "Correct path for a only" expected path)
 
 facetsTest = TestCase (do
-  let expected = "/t/places/facets?q=starbucks&select=locality,region&"
-               ++ "filters={\"country\":\"US\"}&limit=10&min_count=2&"
-               ++ "include_count=false"
+  let expected = "/t/places/facets?q=starbucks&select=locality%2Cregion&filters=%7B%22country%22%3A%22US%22%7D&limit=10&min_count=2&include_count=false"
   let path = toPath $ F.FacetsQuery { F.table        = Places
                                     , F.search       = AndSearch ["starbucks"]
                                     , F.select       = ["locality", "region"]
@@ -346,6 +346,28 @@
   let query = GeocodeQuery $ Point 34.06021 (-118.41828)
   result <- makeRequest token query
   assertEqual "Valid geopulse query" "ok" (status result))
+
+
+multiIntegrationTest :: Token -> Test
+multiIntegrationTest token = TestCase (do
+  let query1 = ReadQuery { table = Places
+                         , search = AndSearch ["McDonalds", "Burger King"]
+                         , select = ["name"]
+                         , limit = Just 50
+                         , offset = Just 10
+                         , includeCount = True
+                         , geo = Just (Circle 34.06021 (-118.41828) 5000.0)
+                         , filters = [EqualStr "name" "Stand"] }
+  let query2 = query1 { filters = [EqualStr "name" "Xerox"] }
+  results <- makeMultiRequest token $ M.fromList [("query1", query1), ("query2", query2)]
+  let result1 = results M.! "query1"
+  let result2 = results M.! "query2"
+  assertEqual "Valid multi query" ["ok","ok"] [status result1, status result2])
+
+errorIntegrationTest :: Token -> Test
+errorIntegrationTest token = TestCase (do
+  result <- makeRawRequest token "/t/foobarbaz"
+  assertEqual "Invalud read query" "error" (status result))
 
 blankReadQuery :: ReadQuery
 blankReadQuery = ReadQuery { table = Places
