diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,11 @@
+## v1.0.0
+
+Endpoints supported:
+* List companies
+* Company information
+* Statements
+  * Balance sheet
+  * Cash flow
+  * Profit and loss
+  * Derived figures
+* Share prices
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2022 Owen Shepherd
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/examples/EarningsPerShare.hs b/examples/EarningsPerShare.hs
new file mode 100644
--- /dev/null
+++ b/examples/EarningsPerShare.hs
@@ -0,0 +1,81 @@
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module Main
+  ( main
+  ) where
+
+import Control.Arrow ((&&&))
+import Control.Monad
+import Data.Time.Clock
+import Data.Time.Calendar
+import Data.Time.LocalTime
+import Data.List.NonEmpty (NonEmpty(..))
+import qualified Data.List.NonEmpty as NE
+import qualified Data.Map as M
+import Data.Maybe
+import qualified Data.Multimap as MM
+import Data.Either
+import qualified Data.Text as T
+import Graphics.Rendering.Chart.Easy hiding (close)
+import Graphics.Rendering.Chart.Backend.Diagrams
+
+import SimFin.Free
+import qualified SimFin.Types.Derived as D
+
+type LinePt = (LocalTime, Double)
+type LineData = (String, NonEmpty LinePt)
+
+to5pm :: Day -> LocalTime
+to5pm = flip LocalTime $ dayFractionToTimeOfDay $ 17 / 24
+
+toPoint :: DerivedRow Double -> Maybe LinePt
+toPoint row = do
+  eps <- earningsPerShareDiluted row
+  let day = reportDate (row :: DerivedRow Double)
+  pure (to5pm day, eps)
+
+stocks :: [StockRef]
+stocks = ["GOOG", "AAPL", "TWTR", "NFLX"]
+
+-- Caution! This can blow through API limits.
+stockQueries :: Integer -> [StatementQueryFree]
+stockQueries year = do
+  stock <- stocks
+  year <- [2016..fromInteger year]
+  period <- [Q1, Q2, Q3, Q4]
+  pure $ StatementQueryFree
+    { stockRef = stock
+    , period = period
+    , year = year
+    , ttm = False
+    }
+
+groupOn' :: Ord b => (a -> b) -> [a] -> [(b, NonEmpty a)]
+groupOn' f lst = M.toList $ MM.toMap $ MM.fromList $ (f &&& id) <$> lst
+
+toLines :: [DerivedRow Double] -> [LineData]
+toLines rows = do
+  (ticker, rows') <- groupOn' D.ticker rows
+  pts <- maybeToList $ traverse toPoint rows'
+  pure (T.unpack ticker, pts)
+
+main :: IO ()
+main = do
+  now <- getCurrentTime
+  let (year, _, _) = toGregorian $ utctDay now
+  ctx <- createDefaultContext
+
+  pricesRes :: [ApiResult (Maybe (DerivedRow Double))] <- traverse (fetchDerived ctx) $ stockQueries year
+  let prices :: [LineData] = toLines $ catMaybes $ rights pricesRes
+
+  toFile def "eps-diluted.svg" $ do
+    layout_title .= "Diluted Earnings per Share"
+    layout_background .= solidFillStyle (opaque white)
+    layout_foreground .= opaque black
+    layout_left_axis_visibility . axis_show_ticks .= False
+    layout_x_axis . laxis_title .= "Date"
+    layout_y_axis . laxis_title .= "Diluted EPS ($)"
+    forM_ prices $ \(name, pts) -> plot $ line name [NE.toList pts]
+  pure ()
diff --git a/examples/Prices.hs b/examples/Prices.hs
new file mode 100644
--- /dev/null
+++ b/examples/Prices.hs
@@ -0,0 +1,55 @@
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module Main
+  ( main
+  ) where
+
+import Control.Monad
+import Data.Time.Calendar
+import Data.Time.LocalTime
+import Data.Maybe
+import Data.Either
+import qualified Data.Text as T
+import Graphics.Rendering.Chart.Easy hiding (close)
+import Graphics.Rendering.Chart.Backend.Diagrams
+
+import SimFin.Free
+
+to5pm :: Day -> LocalTime
+to5pm = flip LocalTime $ dayFractionToTimeOfDay $ 17 / 24
+
+toDataPoint :: PricesRow Double -> Maybe (LocalTime, Double)
+toDataPoint row = do
+  day <- date row
+  pure (to5pm day, close row)
+
+stonks :: [StockRef]
+stonks = ["GOOG", "AAPL", "TWTR", "NFLX"]
+
+type LineData = (String, [(LocalTime, Double)])
+
+toLine :: [PricesRow Double] -> Maybe LineData
+toLine [] = Nothing
+toLine pts@(PricesRow{ticker = t} : _) = Just
+  ( T.unpack t
+  , catMaybes $ toDataPoint <$> pts
+  )
+
+main :: IO ()
+main = do
+  ctx <- createDefaultContext
+
+  pricesRes :: [ApiResult [PricesRow Double]] <- traverse (fetchPrices ctx) stonks
+  let prices :: [LineData] = catMaybes $ toLine <$> rights pricesRes
+
+  toFile def "prices.svg" $ do
+    layout_title .= "Price History"
+    layout_background .= solidFillStyle (opaque white)
+    layout_foreground .= opaque black
+    layout_left_axis_visibility . axis_show_ticks .= False
+    layout_x_axis . laxis_title .= "Date"
+    layout_y_axis . laxis_title .= "Price ($)"
+    forM_ prices $ \(name, pts) -> plot $ line name [pts]
+  pure ()
diff --git a/examples/RelativePerformance.hs b/examples/RelativePerformance.hs
new file mode 100644
--- /dev/null
+++ b/examples/RelativePerformance.hs
@@ -0,0 +1,80 @@
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TupleSections #-}
+
+module Main
+  ( main
+  ) where
+
+import Control.Arrow (first, second)
+import Control.Monad
+import Data.Time.Calendar
+import Data.Time.LocalTime
+import Data.List.NonEmpty (NonEmpty(..))
+import qualified Data.List.NonEmpty as NE
+import Data.Maybe
+import Data.Either
+import Data.Text (Text)
+import qualified Data.Text as T
+import Graphics.Rendering.Chart.Easy hiding (close)
+import Graphics.Rendering.Chart.Backend.Diagrams
+
+import SimFin.Free
+
+type LinePt = (LocalTime, Double)
+
+toPoint :: PricesRow Double -> Maybe LinePt
+toPoint row = do
+  day <- date row
+  pure (to5pm day, close row)
+
+stonks :: [StockRef]
+stonks = ["GOOG", "AAPL", "TWTR", "NFLX"]
+
+type LineData = (String, NonEmpty LinePt)
+
+to5pm :: Day -> LocalTime
+to5pm = flip LocalTime $ dayFractionToTimeOfDay $ 17 / 24
+
+ptsAfter :: LocalTime -> NonEmpty LinePt -> Maybe (NonEmpty LinePt)
+ptsAfter t = NE.nonEmpty . filter ((>= t) . fst) . NE.toList
+
+toLine :: [PricesRow Double] -> Maybe (Text, NonEmpty LinePt)
+toLine [] = Nothing
+toLine pts@(PricesRow{ticker = t} : _) = (t,) <$> NE.nonEmpty (catMaybes $ toPoint <$> pts)
+
+getStart :: LineData -> LocalTime
+getStart (_, (s, _) :| _) = s
+
+lastStart :: [LineData] -> LocalTime
+lastStart = maximum . fmap getStart
+
+afterCommonStart :: [LineData] -> [LineData]
+afterCommonStart ls = catMaybes $ traverse (ptsAfter $ lastStart ls) <$> ls
+
+scaleToRelative :: NonEmpty LinePt -> NonEmpty LinePt
+scaleToRelative xs@((_, x) :| _) = second ((/ x) . (* 100)) <$> xs
+
+toLines :: [[PricesRow Double]] -> [LineData]
+toLines pts = fmap (second scaleToRelative)
+  $ afterCommonStart
+  $ first T.unpack
+  <$> catMaybes (toLine <$> pts)
+
+main :: IO ()
+main = do
+  ctx <- createDefaultContext
+
+  pricesRes :: [ApiResult [PricesRow Double]] <- traverse (fetchPrices ctx) stonks
+  let prices :: [LineData] = toLines $ rights pricesRes
+
+  toFile def "relative-performance.svg" $ do
+    layout_title .= "Relative Performance"
+    layout_background .= solidFillStyle (opaque white)
+    layout_foreground .= opaque black
+    layout_left_axis_visibility . axis_show_ticks .= False
+    layout_x_axis . laxis_title .= "Date"
+    layout_y_axis . laxis_title .= "Performance (relative to start) (%)"
+    forM_ prices $ \(name, pts) -> plot $ line name [NE.toList pts]
+  pure ()
diff --git a/simfin.cabal b/simfin.cabal
new file mode 100644
--- /dev/null
+++ b/simfin.cabal
@@ -0,0 +1,92 @@
+cabal-version:      2.4
+name:               simfin
+version:            1.0.0
+synopsis:           A library to fetch and parse financial data from the SimFin(+) API.
+homepage:           https://github.com/414owen/simfin
+license:            MIT
+license-file:       LICENSE
+author:             Owen Shepherd
+maintainer:         owen@owen.cafe
+category:           Web, Finance
+extra-source-files: CHANGELOG.md
+description:
+
+    This library aims to wrap the [SimFin(+)](https://simfin.com/) API
+    as completely as possible. SimFin provides fundamental financial data.
+
+library
+    exposed-modules:  SimFin.Plus
+                    , SimFin.Free
+                    , SimFin.Common
+                    , SimFin.Types.BalanceSheet
+                    , SimFin.Types.CashFlow
+                    , SimFin.Types.CompanyInfo
+                    , SimFin.Types.CompanyListing
+                    , SimFin.Types.Derived
+                    , SimFin.Types.FiscalPeriod
+                    , SimFin.Types.Industry
+                    , SimFin.Types.Prices
+                    , SimFin.Types.PricesAndRatios
+                    , SimFin.Types.PricesQuery
+                    , SimFin.Types.ProfitAndLoss
+                    , SimFin.Types.Ratios
+                    , SimFin.Types.StatementQuery
+                    , SimFin.Types.StockRef
+                    , SimFin.Types.StringFrac
+                    , SimFin.Util
+    other-modules:    SimFin.Internal
+    build-depends:    base >= 4.13.0.0 && < 5
+                    , composition-extra
+                    , http-client-tls
+                    , http-client
+                    , exceptions
+                    , bytestring
+                    , http-types
+                    , utf8-string
+                    , aeson >=1.5.6.0 && < 2.1
+                    , text
+                    , time
+                    , unordered-containers
+    hs-source-dirs:   src
+    default-language: Haskell2010
+    ghc-options:      -Wall
+
+test-suite simfin-test
+    default-language: Haskell2010
+    type:             exitcode-stdio-1.0
+    hs-source-dirs:   test
+    main-is:          Test.hs
+    build-depends:    base >= 4.13.0.0 && < 5
+                    , simfin
+                    , aeson
+                    , text
+                    , tasty
+                    , tasty-hunit
+
+common examples-common
+    default-language: Haskell2010
+    hs-source-dirs:   examples
+    ghc-options:      -Wall
+    build-depends:    base >= 4.13.0.0 && < 5
+                    , simfin
+                    , time
+                    , Chart >= 1.9.3 && < 1.9.4
+                    , SVGFonts < 1.8
+                    , Chart-diagrams >= 1.9.3 && < 1.9.4
+                    , text
+                    , simfin
+
+executable graph-prices
+    import:           examples-common
+    main-is:          Prices.hs
+
+executable graph-eps
+    import:           examples-common
+    main-is:          EarningsPerShare.hs
+    build-depends:    containers
+                    , multi-containers
+
+executable graph-relative-performance
+    import:           examples-common
+    main-is:          RelativePerformance.hs
+                    
diff --git a/src/SimFin/Common.hs b/src/SimFin/Common.hs
new file mode 100644
--- /dev/null
+++ b/src/SimFin/Common.hs
@@ -0,0 +1,81 @@
+{-|
+Module      : SimFin.Types.Common
+Description : Types and functions common to the free and paid APIs.
+Copyright   : (c) Owen Shepherd, 2022
+License     : MIT
+Maintainer  : owen@owen.cafe
+-}
+
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE LambdaCase #-}
+
+module SimFin.Common
+  ( ApiError(..)
+  , ApiResult
+  , fetchCompanyList
+  , performRequest
+  ) where
+
+import Control.Monad.Catch
+import Control.Monad.IO.Class
+import Data.Aeson
+import Data.ByteString (ByteString)
+import qualified Data.ByteString.Lazy as LBS
+import Data.Text (Text)
+import qualified Data.Text as T
+
+import SimFin.Internal
+import SimFin.Types.CompanyListing
+
+-- | Represents all the types of error the server returns, and that we can encounter
+-- on our side.
+
+data ApiError
+  -- | Can't turn ByteString into JSON
+  = DecodeError LBS.ByteString String
+  -- | Can't turn JSON into result type
+  | ParseError Value String
+  -- | Server returned '{"error": "..."}' along with a non-200 status code.
+  -- This could in theory be parsed into machine-readable format,
+  -- with variants such as `InvalidApiKey | RateLimited | ...`, but 
+  -- the API doesn't guarantee error message stability.
+  | Other Text
+
+instance Show ApiError where
+  show (DecodeError _ err) = "Couldn't decode reponse body. Err: " <> err
+  show (ParseError _ err) = "Couldn't parse JSON value. Err: " <> err
+  show (Other err) = "Server returned error: " <> T.unpack err
+
+-- | The result of calling fetch* is either an error or a successful result.
+
+type ApiResult = Either ApiError
+
+instance FromJSON ApiError where
+  parseJSON = withObject "Root" $ \v -> Other <$> v .: "error"
+
+-- | Make a request, all fetch* functions call this.
+
+performRequest
+  :: ( MonadIO m
+     , FromJSON a
+     )
+  => SimFinContext
+  -> ByteString
+  -> [QueryParam]
+  -> m (ApiResult a)
+performRequest = performGenericRequest DecodeError ParseError
+
+
+------
+-- List companies
+------
+
+-- | Fetch a list of company tickers and SimFin ids.
+-- This is the only endpoint common to free and paid customers.
+
+fetchCompanyList
+  :: (MonadThrow m, MonadIO m)
+  => SimFinContext
+  -> m (Either ApiError [CompanyListingRow])
+fetchCompanyList ctx =
+  fmap unKeyCompanyListing <$> performRequest ctx "companies/list" []
diff --git a/src/SimFin/Free.hs b/src/SimFin/Free.hs
new file mode 100644
--- /dev/null
+++ b/src/SimFin/Free.hs
@@ -0,0 +1,177 @@
+{-|
+Module      : SimFin.Free
+Description : SimFin Free API.
+Copyright   : (c) Owen Shepherd, 2022
+License     : MIT
+Maintainer  : owen@owen.cafe
+-}
+
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module SimFin.Free
+  ( SimFinContext(..)
+  , Industry(..)
+
+  , CompanyListingRow(..)
+  , CompanyInfoRow(..)
+
+  , GeneralBalanceSheetRow(..)
+  , BankBalanceSheetRow(..)
+  , InsuranceBalanceSheetRow(..)
+
+  , GeneralProfitAndLossRow(..)
+  , BankProfitAndLossRow(..)
+  , InsuranceProfitAndLossRow(..)
+
+  , GeneralCashFlowRow(..)
+  , BankCashFlowRow(..)
+  , InsuranceCashFlowRow(..)
+
+  , DerivedRow(..)
+  , PricesRow(..)
+
+  , PricesQueryFree
+  , StatementQueryFree(..)
+
+  , StockRef(..)
+  , FiscalPeriod(..)
+
+  , ApiError(..)
+  , ApiResult
+
+  , createDefaultContext
+  , fetchCompanyList
+
+  , fetchCompanyInfo
+  , fetchBalanceSheet
+  , fetchProfitAndLoss
+  , fetchCashFlow
+  , fetchDerived
+  , fetchPrices
+  ) where
+
+import Control.Monad.Catch
+import Control.Monad.IO.Class
+import Data.Functor.Syntax
+import Data.Maybe (listToMaybe)
+
+import SimFin.Common
+import SimFin.Internal
+import SimFin.Types.BalanceSheet
+import SimFin.Types.CompanyInfo
+import SimFin.Types.CompanyListing
+import SimFin.Types.CashFlow
+import SimFin.Types.Derived
+import SimFin.Types.FiscalPeriod
+import SimFin.Types.Industry
+import SimFin.Types.Prices
+import SimFin.Types.PricesQuery
+import SimFin.Types.ProfitAndLoss
+import SimFin.Types.StatementQuery
+import SimFin.Types.StockRef
+import SimFin.Util
+
+
+------
+-- General Company Info
+------
+
+-- | Fetch general company information. See the [SimFin docs](https://simfin.com/api/v2/documentation/#tag/Company/paths/~1companies~1general/get).
+
+fetchCompanyInfo
+  :: (MonadThrow m, MonadIO m)
+  => SimFinContext
+  -> StockRef
+  -> m (ApiResult (Maybe CompanyInfoRow))
+fetchCompanyInfo ctx refs = do
+  rows :: ApiResult [CompanyInfoRow] <- performRequest ctx "companies/general"
+    $ stockRefsToQueryParams $ pure refs
+  pure $ listToMaybe <$> rows
+
+------
+-- Balance Sheets
+------
+
+-- | Fetch a company's balance sheet statement. As this is the free API version, only one statement
+-- is returned. The returned statement's data is dependent on the company type.
+-- See the [SimFin docs](https://simfin.com/api/v2/documentation/#tag/Company/paths/~1companies~1statements/get).
+
+fetchBalanceSheet
+  :: (MonadThrow m, MonadIO m)
+  => SimFinContext
+  -> StatementQueryFree
+  -> m (ApiResult (Maybe IndustryBalanceSheet))
+fetchBalanceSheet ctx query = do
+  nested :: ApiResult [IndustryBalanceSheets] <- performRequest ctx "companies/statements"
+    $ ("statement", Just "bs") : statementQueryFreeToQueryParams query
+  pure $ listToMaybe . invertIndustries <$> nested
+
+------
+-- P&L
+------
+
+-- | Fetch a company's profit and loss statement. As this is the free API version, only one statement
+-- is returned. The returned statement's data is dependent on the company type.
+-- See the [SimFin docs](https://simfin.com/api/v2/documentation/#tag/Company/paths/~1companies~1statements/get).
+
+fetchProfitAndLoss
+  :: (MonadThrow m, MonadIO m)
+  => SimFinContext
+  -> StatementQueryFree
+  -> m (ApiResult (Maybe IndustryProfitAndLoss))
+fetchProfitAndLoss ctx query = do
+  nested :: ApiResult [IndustryProfitsAndLosses] <- performRequest ctx "companies/statements"
+    $ ("statement", Just "pl") : statementQueryFreeToQueryParams query
+  pure $ listToMaybe . invertIndustries <$> nested
+
+-----
+-- Cash Flows
+------
+
+-- | Fetch a company's cash flow statement. As this is the free API version, only one statement
+-- is returned. The returned statement's data is dependent on the company type.
+-- See the [SimFin docs](https://simfin.com/api/v2/documentation/#tag/Company/paths/~1companies~1statements/get).
+
+fetchCashFlow
+  :: (MonadThrow m, MonadIO m)
+  => SimFinContext
+  -> StatementQueryFree
+  -> m (ApiResult (Maybe IndustryCashFlow))
+fetchCashFlow ctx query = do
+  nested :: ApiResult [IndustryCashFlows] <- performRequest ctx "companies/statements"
+    $ ("statement", Just "cf") : statementQueryFreeToQueryParams query
+  pure $ listToMaybe . invertIndustries <$> nested
+
+------
+-- Derived
+------
+
+-- | Fetch a company's derived figures. As this is the free API version, only one set
+-- of data is returned.
+-- See the [SimFin docs](https://simfin.com/api/v2/documentation/#tag/Company/paths/~1companies~1statements/get).
+
+fetchDerived
+  :: forall m a. (Read a, RealFrac a, MonadThrow m, MonadIO m)
+  => SimFinContext
+  -> StatementQueryFree
+  -> m (ApiResult (Maybe (DerivedRow a)))
+fetchDerived ctx query = do
+  nested :: ApiResult [DerivedRowsKeyed a] <- performRequest ctx "companies/statements"
+    (("statement", Just "derived") : statementQueryFreeToQueryParams query)
+  pure $ listToMaybe . mconcat . fmap unDerivedRows <$> nested
+
+------
+-- Prices
+------
+
+-- | Fetch a company's historical share prices. See the [SimFin docs](https://simfin.com/api/v2/documentation/#tag/Company/paths/~1companies~1prices/get).
+
+fetchPrices
+  :: (Read a, RealFrac a, MonadThrow m, MonadIO m)
+  => SimFinContext
+  -> PricesQueryFree
+  -> m (ApiResult [PricesRow a])
+fetchPrices ctx query =
+  mconcat . fmap unKeyPrices <$$> performRequest ctx "companies/prices"
+    (pricesQueryFreeToQueryParams query)
diff --git a/src/SimFin/Internal.hs b/src/SimFin/Internal.hs
new file mode 100644
--- /dev/null
+++ b/src/SimFin/Internal.hs
@@ -0,0 +1,138 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module SimFin.Internal
+  ( SimFinContext(..)
+  , QueryParam
+  , createKeyedRow
+  , createKeyedRows
+  , toCommaQueryParam
+  , toBoolQueryParam
+  , toTextCommaQueryParam
+  , toShownCommaQueryParam
+  , baseRequest
+  , makeRequest
+  , performGenericRequest
+  ) where
+
+import Control.Monad.IO.Class
+import Data.Aeson
+import Data.Aeson.Types (parse, Parser)
+
+#if MIN_VERSION_aeson(2,0,0)
+import qualified Data.Aeson.Key as K
+import qualified Data.Aeson.KeyMap as KM
+#else
+import qualified Data.HashMap.Strict as HM
+#endif
+
+import Data.ByteString (ByteString)
+import qualified Data.ByteString.Char8 as BS8
+import qualified Data.ByteString.Lazy as LBS
+import Data.Functor.Syntax
+import Data.Text (Text)
+import qualified Data.Text.Encoding as T
+import Network.HTTP.Client
+import Network.HTTP.Types.Status
+
+-- | The context needed to call every fetch* function.
+
+data SimFinContext = SimFinContext
+  { simFinApiKey :: ByteString
+  , simFinManager :: Manager
+  }
+
+baseRequest :: Request
+baseRequest = defaultRequest
+  { host = "simfin.com"
+  , port = 443
+  , secure = True
+  , requestHeaders =
+    [ ("Accept", "application/json")
+    ]
+  }
+
+basePath :: ByteString
+basePath = "/api/v2/"
+
+makeRequest :: ByteString -> ByteString -> [QueryParam] -> Request
+makeRequest apiKey path query =
+  setQueryString (("api-key", Just apiKey) : query)
+  $ baseRequest { path = basePath <> path }
+
+performGenericRequest
+  :: ( MonadIO m
+     , FromJSON a
+     , FromJSON e
+     )
+  => (LBS.ByteString -> String -> e)
+  -> (Value -> String -> e)
+  -> SimFinContext
+  -> ByteString
+  -> [QueryParam]
+  -> m (Either e a)
+performGenericRequest mkDecodeErr mkParseErr SimFinContext{..} path query = do
+  let req = makeRequest simFinApiKey path query
+  res <- liftIO $ httpLbs req simFinManager
+  let body = responseBody res
+  -- Try to parse body into generic JSON
+  pure $ case eitherDecode $ responseBody res of
+    Left err -> Left $ mkDecodeErr body err
+    Right value -> case statusCode $ responseStatus res of
+      200 -> case parse parseJSON value of
+        Error err -> Left $ mkParseErr value err
+        Success a -> Right a
+      _ -> case parse parseJSON value of
+        Error err -> Left $ mkParseErr value err
+        Success a -> Left a
+
+type QueryParam = (ByteString, Maybe ByteString)
+
+#if MIN_VERSION_aeson(2,0,0)
+
+toKey :: Text -> Key
+toKey = K.fromText
+
+toObject :: [(K.Key, Value)] -> Value
+toObject = Object . KM.fromList
+
+#else
+
+toKey :: Text -> Text
+toKey = id
+
+toObject :: [(Text, Value)] -> Value
+toObject = Object . HM.fromList 
+
+#endif
+
+createKeyedRow :: Value -> Parser Value
+createKeyedRow = withObject "Root" $ \root -> do
+  cols <- toKey <$$> root .: "columns"
+  row <- root .: "data"
+  pure $ toObject $ zip cols row
+
+createKeyedRows :: Value -> Parser [Value]
+createKeyedRows = withObject "Root" $ \root -> do
+  cols <- toKey <$$> root .: "columns"
+  rows <- root .: "data"
+  pure $ toObject . zip cols <$> rows
+
+toCommaQueryParam :: ByteString -> (a -> ByteString) -> [a] -> [QueryParam]
+toCommaQueryParam key f as = case as of
+  [] -> []
+  _ -> [(key, Just $ BS8.intercalate "," $ f <$> as)]
+
+-- Chars are truncated to 8-bits
+toShownCommaQueryParam :: Show a => ByteString -> [a] -> [QueryParam]
+toShownCommaQueryParam key = toCommaQueryParam key (BS8.pack . show)
+
+toTextCommaQueryParam :: ByteString -> [Text] -> [QueryParam]
+toTextCommaQueryParam key = toCommaQueryParam key T.encodeUtf8
+
+toBoolQueryParam :: ByteString -> Bool -> [QueryParam]
+toBoolQueryParam key b = case b of
+  False -> []
+  True -> [(key, Nothing)]
diff --git a/src/SimFin/Plus.hs b/src/SimFin/Plus.hs
new file mode 100644
--- /dev/null
+++ b/src/SimFin/Plus.hs
@@ -0,0 +1,185 @@
+{-|
+Module      : SimFin.Plus
+Description : SimFin+ API.
+Copyright   : (c) Owen Shepherd, 2022
+License     : MIT
+Maintainer  : owen@owen.cafe
+-}
+
+{-# LANGUAGE OverloadedStrings #-}
+
+module SimFin.Plus
+  ( SimFinContext(..)
+  , Industry(..)
+
+  , CompanyListingRow(..)
+  , CompanyInfoRow(..)
+
+  , GeneralBalanceSheetRow(..)
+  , BankBalanceSheetRow(..)
+  , InsuranceBalanceSheetRow(..)
+
+  , GeneralProfitAndLossRow(..)
+  , BankProfitAndLossRow(..)
+  , InsuranceProfitAndLossRow(..)
+
+  , GeneralCashFlowRow(..)
+  , BankCashFlowRow(..)
+  , InsuranceCashFlowRow(..)
+
+  , DerivedRow(..)
+  , PricesRow(..)
+  , RatiosRow(..)
+
+  , PricesAndRatiosRow(..)
+
+  , PricesQuery(..)
+  , StatementQuery(..)
+
+  , StockRef(..)
+  , FiscalPeriod(..)
+
+  , ApiError(..)
+  , ApiResult
+
+  , createDefaultContext
+  , fetchCompanyList
+
+  , fetchCompanyInfo
+  , fetchBalanceSheets
+  , fetchProfitsAndLosses
+  , fetchCashFlows
+  , fetchDerived
+  , fetchPrices
+  , fetchPricesAndRatios
+  ) where
+
+import Control.Monad.Catch
+import Control.Monad.IO.Class
+import Data.Functor.Syntax
+import Data.List.NonEmpty (NonEmpty)
+
+import SimFin.Common
+import SimFin.Internal
+import SimFin.Types.BalanceSheet
+import SimFin.Types.CompanyInfo
+import SimFin.Types.CompanyListing
+import SimFin.Types.CashFlow
+import SimFin.Types.Derived
+import SimFin.Types.FiscalPeriod
+import SimFin.Types.Industry
+import SimFin.Types.Prices
+import SimFin.Types.PricesQuery
+import SimFin.Types.PricesAndRatios
+import SimFin.Types.ProfitAndLoss
+import SimFin.Types.Ratios
+import SimFin.Types.StatementQuery
+import SimFin.Types.StockRef
+import SimFin.Util
+
+------
+-- General Company Info
+------
+
+-- | Fetch general company information.
+-- See the [SimFin docs](https://simfin.com/api/v2/documentation/#tag/Company/paths/~1companies~1general/get).
+
+fetchCompanyInfo
+  :: (MonadThrow m, MonadIO m)
+  => SimFinContext
+  -> NonEmpty StockRef
+  -> m (ApiResult [CompanyInfoRow])
+fetchCompanyInfo ctx refs =
+  performRequest ctx "companies/general" (stockRefsToQueryParams refs)
+
+------
+-- Balance Sheets
+------
+
+-- | Fetch a company's balance sheet statements.
+-- See the [SimFin docs](https://simfin.com/api/v2/documentation/#tag/Company/paths/~1companies~1statements/get).
+
+fetchBalanceSheets
+  :: (MonadThrow m, MonadIO m)
+  => SimFinContext
+  -> StatementQuery
+  -> m (ApiResult [IndustryBalanceSheet])
+fetchBalanceSheets ctx query =
+  invertIndustries <$$> performRequest ctx "companies/statements"
+    (("statement", Just "bs") : statementQueryToQueryParams query)
+
+------
+-- P&L
+------
+
+-- | Fetch a company's profit and loss statements.
+-- See the [SimFin docs](https://simfin.com/api/v2/documentation/#tag/Company/paths/~1companies~1statements/get).
+
+fetchProfitsAndLosses
+  :: (MonadThrow m, MonadIO m)
+  => SimFinContext
+  -> StatementQuery
+  -> m (ApiResult [IndustryProfitAndLoss])
+fetchProfitsAndLosses ctx query =
+  invertIndustries <$$> performRequest ctx "companies/statements"
+    (("statement", Just "pl") : statementQueryToQueryParams query)
+
+-----
+-- Cash Flows
+------
+
+-- | Fetch a company's cash flow statements.
+-- See the [SimFin docs](https://simfin.com/api/v2/documentation/#tag/Company/paths/~1companies~1statements/get).
+
+fetchCashFlows
+  :: (MonadThrow m, MonadIO m)
+  => SimFinContext
+  -> StatementQuery
+  -> m (ApiResult [IndustryCashFlow])
+fetchCashFlows ctx query =
+  invertIndustries <$$> performRequest ctx "companies/statements"
+    (("statement", Just "cf") : statementQueryToQueryParams query)
+
+------
+-- Derived
+------
+
+-- | Fetch a company's derived figures.
+-- See the [SimFin docs](https://simfin.com/api/v2/documentation/#tag/Company/paths/~1companies~1statements/get).
+
+fetchDerived
+  :: (Read a, RealFrac a, MonadThrow m, MonadIO m)
+  => SimFinContext
+  -> StatementQuery
+  -> m (ApiResult [DerivedRow a])
+fetchDerived ctx query =
+  mconcat <$$> performRequest ctx "companies/statements"
+    (("statement", Just "derived") : statementQueryToQueryParams query)
+
+------
+-- Prices
+------
+
+-- | Fetch a company's historical share prices.
+-- See the [SimFin docs](https://simfin.com/api/v2/documentation/#tag/Company/paths/~1companies~1prices/get).
+
+fetchPrices
+  :: (Read a, RealFrac a, MonadThrow m, MonadIO m)
+  => SimFinContext
+  -> PricesQuery
+  -> m (ApiResult [PricesRow a])
+fetchPrices ctx query =
+  mconcat . fmap unKeyPrices <$$> performRequest ctx "companies/prices"
+    (pricesQueryToQueryParams query)
+
+-- | Fetch a company's historical share prices, along with key ratios.
+-- See the [SimFin docs](https://simfin.com/api/v2/documentation/#tag/Company/paths/~1companies~1prices/get).
+
+fetchPricesAndRatios
+  :: (Read a, RealFrac a, MonadThrow m, MonadIO m)
+  => SimFinContext
+  -> PricesQuery
+  -> m (ApiResult [PricesAndRatiosRow a])
+fetchPricesAndRatios ctx query =
+  mconcat . fmap unKeyPricesAndRatios <$$> performRequest ctx "companies/prices"
+    (("ratios", Nothing) : pricesQueryToQueryParams query)
diff --git a/src/SimFin/Types/BalanceSheet.hs b/src/SimFin/Types/BalanceSheet.hs
new file mode 100644
--- /dev/null
+++ b/src/SimFin/Types/BalanceSheet.hs
@@ -0,0 +1,581 @@
+{-|
+Module      : SimFin.Types.BalanceSheet
+Description : Types to represent a company's balance sheet statement.
+Copyright   : (c) Owen Shepherd, 2022
+License     : MIT
+Maintainer  : owen@owen.cafe
+-}
+
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE FlexibleInstances #-}
+
+module SimFin.Types.BalanceSheet
+  ( GeneralBalanceSheetRow(..)
+  , BankBalanceSheetRow(..)
+  , InsuranceBalanceSheetRow(..)
+  , IndustryBalanceSheets
+  , IndustryBalanceSheet
+  ) where
+
+import Control.Applicative ((<|>))
+import Data.Aeson
+import Data.Text (Text)
+import Data.Time.Calendar (Day)
+
+import SimFin.Types.Industry
+import SimFin.Internal
+
+------
+-- General
+------
+
+-- | Balance sheet statement for general companies. 
+
+data GeneralBalanceSheetRow
+  = GeneralBalanceSheetRow
+  { simFinId :: Int
+  , ticker :: Text
+  , fiscalPeriod :: String
+  , fiscalYear :: Int
+  , reportDate :: Day
+  , publishDate :: Day
+  , restatedDate :: Day
+  , source :: Text
+  , tTM :: Bool
+  , valueCheck :: Bool
+  , cashCashEquivalentsAndShortTermInvestments :: Maybe Integer
+  , cashAndCashEquivalents :: Maybe Integer
+  , shortTermInvestments :: Maybe Integer
+  , accountsAndNotesReceivable :: Maybe Integer
+  , accountsReceivableNet :: Maybe Integer
+  , notesReceivableNet :: Maybe Integer
+  , unbilledRevenues :: Maybe Integer
+  , inventories :: Maybe Integer
+  , rawMaterials :: Maybe Integer
+  , workInProcess :: Maybe Integer
+  , finishedGoods :: Maybe Integer
+  , otherInventory :: Maybe Integer
+  , otherShortTermAssets :: Maybe Integer
+  , prepaidExpenses :: Maybe Integer
+  , derivativeAndHedgingAssetsShortTerm :: Maybe Integer
+  , assetsHeldForSale :: Maybe Integer
+  , deferredTaxAssetsShortTerm :: Maybe Integer
+  , incomeTaxesReceivable :: Maybe Integer
+  , discontinuedOperationsShortTerm :: Maybe Integer
+  , miscShortTermAssets :: Maybe Integer
+  , totalCurrentAssets :: Maybe Integer
+  , propertyPlantAndEquipmentNet :: Maybe Integer
+  , propertyPlantAndEquipment :: Maybe Integer
+  , accumulatedDepreciation :: Maybe Integer
+  , longTermInvestmentsAndReceivables :: Maybe Integer
+  , longTermInvestments :: Maybe Integer
+  , longTermMarketableSecurities :: Maybe Integer
+  , longTermReceivables :: Maybe Integer
+  , otherLongTermAssets :: Maybe Integer
+  , intangibleAssets :: Maybe Integer
+  , goodwill :: Maybe Integer
+  , otherIntangibleAssets :: Maybe Integer
+  , prepaidExpense :: Maybe Integer
+  , deferredTaxAssetsLongTerm :: Maybe Integer
+  , derivativeAndHedgingAssetsLongTerm :: Maybe Integer
+  , prepaidPensionCosts :: Maybe Integer
+  , discontinuedOperationsLongTerm :: Maybe Integer
+  , investmentsinAffiliates :: Maybe Integer
+  , miscLongTermAssets :: Maybe Integer
+  , totalNoncurrentAssets :: Maybe Integer
+  , totalAssets :: Maybe Integer
+  , payablesAndAccruals :: Maybe Integer
+  , accountsPayable :: Maybe Integer
+  , accruedTaxes :: Maybe Integer
+  , interestAndDividendsPayable :: Maybe Integer
+  , otherPayablesAndAccruals :: Maybe Integer
+  , shortTermDebt :: Maybe Integer
+  , shortTermBorrowings :: Maybe Integer
+  , shortTermCapitalLeases :: Maybe Integer
+  , currentPortionOfLongTermDebt :: Maybe Integer
+  , otherShortTermLiabilities :: Maybe Integer
+  , deferredRevenueShortTerm :: Maybe Integer
+  , liabilitiesfromDerivativesAndHedgingShortTerm :: Maybe Integer
+  , deferredTaxLiabilitiesShortTerm :: Maybe Integer
+  , liabilitiesfromDiscontinuedOperationsShortTerm :: Maybe Integer
+  , miscShortTermLiabilities :: Maybe Integer
+  , totalCurrentLiabilities :: Maybe Integer
+  , longTermDebt :: Maybe Integer
+  , longTermBorrowings :: Maybe Integer
+  , longTermCapitalLeases :: Maybe Integer
+  , otherLongTermLiabilities :: Maybe Integer
+  , accruedLiabilities :: Maybe Integer
+  , pensionLiabilities :: Maybe Integer
+  , pensions :: Maybe Integer
+  , otherPostRetirementBenefits :: Maybe Integer
+  , deferredCompensation :: Maybe Integer
+  , deferredRevenueLongTerm :: Maybe Integer
+  , deferredTaxLiabilitiesLongTerm :: Maybe Integer
+  , liabilitiesfromDerivativesAndHedgingLongTerm :: Maybe Integer
+  , liabilitiesfromDiscontinuedOperationsLongTerm :: Maybe Integer
+  , miscLongTermLiabilities :: Maybe Integer
+  , totalNoncurrentLiabilities :: Maybe Integer
+  , totalLiabilities :: Maybe Integer
+  , preferredEquity :: Maybe Integer
+  , shareCapitalAndAdditionalPaidInCapital :: Maybe Integer
+  , commonStock :: Maybe Integer
+  , additionalPaidinCapital :: Maybe Integer
+  , otherShareCapital :: Maybe Integer
+  , treasuryStock :: Maybe Integer
+  , retainedEarnings :: Maybe Integer
+  , otherEquity :: Maybe Integer
+  , equityBeforeMinorityInterest :: Maybe Integer
+  , minorityInterest :: Maybe Integer
+  , totalEquity :: Maybe Integer
+  , totalLiabilitiesAndEquity :: Maybe Integer
+  } deriving Show
+
+instance FromJSON GeneralBalanceSheetRow where
+  parseJSON = withObject "GeneralBalanceSheetRow" $ \v -> GeneralBalanceSheetRow
+    <$> v .: "SimFinId"
+    <*> v .: "Ticker"
+    <*> v .: "Fiscal Period"
+    <*> v .: "Fiscal Year"
+    <*> v .: "Report Date"
+    <*> v .: "Publish Date"
+    <*> v .: "Restated Date"
+    <*> v .: "Source"
+    <*> v .: "TTM"
+    <*> v .: "Value Check"
+    <*> v .: "Cash, Cash Equivalents & Short Term Investments"
+    <*> v .: "Cash & Cash Equivalents"
+    <*> v .: "Short Term Investments"
+    <*> v .: "Accounts & Notes Receivable"
+    <*> v .: "Accounts Receivable, Net"
+    <*> v .: "Notes Receivable, Net"
+    <*> v .: "Unbilled Revenues"
+    <*> v .: "Inventories"
+    <*> v .: "Raw Materials"
+    <*> v .: "Work In Process"
+    <*> v .: "Finished Goods"
+    <*> v .: "Other Inventory"
+    <*> v .: "Other Short Term Assets"
+    <*> v .: "Prepaid Expenses"
+    <*> v .: "Derivative & Hedging Assets (Short Term)"
+    <*> v .: "Assets Held-for-Sale"
+    <*> v .: "Deferred Tax Assets (Short Term)"
+    <*> v .: "Income Taxes Receivable"
+    <*> v .: "Discontinued Operations (Short Term)"
+    <*> v .: "Misc. Short Term Assets"
+    <*> v .: "Total Current Assets"
+    <*> v .: "Property, Plant & Equipment, Net"
+    <*> v .: "Property, Plant & Equipment"
+    <*> v .: "Accumulated Depreciation"
+    <*> v .: "Long Term Investments & Receivables"
+    <*> v .: "Long Term Investments"
+    <*> v .: "Long Term Marketable Securities"
+    <*> v .: "Long Term Receivables"
+    <*> v .: "Other Long Term Assets"
+    <*> v .: "Intangible Assets"
+    <*> v .: "Goodwill"
+    <*> v .: "Other Intangible Assets"
+    <*> v .: "Prepaid Expense"
+    <*> v .: "Deferred Tax Assets (Long Term)"
+    <*> v .: "Derivative & Hedging Assets (Long Term)"
+    <*> v .: "Prepaid Pension Costs"
+    <*> v .: "Discontinued Operations (Long Term)"
+    <*> v .: "Investments in Affiliates"
+    <*> v .: "Misc. Long Term Assets"
+    <*> v .: "Total Noncurrent Assets"
+    <*> v .: "Total Assets"
+    <*> v .: "Payables & Accruals"
+    <*> v .: "Accounts Payable"
+    <*> v .: "Accrued Taxes"
+    <*> v .: "Interest & Dividends Payable"
+    <*> v .: "Other Payables & Accruals"
+    <*> v .: "Short Term Debt"
+    <*> v .: "Short Term Borrowings"
+    <*> v .: "Short Term Capital Leases"
+    <*> v .: "Current Portion of Long Term Debt"
+    <*> v .: "Other Short Term Liabilities"
+    <*> v .: "Deferred Revenue (Short Term)"
+    <*> v .: "Liabilities from Derivatives & Hedging (Short Term)"
+    <*> v .: "Deferred Tax Liabilities (Short Term)"
+    <*> v .: "Liabilities from Discontinued Operations (Short Term)"
+    <*> v .: "Misc. Short Term Liabilities"
+    <*> v .: "Total Current Liabilities"
+    <*> v .: "Long Term Debt"
+    <*> v .: "Long Term Borrowings"
+    <*> v .: "Long Term Capital Leases"
+    <*> v .: "Other Long Term Liabilities"
+    <*> v .: "Accrued Liabilities"
+    <*> v .: "Pension Liabilities"
+    <*> v .: "Pensions"
+    <*> v .: "Other Post-Retirement Benefits"
+    <*> v .: "Deferred Compensation"
+    <*> v .: "Deferred Revenue (Long Term)"
+    <*> v .: "Deferred Tax Liabilities (Long Term)"
+    <*> v .: "Liabilities from Derivatives & Hedging (Long Term)"
+    <*> v .: "Liabilities from Discontinued Operations (Long Term)"
+    <*> v .: "Misc. Long Term Liabilities"
+    <*> v .: "Total Noncurrent Liabilities"
+    <*> v .: "Total Liabilities"
+    <*> v .: "Preferred Equity"
+    <*> v .: "Share Capital & Additional Paid-In Capital"
+    <*> v .: "Common Stock"
+    <*> v .: "Additional Paid in Capital"
+    <*> v .: "Other Share Capital"
+    <*> v .: "Treasury Stock"
+    <*> v .: "Retained Earnings"
+    <*> v .: "Other Equity"
+    <*> v .: "Equity Before Minority Interest"
+    <*> v .: "Minority Interest"
+    <*> v .: "Total Equity"
+    <*> v .: "Total Liabilities & Equity"
+
+-- | Wrapper to parse a GeneralBalanceSheetRow record from SimFin's JSON format.
+
+newtype GeneralBalanceSheetsKeyed = GeneralBalanceSheetsKeyed { unKeyGeneralBalanceSheets :: [GeneralBalanceSheetRow] }
+
+instance FromJSON GeneralBalanceSheetsKeyed where
+  parseJSON o = GeneralBalanceSheetsKeyed <$> (traverse parseJSON =<< createKeyedRows o)
+
+
+------
+-- Bank
+------
+
+-- | Balance sheet statement for banks. 
+
+data BankBalanceSheetRow =
+  BankBalanceSheetRow
+  { simFinId :: Int
+  , ticker :: Text
+  , fiscalPeriod :: String
+  , fiscalYear :: Int
+  , reportDate :: Day
+  , publishDate :: Day
+  , restatedDate :: Day
+  , source :: Text
+  , tTM :: Bool
+  , valueCheck :: Bool
+  , cashCashEquivalentsAndShortTermInvestments :: Maybe Integer
+  , interbankAssets :: Maybe Integer
+  , fedFundsSoldAndRepos :: Maybe Integer
+  , otherInterbankAssets :: Maybe Integer
+  , shortAndLongTermInvestments :: Maybe Integer
+  , tradingSecurities :: Maybe Integer
+  , investmentSecuritiesAvailableforSale :: Maybe Integer
+  , investmentSecuritiesHeldtoMaturity :: Maybe Integer
+  , realEstateInvestments :: Maybe Integer
+  , otherInvestments :: Maybe Integer
+  , accountsAndNotesReceivable :: Maybe Integer
+  , netLoans :: Maybe Integer
+  , reserveforLoanLosses :: Maybe Integer
+  , totalLoans :: Maybe Integer
+  , totalCommercialLoans :: Maybe Integer
+  , commercialRealEstateLoans :: Maybe Integer
+  , otherCommercialLoans :: Maybe Integer
+  , totalConsumerLoans :: Maybe Integer
+  , creditCardLoans :: Maybe Integer
+  , homeEquityLoans :: Maybe Integer
+  , familyResidentialLoans :: Maybe Integer
+  , autoLoans :: Maybe Integer
+  , studentLoans :: Maybe Integer
+  , otherConsumerLoans :: Maybe Integer
+  , otherLoans :: Maybe Integer
+  , netFixedAssets :: Maybe Integer
+  , propertyPlantAndEquipmentNet :: Maybe Integer
+  , operatingLeaseAssets :: Maybe Integer
+  , otherFixedAssets :: Maybe Integer
+  , intangibleAssets :: Maybe Integer
+  , goodwill :: Maybe Integer
+  , otherIntangibleAssets :: Maybe Integer
+  , investmentsInAssociates :: Maybe Integer
+  , deferredTaxAssetsShortTerm :: Maybe Integer
+  , derivativesAndHedgingAssets :: Maybe Integer
+  , discontinuedOperationsAssets :: Maybe Integer
+  , customerAcceptancesAndLiabilities :: Maybe Integer
+  , otherAssets :: Maybe Integer
+  , totalAssets :: Maybe Integer
+  , totalDeposits :: Maybe Integer
+  , demandDeposits :: Maybe Integer
+  , interestBearingDeposits :: Maybe Integer
+  , savingDeposits :: Maybe Integer
+  , timeDeposits :: Maybe Integer
+  , otherDeposits :: Maybe Integer
+  , shortTermDebt :: Maybe Integer
+  , securitiesSoldUnderRepo :: Maybe Integer
+  , tradingAccountLiabilities :: Maybe Integer
+  , shortTermCapitalLeases :: Maybe Integer
+  , currentPortionofLongTermDebt :: Maybe Integer
+  , shortTermBorrowings :: Maybe Integer
+  , payablesBrokerDealers :: Maybe Integer
+  , longTermDebt :: Maybe Integer
+  , longTermCapitalLeases :: Maybe Integer
+  , longTermBorrowings :: Maybe Integer
+  , pensionLiabilities :: Maybe Integer
+  , pensions :: Maybe Integer
+  , otherPostRetirementBenefits :: Maybe Integer
+  , deferredTaxLiabilitiesShortTerm :: Maybe Integer
+  , derivativesAndHedgingLiabilities :: Maybe Integer
+  , discontinuedOperationsLiabilities :: Maybe Integer
+  , otherLiabilities :: Maybe Integer
+  , totalLiabilities :: Maybe Integer
+  , preferredEquity :: Maybe Integer
+  , shareCapitalAndAdditionalPaidInCapital :: Maybe Integer
+  , commonStock :: Maybe Integer
+  , additionalPaidInCapital :: Maybe Integer
+  , otherShareCapital :: Maybe Integer
+  , treasuryStock :: Maybe Integer
+  , retainedEarnings :: Maybe Integer
+  , otherEquity :: Maybe Integer
+  , equityBeforeMinorityInterest :: Maybe Integer
+  , minorityInterest :: Maybe Integer
+  , totalEquity :: Maybe Integer
+  , totalLiabilitiesAndEquity :: Maybe Integer
+  } deriving Show
+
+instance FromJSON BankBalanceSheetRow where
+  parseJSON = withObject "BankBalanceSheetRow" $ \v -> BankBalanceSheetRow
+    <$> v .: "SimFinId"
+    <*> v .: "Ticker"
+    <*> v .: "Fiscal Period"
+    <*> v .: "Fiscal Year"
+    <*> v .: "Report Date"
+    <*> v .: "Publish Date"
+    <*> v .: "Restated Date"
+    <*> v .: "Source"
+    <*> v .: "TTM"
+    <*> v .: "Value Check"
+    <*> v .: "Cash, Cash Equivalents & Short Term Investments"
+    <*> v .: "Interbank Assets"
+    <*> v .: "Fed Funds Sold & Repos"
+    <*> v .: "Other Interbank Assets"
+    <*> v .: "Short & Long Term Investments"
+    <*> v .: "Trading Securities"
+    <*> v .: "Investment Securities Available for Sale"
+    <*> v .: "Investment Securities Held to Maturity"
+    <*> v .: "Real Estate Investments"
+    <*> v .: "Other Investments"
+    <*> v .: "Accounts & Notes Receivable"
+    <*> v .: "Net Loans"
+    <*> v .: "Reserve for Loan Losses"
+    <*> v .: "Total Loans"
+    <*> v .: "Total Commercial Loans"
+    <*> v .: "Commercial Real Estate Loans"
+    <*> v .: "Other Commercial Loans"
+    <*> v .: "Total Consumer Loans"
+    <*> v .: "Credit Card Loans"
+    <*> v .: "Home Equity Loans"
+    <*> v .: "Family Residential Loans"
+    <*> v .: "Auto Loans"
+    <*> v .: "Student Loans"
+    <*> v .: "Other Consumer Loans"
+    <*> v .: "Other Loans"
+    <*> v .: "Net Fixed Assets"
+    <*> v .: "Property, Plant & Equipment, Net"
+    <*> v .: "Operating Lease Assets"
+    <*> v .: "Other Fixed Assets"
+    <*> v .: "Intangible Assets"
+    <*> v .: "Goodwill"
+    <*> v .: "Other Intangible Assets"
+    <*> v .: "Investments in Associates"
+    <*> v .: "Deferred Tax Assets (Short Term)"
+    <*> v .: "Derivatives & Hedging (Assets)"
+    <*> v .: "Discontinued Operations (Assets)"
+    <*> v .: "Customer Acceptances & Liabilities"
+    <*> v .: "Other Assets"
+    <*> v .: "Total Assets"
+    <*> v .: "Total Deposits"
+    <*> v .: "Demand Deposits"
+    <*> v .: "Interest Bearing Deposits"
+    <*> v .: "Saving Deposits"
+    <*> v .: "Time Deposits"
+    <*> v .: "Other Deposits"
+    <*> v .: "Short Term Debt"
+    <*> v .: "Securities Sold Under Repo"
+    <*> v .: "Trading Account Liabilities"
+    <*> v .: "Short Term Capital Leases"
+    <*> v .: "Current Portion of Long Term Debt"
+    <*> v .: "Short Term Borrowings"
+    <*> v .: "Payables Broker Dealers"
+    <*> v .: "Long Term Debt"
+    <*> v .: "Long Term Capital Leases"
+    <*> v .: "Long Term Borrowings"
+    <*> v .: "Pension Liabilities"
+    <*> v .: "Pensions"
+    <*> v .: "Other Post-Retirement Benefits"
+    <*> v .: "Deferred Tax Liabilities (Short Term)"
+    <*> v .: "Derivatives & Hedging (Liabilities)"
+    <*> v .: "Discontinued Operations (Liabilities)"
+    <*> v .: "Other Liabilities"
+    <*> v .: "Total Liabilities"
+    <*> v .: "Preferred Equity"
+    <*> v .: "Share Capital & Additional Paid-In Capital"
+    <*> v .: "Common Stock"
+    <*> v .: "Additional Paid in Capital"
+    <*> v .: "Other Share Capital"
+    <*> v .: "Treasury Stock"
+    <*> v .: "Retained Earnings"
+    <*> v .: "Other Equity"
+    <*> v .: "Equity Before Minority Interest"
+    <*> v .: "Minority Interest"
+    <*> v .: "Total Equity"
+    <*> v .: "Total Liabilities & Equity"
+
+-- | Wrapper to parse a BankBalanceSheetRow record from SimFin's JSON format.
+
+newtype BankBalanceSheetsKeyed = BankBalanceSheetsKeyed { unKeyBankBalanceSheets :: [BankBalanceSheetRow] }
+
+instance FromJSON BankBalanceSheetsKeyed where
+  parseJSON o = BankBalanceSheetsKeyed <$> (traverse parseJSON =<< createKeyedRows o)
+
+
+------
+-- Insurance
+------
+
+-- | Balance sheet statement for insurance companies. 
+
+data InsuranceBalanceSheetRow =
+  InsuranceBalanceSheetRow
+  { simFinId :: Int
+  , ticker :: Text
+  , fiscalPeriod :: String
+  , fiscalYear :: Int
+  , reportDate :: Day
+  , publishDate :: Day
+  , restatedDate :: Day
+  , source :: Text
+  , tTM :: Bool
+  , valueCheck :: Bool
+  , totalInvestments :: Maybe Integer
+  , fixedIncomeTradingAFSAndShortTermInv :: Maybe Integer
+  , loansAndMortgages :: Maybe Integer
+  , fixedIncomeSecuritiesHTM :: Maybe Integer
+  , equitySecurities :: Maybe Integer
+  , realEstateInvestments :: Maybe Integer
+  , otherInvestments :: Maybe Integer
+  , cashCashEquivalentsAndShortTermInvestments :: Maybe Integer
+  , accountsAndNotesReceivable :: Maybe Integer
+  , propertyPlantAndEquipmentNet :: Maybe Integer
+  , deferredPolicyAcquisitionCosts :: Maybe Integer
+  , otherAssets :: Maybe Integer
+  , totalAssets :: Maybe Integer
+  , insuranceReserves :: Maybe Integer
+  , reserveForOutstandingClaimsAndLosses :: Maybe Integer
+  , premiumReserveUnearned :: Maybe Integer
+  , lifePolicyBenefits :: Maybe Integer
+  , otherInsuranceReserves :: Maybe Integer
+  , shortTermDebt :: Maybe Integer
+  , otherShortTermLiabilities :: Maybe Integer
+  , longTermDebt :: Maybe Integer
+  , pensionLiabilities :: Maybe Integer
+  , pensions :: Maybe Integer
+  , otherPostRetirementBenefits :: Maybe Integer
+  , otherLongTermLiabilities :: Maybe Integer
+  , fundsForFutureAppropriations :: Maybe Integer
+  , totalLiabilities :: Maybe Integer
+  , preferredEquity :: Maybe Integer
+  , policyholdersEquity :: Maybe Integer
+  , shareCapitalAndAdditionalPaidInCapital :: Maybe Integer
+  , commonStock :: Maybe Integer
+  , additionalPaidInCapital :: Maybe Integer
+  , otherShareCapital :: Maybe Integer
+  , treasuryStock :: Maybe Integer
+  , retainedEarnings :: Maybe Integer
+  , otherEquity :: Maybe Integer
+  , equityBeforeMinorityInterest :: Maybe Integer
+  , minorityInterest :: Maybe Integer
+  , totalEquity :: Maybe Integer
+  , totalLiabilitiesAndEquity :: Maybe Integer
+  } deriving Show
+
+instance FromJSON InsuranceBalanceSheetRow where
+  parseJSON = withObject "InsuranceBalanceSheetRow" $ \v -> InsuranceBalanceSheetRow
+    <$> v .: "SimFinId"
+    <*> v .: "Ticker"
+    <*> v .: "Fiscal Period"
+    <*> v .: "Fiscal Year"
+    <*> v .: "Report Date"
+    <*> v .: "Publish Date"
+    <*> v .: "Restated Date"
+    <*> v .: "Source"
+    <*> v .: "TTM"
+    <*> v .: "Value Check"
+    <*> v .: "Total Investments"
+    <*> v .: "Fixed Income-Trading/AFS & Short Term Inv."
+    <*> v .: "Loans & Mortgages"
+    <*> v .: "Fixed Income Securities HTM"
+    <*> v .: "Equity Securities"
+    <*> v .: "Real Estate Investments"
+    <*> v .: "Other Investments"
+    <*> v .: "Cash, Cash Equivalents & Short Term Investments"
+    <*> v .: "Accounts & Notes Receivable"
+    <*> v .: "Property, Plant & Equipment, Net"
+    <*> v .: "Deferred Policy Acquisition Costs"
+    <*> v .: "Other Assets"
+    <*> v .: "Total Assets"
+    <*> v .: "Insurance Reserves"
+    <*> v .: "Reserve for Outstanding Claims & Losses"
+    <*> v .: "Premium Reserve (Unearned)"
+    <*> v .: "Life Policy Benefits"
+    <*> v .: "Other Insurance Reserves"
+    <*> v .: "Short Term Debt"
+    <*> v .: "Other Short Term Liabilities"
+    <*> v .: "Long Term Debt"
+    <*> v .: "Pension Liabilities"
+    <*> v .: "Pensions"
+    <*> v .: "Other Post-Retirement Benefits"
+    <*> v .: "Other Long Term Liabilities"
+    <*> v .: "Funds for Future Appropriations"
+    <*> v .: "Total Liabilities"
+    <*> v .: "Preferred Equity"
+    <*> v .: "Policyholders Equity"
+    <*> v .: "Share Capital & Additional Paid-In Capital"
+    <*> v .: "Common Stock"
+    <*> v .: "Additional Paid in Capital"
+    <*> v .: "Other Share Capital"
+    <*> v .: "Treasury Stock"
+    <*> v .: "Retained Earnings"
+    <*> v .: "Other Equity"
+    <*> v .: "Equity Before Minority Interest"
+    <*> v .: "Minority Interest"
+    <*> v .: "Total Equity"
+    <*> v .: "Total Liabilities & Equity"
+
+-- | Wrapper to parse an InsuranceBalanceSheetRow record from SimFin's JSON format.
+
+newtype InsuranceBalanceSheetsKeyed = InsuranceBalanceSheetsKeyed { unKeyInsuranceBalanceSheets :: [InsuranceBalanceSheetRow] }
+
+instance FromJSON InsuranceBalanceSheetsKeyed where
+  parseJSON o = InsuranceBalanceSheetsKeyed <$> (traverse parseJSON =<< createKeyedRows o)
+
+
+------
+-- Industry
+------
+
+type IndustryBalanceSheetsKeyed
+  = Industry GeneralBalanceSheetsKeyed BankBalanceSheetsKeyed InsuranceBalanceSheetsKeyed
+
+-- | Discrimination of balance sheet lists.
+
+type IndustryBalanceSheets
+  = Industry [GeneralBalanceSheetRow] [BankBalanceSheetRow] [InsuranceBalanceSheetRow]
+
+-- | Discrimination of balance sheets.
+
+type IndustryBalanceSheet
+  = Industry GeneralBalanceSheetRow BankBalanceSheetRow InsuranceBalanceSheetRow
+
+instance FromJSON IndustryBalanceSheetsKeyed where
+  parseJSON root = General <$> parseJSON root
+    <|> Bank <$> parseJSON root
+    <|> Insurance <$> parseJSON root
+
+unKeyIndustryBalanceSheets :: IndustryBalanceSheetsKeyed -> IndustryBalanceSheets
+unKeyIndustryBalanceSheets = mapIndustry
+  unKeyGeneralBalanceSheets
+  unKeyBankBalanceSheets
+  unKeyInsuranceBalanceSheets
+
+instance FromJSON IndustryBalanceSheets where
+  parseJSON root = unKeyIndustryBalanceSheets <$> parseJSON root
diff --git a/src/SimFin/Types/CashFlow.hs b/src/SimFin/Types/CashFlow.hs
new file mode 100644
--- /dev/null
+++ b/src/SimFin/Types/CashFlow.hs
@@ -0,0 +1,492 @@
+{-|
+Module      : SimFin.Types.CashFlow
+Description : Types to represent a company's cash flow statement.
+Copyright   : (c) Owen Shepherd, 2022
+License     : MIT
+Maintainer  : owen@owen.cafe
+-}
+
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE FlexibleInstances #-}
+
+module SimFin.Types.CashFlow
+  ( GeneralCashFlowRow(..)
+  , BankCashFlowRow(..)
+  , InsuranceCashFlowRow(..)
+  , IndustryCashFlows
+  , IndustryCashFlow
+  ) where
+
+import Control.Applicative ((<|>))
+import Data.Aeson
+import Data.Text (Text)
+import Data.Time.Calendar (Day)
+
+import SimFin.Types.Industry
+import SimFin.Internal
+
+
+------
+-- General
+------
+
+-- | Cash flow statement for general companies. 
+
+data GeneralCashFlowRow
+  = GeneralCashFlowRow
+  { simFinId :: Int
+  , ticker :: Text
+  , fiscalPeriod :: String
+  , fiscalYear :: Int
+  , reportDate :: Day
+  , publishDate :: Day
+  , restatedDate :: Day
+  , source :: Text
+  , tTM :: Bool
+  , valueCheck :: Bool
+  , netIncomeStartingLine :: Maybe Integer
+  , netIncome :: Maybe Integer
+  , netIncomeFromDiscontinuedOperations :: Maybe Integer
+  , otherAdjustmensts :: Maybe Integer
+  , depreciationAndAmortizatison :: Maybe Integer
+  , nonCashItems :: Maybe Integer
+  , stockBasedCompensatison :: Maybe Integer
+  , deferredIncomeTaxes :: Maybe Integer
+  , otherNonCashAdjustments :: Maybe Integer
+  , changeInWorkingCapital :: Maybe Integer
+  , changeInAccountsReceivable :: Maybe Integer
+  , changeInInventories :: Maybe Integer
+  , changeInAccountsPayable :: Maybe Integer
+  , changeInOther :: Maybe Integer
+  , netCashFromDiscontinuedOperationsOperating :: Maybe Integer
+  , netCashFromOpesratingActivities :: Maybe Integer
+  , changeInFixedAssetsAndIntsangibles :: Maybe Integer
+  , dispositionOfFixedAssetssAndIntangibles :: Maybe Integer
+  , dispositionOfFixedAsssets :: Maybe Integer
+  , dispositionOfIntangibleAssets :: Maybe Integer
+  , acquisitionOfFixedAssetsAndIntangibles :: Maybe Integer
+  , purchaseOfFixedAssets :: Maybe Integer
+  , acquisitionOfIntangibleAssets :: Maybe Integer
+  , otherChangeInFixedAssetsAndIntangibles :: Maybe Integer
+  , netChangeInLongTermInvestment :: Maybe Integer
+  , decreaseInLongTermInvestment :: Maybe Integer
+  , increaseInLongTermInvestment :: Maybe Integer
+  , netCashFromAcquisitionsAndDivestitures :: Maybe Integer
+  , netCashFromDivestitures :: Maybe Integer
+  , cashForAcquisitionOfSubsidiaries :: Maybe Integer
+  , cashForJointVentures :: Maybe Integer
+  , netCashFromOtherAcquisitions :: Maybe Integer
+  , otherInvestingActivities :: Maybe Integer
+  , netCashFromDiscontinuedOperationsInvesting :: Maybe Integer
+  , netCashFromInvestingActivities :: Maybe Integer
+  , dividendsPaid :: Maybe Integer
+  , cashFromRepaymentOfDebt :: Maybe Integer
+  , cashFromRepaymentOfShortTermDebtNet :: Maybe Integer
+  , cashFromRepaymentOfLongTermDebtNet :: Maybe Integer
+  , repaymentsOfLongTermDebt :: Maybe Integer
+  , cashFromLongTermDebt :: Maybe Integer
+  , cashFromRepurchaseOfEquity :: Maybe Integer
+  , increaseInCapitalStock :: Maybe Integer
+  , decreaseInCapitalStock :: Maybe Integer
+  , otherFinancingActivities :: Maybe Integer
+  , netCashFromDiscontinuedOperationsFinancing :: Maybe Integer
+  , netCashFromFinancingActivities :: Maybe Integer
+  , netCashBeforeDiscOperationsAndFX :: Maybe Integer
+  , changeInCashFromDiscOperationsAndOther :: Maybe Integer
+  , netCashBeforeFX :: Maybe Integer
+  , effectOfForeignExchangeRates :: Maybe Integer
+  , netChangeInCash :: Maybe Integer
+  } deriving Show
+
+instance FromJSON GeneralCashFlowRow where
+  parseJSON = withObject "GeneralCashFlowRow" $ \v -> GeneralCashFlowRow
+    <$> v .: "SimFinId"
+    <*> v .: "Ticker"
+    <*> v .: "Fiscal Period"
+    <*> v .: "Fiscal Year"
+    <*> v .: "Report Date"
+    <*> v .: "Publish Date"
+    <*> v .: "Restated Date"
+    <*> v .: "Source"
+    <*> v .: "TTM"
+    <*> v .: "Value Check"
+    <*> v .: "Net Income/Starting Line"
+    <*> v .: "Net Income"
+    <*> v .: "Net Income from Discontinued Operations"
+    <*> v .: "Other Adjustments"
+    <*> v .: "Depreciation & Amortization"
+    <*> v .: "Non-Cash Items"
+    <*> v .: "Stock-Based Compensation"
+    <*> v .: "Deferred Income Taxes"
+    <*> v .: "Other Non-Cash Adjustments"
+    <*> v .: "Change in Working Capital"
+    <*> v .: "Change in Accounts Receivable"
+    <*> v .: "Change in Inventories"
+    <*> v .: "Change in Accounts Payable"
+    <*> v .: "Change in Other"
+    <*> v .: "Net Cash from Discontinued Operations (Operating)"
+    <*> v .: "Net Cash from Operating Activities"
+    <*> v .: "Change in Fixed Assets & Intangibles"
+    <*> v .: "Disposition of Fixed Assets & Intangibles"
+    <*> v .: "Disposition of Fixed Assets"
+    <*> v .: "Disposition of Intangible Assets"
+    <*> v .: "Acquisition of Fixed Assets & Intangibles"
+    <*> v .: "Purchase of Fixed Assets"
+    <*> v .: "Acquisition of Intangible Assets"
+    <*> v .: "Other Change in Fixed Assets & Intangibles"
+    <*> v .: "Net Change in Long Term Investment"
+    <*> v .: "Decrease in Long Term Investment"
+    <*> v .: "Increase in Long Term Investment"
+    <*> v .: "Net Cash from Acquisitions & Divestitures"
+    <*> v .: "Net Cash from Divestitures"
+    <*> v .: "Cash for Acquisition of Subsidiaries"
+    <*> v .: "Cash for Joint Ventures"
+    <*> v .: "Net Cash from Other Acquisitions"
+    <*> v .: "Other Investing Activities"
+    <*> v .: "Net Cash from Discontinued Operations (Investing)"
+    <*> v .: "Net Cash from Investing Activities"
+    <*> v .: "Dividends Paid"
+    <*> v .: "Cash from (Repayment of) Debt"
+    <*> v .: "Cash from (Repayment of) Short Term Debt, Net"
+    <*> v .: "Cash from (Repayment of) Long Term Debt, Net"
+    <*> v .: "Repayments of Long Term Debt"
+    <*> v .: "Cash from Long Term Debt"
+    <*> v .: "Cash from (Repurchase of) Equity"
+    <*> v .: "Increase in Capital Stock"
+    <*> v .: "Decrease in Capital Stock"
+    <*> v .: "Other Financing Activities"
+    <*> v .: "Net Cash from Discontinued Operations (Financing)"
+    <*> v .: "Net Cash from Financing Activities"
+    <*> v .: "Net Cash Before Disc. Operations and FX"
+    <*> v .: "Change in Cash from Disc. Operations and Other"
+    <*> v .: "Net Cash Before FX"
+    <*> v .: "Effect of Foreign Exchange Rates"
+    <*> v .: "Net Change in Cash"
+
+-- | Wrapper to parse a GeneralCashFlowRow record from SimFin's JSON format.
+
+newtype GeneralCashFlowsKeyed = GeneralCashFlowsKeyed { unKeyGeneralCashFlows :: [GeneralCashFlowRow] }
+
+instance FromJSON GeneralCashFlowsKeyed where
+  parseJSON o = GeneralCashFlowsKeyed <$> (traverse parseJSON =<< createKeyedRows o)
+
+
+------
+-- Bank
+------
+
+-- | Cash flow statement for banks. 
+
+data BankCashFlowRow
+  = BankCashFlowRow
+  { simFinId :: Int
+  , ticker :: Text
+  , fiscalPeriod :: String
+  , fiscalYear :: Int
+  , reportDate :: Day
+  , publishDate :: Day
+  , restatedDate :: Day
+  , source :: Text
+  , tTM :: Bool
+  , valueCheck :: Bool
+  , netIncomeStartingLine :: Maybe Integer
+  , netIncome :: Maybe Integer
+  , netIncomeFromDiscontinuedOperations :: Maybe Integer
+  , otherAdjustments :: Maybe Integer
+  , depreciationAndAmortization :: Maybe Integer
+  , provisionForLoanLosses :: Maybe Integer
+  , nonCashItems :: Maybe Integer
+  , gainOnSaleOfSecuritiesAndLoans :: Maybe Integer
+  , deferredIncomeTaxes :: Maybe Integer
+  , stockBasedCompensation :: Maybe Integer
+  , otherNonCashAdjustments :: Maybe Integer
+  , changeInWorkingCapital :: Maybe Integer
+  , tradingAssetsAndLiabilities :: Maybe Integer
+  , netChangeOfInvestments :: Maybe Integer
+  , netChangeOfInterbankAssets :: Maybe Integer
+  , netChangeOfInterbankLiabilities :: Maybe Integer
+  , netChangeInOperatingLoans :: Maybe Integer
+  , accruedInterestReceivable :: Maybe Integer
+  , accruedInterestPayable :: Maybe Integer
+  , otherOperatingAssetsLiabilities :: Maybe Integer
+  , netCashFromDiscontinuedOperationsOperating :: Maybe Integer
+  , netCashFromOperatingActivities :: Maybe Integer
+  , changeInFixedAssetsAndIntangibles :: Maybe Integer
+  , dispositionOfFixedAssetsAndIntangibles :: Maybe Integer
+  , capitalExpenditures :: Maybe Integer
+  , netChangeInInvestments :: Maybe Integer
+  , decreaseInInvestments :: Maybe Integer
+  , decreaseInHTMInvestments :: Maybe Integer
+  , decreaseInAFSInvestments :: Maybe Integer
+  , increaseInInvestments :: Maybe Integer
+  , increaseInHTMInvestments :: Maybe Integer
+  , increaseInAFSInvestments :: Maybe Integer
+  , netChangeInOtherInvestments :: Maybe Integer
+  , netChangeInLoansAndInterbank :: Maybe Integer
+  , netChangeInCustomerLoans :: Maybe Integer
+  , netChangeInInterbankAssets :: Maybe Integer
+  , netChangeInOtherLoans :: Maybe Integer
+  , netCashFromAcquisitionsAndDivestitures :: Maybe Integer
+  , netCashFromDivestitures :: Maybe Integer
+  , cashForAcquisitionOfSubsidiaries :: Maybe Integer
+  , cashForJointVentures :: Maybe Integer
+  , netCashFromOtherAcquisitions :: Maybe Integer
+  , otherInvestingActivities :: Maybe Integer
+  , netCashFromDiscontinuedOperationsInvesting :: Maybe Integer
+  , netCashFromInvestingActivities :: Maybe Integer
+  , dividendsPaid :: Maybe Integer
+  , cashFromRepaymentOfDebt :: Maybe Integer
+  , cashFromRepaymentOfShortTermDebtNet :: Maybe Integer
+  , netChangeInInterbankTransfers :: Maybe Integer
+  , cashFromRepaymentOfLongTermDebtNet :: Maybe Integer
+  , repaymentsOfLongTermDebt :: Maybe Integer
+  , cashFromLongTermDebt :: Maybe Integer
+  , cashFromRepurchaseOfEquity :: Maybe Integer
+  , increaseInCapitalStock :: Maybe Integer
+  , decreaseInCapitalStock :: Maybe Integer
+  , netChangeInDeposits :: Maybe Integer
+  , otherFinancingActivities :: Maybe Integer
+  , netCashFromDiscontinuedOperationsFinancing :: Maybe Integer
+  , netCashFromFinancingActivities :: Maybe Integer
+  , netCashBeforeDiscOperationsAndFX :: Maybe Integer
+  , changeInCashFromDiscOperationsAndOther :: Maybe Integer
+  , netCashBeforeFX :: Maybe Integer
+  , effectOfForeignExchangeRates :: Maybe Integer
+  , netChangeInCash :: Maybe Integer
+  } deriving Show
+
+instance FromJSON BankCashFlowRow where
+  parseJSON = withObject "BankCashFlowRow" $ \v -> BankCashFlowRow
+    <$> v .: "SimFinId"
+    <*> v .: "Ticker"
+    <*> v .: "Fiscal Period"
+    <*> v .: "Fiscal Year"
+    <*> v .: "Report Date"
+    <*> v .: "Publish Date"
+    <*> v .: "Restated Date"
+    <*> v .: "Source"
+    <*> v .: "TTM"
+    <*> v .: "Value Check"
+    <*> v .: "Net Income/Starting Line"
+    <*> v .: "Net Income"
+    <*> v .: "Net Income from Discontinued Operations"
+    <*> v .: "Other Adjustments"
+    <*> v .: "Depreciation & Amortization"
+    <*> v .: "Provision for Loan Losses"
+    <*> v .: "Non-Cash Items"
+    <*> v .: "Gain On Sale of Securities & Loans"
+    <*> v .: "Deferred Income Taxes"
+    <*> v .: "Stock-Based Compensation"
+    <*> v .: "Other Non-Cash Adjustments"
+    <*> v .: "Change in Working Capital"
+    <*> v .: "Trading Assets & Liabilities"
+    <*> v .: "Net Change of Investments"
+    <*> v .: "Net Change of Interbank Assets"
+    <*> v .: "Net Change of Interbank Liabilities"
+    <*> v .: "Net Change in Operating Loans"
+    <*> v .: "Accrued Interest Receivable"
+    <*> v .: "Accrued Interest Payable"
+    <*> v .: "Other Operating Assets/Liabilities"
+    <*> v .: "Net Cash from Discontinued Operations (Operating)"
+    <*> v .: "Net Cash from Operating Activities"
+    <*> v .: "Change in Fixed Assets & Intangibles"
+    <*> v .: "Disposition of Fixed Assets & Intangibles"
+    <*> v .: "Capital Expenditures"
+    <*> v .: "Net Change in Investments"
+    <*> v .: "Decrease in Investments"
+    <*> v .: "Decrease in HTM Investments"
+    <*> v .: "Decrease in AFS Investments"
+    <*> v .: "Increase in Investments"
+    <*> v .: "Increase in HTM Investments"
+    <*> v .: "Increase in AFS Investments"
+    <*> v .: "Net Change in Other Investments"
+    <*> v .: "Net Change in Loans & Interbank"
+    <*> v .: "Net Change in Customer Loans"
+    <*> v .: "Net Change in Interbank Assets"
+    <*> v .: "Net Change in Other Loans"
+    <*> v .: "Net Cash from Acquisitions & Divestitures"
+    <*> v .: "Net Cash from Divestitures"
+    <*> v .: "Cash for Acquisition of Subsidiaries"
+    <*> v .: "Cash for Joint Ventures"
+    <*> v .: "Net Cash from Other Acquisitions"
+    <*> v .: "Other Investing Activities"
+    <*> v .: "Net Cash from Discontinued Operations (Investing)"
+    <*> v .: "Net Cash from Investing Activities"
+    <*> v .: "Dividends Paid"
+    <*> v .: "Cash from (Repayment of) Debt"
+    <*> v .: "Cash from (Repayment of) Short Term Debt, Net"
+    <*> v .: "Net Change in Interbank Transfers"
+    <*> v .: "Cash from (Repayment of) Long Term Debt, Net"
+    <*> v .: "Repayments of Long Term Debt"
+    <*> v .: "Cash from Long Term Debt"
+    <*> v .: "Cash from (Repurchase of) Equity"
+    <*> v .: "Increase in Capital Stock"
+    <*> v .: "Decrease in Capital Stock"
+    <*> v .: "Net Change In Deposits"
+    <*> v .: "Other Financing Activities"
+    <*> v .: "Net Cash from Discontinued Operations (Financing)"
+    <*> v .: "Net Cash from Financing Activities"
+    <*> v .: "Net Cash Before Disc. Operations and FX"
+    <*> v .: "Change in Cash from Disc. Operations and Other"
+    <*> v .: "Net Cash Before FX"
+    <*> v .: "Effect of Foreign Exchange Rates"
+    <*> v .: "Net Change in Cash"
+
+-- | Wrapper to parse a BankCashFlowRow record from SimFin's JSON format.
+
+newtype BankCashFlowsKeyed = BankCashFlowsKeyed { unKeyBankCashFlows :: [BankCashFlowRow] }
+
+instance FromJSON BankCashFlowsKeyed where
+  parseJSON o = BankCashFlowsKeyed <$> (traverse parseJSON =<< createKeyedRows o)
+
+
+------
+-- Insurance
+------
+
+-- | Cash flow statement for Insurance companies. 
+
+data InsuranceCashFlowRow
+  = InsuranceCashFlowRow
+  { simFinId :: Int
+  , ticker :: Text
+  , fiscalPeriod :: String
+  , fiscalYear :: Int
+  , reportDate :: Day
+  , publishDate :: Day
+  , restatedDate :: Day
+  , source :: Text
+  , tTM :: Bool
+  , valueCheck :: Bool
+  , netIncomeStartingLine :: Maybe Integer
+  , netIncome :: Maybe Integer
+  , netIncomeFromDiscontinuedOperations :: Maybe Integer
+  , otherAdjustments :: Maybe Integer
+  , depreciationAndAmortization :: Maybe Integer
+  , nonCashItems :: Maybe Integer
+  , stockBasedCompensation :: Maybe Integer
+  , deferredIncomeTaxes :: Maybe Integer
+  , otherNonCashAdjustments :: Maybe Integer
+  , changeInWorkingCapital :: Maybe Integer
+  , netCashFromDiscontinuedOperationsOperating :: Maybe Integer
+  , netCashFromOperatingActivities :: Maybe Integer
+  , changeInFixedAssetsAndIntangibles :: Maybe Integer
+  , dispositionOfFixedAssetsAndIntangibles :: Maybe Integer
+  , acquisitionOfFixedAssetsAndIntangibles :: Maybe Integer
+  , netChangeInInvestments :: Maybe Integer
+  , increaseInInvestments :: Maybe Integer
+  , decreaseInInvestments :: Maybe Integer
+  , otherInvestingActivities :: Maybe Integer
+  , netCashFromDiscontinuedOperationsInvesting :: Maybe Integer
+  , netCashFromInvestingActivities :: Maybe Integer
+  , dividendsPaid :: Maybe Integer
+  , cashFromRepaymentOfDebt :: Maybe Integer
+  , cashFromRepaymentOfShortTermDebtNet :: Maybe Integer
+  , cashFromRepaymentOfLongTermDebtNet :: Maybe Integer
+  , repaymentsOfLongTermDebt :: Maybe Integer
+  , cashFromLongTermDebt :: Maybe Integer
+  , cashFromRepurchaseOfEquity :: Maybe Integer
+  , increaseInCapitalStock :: Maybe Integer
+  , decreaseInCapitalStock :: Maybe Integer
+  , changeInInsuranceReserves :: Maybe Integer
+  , otherFinancingActivities :: Maybe Integer
+  , netCashFromDiscontinuedOperationsFinancing :: Maybe Integer
+  , netCashFromFinancingActivities :: Maybe Integer
+  , netCashBeforeDiscOperationsAndFX :: Maybe Integer
+  , changeInCashFromDiscOperationsAndOther :: Maybe Integer
+  , netCashBeforeFX :: Maybe Integer
+  , effectOfForeignExchangeRates :: Maybe Integer
+  , netChangeInCash :: Maybe Integer
+  } deriving Show
+
+instance FromJSON InsuranceCashFlowRow where
+  parseJSON = withObject "InsuranceCashFlowRow" $ \v -> InsuranceCashFlowRow
+    <$> v .: "SimFinId"
+    <*> v .: "Ticker"
+    <*> v .: "Fiscal Period"
+    <*> v .: "Fiscal Year"
+    <*> v .: "Report Date"
+    <*> v .: "Publish Date"
+    <*> v .: "Restated Date"
+    <*> v .: "Source"
+    <*> v .: "TTM"
+    <*> v .: "Value Check"
+    <*> v .: "Net Income/Starting Line"
+    <*> v .: "Net Income"
+    <*> v .: "Net Income from Discontinued Operations"
+    <*> v .: "Other Adjustments"
+    <*> v .: "Depreciation & Amortization"
+    <*> v .: "Non-Cash Items"
+    <*> v .: "Stock-Based Compensation"
+    <*> v .: "Deferred Income Taxes"
+    <*> v .: "Other Non-Cash Adjustments"
+    <*> v .: "Change in Working Capital"
+    <*> v .: "Net Cash from Discontinued Operations (Operating)"
+    <*> v .: "Net Cash from Operating Activities"
+    <*> v .: "Change in Fixed Assets & Intangibles"
+    <*> v .: "Disposition of Fixed Assets & Intangibles"
+    <*> v .: "Acquisition of Fixed Assets & Intangibles"
+    <*> v .: "Net Change in Investments"
+    <*> v .: "Increase in Investments"
+    <*> v .: "Decrease in Investments"
+    <*> v .: "Other Investing Activities"
+    <*> v .: "Net Cash from Discontinued Operations (Investing)"
+    <*> v .: "Net Cash from Investing Activities"
+    <*> v .: "Dividends Paid"
+    <*> v .: "Cash from (Repayment of) Debt"
+    <*> v .: "Cash from (Repayment of) Short Term Debt, Net"
+    <*> v .: "Cash from (Repayment of) Long Term Debt, Net"
+    <*> v .: "Repayments of Long Term Debt"
+    <*> v .: "Cash from Long Term Debt"
+    <*> v .: "Cash from (Repurchase of) Equity"
+    <*> v .: "Increase in Capital Stock"
+    <*> v .: "Decrease in Capital Stock"
+    <*> v .: "Change in Insurance Reserves"
+    <*> v .: "Other Financing Activities"
+    <*> v .: "Net Cash from Discontinued Operations (Financing)"
+    <*> v .: "Net Cash from Financing Activities"
+    <*> v .: "Net Cash Before Disc. Operations and FX"
+    <*> v .: "Change in Cash from Disc. Operations and Other"
+    <*> v .: "Net Cash Before FX"
+    <*> v .: "Effect of Foreign Exchange Rates"
+    <*> v .: "Net Change in Cash"
+
+-- | Wrapper to parse an InsuranceCashFlowRow record from SimFin's JSON format.
+
+newtype InsuranceCashFlowsKeyed = InsuranceCashFlowsKeyed { unKeyInsuranceCashFlows :: [InsuranceCashFlowRow] }
+
+instance FromJSON InsuranceCashFlowsKeyed where
+  parseJSON o = InsuranceCashFlowsKeyed <$> (traverse parseJSON =<< createKeyedRows o)
+
+
+------
+-- Industry
+------
+
+type IndustryCashFlowsKeyed
+  = Industry GeneralCashFlowsKeyed BankCashFlowsKeyed InsuranceCashFlowsKeyed
+
+-- | Discrimination of cash flow lists.
+
+type IndustryCashFlows
+  = Industry [GeneralCashFlowRow] [BankCashFlowRow] [InsuranceCashFlowRow]
+
+-- | Discrimination of cash flows.
+
+type IndustryCashFlow
+  = Industry GeneralCashFlowRow BankCashFlowRow InsuranceCashFlowRow
+
+instance FromJSON IndustryCashFlowsKeyed where
+  parseJSON root = Insurance <$> parseJSON root
+    <|> Bank <$> parseJSON root
+    <|> General <$> parseJSON root
+
+unKeyIndustryCashFlows :: IndustryCashFlowsKeyed -> IndustryCashFlows
+unKeyIndustryCashFlows = mapIndustry
+  unKeyGeneralCashFlows
+  unKeyBankCashFlows
+  unKeyInsuranceCashFlows
+
+instance FromJSON IndustryCashFlows where
+  parseJSON root = unKeyIndustryCashFlows <$> parseJSON root
diff --git a/src/SimFin/Types/CompanyInfo.hs b/src/SimFin/Types/CompanyInfo.hs
new file mode 100644
--- /dev/null
+++ b/src/SimFin/Types/CompanyInfo.hs
@@ -0,0 +1,48 @@
+{-|
+Module      : SimFin.Types.CompanyInfo
+Description : General information about a company.
+Copyright   : (c) Owen Shepherd, 2022
+License     : MIT
+Maintainer  : owen@owen.cafe
+-}
+
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module SimFin.Types.CompanyInfo
+  ( CompanyInfoRow(..)
+  ) where
+
+import Control.Monad ((>=>))
+import Data.Aeson
+import Data.Aeson.Types (Parser)
+import Data.Text (Text)
+
+import SimFin.Internal
+
+-- | Genreal information about a company.
+-- See the [SimFin docs](https://simfin.com/api/v2/documentation/#tag/Company/paths/~1companies~1general/get).
+
+data CompanyInfoRow
+  = CompanyInfoRow
+  { simFinId :: Int
+  , ticker :: Text
+  , companyName :: Text
+  , industryId :: Int
+  , monthFYEnd :: Int
+  , numberEmployees :: Int
+  , businessSummary :: Text
+  } deriving Show
+
+instance FromJSON CompanyInfoRow where
+  parseJSON = createKeyedRow >=> withObject "CompanyInfoRow" f
+    where
+      f :: Object -> Parser CompanyInfoRow
+      f = \v -> CompanyInfoRow
+        <$> v .: "SimFinId"
+        <*> v .: "Ticker"
+        <*> v .: "Company Name"
+        <*> v .: "IndustryId"
+        <*> v .: "Month FY End"
+        <*> v .: "Number Employees"
+        <*> v .: "Business Summary"
diff --git a/src/SimFin/Types/CompanyListing.hs b/src/SimFin/Types/CompanyListing.hs
new file mode 100644
--- /dev/null
+++ b/src/SimFin/Types/CompanyListing.hs
@@ -0,0 +1,41 @@
+{-|
+Module      : SimFin.Types.CompanyListing
+Description : Item of a list of company tickers and their SimFin IDs.
+Copyright   : (c) Owen Shepherd, 2022
+License     : MIT
+Maintainer  : owen@owen.cafe
+-}
+
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module SimFin.Types.CompanyListing
+  ( CompanyListingRow(..)
+  , CompanyListingKeyed(..)
+  ) where
+
+import Data.Aeson
+import Data.Text (Text)
+
+import SimFin.Internal
+
+-- | SimFin ID and company ticker. See the [SimFin docs](https://simfin.com/api/v2/documentation/#tag/Company/paths/~1companies~1list/get).
+
+data CompanyListingRow
+  = CompanyListingRow
+  { simFinId :: Int
+  , ticker :: Text
+  } deriving Show
+
+-- | Wrapper to parse a CompanyListing record from SimFin's JSON format.
+-- You probably don't want to use this.
+
+newtype CompanyListingKeyed = CompanyListingKeyed { unKeyCompanyListing :: [CompanyListingRow] }
+
+instance FromJSON CompanyListingRow where
+  parseJSON = withObject "CompanyListing" $ \v -> CompanyListingRow
+    <$> v .: "SimFinId"
+    <*> v .: "Ticker"
+
+instance FromJSON CompanyListingKeyed where
+  parseJSON o = CompanyListingKeyed <$> (traverse parseJSON =<< createKeyedRows o)
diff --git a/src/SimFin/Types/Derived.hs b/src/SimFin/Types/Derived.hs
new file mode 100644
--- /dev/null
+++ b/src/SimFin/Types/Derived.hs
@@ -0,0 +1,108 @@
+{-|
+Module      : SimFin.Types.Derived
+Description : Types to represent SimFin derived figures.
+Copyright   : (c) Owen Shepherd, 2022
+License     : MIT
+Maintainer  : owen@owen.cafe
+-}
+
+{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE FlexibleInstances #-}
+
+module SimFin.Types.Derived
+  ( DerivedRow(..)
+  , DerivedRowsKeyed(..)
+  ) where
+
+import Data.Aeson
+import Data.Text (Text)
+import Data.Time.Calendar (Day)
+
+import SimFin.Types.StringFrac
+import SimFin.Types.FiscalPeriod
+import SimFin.Internal
+
+-- | Cash flow statement for general companies. 
+
+data DerivedRow a
+  = DerivedRow
+  { simFinId :: Int
+  , ticker :: Text
+  , fiscalPeriod :: FiscalPeriod
+  , fiscalYear :: Int
+  , reportDate :: Day
+  , publishDate :: Day
+  , restatedDate :: Day
+  , source :: Text
+  , tTM :: Bool
+  , valueCheck :: Bool
+  , eBITDA :: Maybe a
+  , totalDebt :: Maybe a
+  , freeCashFlow :: Maybe a
+  , grossProfitMargin :: Maybe a
+  , operatingMargin :: Maybe a
+  , netProfitMargin :: Maybe a
+  , returnOnEquity :: Maybe a
+  , returnOnAssets :: Maybe a
+  , freeCashFlowToNetIncome :: Maybe a
+  , currentRatio :: Maybe a
+  , liabilitiesToEquityRatio :: Maybe a
+  , debtRatio :: Maybe a
+  , earningsPerShareBasic :: Maybe a
+  , earningsPerShareDiluted :: Maybe a
+  , salesPerShare :: Maybe a
+  , equityPerShare :: Maybe a
+  , freeCashFlowPerShare :: Maybe a
+  , dividendsPerShare :: Maybe a
+  , piotroskiFScore :: Maybe Int
+  , returnOnInvestedCapital :: Maybe a
+  , cashReturnOnInvestedCapital :: Maybe a
+  , dividendPayoutRatio :: Maybe a
+  , netDebtEBITDA :: Maybe a
+  , netDebtEBIT :: Maybe a
+  } deriving (Functor, Show)
+
+instance (Read a, RealFrac a) => FromJSON (DerivedRow a) where
+  parseJSON = withObject "DerivedRow" $ \v -> fmap (fmap unStringFrac) $ DerivedRow
+    <$> v .: "SimFinId"
+    <*> v .: "Ticker"
+    <*> v .: "Fiscal Period"
+    <*> v .: "Fiscal Year"
+    <*> v .: "Report Date"
+    <*> v .: "Publish Date"
+    <*> v .: "Restated Date"
+    <*> v .: "Source"
+    <*> v .: "TTM"
+    <*> v .: "Value Check"
+    <*> v .: "EBITDA"
+    <*> v .: "Total Debt"
+    <*> v .: "Free Cash Flow"
+    <*> v .: "Gross Profit Margin"
+    <*> v .: "Operating Margin"
+    <*> v .: "Net Profit Margin"
+    <*> v .: "Return on Equity"
+    <*> v .: "Return on Assets"
+    <*> v .: "Free Cash Flow to Net Income"
+    <*> v .: "Current Ratio"
+    <*> v .: "Liabilities to Equity Ratio"
+    <*> v .: "Debt Ratio"
+    <*> v .: "Earnings Per Share, Basic"
+    <*> v .: "Earnings Per Share, Diluted"
+    <*> v .: "Sales Per Share"
+    <*> v .: "Equity Per Share"
+    <*> v .: "Free Cash Flow Per Share"
+    <*> v .: "Dividends Per Share"
+    <*> v .: "Piotroski F-Score"
+    <*> v .: "Return On Invested Capital"
+    <*> v .: "Cash Return On Invested Capital"
+    <*> v .: "Dividend Payout Ratio"
+    <*> v .: "Net Debt / EBITDA"
+    <*> v .: "Net Debt / EBIT"
+
+-- | Wrapper to parse a DerivedRow record from SimFin's JSON format.
+
+newtype DerivedRowsKeyed a = DerivedRowsKeyed { unDerivedRows :: [DerivedRow a] }
+
+instance (Read a, RealFrac a) => FromJSON (DerivedRowsKeyed a) where
+  parseJSON o = fmap DerivedRowsKeyed $ traverse parseJSON =<< createKeyedRows o
diff --git a/src/SimFin/Types/FiscalPeriod.hs b/src/SimFin/Types/FiscalPeriod.hs
new file mode 100644
--- /dev/null
+++ b/src/SimFin/Types/FiscalPeriod.hs
@@ -0,0 +1,50 @@
+{-|
+Module      : SimFin.Types.FiscalPeriod
+Description : Types that describe a fiscal period.
+Copyright   : (c) Owen Shepherd, 2022
+License     : MIT
+Maintainer  : owen@owen.cafe
+-}
+
+{-# LANGUAGE OverloadedStrings #-}
+
+module SimFin.Types.FiscalPeriod
+  ( FiscalPeriod(..)
+  , fiscalPeriodParam
+  ) where
+
+import Data.Aeson
+import Data.ByteString (ByteString)
+import qualified Data.Text as T
+
+-- | A fiscal period as understood by the SimFin API.
+
+data FiscalPeriod = Q1 | Q2 | Q3 | Q4 | H1 | H2 | FullYear | FirstNineMonths | SixMonths
+  deriving (Eq, Show)
+
+instance FromJSON FiscalPeriod where
+  parseJSON = withText "FiscalPeriod" $ \t -> case T.toLower t of
+    "q1" -> pure Q1
+    "q2" -> pure Q2
+    "q3" -> pure Q3
+    "q4" -> pure Q4
+    "h1" -> pure H1
+    "h2" -> pure H2
+    "fy" -> pure FullYear
+    "9m" -> pure FirstNineMonths
+    "6m" -> pure SixMonths
+    _ -> fail "Invalid fiscal year string"
+
+-- | Converts a fiscal period into a query string fragment as understood by the SimFin API.
+
+fiscalPeriodParam :: FiscalPeriod -> ByteString
+fiscalPeriodParam a = case a of
+  Q1 -> "q1"
+  Q2 -> "q2"
+  Q3 -> "q3"
+  Q4 -> "q4"
+  H1 -> "h1"
+  H2 -> "h2"
+  FullYear-> "fy"
+  FirstNineMonths -> "9m"
+  SixMonths -> "6m"
diff --git a/src/SimFin/Types/Industry.hs b/src/SimFin/Types/Industry.hs
new file mode 100644
--- /dev/null
+++ b/src/SimFin/Types/Industry.hs
@@ -0,0 +1,43 @@
+{-|
+Module      : SimFin.Types.Industry
+Description : Parameterized sum type to distinguish between different industry specific data.
+Copyright   : (c) Owen Shepherd, 2022
+License     : MIT
+Maintainer  : owen@owen.cafe
+-}
+
+module SimFin.Types.Industry
+  ( Industry(..)
+  , mapIndustry
+  , invertIndustries
+  ) where
+
+import Data.Foldable
+
+-- | Distinguish between different industry-specific data.
+
+data Industry general bank insurance
+  = General general
+  | Bank bank
+  | Insurance insurance
+  deriving Show
+
+-- | Map all discriminations of an Industry.
+
+mapIndustry :: (a -> a') -> (b -> b') -> (c -> c') -> Industry a b c -> Industry a' b' c'
+mapIndustry f g h industry = case industry of
+  General a -> General $ f a
+  Bank a -> Bank $ g a
+  Insurance a -> Insurance $ h a
+
+invertIndustry :: Industry [a] [b] [c] -> [Industry a b c]
+invertIndustry ind = case ind of
+  General as -> General <$> as
+  Bank bs -> Bank <$> bs
+  Insurance cs -> Insurance <$> cs
+
+-- | List of discriminations of lists to list of discriminations.
+-- Used to removing extra nesting from the statements API results.
+
+invertIndustries :: [Industry [a] [b] [c]] -> [Industry a b c]
+invertIndustries = fold . fmap invertIndustry
diff --git a/src/SimFin/Types/Prices.hs b/src/SimFin/Types/Prices.hs
new file mode 100644
--- /dev/null
+++ b/src/SimFin/Types/Prices.hs
@@ -0,0 +1,62 @@
+{-|
+Module      : SimFin.Types.Prices
+Description : Types to represent SimFin price results.
+Copyright   : (c) Owen Shepherd, 2022
+License     : MIT
+Maintainer  : owen@owen.cafe
+-}
+
+{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module SimFin.Types.Prices
+  ( PricesRow(..)
+  , PricesKeyed(..)
+  ) where
+
+import Data.Aeson
+import Data.Text (Text)
+import Data.Time.Calendar (Day)
+
+import SimFin.Types.StringFrac
+import SimFin.Internal
+
+-- | Prices of a company over a single day. 
+
+data PricesRow a
+  = PricesRow
+  { simFinId :: Int
+  , ticker :: Text
+  , date :: Maybe Day
+  , open :: a
+  , high :: a
+  , low :: a
+  , close :: a
+  , adjClose :: a
+  , volume :: Integer
+  , dividend :: Maybe a
+  , commonSharesOutstanding :: Maybe Integer
+  } deriving (Functor, Show)
+
+instance (Read a, RealFrac a) => FromJSON (PricesRow a) where
+  parseJSON = withObject "PricesRow" $ \v -> fmap (fmap unStringFrac) $ PricesRow
+    <$> v .: "SimFinId"
+    <*> v .: "Ticker"
+    <*> v .: "Date"
+    <*> v .: "Open"
+    <*> v .: "High"
+    <*> v .: "Low"
+    <*> v .: "Close"
+    <*> v .: "Adj. Close"
+    <*> v .: "Volume"
+    <*> v .: "Dividend"
+    <*> v .: "Common Shares Outstanding"
+
+-- | Wrapper to parse a PricesRow record from SimFin's JSON format.
+
+newtype PricesKeyed a = PricesKeyed { unKeyPrices :: [PricesRow a] }
+
+instance (Read a, RealFrac a) => FromJSON (PricesKeyed a) where
+  parseJSON o = PricesKeyed <$> (traverse parseJSON =<< createKeyedRows o)
diff --git a/src/SimFin/Types/PricesAndRatios.hs b/src/SimFin/Types/PricesAndRatios.hs
new file mode 100644
--- /dev/null
+++ b/src/SimFin/Types/PricesAndRatios.hs
@@ -0,0 +1,43 @@
+{-|
+Module      : SimFin.Types.Prices
+Description : Type to represent a combination of SimFin prices and ratios.
+Copyright   : (c) Owen Shepherd, 2022
+License     : MIT
+Maintainer  : owen@owen.cafe
+-}
+
+{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module SimFin.Types.PricesAndRatios
+  ( PricesAndRatiosRow(..)
+  , PricesAndRatiosKeyed(..)
+  ) where
+
+import Data.Aeson
+
+import SimFin.Types.Prices
+import SimFin.Types.Ratios
+import SimFin.Internal
+
+-- | Represents a company's prices and ratios.
+
+data PricesAndRatiosRow a
+  = PricesAndRatiosRow
+  { prices :: PricesRow a
+  , ratios :: RatiosRow a
+  } deriving (Functor, Show)
+
+instance (Read a, RealFrac a) => FromJSON (PricesAndRatiosRow a) where
+  parseJSON v = PricesAndRatiosRow
+    <$> parseJSON v
+    <*> parseJSON v
+
+-- | Wrapper to parse a PricesAndRatiosRow record from SimFin's JSON format.
+-- You probably don't want to use this.
+
+newtype PricesAndRatiosKeyed a = PricesAndRatiosKeyed { unKeyPricesAndRatios :: [PricesAndRatiosRow a] }
+
+instance (Read a, RealFrac a) => FromJSON (PricesAndRatiosKeyed a) where
+  parseJSON o = PricesAndRatiosKeyed <$> (traverse parseJSON =<< createKeyedRows o)
diff --git a/src/SimFin/Types/PricesQuery.hs b/src/SimFin/Types/PricesQuery.hs
new file mode 100644
--- /dev/null
+++ b/src/SimFin/Types/PricesQuery.hs
@@ -0,0 +1,72 @@
+{-|
+Module      : SimFin.Types.PricesQuery
+Description : Types to represent SimFin price queries.
+Copyright   : (c) Owen Shepherd, 2022
+License     : MIT
+Maintainer  : owen@owen.cafe
+-}
+
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+
+module SimFin.Types.PricesQuery
+  ( PricesQuery(..)
+  , PricesQueryFree
+  , pricesQueryToQueryParams
+  , pricesQueryFreeToQueryParams
+  ) where
+
+import Data.List.NonEmpty (NonEmpty)
+import Data.Maybe (maybeToList)
+import Data.Time.Calendar (Day)
+
+import SimFin.Types.StockRef
+import SimFin.Internal
+
+-- | This represents all options the prices endpoint supports.
+-- Some of these parameters are only available to SimFin+ users.
+-- For free users, please use 'PricesQueryFree'.
+
+data PricesQuery
+  = PricesQuery
+  { stockRefs :: NonEmpty StockRef
+  , start :: Maybe Day
+  , end :: Maybe Day
+  , asReported :: Bool
+  } deriving Show
+
+-- | Represents all the parameters available to free users.
+
+type PricesQueryFree = StockRef
+
+-- | Turn a 'PricesQuery' into query parameters for the SimFin "prices" endpoint.
+
+pricesQueryToQueryParams :: PricesQuery -> [QueryParam]
+pricesQueryToQueryParams PricesQuery{..} = 
+  let
+    refParams = stockRefsToQueryParams stockRefs
+    startParam = toShownCommaQueryParam "start "$ maybeToList start
+    endParam = toShownCommaQueryParam "end" $ maybeToList end
+    asReportedParam = toBoolQueryParam "asreported" asReported
+  in
+  mconcat
+    [ refParams
+    , startParam
+    , endParam
+    , asReportedParam
+    ]
+
+freeToPlus :: PricesQueryFree -> PricesQuery
+freeToPlus stockRef
+  = PricesQuery
+  { stockRefs = pure stockRef
+  , start = Nothing
+  , end = Nothing
+  , asReported = False
+  }
+
+-- | Turn a 'PricesQueryFree' into query parameters for the SimFin "prices" endpoint.
+
+pricesQueryFreeToQueryParams :: PricesQueryFree -> [QueryParam]
+pricesQueryFreeToQueryParams = pricesQueryToQueryParams . freeToPlus
diff --git a/src/SimFin/Types/ProfitAndLoss.hs b/src/SimFin/Types/ProfitAndLoss.hs
new file mode 100644
--- /dev/null
+++ b/src/SimFin/Types/ProfitAndLoss.hs
@@ -0,0 +1,497 @@
+{-|
+Module      : SimFin.Types.ProfitAndLoss
+Description : Types to represent a company's profit and loss statement.
+Copyright   : (c) Owen Shepherd, 2022
+License     : MIT
+Maintainer  : owen@owen.cafe
+-}
+
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE FlexibleInstances #-}
+
+module SimFin.Types.ProfitAndLoss
+  ( GeneralProfitAndLossRow(..)
+  , BankProfitAndLossRow(..)
+  , InsuranceProfitAndLossRow(..)
+  , IndustryProfitsAndLosses
+  , IndustryProfitAndLoss
+  ) where
+
+import Control.Applicative ((<|>))
+import Data.Aeson
+import Data.Text (Text)
+import Data.Time.Calendar (Day)
+
+import SimFin.Types.Industry
+import SimFin.Internal
+
+
+------
+-- General
+------
+
+-- | Cash flow statement for general companies. 
+
+data GeneralProfitAndLossRow
+  = GeneralProfitAndLossRow
+  { simFinId :: Int
+  , ticker :: Text
+  , fiscalPeriod :: String
+  , fiscalYear :: Int
+  , reportDate :: Day
+  , publishDate :: Day
+  , restatedDate :: Day
+  , source :: Text
+  , tTM :: Bool
+  , valueCheck :: Bool
+  , revenue :: Maybe Integer
+  , salesAndServicesRevenue :: Maybe Integer
+  , financingRevenue :: Maybe Integer
+  , otherRevenue :: Maybe Integer
+  , costOfRevenue :: Maybe Integer
+  , costOfGoodsAndServices :: Maybe Integer
+  , costOfFinancingRevenue :: Maybe Integer
+  , costOfOtherRevenue :: Maybe Integer
+  , grossProfit :: Maybe Integer
+  , otherOperatingIncome :: Maybe Integer
+  , operatingExpenses :: Maybe Integer
+  , sellingGeneralAndAdministrative :: Maybe Integer
+  , sellingAndMarketing :: Maybe Integer
+  , generalAndAdministrative :: Maybe Integer
+  , researchAndDevelopment :: Maybe Integer
+  , depreciationAndAmortization :: Maybe Integer
+  , provisionForDoubtfulAccounts :: Maybe Integer
+  , otherOperatingExpenses :: Maybe Integer
+  , operatingIncomeLoss :: Maybe Integer
+  , nonOperatingIncomeLoss :: Maybe Integer
+  , interestExpenseNet :: Maybe Integer
+  , interestExpense :: Maybe Integer
+  , interestIncome :: Maybe Integer
+  , otherInvestmentIncomeLoss :: Maybe Integer
+  , foreignExchangeGainLoss :: Maybe Integer
+  , incomeLossFromAffiliates :: Maybe Integer
+  , otherNonOperatingIncomeLoss :: Maybe Integer
+  , pretaxIncomeLossAdj :: Maybe Integer
+  , abnormalGainsLosses :: Maybe Integer
+  , acquiredInProcessRAndD :: Maybe Integer
+  , mergerAndAcquisitionExpense :: Maybe Integer
+  , abnormalDerivatives :: Maybe Integer
+  , disposalOfAssets :: Maybe Integer
+  , earlyExtinguishmentOfDebt :: Maybe Integer
+  , assetWriteDown :: Maybe Integer
+  , impairmentOfGoodwillAndIntangibles :: Maybe Integer
+  , saleOfBusiness :: Maybe Integer
+  , legalSettlement :: Maybe Integer
+  , restructuringCharges :: Maybe Integer
+  , saleOfInvestmentsAndUnrealizedInvestments :: Maybe Integer
+  , insuranceSettlement :: Maybe Integer
+  , otherAbnormalItems :: Maybe Integer
+  , pretaxIncomeLoss :: Maybe Integer
+  , incomeTaxExpenseBenefitNet :: Maybe Integer
+  , currentIncomeTax :: Maybe Integer
+  , deferredIncomeTax :: Maybe Integer
+  , taxAllowanceCredit :: Maybe Integer
+  , incomeLossFromAffiliatesNetOfTaxes :: Maybe Integer
+  , incomeLossFromContinuingOperations :: Maybe Integer
+  , netExtraordinaryGainsLosses :: Maybe Integer
+  , discontinuedOperations :: Maybe Integer
+  , accountingChargesAndOther :: Maybe Integer
+  , incomeLossInclMinorityInterest :: Maybe Integer
+  , minorityInterest :: Maybe Integer
+  , netIncome :: Maybe Integer
+  , preferredDividends :: Maybe Integer
+  , otherAdjustments :: Maybe Integer
+  , netIncomeCommon :: Maybe Integer
+  } deriving Show
+
+instance FromJSON GeneralProfitAndLossRow where
+  parseJSON = withObject "GeneralProfitAndLossRow" $ \v -> GeneralProfitAndLossRow
+    <$> v .: "SimFinId"
+    <*> v .: "Ticker"
+    <*> v .: "Fiscal Period"
+    <*> v .: "Fiscal Year"
+    <*> v .: "Report Date"
+    <*> v .: "Publish Date"
+    <*> v .: "Restated Date"
+    <*> v .: "Source"
+    <*> v .: "TTM"
+    <*> v .: "Value Check"
+    <*> v .: "Revenue"
+    <*> v .: "Sales & Services Revenue"
+    <*> v .: "Financing Revenue"
+    <*> v .: "Other Revenue"
+    <*> v .: "Cost of Revenue"
+    <*> v .: "Cost of Goods & Services"
+    <*> v .: "Cost of Financing Revenue"
+    <*> v .: "Cost of Other Revenue"
+    <*> v .: "Gross Profit"
+    <*> v .: "Other Operating Income"
+    <*> v .: "Operating Expenses"
+    <*> v .: "Selling, General & Administrative"
+    <*> v .: "Selling & Marketing"
+    <*> v .: "General & Administrative"
+    <*> v .: "Research & Development"
+    <*> v .: "Depreciation & Amortization"
+    <*> v .: "Provision for Doubtful Accounts"
+    <*> v .: "Other Operating Expenses"
+    <*> v .: "Operating Income (Loss)"
+    <*> v .: "Non-Operating Income (Loss)"
+    <*> v .: "Interest Expense, Net"
+    <*> v .: "Interest Expense"
+    <*> v .: "Interest Income"
+    <*> v .: "Other Investment Income (Loss)"
+    <*> v .: "Foreign Exchange Gain (Loss)"
+    <*> v .: "Income (Loss) from Affiliates"
+    <*> v .: "Other Non-Operating Income (Loss)"
+    <*> v .: "Pretax Income (Loss), Adj."
+    <*> v .: "Abnormal Gains (Losses)"
+    <*> v .: "Acquired In-Process R&D"
+    <*> v .: "Merger & Acquisition Expense"
+    <*> v .: "Abnormal Derivatives"
+    <*> v .: "Disposal of Assets"
+    <*> v .: "Early Extinguishment of Debt"
+    <*> v .: "Asset Write-Down"
+    <*> v .: "Impairment of Goodwill & Intangibles"
+    <*> v .: "Sale of Business"
+    <*> v .: "Legal Settlement"
+    <*> v .: "Restructuring Charges"
+    <*> v .: "Sale of Investments & Unrealized Investments"
+    <*> v .: "Insurance Settlement"
+    <*> v .: "Other Abnormal Items"
+    <*> v .: "Pretax Income (Loss)"
+    <*> v .: "Income Tax (Expense) Benefit, Net"
+    <*> v .: "Current Income Tax"
+    <*> v .: "Deferred Income Tax"
+    <*> v .: "Tax Allowance/Credit"
+    <*> v .: "Income (Loss) from Affiliates, Net of Taxes"
+    <*> v .: "Income (Loss) from Continuing Operations"
+    <*> v .: "Net Extraordinary Gains (Losses)"
+    <*> v .: "Discontinued Operations"
+    <*> v .: "Accounting Charges & Other"
+    <*> v .: "Income (Loss) Incl. Minority Interest"
+    <*> v .: "Minority Interest"
+    <*> v .: "Net Income"
+    <*> v .: "Preferred Dividends"
+    <*> v .: "Other Adjustments"
+    <*> v .: "Net Income (Common)"
+
+-- | Wrapper to parse a GeneralProfitAndLossRow record from SimFin's JSON format.
+
+newtype GeneralProfitsAndLossesKeyed = GeneralProfitsAndLossesKeyed { unKeyGeneralProfitsAndLosses :: [GeneralProfitAndLossRow] }
+
+instance FromJSON GeneralProfitsAndLossesKeyed where
+  parseJSON o = GeneralProfitsAndLossesKeyed <$> (traverse parseJSON =<< createKeyedRows o)
+
+
+------
+-- Bank
+------
+
+-- | Cash flow statement for banks. 
+
+data BankProfitAndLossRow
+  = BankProfitAndLossRow
+  { simFinId :: Int
+  , ticker :: Text
+  , fiscalPeriod :: String
+  , fiscalYear :: Int
+  , reportDate :: Day
+  , publishDate :: Day
+  , restatedDate :: Day
+  , source :: Text
+  , tTM :: Bool
+  , valueCheck :: Bool
+  , revenue :: Maybe Integer
+  , netInterestIncome :: Maybe Integer
+  , totalInterestIncome :: Maybe Integer
+  , totalInterestExpense :: Maybe Integer
+  , totalNonInterestIncome :: Maybe Integer
+  , tradingAccountProfitsLosses :: Maybe Integer
+  , investmentIncomeLoss :: Maybe Integer
+  , saleOfLoanIncomeLoss :: Maybe Integer
+  , commissionsAndFeesEarned :: Maybe Integer
+  , netOTTILossesRecognisedInEarnings :: Maybe Integer
+  , otherNonInterestIncome :: Maybe Integer
+  , provisionForLoanLosses :: Maybe Integer
+  , netRevenueAfterProvisions :: Maybe Integer
+  , totalNonInterestExpense :: Maybe Integer
+  , commissionsAndFeesPaid :: Maybe Integer
+  , otherOperatingExpenses :: Maybe Integer
+  , operatingIncomeLoss :: Maybe Integer
+  , nonOperatingIncomeLoss :: Maybe Integer
+  , incomeLossFromAffiliates :: Maybe Integer
+  , otherNonOperatingIncomeLoss :: Maybe Integer
+  , pretaxIncomeLossAdj :: Maybe Integer
+  , abnormalGainsLosses :: Maybe Integer
+  , debtValuationAdjustment :: Maybe Integer
+  , creditValuationAdjustment :: Maybe Integer
+  , mergerAndAcquisitionExpense :: Maybe Integer
+  , disposalOfAssets :: Maybe Integer
+  , earlyExtinguishmentOfDebt :: Maybe Integer
+  , assetWriteDown :: Maybe Integer
+  , impairmentOfGoodwillAndIntangibles :: Maybe Integer
+  , saleOfBusiness :: Maybe Integer
+  , legalSettlement :: Maybe Integer
+  , restructuringCharges :: Maybe Integer
+  , otherAbnormalItems :: Maybe Integer
+  , pretaxIncomeLoss :: Maybe Integer
+  , incomeTaxExpenseBenefitNet :: Maybe Integer
+  , currentIncomeTax :: Maybe Integer
+  , deferredIncomeTax :: Maybe Integer
+  , taxAllowanceCredit :: Maybe Integer
+  , incomeLossFromAffiliatesNetOfTaxes :: Maybe Integer
+  , incomeLossFromContinuingOperations :: Maybe Integer
+  , netExtraordinaryGainsLosses :: Maybe Integer
+  , discontinuedOperations :: Maybe Integer
+  , accountingChargesAndOther :: Maybe Integer
+  , incomeLossInclMinorityInterest :: Maybe Integer
+  , minorityInterest :: Maybe Integer
+  , netIncome :: Maybe Integer
+  , preferredDividends :: Maybe Integer
+  , otherAdjustments :: Maybe Integer
+  , netIncomeCommon :: Maybe Integer
+  } deriving Show
+
+instance FromJSON BankProfitAndLossRow where
+  parseJSON = withObject "BankProfitAndLossRow" $ \v -> BankProfitAndLossRow
+    <$> v .: "SimFinId"
+    <*> v .: "Ticker"
+    <*> v .: "Fiscal Period"
+    <*> v .: "Fiscal Year"
+    <*> v .: "Report Date"
+    <*> v .: "Publish Date"
+    <*> v .: "Restated Date"
+    <*> v .: "Source"
+    <*> v .: "TTM"
+    <*> v .: "Value Check"
+    <*> v .: "Revenue"
+    <*> v .: "Net Interest Income"
+    <*> v .: "Total Interest Income"
+    <*> v .: "Total Interest Expense"
+    <*> v .: "Total Non-Interest Income"
+    <*> v .: "Trading Account Profits/Losses"
+    <*> v .: "Investment Income (Loss)"
+    <*> v .: "Sale of Loan Income (Loss)"
+    <*> v .: "Commissions & Fees Earned"
+    <*> v .: "Net OTTI Losses Recognised in Earnings"
+    <*> v .: "Other Non-Interest Income"
+    <*> v .: "Provision for Loan Losses"
+    <*> v .: "Net Revenue after Provisions"
+    <*> v .: "Total Non-Interest Expense"
+    <*> v .: "Commissions & Fees Paid"
+    <*> v .: "Other Operating Expenses"
+    <*> v .: "Operating Income (Loss)"
+    <*> v .: "Non-Operating Income (Loss)"
+    <*> v .: "Income (Loss) from Affiliates"
+    <*> v .: "Other Non-Operating Income (Loss)"
+    <*> v .: "Pretax Income (Loss), Adj."
+    <*> v .: "Abnormal Gains (Losses)"
+    <*> v .: "Debt Valuation Adjustment"
+    <*> v .: "Credit Valuation Adjustment"
+    <*> v .: "Merger & Acquisition Expense"
+    <*> v .: "Disposal of Assets"
+    <*> v .: "Early Extinguishment of Debt"
+    <*> v .: "Asset Write-Down"
+    <*> v .: "Impairment of Goodwill & Intangibles"
+    <*> v .: "Sale of Business"
+    <*> v .: "Legal Settlement"
+    <*> v .: "Restructuring Charges"
+    <*> v .: "Other Abnormal Items"
+    <*> v .: "Pretax Income (Loss)"
+    <*> v .: "Income Tax (Expense) Benefit, Net"
+    <*> v .: "Current Income Tax"
+    <*> v .: "Deferred Income Tax"
+    <*> v .: "Tax Allowance/Credit"
+    <*> v .: "Income (Loss) from Affiliates, Net of Taxes"
+    <*> v .: "Income (Loss) from Continuing Operations"
+    <*> v .: "Net Extraordinary Gains (Losses)"
+    <*> v .: "Discontinued Operations"
+    <*> v .: "Accounting Charges & Other"
+    <*> v .: "Income (Loss) Incl. Minority Interest"
+    <*> v .: "Minority Interest"
+    <*> v .: "Net Income"
+    <*> v .: "Preferred Dividends"
+    <*> v .: "Other Adjustments"
+    <*> v .: "Net Income (Common)"
+
+-- | Wrapper to parse a BankProfitAndLossRow record from SimFin's JSON format.
+
+newtype BankProfitsAndLossesKeyed = BankProfitsAndLossesKeyed { unKeyBankProfitsAndLosses :: [BankProfitAndLossRow] }
+
+instance FromJSON BankProfitsAndLossesKeyed where
+  parseJSON o = BankProfitsAndLossesKeyed <$> (traverse parseJSON =<< createKeyedRows o)
+
+
+------
+-- Insurance
+------
+
+-- | Cash flow statement for insurance companies. 
+
+data InsuranceProfitAndLossRow
+  = InsuranceProfitAndLossRow
+  { simFinId :: Int
+  , ticker :: Text
+  , fiscalPeriod :: String
+  , fiscalYear :: Int
+  , reportDate :: Day
+  , publishDate :: Day
+  , restatedDate :: Day
+  , source :: Text
+  , tTM :: Bool
+  , valueCheck :: Bool
+  , revenue :: Maybe Integer
+  , netPremiumsEarned :: Maybe Integer
+  , investmentIncomeLoss :: Maybe Integer
+  , incomeFromRealEstate :: Maybe Integer
+  , otherOperatingIncome :: Maybe Integer
+  , policyChargesAndFees :: Maybe Integer
+  , totalRealizedInvestmentGains :: Maybe Integer
+  , totalOTTIRealized :: Maybe Integer
+  , otherRealizedInvestmentGains :: Maybe Integer
+  , otherIncome :: Maybe Integer
+  , totalClaimsAndLosses :: Maybe Integer
+  , claimsAndLosses :: Maybe Integer
+  , longTermCharges :: Maybe Integer
+  , otherClaimsAndLosses :: Maybe Integer
+  , underwritingExpenseAndAcquisitionCost :: Maybe Integer
+  , otherOperatingExpenses :: Maybe Integer
+  , operatingIncomeLoss :: Maybe Integer
+  , nonOperatingIncomeLoss :: Maybe Integer
+  , incomeLossFromAffiliates :: Maybe Integer
+  , interestExpenseNet :: Maybe Integer
+  , otherNonOperatingIncomeLoss :: Maybe Integer
+  , pretaxIncomeLossAdj :: Maybe Integer
+  , abnormalGainsLosses :: Maybe Integer
+  , mergerAndAcquisitionExpense :: Maybe Integer
+  , abnormalDerivatives :: Maybe Integer
+  , disposalOfAssets :: Maybe Integer
+  , earlyExtinguishmentOfDebt :: Maybe Integer
+  , assetWriteDown :: Maybe Integer
+  , impairmentOfGoodwillAndIntangibles :: Maybe Integer
+  , saleOfBusiness :: Maybe Integer
+  , legalSettlement :: Maybe Integer
+  , restructuringCharges :: Maybe Integer
+  , netInvestmentLosses :: Maybe Integer
+  , foreignExchange :: Maybe Integer
+  , otherAbnormalItems :: Maybe Integer
+  , pretaxIncomeLoss :: Maybe Integer
+  , incomeTaxExpenseBenefitNet :: Maybe Integer
+  , currentIncomeTax :: Maybe Integer
+  , deferredIncomeTax :: Maybe Integer
+  , taxAllowanceCredit :: Maybe Integer
+  , incomeLossFromAffiliatesNetOfTaxes :: Maybe Integer
+  , incomeLossFromContinuingOperations :: Maybe Integer
+  , netExtraordinaryGainsLosses :: Maybe Integer
+  , discontinuedOperations :: Maybe Integer
+  , accountingChargesAndOther :: Maybe Integer
+  , incomeLossInclMinorityInterest :: Maybe Integer
+  , minorityInterest :: Maybe Integer
+  , netIncome :: Maybe Integer
+  , preferredDividends :: Maybe Integer
+  , otherAdjustments :: Maybe Integer
+  , netIncomeCommon :: Maybe Integer
+  } deriving Show
+
+instance FromJSON InsuranceProfitAndLossRow where
+  parseJSON = withObject "InsuranceProfitAndLossRow" $ \v -> InsuranceProfitAndLossRow
+    <$> v .: "SimFinId"
+    <*> v .: "Ticker"
+    <*> v .: "Fiscal Period"
+    <*> v .: "Fiscal Year"
+    <*> v .: "Report Date"
+    <*> v .: "Publish Date"
+    <*> v .: "Restated Date"
+    <*> v .: "Source"
+    <*> v .: "TTM"
+    <*> v .: "Value Check"
+    <*> v .: "Revenue"
+    <*> v .: "Net Premiums Earned"
+    <*> v .: "Investment Income (Loss)"
+    <*> v .: "Income from Real Estate"
+    <*> v .: "Other Operating Income"
+    <*> v .: "Policy Charges & Fees"
+    <*> v .: "Total Realized Investment Gains"
+    <*> v .: "Total OTTI Realized"
+    <*> v .: "Other Realized Investment Gains"
+    <*> v .: "Other Income"
+    <*> v .: "Total Claims & Losses"
+    <*> v .: "Claims & Losses"
+    <*> v .: "Long Term Charges"
+    <*> v .: "Other Claims & Losses"
+    <*> v .: "Underwriting Expense & Acquisition Cost"
+    <*> v .: "Other Operating Expenses"
+    <*> v .: "Operating Income (Loss)"
+    <*> v .: "Non-Operating Income (Loss)"
+    <*> v .: "Income (Loss) from Affiliates"
+    <*> v .: "Interest Expense, Net"
+    <*> v .: "Other Non-Operating Income (Loss)"
+    <*> v .: "Pretax Income (Loss), Adj."
+    <*> v .: "Abnormal Gains (Losses)"
+    <*> v .: "Merger & Acquisition Expense"
+    <*> v .: "Abnormal Derivatives"
+    <*> v .: "Disposal of Assets"
+    <*> v .: "Early Extinguishment of Debt"
+    <*> v .: "Asset Write-Down"
+    <*> v .: "Impairment of Goodwill & Intangibles"
+    <*> v .: "Sale of Business"
+    <*> v .: "Legal Settlement"
+    <*> v .: "Restructuring Charges"
+    <*> v .: "Net Investment Losses"
+    <*> v .: "Foreign Exchange"
+    <*> v .: "Other Abnormal Items"
+    <*> v .: "Pretax Income (Loss)"
+    <*> v .: "Income Tax (Expense) Benefit, Net"
+    <*> v .: "Current Income Tax"
+    <*> v .: "Deferred Income Tax"
+    <*> v .: "Tax Allowance/Credit"
+    <*> v .: "Income (Loss) from Affiliates, Net of Taxes"
+    <*> v .: "Income (Loss) from Continuing Operations"
+    <*> v .: "Net Extraordinary Gains (Losses)"
+    <*> v .: "Discontinued Operations"
+    <*> v .: "Accounting Charges & Other"
+    <*> v .: "Income (Loss) Incl. Minority Interest"
+    <*> v .: "Minority Interest"
+    <*> v .: "Net Income"
+    <*> v .: "Preferred Dividends"
+    <*> v .: "Other Adjustments"
+    <*> v .: "Net Income (Common)"
+
+-- | Wrapper to parse an InsuranceProfitAndLossRow record from SimFin's JSON format.
+
+newtype InsuranceProfitsAndLossesKeyed = InsuranceProfitsAndLossesKeyed { unKeyInsuranceProfitsAndLosses :: [InsuranceProfitAndLossRow] }
+
+instance FromJSON InsuranceProfitsAndLossesKeyed where
+  parseJSON o = InsuranceProfitsAndLossesKeyed <$> (traverse parseJSON =<< createKeyedRows o)
+
+------
+-- Industry
+------
+
+type IndustryProfitsAndLossesKeyed
+  = Industry GeneralProfitsAndLossesKeyed BankProfitsAndLossesKeyed InsuranceProfitsAndLossesKeyed
+
+-- | Discrimination of profit and loss lists.
+
+type IndustryProfitsAndLosses
+  = Industry [GeneralProfitAndLossRow] [BankProfitAndLossRow] [InsuranceProfitAndLossRow]
+
+-- | Discrimination of profit and losses.
+
+type IndustryProfitAndLoss
+  = Industry GeneralProfitAndLossRow BankProfitAndLossRow InsuranceProfitAndLossRow
+
+instance FromJSON IndustryProfitsAndLossesKeyed where
+  parseJSON root = General <$> parseJSON root
+    <|> Insurance <$> parseJSON root
+    <|> Bank <$> parseJSON root
+
+unKeyIndustryProfitsAndLosses :: IndustryProfitsAndLossesKeyed -> IndustryProfitsAndLosses
+unKeyIndustryProfitsAndLosses = mapIndustry
+  unKeyGeneralProfitsAndLosses
+  unKeyBankProfitsAndLosses
+  unKeyInsuranceProfitsAndLosses
+
+instance FromJSON IndustryProfitsAndLosses where
+  parseJSON root = unKeyIndustryProfitsAndLosses <$> parseJSON root
diff --git a/src/SimFin/Types/Ratios.hs b/src/SimFin/Types/Ratios.hs
new file mode 100644
--- /dev/null
+++ b/src/SimFin/Types/Ratios.hs
@@ -0,0 +1,57 @@
+{-|
+Module      : SimFin.Types.Ratios
+Description : Ratios derived from a business' financial statements.
+Copyright   : (c) Owen Shepherd, 2022
+License     : MIT
+Maintainer  : owen@owen.cafe
+-}
+
+{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module SimFin.Types.Ratios
+  ( RatiosRow(..)
+  ) where
+
+import Data.Aeson
+
+import SimFin.Types.StringFrac
+
+-- | Record modelling the extra data returned by calling the share price API endpoint with the "&ratios"
+-- query parameter. See the [SimFin docs](https://simfin.com/api/v2/documentation/#tag/Company/paths/~1companies~1prices/get).
+
+data RatiosRow a
+  = RatiosRow
+  { marketCap :: Integer
+  , priceToEarningsRatioQuarterly :: Maybe a
+  , priceToEarningsRatioTTM :: Maybe a
+  , priceToSalesRatioQuarterly :: Maybe a
+  , priceToSalesRatioTTM :: Maybe a
+  , priceToBookValueTTM :: Maybe a
+  , priceToFreeCashFlowQuarterly :: Maybe a
+  , priceToFreeCashFlowTTM :: Maybe a
+  , enterpriseValueTTM :: Maybe a
+  , eVEBITDATTM :: Maybe a
+  , eVSalesTTM :: Maybe a
+  , eVFCFTTM :: Maybe a
+  , bookToMarketValueTTM :: Maybe a
+  , operatingIncomeEVTTM :: Maybe a
+  } deriving (Functor, Show)
+
+instance (Read a, RealFrac a) => FromJSON (RatiosRow a) where
+  parseJSON = withObject "RatiosRow" $ \v -> fmap (fmap unStringFrac) $ RatiosRow
+    <$> v .: "Market-Cap"
+    <*> v .: "Price to Earnings Ratio (quarterly)"
+    <*> v .: "Price to Earnings Ratio (ttm)"
+    <*> v .: "Price to Sales Ratio (quarterly)"
+    <*> v .: "Price to Sales Ratio (ttm)"
+    <*> v .: "Price to Book Value (ttm)"
+    <*> v .: "Price to Free Cash Flow (quarterly)"
+    <*> v .: "Price to Free Cash Flow (ttm)"
+    <*> v .: "Enterprise Value (ttm)"
+    <*> v .: "EV/EBITDA (ttm)"
+    <*> v .: "EV/Sales (ttm)"
+    <*> v .: "EV/FCF (ttm)"
+    <*> v .: "Book to Market Value (ttm)"
+    <*> v .: "Operating Income/EV (ttm)"
diff --git a/src/SimFin/Types/StatementQuery.hs b/src/SimFin/Types/StatementQuery.hs
new file mode 100644
--- /dev/null
+++ b/src/SimFin/Types/StatementQuery.hs
@@ -0,0 +1,100 @@
+{-|
+Module      : SimFin.Types.StatementQuery
+Description : Types to represent SimFin statement queries.
+Copyright   : (c) Owen Shepherd, 2022
+License     : MIT
+Maintainer  : owen@owen.cafe
+-}
+
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+
+module SimFin.Types.StatementQuery
+  ( StatementQuery(..)
+  , StatementQueryFree(..)
+  , statementQueryToQueryParams
+  , statementQueryFreeToQueryParams
+  ) where
+
+import Data.List.NonEmpty (NonEmpty)
+import Data.Maybe (maybeToList)
+import Data.Time.Calendar (Day)
+
+import SimFin.Types.FiscalPeriod
+import SimFin.Types.StockRef
+import SimFin.Internal
+
+-- | This represents all options the statement endpoint supports, minus the "statement"
+-- parameter itself, which is set by simply calling the right function.
+-- Some of these parameters are only available to SimFin+ users.
+-- For free users, please use 'StatementQueryFree'.
+-- If you provide a zero-length list for any field, the query parameter will be omitted,
+-- and the API will try to return all relevant available statements.
+
+data StatementQuery
+  = StatementQuery
+  { stockRefs :: NonEmpty StockRef
+  , periods :: [FiscalPeriod]
+  , years :: [Int]
+  , start :: Maybe Day
+  , end :: Maybe Day
+  , ttm :: Bool
+  , asReported :: Bool
+  -- TODO we don't model the result of this yet
+  , shares :: Bool
+  } deriving Show
+
+-- | Turn a 'StatementQuery' into query parameters for the SimFin "statements" endpoint.
+
+statementQueryToQueryParams :: StatementQuery -> [QueryParam]
+statementQueryToQueryParams StatementQuery{..} =
+  let
+    refParams = stockRefsToQueryParams stockRefs
+    startParam = toShownCommaQueryParam "start "$ maybeToList start
+    endParam = toShownCommaQueryParam "end" $ maybeToList end
+    periodParam = toCommaQueryParam "period" fiscalPeriodParam periods
+    yearParam = toShownCommaQueryParam "fyear" years
+    ttmParam = toBoolQueryParam "ttm" ttm
+    asReportedParam = toBoolQueryParam "asreported" asReported
+    sharesParam = toBoolQueryParam "shares" shares
+  in
+  mconcat
+    [ refParams
+    , startParam
+    , endParam
+    , periodParam
+    , yearParam
+    , ttmParam
+    , asReportedParam
+    , sharesParam
+    ]
+
+-- | This is a subset of the StatementQuery type, which models the parameters available
+-- to non-SimFin+ users.
+
+data StatementQueryFree
+  = StatementQueryFree
+  { stockRef :: StockRef
+  , period :: FiscalPeriod
+  , year :: Int
+  , ttm :: Bool
+  }
+
+freeStatementQueryToPaidStatementQuery :: StatementQueryFree -> StatementQuery
+freeStatementQueryToPaidStatementQuery StatementQueryFree{..}
+  = StatementQuery
+  { stockRefs = pure stockRef
+  , periods = pure period
+  , years = pure year
+  , start = Nothing
+  , end = Nothing
+  , ttm = ttm
+  , asReported = False
+  , shares = False
+  }
+
+-- | Turn a 'StatementQueryFree' into query parameters for the SimFin "statements" endpoint.
+
+statementQueryFreeToQueryParams :: StatementQueryFree -> [QueryParam]
+statementQueryFreeToQueryParams = statementQueryToQueryParams . freeStatementQueryToPaidStatementQuery
diff --git a/src/SimFin/Types/StockRef.hs b/src/SimFin/Types/StockRef.hs
new file mode 100644
--- /dev/null
+++ b/src/SimFin/Types/StockRef.hs
@@ -0,0 +1,51 @@
+{-|
+Module      : SimFin.Types.StockRef
+Description : Type to represent a reference to a company.
+Copyright   : (c) Owen Shepherd, 2022
+License     : MIT
+Maintainer  : owen@owen.cafe
+-}
+
+{-# LANGUAGE OverloadedStrings #-}
+
+module SimFin.Types.StockRef
+  ( StockRef(..)
+  , stockRefsToQueryParams
+  ) where
+
+import Control.Arrow (first, second)
+import Data.List (foldl')
+import Data.List.NonEmpty (NonEmpty)
+import Data.String
+import Data.Text (Text)
+import qualified Data.Text as T
+
+import SimFin.Internal
+
+-- | A stock ref is a SimSin ID or a ticker.
+
+data StockRef = SimFinId Int | Ticker Text
+  deriving Show
+
+instance IsString StockRef where
+  fromString = Ticker . T.pack
+
+-- | Collection of discriminations to discrimination of collections.
+
+separateStockRefs :: Foldable t => t StockRef -> ([Int], [Text])
+separateStockRefs = foldl' f ([], [])
+  where
+    f :: ([Int], [Text]) -> StockRef -> ([Int], [Text])
+    f acc (SimFinId n) = first (n:) acc
+    f acc (Ticker t) = second (t:) acc
+
+-- | Convert one or more stock references into a list of query parameters.
+
+stockRefsToQueryParams :: NonEmpty StockRef -> [QueryParam]
+stockRefsToQueryParams refs =
+  let
+    (ids, tickers) = separateStockRefs refs
+    tickerParam = toTextCommaQueryParam "ticker" tickers
+    idParam = toShownCommaQueryParam "id" ids
+  in tickerParam <> idParam
+
diff --git a/src/SimFin/Types/StringFrac.hs b/src/SimFin/Types/StringFrac.hs
new file mode 100644
--- /dev/null
+++ b/src/SimFin/Types/StringFrac.hs
@@ -0,0 +1,31 @@
+{-|
+Module      : SimFin.Types.StringFrac
+Description : Wrapper for parsing JSON strings or numbers as a RealFrac.
+Copyright   : (c) Owen Shepherd, 2022
+License     : MIT
+Maintainer  : owen@owen.cafe
+-}
+
+module SimFin.Types.StringFrac
+  ( StringFrac(..)
+  ) where
+
+import Data.Aeson hiding (String)
+import qualified Data.Aeson as A
+import Data.Aeson.Types (typeMismatch)
+import qualified Data.Text as T
+import Text.Read (readEither)
+
+-- | Wrapper that parses the string '"1.23"' and the number '1.23' the same.
+-- Uses the read instance for the 'String', and 'realToFrac' ('Data.Scientific.Scientific' -> a)
+-- for the number.
+
+newtype StringFrac a = StringFrac { unStringFrac :: a }
+  deriving Show
+
+instance (Read a, RealFrac a) => FromJSON (StringFrac a) where
+  parseJSON (A.String s) = case readEither $ T.unpack s of
+    Left err -> fail err
+    Right a -> pure $ StringFrac a
+  parseJSON (Number n) = pure $ StringFrac $ realToFrac n
+  parseJSON v = typeMismatch "Number or String" v
diff --git a/src/SimFin/Util.hs b/src/SimFin/Util.hs
new file mode 100644
--- /dev/null
+++ b/src/SimFin/Util.hs
@@ -0,0 +1,40 @@
+{-|
+Module      : SimFin.Util
+Description : Common utilities.
+Copyright   : (c) Owen Shepherd, 2022
+License     : MIT
+Maintainer  : owen@owen.cafe
+-}
+
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module SimFin.Util
+  ( createDefaultContext
+  , apiKeyEnvVariable
+  ) where
+
+import Control.Monad.IO.Class
+import qualified Data.ByteString.UTF8 as BSU
+import Network.HTTP.Client.TLS
+import System.Environment (lookupEnv)
+
+import SimFin.Internal
+
+-- | The environment variable 'createDefaultContext' will try to get your
+-- API key from.
+
+apiKeyEnvVariable :: String
+apiKeyEnvVariable = "SIM_FIN_API_KEY"
+
+-- | Try to make a new http-client manager, and parse your api key from 
+-- 'apiKeyEnvVariable'.
+
+createDefaultContext :: (MonadFail m, MonadIO m) => m SimFinContext
+createDefaultContext = do
+  manager <- newTlsManager
+  apiKeyOpt <- liftIO $ lookupEnv apiKeyEnvVariable
+  case apiKeyOpt of
+    Nothing -> fail $ "Couldn't find environment variable '" <> apiKeyEnvVariable <> "'"
+    Just apiKey -> pure $ SimFinContext (BSU.fromString apiKey) manager
+
diff --git a/test/Test.hs b/test/Test.hs
new file mode 100644
--- /dev/null
+++ b/test/Test.hs
@@ -0,0 +1,125 @@
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module Main
+  ( main
+  ) where
+
+import Control.Monad.IO.Class
+import Data.Aeson
+import Data.Text (Text)
+import Test.Tasty
+import Test.Tasty.HUnit
+
+import SimFin.Free
+
+main :: IO ()
+main = do
+  ctx <- createDefaultContext
+  defaultMain (tests ctx)
+
+testStatementQuery :: Text -> StatementQueryFree
+testStatementQuery ref = StatementQueryFree
+  { stockRef = Ticker ref
+  , period = FullYear
+  , year = 2020
+  , ttm = False
+  }
+
+type UnitIndustry = Industry () () ()
+
+general :: UnitIndustry
+general = General ()
+
+bank :: UnitIndustry
+bank = Bank ()
+
+insurance :: UnitIndustry
+insurance = Insurance ()
+
+industryMatches :: Industry a b c -> Industry d e f -> Bool
+industryMatches a b = case (a, b) of
+  (General _, General _) -> True
+  (Bank _, Bank _) -> True
+  (Insurance _, Insurance _) -> True
+  _ -> False
+
+testFetchMaybe :: IO (ApiResult (Maybe a)) -> IO ()
+testFetchMaybe f = do
+  res <- f
+  flip withRight res $ withMaybe $ const $ pure ()
+
+testFetchIndustry :: UnitIndustry -> IO (ApiResult (Maybe (Industry a b c))) -> IO ()
+testFetchIndustry ind f = do
+  res <- f
+  flip withRight res $ withMaybe $ assertBool "Industry Matches" . industryMatches ind
+
+failWithEmpty :: IO ()
+failWithEmpty = assertFailure "Retrieved Nothing successfully"
+
+testFetchList :: IO (ApiResult [a]) -> IO ()
+testFetchList f = do
+  res <- f
+  withRight ensureNE res
+
+withRight :: Show c => (a -> IO b) -> Either c a -> IO b
+withRight _ (Left a) = assertFailure $ "Retrieved Left: " <> show a
+withRight f (Right a) = f a
+
+withMaybe :: (a -> IO ()) -> Maybe a -> IO ()
+withMaybe _ Nothing = failWithEmpty
+withMaybe f (Just a) = f a
+
+ensureNE :: [a] -> IO ()
+ensureNE [] = failWithEmpty
+ensureNE _ = pure ()
+
+tests :: SimFinContext -> TestTree
+tests ctx = testGroup "SimFin"
+  [ testCase "List companies" $ testFetchList $ fetchCompanyList ctx
+  , testCase "Company Info" $ testFetchMaybe $ fetchCompanyInfo ctx "AAPL"
+  , testGroup "Balance Sheet"
+    [ testCase "General" $ testBalanceSheet "AAPL" general
+    , testCase "Bank" $ testBalanceSheet "C" bank
+    , testCase "Insurance" $ testBalanceSheet "CB" insurance
+    ]
+  , testGroup "Profit & Loss"
+    [ testCase "General" $ testProfitAndLoss "AAPL" general
+    , testCase "Bank" $ testProfitAndLoss "C" bank
+    , testCase "Insurance" $ testProfitAndLoss "CB" insurance
+    ]
+  , testGroup "Cash Flow"
+    [ testCase "General" $ testCashFlow "AAPL" general
+    , testCase "Bank" $ testCashFlow "C" bank
+    , testCase "Insurance" $ testCashFlow "CB" insurance
+    ]
+    -- This endpoint isn't different by industry types
+  , testGroup "Derived"
+    [ testCase "General" $ testDerived "AAPL"
+    , testCase "Bank" $ testDerived "C"
+    , testCase "Insurance" $ testDerived "CB"
+    ]
+  , testGroup "Price"
+    [ testCase "General" $ testFetchPrices "AAPL"
+    , testCase "Bank" $ testFetchPrices "C"
+    , testCase "Insurance" $ testFetchPrices "CB"
+    ]
+  ]
+  where
+    testStmt :: (SimFinContext -> StatementQueryFree -> IO a) -> Text -> IO a
+    testStmt f ticker = f ctx $ testStatementQuery ticker
+
+    testBalanceSheet :: Text -> UnitIndustry -> Assertion
+    testBalanceSheet ticker industry = testFetchIndustry industry $ testStmt fetchBalanceSheet ticker
+
+    testProfitAndLoss :: Text -> UnitIndustry -> Assertion
+    testProfitAndLoss ticker industry = testFetchIndustry industry $ testStmt fetchProfitAndLoss ticker
+
+    testCashFlow :: Text -> UnitIndustry -> Assertion
+    testCashFlow ticker industry = testFetchIndustry industry $ testStmt fetchCashFlow ticker
+
+    testDerived :: Text -> Assertion
+    testDerived ticker = testFetchMaybe $ testStmt fetchDerived ticker
+
+    testFetchPrices :: StockRef -> IO ()
+    testFetchPrices ticker = testFetchList $ fetchPrices ctx ticker
