diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -8,6 +8,6 @@
 ...
 - location:
     git: https://github.com/ooblahman/airtable-api.git
-    commit: 6895cbbc4f83069a8455fcc00f6e313d867ca296
+    commit: HEAD
   extra-dep: true
 ```
diff --git a/airtable-api.cabal b/airtable-api.cabal
--- a/airtable-api.cabal
+++ b/airtable-api.cabal
@@ -1,5 +1,5 @@
 name:                airtable-api
-version:             0.2.1.1
+version:             0.3.2.2
 synopsis:            Requesting and introspecting Tables within an Airtable project.
 -- description:
 homepage:            https://github.com/ooblahman/airtable-api
diff --git a/src/Airtable/Query.hs b/src/Airtable/Query.hs
--- a/src/Airtable/Query.hs
+++ b/src/Airtable/Query.hs
@@ -5,13 +5,16 @@
       -- * Configuration for Airtable requests.
     , AirtableOptions(..)
     , defaultAirtableOptions
-    , airtableOptionsToWreqOptions
+    , mkWreqOptions
     , tableNameToUrl
       -- * API
     , getRecords
+    , getRecordsFromUrl
     , createRecord
     , updateRecord
     , deleteRecord
+    -- * Low-level helpers
+    , respJsonBody
     ) where
 
 
@@ -54,8 +57,8 @@
 base_url = "https://api.airtable.com/"
 
 -- | Produce Wreq options from 'AirtableOptions'
-airtableOptionsToWreqOptions :: AirtableOptions -> Options
-airtableOptionsToWreqOptions opts = 
+mkWreqOptions :: AirtableOptions -> Options
+mkWreqOptions opts = 
   defaults & header "Authorization" .~ ["Bearer " <> BC.pack (apiKey opts)]
 
 -- | Produce a request URL to a table.
@@ -71,28 +74,21 @@
 
 -- * API
 
-fromResp :: (HasCallStack, FromJSON a) => Response ByteString -> a
-fromResp r = decoder $ r ^. responseBody
-  where
-    decoder b = case eitherDecode b of
-      Left e  -> error e
-      Right r -> r
-
 -- | Retrieve the records for a table from airtable.com given its name. Handles pagination correctly.
 getRecords :: (HasCallStack, FromJSON a) => AirtableOptions -> TableName -> IO (Table a)
 getRecords opts tname = 
-  getRecordsFromUrl (airtableOptionsToWreqOptions opts) (tableNameToUrl opts tname)
+  getRecordsFromUrl (mkWreqOptions opts) (tableNameToUrl opts tname)
 
--- | Retrieve the records for a table given a URL and network options.
+-- | Retrieve the records for a table given a URL and network (Wreq) options.
 getRecordsFromUrl :: (HasCallStack, FromJSON a) => Options -> String -> IO (Table a)
 getRecordsFromUrl opts url = do
   resp <- getWith opts url
-  getMore (fromResp resp)
+  getMore (respJsonBody resp)
   where
     getMore tbl = case tableOffset tbl of
       Just offset -> do
         resp <- getWith (opts & param "offset" .~ [offset]) url
-        getMore $ fromResp resp <> tbl
+        getMore $ respJsonBody resp <> tbl
       Nothing ->
         pure tbl
 
@@ -100,10 +96,10 @@
 createRecord :: (HasCallStack, ToJSON a, FromJSON a) => AirtableOptions -> TableName -> a -> IO (Record a)
 createRecord opts tname a = do
   resp <- postWith netOpts url payload
-  return $ fromResp resp
+  return $ respJsonBody resp
   where
     url = tableNameToUrl opts tname
-    netOpts = airtableOptionsToWreqOptions opts 
+    netOpts = mkWreqOptions opts 
     payload = toJSON a
 
 -- | Update a record on a given table, using the supplied fields ('a').
@@ -113,7 +109,7 @@
     customPayloadMethodWith "PATCH" netOpts url payload
   where
     url = tableNameToUrl opts tname <> "/" <> rec2str recId
-    netOpts = airtableOptionsToWreqOptions opts
+    netOpts = mkWreqOptions opts
     payload = toJSON a
 
 -- | Delete a record on a table. 
@@ -121,6 +117,14 @@
 deleteRecord opts tname recId = 
   void $ deleteWith netOpts url 
   where
-    netOpts = airtableOptionsToWreqOptions opts
+    netOpts = mkWreqOptions opts
     url = tableNameToUrl opts tname <> "/" <> rec2str recId
 
+-- * Low-level helpers
+
+respJsonBody :: (HasCallStack, FromJSON a) => Response ByteString -> a
+respJsonBody r = decoder $ r ^. responseBody
+  where
+    decoder b = case eitherDecode b of
+      Left e  -> error e
+      Right r -> r
diff --git a/src/Airtable/Table.hs b/src/Airtable/Table.hs
--- a/src/Airtable/Table.hs
+++ b/src/Airtable/Table.hs
@@ -12,13 +12,11 @@
     -- * RecordID
     , RecordID(..)
     , rec2str
-    -- * IsRecord class
-    , IsRecord(..)
+    -- * HasRecordId class
+    , HasRecordId(..)
     -- * Table
     , Table(..)
     , TableName
-    , parseRecord
-    , parseFields
     -- * Table methods
     , toList
     , exists
@@ -53,6 +51,7 @@
 
 -- | Airtable's record ID for use in indexing records
 newtype RecordID = RecordID Text deriving ( FromJSON
+                                          , ToJSON
                                           , Show
                                           , Read
                                           , Eq
@@ -65,22 +64,32 @@
 rec2str :: RecordID -> String
 rec2str (RecordID rec) = T.unpack rec
 
--- * IsRecord class
+-- * HasRecordId class
 
 -- | A convenience typeclass for selecting records using RecordID-like keys.
-class IsRecord a where
+class HasRecordId a where
   toRecId :: a -> RecordID
 
-instance IsRecord RecordID where
+instance HasRecordId RecordID where
   toRecId = id
 
-instance IsRecord String where
+instance HasRecordId String where
   toRecId = RecordID . T.pack
 
 -- * Table
 
 -- | An airtable record.
-data Record a = Record { recordId :: RecordID, recordObj :: a, createdTime :: UTCTime }
+data Record a = Record 
+  { recordId :: RecordID
+  , recordObj :: a
+  , createdTime :: UTCTime 
+  } deriving ( Show
+             , Read
+             , Generic
+             , Eq
+             , Ord
+             , Functor
+             )
 
 instance (FromJSON a) => FromJSON (Record a) where
   parseJSON = withObject "record object" $ \v -> do
@@ -88,19 +97,26 @@
            <*> v .: "fields"
            <*> v .: "createdTime"
 
-instance IsRecord (Record a) where
+instance (ToJSON a) => ToJSON (Record a) where
+  toJSON rec = object [ "id" .= recordId rec
+                      , "fields" .= recordObj rec
+                      , "createdTime" .= createdTime rec
+                      ]
+
+instance HasRecordId (Record a) where
   toRecId = recordId
 
--- | Airtable's table type
-data Table a = Table { tableRecords :: Map.HashMap RecordID a
-                     , tableOffset :: Maybe Text
-                     } deriving
-                     ( Show
-                     , Read
-                     , Eq
-                     , Generic
-                     , Functor
-                     )
+-- | An airtable table. 
+data Table a = Table 
+  { tableRecords :: Map.HashMap RecordID (Record a) -- ^ A mapping from RecordID to Record
+  , tableOffset :: Maybe Text -- ^ The "offset" parameter for the table. Is used to deal with airtable's pagination.
+  } deriving
+  ( Show
+  , Read
+  , Eq
+  , Generic
+  , Functor
+  )
 
 -- | Synonym used in querying tables from the API.
 type TableName = String
@@ -112,22 +128,9 @@
     offset <- v .:? "offset"
     return $ Table parsedRecs offset
     where
-      parseRec tbl = withObject "record object" $ \v -> 
-            do  recId <- v .: "id"
-                fields <- v .: "fields"
-                obj <- parseJSON fields
-                return $ Map.insert recId obj tbl
-
-parseRecord :: (UTCTime -> RecordID -> Value -> Parser a) -> Value -> Parser a
-parseRecord action = withObject "record object" $ \v -> do
-    created  <- v .: "createdTime"
-    recordId <- v .: "id"
-    fields   <- v .: "fields"
-    action created recordId fields
-
-parseFields :: (Value -> Parser a) -> Value -> Parser a
-parseFields action (Object v) = v .: "fields" >>= action
-parseFields _action invalid = typeMismatch "parseFields_" invalid
+      parseRec tbl v = do  
+        rec <- parseJSON v
+        return $ Map.insert (recordId rec) rec tbl
 
 instance Monoid (Table a) where
   mempty = Table mempty Nothing
@@ -136,16 +139,16 @@
 -- * Table methods
 
 -- | Convert a 'Table' to a list of key-record pairs.
-toList :: Table a -> [(RecordID, a)]
+toList :: Table a -> [(RecordID, Record a)]
 toList = Map.toList . tableRecords
 
 -- | Check if a record exists at the given key in a table.
-exists :: (IsRecord r) => Table a -> r -> Bool
+exists :: (HasRecordId r) => Table a -> r -> Bool
 exists tbl rec = Map.member (toRecId rec) (tableRecords tbl)
 
 -- | Unsafely lookup a record using its RecordID. Will throw a pretty-printed error
 --   if record does not exist.
-select :: (HasCallStack, IsRecord r, Show a) => Table a -> r -> a
+select :: (HasCallStack, HasRecordId r, Show a) => Table a -> r -> Record a
 select tbl rec = tableRecords tbl `lookup` toRecId rec
   where
     lookup mp k = case Map.lookup k mp of
@@ -153,11 +156,11 @@
       Nothing -> error $ "lookup failed in map: " <> show k
 
 -- | Safely lookup a record using its RecordID.
-selectMaybe :: (IsRecord r) => Table a -> r -> Maybe a
+selectMaybe :: (HasRecordId r) => Table a -> r -> Maybe (Record a)
 selectMaybe tbl rec = toRecId rec `Map.lookup` tableRecords tbl
 
 -- | Read all records.
-selectAll :: Table a -> [a]
+selectAll :: Table a -> [Record a]
 selectAll = map snd . toList
 
 -- | Read all RecordID's.
@@ -165,13 +168,13 @@
 selectAllKeys = map fst . toList
 
 -- | Select all records satisfying a condition.
-selectWhere :: Table a -> (RecordID -> a -> Bool) -> [a]
-selectWhere tbl f = map snd $ filter (uncurry f) (toList tbl)
+selectWhere :: Table a -> (Record a -> Bool) -> [Record a]
+selectWhere tbl f = filter f (selectAll tbl)
 
 -- | Select all RecordID's satisfying a condition.
-selectKeyWhere :: Table a -> (RecordID -> a -> Bool) -> [RecordID]
-selectKeyWhere tbl f = map fst $ filter (uncurry f) (toList tbl)
+selectKeyWhere :: Table a -> (Record a -> Bool) -> [RecordID]
+selectKeyWhere tbl f = map recordId $ filter f (selectAll tbl)
 
 -- | Delete all Records satisfying a condition.
-deleteWhere :: Table a -> (RecordID -> a -> Bool) -> Table a
-deleteWhere (Table recs off) f = Table (Map.filterWithKey (\k v -> not $ f k v) recs) off
+deleteWhere :: Table a -> (Record a -> Bool) -> Table a
+deleteWhere (Table recs off) f = Table (Map.filter (\v -> not $ f v) recs) off
