diff --git a/Changelog.md b/Changelog.md
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,3 +1,6 @@
+## 0.1.3
+* Add IO version of `decodeXls` function.
+
 ## 0.1.2
 
 * Upgrade libxls upstream package version from 1.4.0 to 1.5.1
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,5 +1,9 @@
 ## Haskell xls Parsing
 
+[![Hackage](https://img.shields.io/hackage/v/xls.svg?style=flat)](https://hackage.haskell.org/package/xls)
+[![Build Status](https://travis-ci.com/harendra-kumar/xls.svg?branch=master)](https://travis-ci.com/harendra-kumar/xls)
+[![Build status](https://ci.appveyor.com/api/projects/status/nwknaf0gw1p9vqnv?svg=true)](https://ci.appveyor.com/project/harendra-kumar/xls)
+
 `xls` is a Haskell library to parse Microsoft Excel spreadsheet files. It
 parses the xls file format (extension `.xls`) more specifically known as
 `BIFF/Excel 97-2004`.
@@ -7,15 +11,17 @@
 It can be useful for mining data from old Microsoft Excel spreadsheets.
 
 ## API
-Use `decodeXls` to get a streaming Conduit. For example to convert an
-xls file to comma separated csv:
+Use `decodeXlsIO` to get a list of all worksheets. For example to convert all
+worksheets in an xls file to comma separated csv:
 
 ```haskell
+import Data.List (intercalate)
+import Data.Xls (decodeXlsIO)
+
 xlsToCSV :: String -> IO ()
-xlsToCSV file =
-      runResourceT
-    $ decodeXls file
-    $$ CL.mapM_ (liftIO . putStrLn . intercalate ",")
+xlsToCSV file = do
+    worksheets <- decodeXlsIO file
+    mapM_ (mapM_ (putStrLn . intercalate ",")) worksheets
 ```
 
 An `xls2csv` utility is shipped with the package.
diff --git a/bin/xls2csv.hs b/bin/xls2csv.hs
--- a/bin/xls2csv.hs
+++ b/bin/xls2csv.hs
@@ -1,21 +1,16 @@
 #!/usr/bin/env stack
 -- stack --resolver lts runhaskell --package getopt-generics
 
-import           Control.Monad.IO.Class
-import           Control.Monad.Trans.Resource
-import           Data.Conduit
-import           Data.Conduit.List            as CL
-import           Data.List                    (intercalate)
-import           Data.Xls
-import           WithCli
+import Data.List (intercalate)
+import Data.Xls (decodeXlsIO)
+import WithCli (withCli)
 
 -- TODO need to escape the separator and the escaping quotes themselves
 
 xlsToCSV :: String -> IO ()
-xlsToCSV file =
-      runResourceT
-    $ runConduit
-    $ decodeXls file
-    .| CL.mapM_ (liftIO . putStrLn . intercalate ",")
+xlsToCSV file = do
+    worksheets <- decodeXlsIO file
+    mapM_ (mapM_ (putStrLn . intercalate ",")) worksheets
 
+main :: IO ()
 main = withCli xlsToCSV
diff --git a/lib/Data/Xls.hs b/lib/Data/Xls.hs
--- a/lib/Data/Xls.hs
+++ b/lib/Data/Xls.hs
@@ -19,15 +19,21 @@
 {-# LANGUAGE DeriveDataTypeable #-}
 #endif
 
-module Data.Xls (decodeXls, XlsException(..)) where
+module Data.Xls
+    ( decodeXlsIO
+    , decodeXls
+    , XlsException(..)
+    )
+where
 
-import           Control.Exception            (Exception, throwIO)
+import           Control.Exception (Exception, throwIO, bracket)
 import           Control.Monad.IO.Class
+import           Control.Monad (when, void)
 import           Control.Monad.Trans.Resource
-import           Data.Conduit                 hiding (Conduit, Sink, Source)
+import           Data.Conduit hiding (Conduit, Sink, Source)
 import           Data.Data
 import           Data.Int
-import           Data.Maybe                   (catMaybes, fromJust, isJust)
+import           Data.Maybe (catMaybes, fromJust, isJust, fromMaybe)
 import           Foreign.C
 import           Foreign.Ptr
 import           Text.Printf
@@ -80,8 +86,9 @@
 -- rows in a worksheet. Each row represented by a list of Strings, each String
 -- representing an individual cell.
 --
--- Currently there is no separation of worksheets, all worksheets in a
--- workbook get concatenated.
+-- Important Note: This API concatenates rows from all worksheets into a single
+-- stream. Please use the non-streaming 'decodeXlsIO' API to get individual
+-- worksheets.
 --
 -- Throws 'XlsException'
 --
@@ -104,6 +111,27 @@
             count <- liftIO $ c_xls_wb_sheetcount pWB
             mapM_ (decodeOneWorkSheet file pWB) [0 .. count - 1]
 
+
+-- | Parse a Microsoft excel xls workbook file into a list of worksheets, each
+-- worksheet consists of a list of rows and each row consists of a list of
+-- cells.  Cells are plain 'String'.
+--
+-- Throws 'XlsException'
+--
+decodeXlsIO
+    :: FilePath
+    -> IO [[[String]]]
+decodeXlsIO file = do
+    file' <- newCString file
+    pWB <- newCString "UTF-8" >>= c_xls_open file'
+    when (pWB == nullPtr) $
+        throwIO $ XlsFileNotFound
+                $ "XLS file " ++ file ++ " not found."
+    count <- liftIO $ c_xls_wb_sheetcount pWB
+    results <- mapM (decodeOneWorkSheetIO file pWB) [0 .. count - 1]
+    void $ c_xls_close_WB pWB
+    return results
+
 decodeOneWorkSheet
     :: MonadResource m
     => FilePath -> XLSWorkbook -> CInt -> ConduitM i [String] m ()
@@ -123,19 +151,57 @@
 
         decodeWS = decodeRows
 
+decodeOneWorkSheetIO
+    :: FilePath
+    -> XLSWorkbook
+    -> CInt
+    -> IO [[String]]
+decodeOneWorkSheetIO file pWB index =
+    bracket alloc cleanup decodeRowsIO
+    where
+        alloc = do
+            pWS <- c_xls_getWorkSheet pWB index
+            if pWS == nullPtr then
+                throwIO $ XlsParseError
+                        $ "XLS file "
+                        ++ file
+                        ++ " could not be parsed."
+            else do
+              c_xls_parseWorkSheet pWS
+              return pWS
+        cleanup = c_xls_close_WS
+
 decodeRows :: MonadResource m => XLSWorksheet -> ConduitM i [String] m ()
 decodeRows pWS = do
     rows <- liftIO $ c_xls_ws_rowcount pWS
     cols <- liftIO $ c_xls_ws_colcount pWS
     mapM_ (decodeOneRow pWS cols) [r | r <- [0 .. rows - 1]]
 
+decodeRowsIO
+    :: XLSWorksheet
+    -> IO [[String]]
+decodeRowsIO pWS = do
+    rows <- c_xls_ws_rowcount pWS
+    cols <- c_xls_ws_colcount pWS
+    mapM (decodeOneRowIO pWS cols) [r | r <- [0 .. rows - 1]]
+
 decodeOneRow
     :: MonadResource m
     => XLSWorksheet -> Int16 -> Int16 -> ConduitM i [String] m ()
 decodeOneRow pWS cols rowindex =
     mapM (liftIO . (c_xls_cell pWS rowindex)) [0 .. cols - 1]
-        >>= mapM (liftIO. decodeOneCell)
+        >>= mapM (liftIO . decodeOneCell)
         >>= yield . catMaybes
+
+decodeOneRowIO
+    :: XLSWorksheet
+    -> Int16
+    -> Int16
+    -> IO [String]
+decodeOneRowIO pWS cols rowindex =
+    mapM (c_xls_cell pWS rowindex) [0 .. cols - 1]
+        >>= mapM decodeOneCell
+        >>= pure . (map $ fromMaybe "")
 
 data CellType = Numerical | Formula | Str | Other
 
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -1,2 +1,11 @@
+import           Data.Xls
+import           Test.Hspec
+
 main :: IO ()
-main = putStrLn "Test suite not yet implemented"
+main = hspec $ describe "Sanity check" $ do
+    it "Test file parsing" $ do
+        content <- decodeXlsIO "test/data/test.xls"
+        content `shouldBe` testFileContent
+
+testFileContent :: [[[String]]]
+testFileContent = [[["1.000000000000000","2.3","text"]],[["1.000000000000000","2.3","text"]]]
diff --git a/test/data/test.xls b/test/data/test.xls
new file mode 100644
Binary files /dev/null and b/test/data/test.xls differ
diff --git a/xls.cabal b/xls.cabal
--- a/xls.cabal
+++ b/xls.cabal
@@ -1,5 +1,5 @@
 name:                xls
-version:             0.1.2
+version:             0.1.3
 synopsis:            Parse Microsoft Excel xls files (BIFF/Excel 97-2004)
 description:
  Parse Microsoft Excel spreadsheet files in @.xls@ file format
@@ -29,6 +29,7 @@
     lib/libxls/include/*.h
     lib/libxls/include/libxls/*.h
     lib/libxls/include/libxls/*.c.h
+    test/data/test.xls
 
 flag force-has-iconv
   description: force using iconv library on Windows
@@ -82,6 +83,7 @@
   main-is:             Spec.hs
   build-depends:        base >= 4.7 && < 5
                       , xls
+                      , hspec
   ghc-options:         -threaded -rtsopts -with-rtsopts=-N
 
 source-repository head
