diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,11 @@
+### v0.3.0.1
+
+- [Producer] Write errors to stderr rather than stdout.
+
+- [Consumer CLI] Exist with a failure code (1) when the run is not considered
+  successful (i.e.  if a limit was specified, and the CLI failed to retrieve
+  that many records).
+
 ### v0.3.0.0
 
 All changes were in the Producer client.
diff --git a/aws-kinesis-client.cabal b/aws-kinesis-client.cabal
--- a/aws-kinesis-client.cabal
+++ b/aws-kinesis-client.cabal
@@ -1,5 +1,5 @@
 name:                aws-kinesis-client
-version:             0.3.0.0
+version:             0.3.0.1
 synopsis:            A producer & consumer client library for AWS Kinesis
 -- description:
 license:             Apache-2.0
diff --git a/cli/CLI.hs b/cli/CLI.hs
--- a/cli/CLI.hs
+++ b/cli/CLI.hs
@@ -75,6 +75,7 @@
 import Options.Applicative
 import qualified Network.HTTP.Conduit as HC
 import Prelude.Unicode
+import System.Exit
 import System.IO.Error
 
 data CLIError
@@ -113,7 +114,7 @@
 
 app
   ∷ MonadCLI m
-  ⇒ Codensity m ()
+  ⇒ Codensity m ExitCode
 app = do
   CLIOptions{..} ← ask
   manager ← managedHttpManager
@@ -172,11 +173,17 @@
     state ← consumerStreamState consumer
     liftIO ∘ BL8.writeFile outPath $ A.encode state
 
+  return $
+    if successful
+      then ExitSuccess
+      else ExitFailure 1
+
 main ∷ IO ()
-main =
-  eitherT (fail ∘ show) return $
+main = do
+  exitCode ← eitherT (fail ∘ show) return $
     liftIO (execParser parserInfo)
       ≫= runReaderT (lowerCodensity app)
+  exitWith exitCode
 
 managedHttpManager
   ∷ ( MonadIO m
diff --git a/src/Aws/Kinesis/Client/Producer.hs b/src/Aws/Kinesis/Client/Producer.hs
--- a/src/Aws/Kinesis/Client/Producer.hs
+++ b/src/Aws/Kinesis/Client/Producer.hs
@@ -108,6 +108,7 @@
 import Data.Typeable
 import Prelude.Unicode
 import qualified System.Random as R
+import System.IO
 
 -- | There are two endpoints which may be used to send records to Kinesis.
 --
@@ -444,8 +445,7 @@
   awaitForever $ \item → do
     let handler e = do
           liftIO $ do
-            putStrLn $ "Error: " ++ show e
-            putStrLn "Will wait 5s"
+            hPutStrLn stderr $ "Kinesis producer client error (will wait 5s): " ++ show e
             threadDelay 5000000
           leftover $ item & mqiRemainingAttempts -~ 1
 
@@ -485,7 +485,7 @@
       case filter messageQueueItemIsEligible items of
         [] → return []
         eligibleItems → do
-          handleError (\e → eligibleItems <$ liftIO (print e)) $ do
+          handleError (\e → eligibleItems <$ liftIO (hPutStrLn stderr $ show e)) $ do
             requestEntries ← for eligibleItems $ \m → do
               let partitionKey = m ^. mqiPartitionKey
               return Kin.PutRecordsRequestEntry
