diff --git a/amazonka-s3.cabal b/amazonka-s3.cabal
--- a/amazonka-s3.cabal
+++ b/amazonka-s3.cabal
@@ -1,5 +1,5 @@
 name:                  amazonka-s3
-version:               0.0.7
+version:               0.0.8
 synopsis:              Amazon Simple Storage Service SDK.
 homepage:              https://github.com/brendanhay/amazonka
 license:               OtherLicense
@@ -89,10 +89,11 @@
         , Network.AWS.S3.Types
         , Network.AWS.S3.UploadPart
         , Network.AWS.S3.UploadPartCopy
+        , Network.AWS.S3.Waiters
 
     other-modules:
           Network.AWS.S3.Internal
 
     build-depends:
-          amazonka-core == 0.0.7.*
+          amazonka-core == 0.0.8.*
         , base          >= 4.7     && < 5
diff --git a/gen/Network/AWS/S3.hs b/gen/Network/AWS/S3.hs
--- a/gen/Network/AWS/S3.hs
+++ b/gen/Network/AWS/S3.hs
@@ -68,6 +68,7 @@
     , module Network.AWS.S3.Types
     , module Network.AWS.S3.UploadPart
     , module Network.AWS.S3.UploadPartCopy
+    , module Network.AWS.S3.Waiters
     ) where
 
 import Network.AWS.S3.AbortMultipartUpload
@@ -120,3 +121,4 @@
 import Network.AWS.S3.Types
 import Network.AWS.S3.UploadPart
 import Network.AWS.S3.UploadPartCopy
