diff --git a/handa-gdata.cabal b/handa-gdata.cabal
--- a/handa-gdata.cabal
+++ b/handa-gdata.cabal
@@ -1,10 +1,11 @@
 name: handa-gdata
-version: 0.6.7
+version: 0.6.9.1
 cabal-version: >=1.6
 
 -- CHANGELOG:
 -- 0.6.2: Improve getCachedTokens
--- 
+-- 0.6.8: Add FusionTables.bulkImportRows
+-- 0.6.9: Add FusionTables.createColumn
 
 build-type: Simple
 license: MIT
@@ -89,7 +90,7 @@
                  , filepath          -any
                  , GenericPretty     >= 1.0.0
                  , HTTP              >= 4000.2.5
-                 , http-conduit      >= 1.9.0
+                 , http-conduit      >= 1.9.0 && < 2.0
                  , json              >= 0.5
                  , old-locale        -any
                  , pretty            -any
@@ -130,7 +131,7 @@
                  , cmdargs           >= 0.9.4
                  , GenericPretty     >= 1.0.0
                  , HTTP              >= 4000.2.5
-                 , http-conduit      >= 1.9.0
+                 , http-conduit      >= 1.9.0 && < 2.0
                  , json              >= 0.5
                  , network           -any
                  , old-locale        -any
diff --git a/src/Network/Google/FusionTables.hs b/src/Network/Google/FusionTables.hs
--- a/src/Network/Google/FusionTables.hs
+++ b/src/Network/Google/FusionTables.hs
@@ -22,17 +22,17 @@
     TableId, TableMetadata(..), ColumnMetadata(..), CellType(..)
     
   -- * One-to-one wrappers around API routines, with parsing of JSON results
-  , createTable
+  , createTable, createColumn
   , listTables, listColumns
 --  , sqlQuery
     
   -- * Higher level interface to common SQL queries    
   , insertRows
     -- , filterRows
-    -- bulkImportRows
+  , bulkImportRows
 ) where
 
-import           Control.Monad (liftM)
+import           Control.Monad (liftM, unless)
 import           Data.Maybe (mapMaybe)
 import           Data.List as L
 import qualified Data.ByteString.Char8 as B
@@ -45,7 +45,7 @@
 -- TODO: Ideally this dependency wouldn't exist here and the user could select their
 -- own JSON parsing lib (e.g. Aeson).
 import           Text.JSON (JSObject(..), JSValue(..), Result(Ok,Error),
-                            decode, valFromObj, toJSObject, toJSString)
+                            decode, valFromObj, toJSObject, toJSString, fromJSString)
 import           Text.JSON.Pretty (pp_value)
 
 -- For easy pretty printing:
@@ -121,7 +121,24 @@
                 , ("type", str$ show colTy) ]
    str = JSString . toJSString
 
+-- | Create a new column in a given table.  Returns the metadata for the new column.
+createColumn :: AccessToken -> TableId -> (FTString,CellType) -> IO ColumnMetadata
+createColumn tok tab_tableId (colName,colTy) =
+  do response <- doRequest req
+     let Ok final = parseColumn response
+     return final
+ where
+   req = appendHeaders [("Content-Type", "application/json")] $
+          appendBody (BL.pack json)
+          (makeRequest tok fusiontableApi "POST"
+            (fusiontableHost, "fusiontables/v1/tables/"++tab_tableId++"/columns" ))
+   json :: String
+   json = render$ pp_value$ JSObject$ toJSObject$
+          [ ("name",str colName)
+          , ("type",str$ show colTy) ]
+   str = JSString . toJSString
 
+
 -- | Designed to mirror the types listed here:
 --   <https://developers.google.com/fusiontables/docs/v1/reference/column>
 data CellType = NUMBER | STRING | LOCATION | DATETIME
@@ -191,7 +208,9 @@
 -- | Insert one or more rows into a table.  Rows are represented as lists of strings.
 -- The columns being written are passed in as a separate list.  The length of all
 -- rows must match eachother and must match the list of column names.
--- 
+--
+-- NOTE: this method has a major limitation.  SQL queries are encoded into the URL,
+-- and it is very easy to exceed the maximum URL length accepted by Google APIs.
 insertRows :: AccessToken -> TableId
               -> [FTString]   -- ^ Which columns to write.
               -> [[FTString]] -- ^ Rows 
@@ -221,19 +240,48 @@
 -- | Implement a larger quantity of rows, but with the caveat that the number and order
 -- of columns must exactly match the schema of the fusion table on the server.
 -- `bulkImportRows` will perform a listing of the columns to verify this before uploading.
+--
+-- This function also checks that the server reports receiving the same number of
+-- rows as were uploaded.
+--
+-- NOTE: also use this function for LONG rows, even if there is only one.  Really,
+-- you should almost always use this function rather thna `insertRows`.
 bulkImportRows :: AccessToken -> TableId
               -> [FTString]   -- ^ Which columns to write.
               -> [[FTString]] -- ^ Rows 
               -> IO ()
 bulkImportRows tok tid cols rows = do 
-  -- listColumns
+  targetSchema <- fmap (map col_name) $ listColumns tok tid
 
-  let csv = "38"
-      req = appendBody (BL.pack csv)
+  unless (targetSchema == cols) $ 
+    error$ "bulkImportRows: upload schema (1) did not match server side schema (2):\n (1) "++
+           show cols ++"\n (2) " ++ show targetSchema
+
+  let csv = unlines rowlns -- (header : rowlns)  -- ARGH, they don't accept the header.
+                           -- All sanity checking must be client side [2013.11.30].
+      -- header = concat$ intersperse "," $ map show cols
+      rowlns = [ concat$ intersperse "," [ "\"" ++f++"\"" | f <- row ]
+               | row <- rows ]
+      req = appendBody (BL.pack csv) $ 
          (makeRequest tok fusiontableApi "POST"
-           (fusiontableHost, "fusiontables/v1/tables/"++tid++"/import" ))
-  
-  error "implement bulkImportRows"
+           (fusiontableHost, "upload/fusiontables/v1/tables/"++tid++"/import" ))
+         {
+           queryString = B.pack $ H.urlEncodeVars [("isStrict", "true")]
+         }
+  resp <- doRequest req
+  let Ok received = parseResponse resp  
+  unless (received == length rows) $
+    error$ "attempted to upload "++show (length rows)++
+           " rows, but "++show received++" received by server."
+  return ()
+
+ where 
+   parseResponse :: JSValue -> Result Int
+   parseResponse (JSObject ob) = do
+   --  JS allTables <- valFromObj "items" ob
+--   JSString "fusiontables#import" <- valFromObj "kind" ob -- Sanity check.
+   JSString num <- valFromObj "numRowsReceived" ob 
+   return$ read$ fromJSString num
 
 
 -- TODO: provide some basic select functionality
