diff --git a/antiope-s3.cabal b/antiope-s3.cabal
--- a/antiope-s3.cabal
+++ b/antiope-s3.cabal
@@ -4,10 +4,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 887a27d58e6bcfd56a2a70265fa45a4b9aeda0ad3194e4c7e264fe8c4c445592
+-- hash: 114ac89b35e20a7f50e38154812d361792e7c7cade2c02278af6acbe429af480
 
 name:           antiope-s3
-version:        6.2.0
+version:        6.3.0
 description:    Please see the README on Github at <https://github.com/arbor/antiope#readme>
 category:       Services
 homepage:       https://github.com/arbor/antiope#readme
diff --git a/src/Antiope/S3.hs b/src/Antiope/S3.hs
--- a/src/Antiope/S3.hs
+++ b/src/Antiope/S3.hs
@@ -12,6 +12,7 @@
 , lsPrefix
 , deleteFiles
 , deleteFilesExcept
+, fileExists
 , Region(..)
 , BucketName(..)
 , ObjectKey(..)
@@ -19,6 +20,7 @@
 , S3Uri(..)
 ) where
 
+import Antiope.Core.Error           (handle404ToNone)
 import Antiope.S3.Internal
 import Antiope.S3.Types             (S3Uri (S3Uri, objectKey))
 import Conduit
@@ -27,7 +29,7 @@
 import Control.Monad.Trans.AWS      hiding (send)
 import Control.Monad.Trans.Resource
 import Data.Conduit.List            (unfoldM)
-import Data.Maybe                   (catMaybes)
+import Data.Maybe                   (catMaybes, isJust)
 import Data.Monoid                  ((<>))
 import Data.Text                    as T (Text, pack, unpack)
 import Network.AWS                  (MonadAWS)
@@ -170,3 +172,11 @@
   case (objectKey <$> existing) List.\\ uris of
     [] -> pure []
     xs -> deleteFiles b xs
+
+-- | Checks if the file exists on S3
+fileExists :: MonadAWS m
+  => S3Uri
+  -> m Bool
+fileExists (S3Uri b k) =
+  isJust <$> handle404ToNone (AWS.send (headObject b k))
+