+import Network.AWS.S3.Waiters
diff --git a/gen/Network/AWS/S3/CompleteMultipartUpload.hs b/gen/Network/AWS/S3/CompleteMultipartUpload.hs
--- a/gen/Network/AWS/S3/CompleteMultipartUpload.hs
+++ b/gen/Network/AWS/S3/CompleteMultipartUpload.hs
@@ -103,7 +103,7 @@
 data CompleteMultipartUploadResponse = CompleteMultipartUploadResponse
     { _cmur1Bucket               :: Maybe Text
     , _cmur1ETag                 :: Maybe Text
-    , _cmur1Expiration           :: Maybe RFC822
+    , _cmur1Expiration           :: Maybe Text
     , _cmur1Key                  :: Maybe Text
     , _cmur1Location             :: Maybe Text
     , _cmur1SSEKMSKeyId          :: Maybe (Sensitive Text)
@@ -119,7 +119,7 @@
 --
 -- * 'cmur1ETag' @::@ 'Maybe' 'Text'
 --
--- * 'cmur1Expiration' @::@ 'Maybe' 'UTCTime'
+-- * 'cmur1Expiration' @::@ 'Maybe' 'Text'
 --
 -- * 'cmur1Key' @::@ 'Maybe' 'Text'
 --
@@ -152,8 +152,8 @@
 
 -- | If the object expiration is configured, this will contain the expiration date
 -- (expiry-date) and rule ID (rule-id). The value of rule-id is URL encoded.
-cmur1Expiration :: Lens' CompleteMultipartUploadResponse (Maybe UTCTime)
-cmur1Expiration = lens _cmur1Expiration (\s a -> s { _cmur1Expiration = a }) . mapping _Time
+cmur1Expiration :: Lens' CompleteMultipartUploadResponse (Maybe Text)
+cmur1Expiration = lens _cmur1Expiration (\s a -> s { _cmur1Expiration = a })
 
 cmur1Key :: Lens' CompleteMultipartUploadResponse (Maybe Text)
 cmur1Key = lens _cmur1Key (\s a -> s { _cmur1Key = a })
diff --git a/gen/Network/AWS/S3/CopyObject.hs b/gen/Network/AWS/S3/CopyObject.hs
--- a/gen/Network/AWS/S3/CopyObject.hs
+++ b/gen/Network/AWS/S3/CopyObject.hs
@@ -386,7 +386,7 @@
 data CopyObjectResponse = CopyObjectResponse
     { _corCopyObjectResult     :: Maybe CopyObjectResult
     , _corCopySourceVersionId  :: Maybe Text
-    , _corExpiration           :: Maybe RFC822
+    , _corExpiration           :: Maybe Text
     , _corSSECustomerAlgorithm :: Maybe Text
     , _corSSECustomerKeyMD5    :: Maybe Text
     , _corSSEKMSKeyId          :: Maybe (Sensitive Text)
@@ -401,7 +401,7 @@
 --
 -- * 'corCopySourceVersionId' @::@ 'Maybe' 'Text'
 --
--- * 'corExpiration' @::@ 'Maybe' 'UTCTime'
+-- * 'corExpiration' @::@ 'Maybe' 'Text'
 --
 -- * 'corSSECustomerAlgorithm' @::@ 'Maybe' 'Text'
 --
@@ -431,8 +431,8 @@
     lens _corCopySourceVersionId (\s a -> s { _corCopySourceVersionId = a })
 
 -- | If the object expiration is configured, the response includes this header.
-corExpiration :: Lens' CopyObjectResponse (Maybe UTCTime)
-corExpiration = lens _corExpiration (\s a -> s { _corExpiration = a }) . mapping _Time
+corExpiration :: Lens' CopyObjectResponse (Maybe Text)
+corExpiration = lens _corExpiration (\s a -> s { _corExpiration = a })
 
 -- | If server-side encryption with a customer-provided encryption key was
 -- requested, the response will include this header confirming the encryption
diff --git a/gen/Network/AWS/S3/GetObject.hs b/gen/Network/AWS/S3/GetObject.hs
--- a/gen/Network/AWS/S3/GetObject.hs
+++ b/gen/Network/AWS/S3/GetObject.hs
@@ -271,7 +271,7 @@
     , _gorContentType             :: Maybe Text
     , _gorDeleteMarker            :: Maybe Bool
     , _gorETag                    :: Maybe Text
-    , _gorExpiration              :: Maybe RFC822
+    , _gorExpiration              :: Maybe Text
     , _gorExpires                 :: Maybe RFC822
     , _gorLastModified            :: Maybe RFC822
     , _gorMetadata                :: Map (CI Text) Text
@@ -309,7 +309,7 @@
 --
 -- * 'gorETag' @::@ 'Maybe' 'Text'
 --
--- * 'gorExpiration' @::@ 'Maybe' 'UTCTime'
+-- * 'gorExpiration' @::@ 'Maybe' 'Text'
 --
 -- * 'gorExpires' @::@ 'Maybe' 'UTCTime'
 --
@@ -410,8 +410,8 @@
 -- response includes this header. It includes the expiry-date and rule-id key
 -- value pairs providing object expiration information. The value of the rule-id
 -- is URL encoded.
-gorExpiration :: Lens' GetObjectResponse (Maybe UTCTime)
-gorExpiration = lens _gorExpiration (\s a -> s { _gorExpiration = a }) . mapping _Time
+gorExpiration :: Lens' GetObjectResponse (Maybe Text)
+gorExpiration = lens _gorExpiration (\s a -> s { _gorExpiration = a })
 
 -- | The date and time at which the object is no longer cacheable.
 gorExpires :: Lens' GetObjectResponse (Maybe UTCTime)
diff --git a/gen/Network/AWS/S3/HeadObject.hs b/gen/Network/AWS/S3/HeadObject.hs
--- a/gen/Network/AWS/S3/HeadObject.hs
+++ b/gen/Network/AWS/S3/HeadObject.hs
@@ -207,7 +207,7 @@
     , _horContentType             :: Maybe Text
     , _horDeleteMarker            :: Maybe Bool
     , _horETag                    :: Maybe Text
-    , _horExpiration              :: Maybe RFC822
+    , _horExpiration              :: Maybe Text
     , _horExpires                 :: Maybe RFC822
     , _horLastModified            :: Maybe RFC822
     , _horMetadata                :: Map (CI Text) Text
@@ -243,7 +243,7 @@
 --
 -- * 'horETag' @::@ 'Maybe' 'Text'
 --
--- * 'horExpiration' @::@ 'Maybe' 'UTCTime'
+-- * 'horExpiration' @::@ 'Maybe' 'Text'
 --
 -- * 'horExpires' @::@ 'Maybe' 'UTCTime'
 --
@@ -338,8 +338,8 @@
 -- response includes this header. It includes the expiry-date and rule-id key
 -- value pairs providing object expiration information. The value of the rule-id
 -- is URL encoded.
-horExpiration :: Lens' HeadObjectResponse (Maybe UTCTime)
-horExpiration = lens _horExpiration (\s a -> s { _horExpiration = a }) . mapping _Time
+horExpiration :: Lens' HeadObjectResponse (Maybe Text)
+horExpiration = lens _horExpiration (\s a -> s { _horExpiration = a })
 
 -- | The date and time at which the object is no longer cacheable.
 horExpires :: Lens' HeadObjectResponse (Maybe UTCTime)
diff --git a/gen/Network/AWS/S3/PutObject.hs b/gen/Network/AWS/S3/PutObject.hs
--- a/gen/Network/AWS/S3/PutObject.hs
+++ b/gen/Network/AWS/S3/PutObject.hs
@@ -307,7 +307,7 @@
 
 data PutObjectResponse = PutObjectResponse
     { _porETag                 :: Maybe Text
-    , _porExpiration           :: Maybe RFC822
+    , _porExpiration           :: Maybe Text
     , _porSSECustomerAlgorithm :: Maybe Text
     , _porSSECustomerKeyMD5    :: Maybe Text
     , _porSSEKMSKeyId          :: Maybe (Sensitive Text)
@@ -321,7 +321,7 @@
 --
 -- * 'porETag' @::@ 'Maybe' 'Text'
 --
--- * 'porExpiration' @::@ 'Maybe' 'UTCTime'
+-- * 'porExpiration' @::@ 'Maybe' 'Text'
 --
 -- * 'porSSECustomerAlgorithm' @::@ 'Maybe' 'Text'
 --
@@ -350,8 +350,8 @@
 
 -- | If the object expiration is configured, this will contain the expiration date
 -- (expiry-date) and rule ID (rule-id). The value of rule-id is URL encoded.
-porExpiration :: Lens' PutObjectResponse (Maybe UTCTime)
-porExpiration = lens _porExpiration (\s a -> s { _porExpiration = a }) . mapping _Time
+porExpiration :: Lens' PutObjectResponse (Maybe Text)
+porExpiration = lens _porExpiration (\s a -> s { _porExpiration = a })
 
 -- | If server-side encryption with a customer-provided encryption key was
 -- requested, the response will include this header confirming the encryption
diff --git a/gen/Network/AWS/S3/Types.hs b/gen/Network/AWS/S3/Types.hs
--- a/gen/Network/AWS/S3/Types.hs
+++ b/gen/Network/AWS/S3/Types.hs
@@ -7,6 +7,7 @@
 {-# LANGUAGE OverloadedStrings           #-}
 {-# LANGUAGE RecordWildCards             #-}
 {-# LANGUAGE TypeFamilies                #-}
+{-# LANGUAGE ViewPatterns                #-}
 
 {-# OPTIONS_GHC -fno-warn-unused-imports #-}
 
@@ -417,7 +418,6 @@
     , module Network.AWS.S3.Internal
     ) where
 
-import Network.AWS.Error
 import Network.AWS.Prelude
 import Network.AWS.Signing
 import Network.AWS.S3.Internal
@@ -430,18 +430,44 @@
     type Sg S3 = V4
     type Er S3 = RESTError
 
-    service = Service
-        { _svcAbbrev       = "S3"
-        , _svcPrefix       = "s3"
-        , _svcVersion      = "2006-03-01"
-        , _svcTargetPrefix = Nothing
-        , _svcJSONVersion  = Nothing
-        }
+    service = service'
+      where
+        service' :: Service S3
+        service' = Service
+            { _svcAbbrev       = "S3"
+            , _svcPrefix       = "s3"
+            , _svcVersion      = "2006-03-01"
+            , _svcTargetPrefix = Nothing
+            , _svcJSONVersion  = Nothing
+            , _svcHandle       = handle
+            , _svcRetry        = retry
+            }
 
-    handle = restError statusSuccess
+        handle :: Status
+               -> Maybe (LazyByteString -> ServiceError RESTError)
+        handle = restError statusSuccess service'
 
+        retry :: Retry S3
+        retry = Exponential
+            { _retryBase     = 0.05
+            , _retryGrowth   = 2
+            , _retryAttempts = 5
+            , _retryCheck    = check
+            }
+
+        check :: Status
+              -> RESTError
+              -> Bool
+        check (statusCode -> s) (awsErrorCode -> e)
+            | s == 400 && "RequestTimeout" == e = True -- Timeouts
+            | s == 500  = True -- General Server Error
+            | s == 509  = True -- Limit Exceeded
+            | s == 503  = True -- Service Unavailable
+            | otherwise = False
+
 ns :: Text
 ns = "http://s3.amazonaws.com/doc/2006-03-01/"
+{-# INLINE ns #-}
 
 data Event
     = S3ObjectCreatedCompleteMultipartUpload -- ^ s3:ObjectCreated:CompleteMultipartUpload
diff --git a/gen/Network/AWS/S3/Waiters.hs b/gen/Network/AWS/S3/Waiters.hs
new file mode 100644
--- /dev/null
+++ b/gen/Network/AWS/S3/Waiters.hs
@@ -0,0 +1,63 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TypeFamilies      #-}
+
+-- Module      : Network.AWS.S3.Waiters
+-- Copyright   : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
+-- License     : This Source Code Form is subject to the terms of
+--               the Mozilla Public License, v. 2.0.
+--               A copy of the MPL can be found in the LICENSE file or
+--               you can obtain it at http://mozilla.org/MPL/2.0/.
+-- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>
+-- Stability   : experimental
+-- Portability : non-portable (GHC extensions)
+--
+-- Derived from AWS service descriptions, licensed under Apache 2.0.
+
+module Network.AWS.S3.Waiters where
+
+import Network.AWS.S3.HeadBucket
+import Network.AWS.S3.HeadObject
+import Network.AWS.S3.Types
+import Network.AWS.Waiters
+
+bucketExists :: Wait HeadBucket
+bucketExists = Wait
+    { _waitName      = "BucketExists"
+    , _waitAttempts  = 20
+    , _waitDelay     = 5
+    , _waitAcceptors =
+        [ matchStatus 200 AcceptSuccess
+        , matchStatus 404 AcceptRetry
+        ]
+    }
+
+bucketNotExists :: Wait HeadBucket
+bucketNotExists = Wait
+    { _waitName      = "BucketNotExists"
+    , _waitAttempts  = 20
+    , _waitDelay     = 5
+    , _waitAcceptors =
+        [ matchStatus 404 AcceptSuccess
+        ]
+    }
+
+objectExists :: Wait HeadObject
+objectExists = Wait
+    { _waitName      = "ObjectExists"
+    , _waitAttempts  = 20
+    , _waitDelay     = 5
+    , _waitAcceptors =
+        [ matchStatus 200 AcceptSuccess
+        , matchStatus 404 AcceptRetry
+        ]
+    }
+
+objectNotExists :: Wait HeadObject
+objectNotExists = Wait
+    { _waitName      = "ObjectNotExists"
+    , _waitAttempts  = 20
+    , _waitDelay     = 5
+    , _waitAcceptors =
+        [ matchStatus 404 AcceptSuccess
+        ]
+    }
