diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,17 @@
 Changelog
 =========
 
+Version 0.2.8.0
+---------------
+
+*December 14, 2020*
+
+<https://github.com/mstksg/advent-of-code-api/releases/tag/v0.2.8.0>
+
+*   Add servant endpoint to query calendar page for a year to infer next puzzle
+    and time to puzzle in seconds, backed by the `NextDayTime` data type.
+*   Add `AoCNextDayTime` to `AoC` to support the above operation.
+
 Version 0.2.7.1
 ---------------
 
diff --git a/advent-of-code-api.cabal b/advent-of-code-api.cabal
--- a/advent-of-code-api.cabal
+++ b/advent-of-code-api.cabal
@@ -4,10 +4,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 42fe072e1e23c3a691ec787cf5e1118785d56470c1ec117048fc826c4e47609c
+-- hash: 6cf4283a8e57b7716c98e2f2914b0396dc18f396a3ff0bb7f5df28750eefbb92
 
 name:           advent-of-code-api
-version:        0.2.7.1
+version:        0.2.8.0
 synopsis:       Advent of Code REST API bindings and servant API
 description:    Haskell bindings for Advent of Code REST API and a servant API.  Please use
                 responsibly! See README.md or "Advent" module for an introduction and
diff --git a/src/Advent.hs b/src/Advent.hs
--- a/src/Advent.hs
+++ b/src/Advent.hs
@@ -50,6 +50,7 @@
     AoC(..)
   , Part(..)
   , Day(..)
+  , NextDayTime(..)
   , AoCOpts(..)
   , SubmitRes(..), showSubmitRes
   , runAoC
@@ -224,6 +225,18 @@
     AoCGlobalLeaderboard
         :: AoC GlobalLeaderboard
 
+    -- | From the calendar, fetch the next release's day and the
+    -- number of seconds util its release, if there is any at all.
+    --
+    -- This does an actual request to the AoC servers, and is only accurate
+    -- to the second; to infer this information (to the millisecond level)
+    -- from the system clock, you should probably use 'timeToRelease' and
+    -- 'aocServerTime' instead, which requires no network requests.
+    --
+    -- @since 0.2.8.0
+    AoCNextDayTime
+        :: AoC NextDayTime
+
 deriving instance Show (AoC a)
 deriving instance Typeable (AoC a)
 
@@ -235,6 +248,7 @@
 aocDay (AoCLeaderboard _) = Nothing
 aocDay (AoCDailyLeaderboard d) = Just d
 aocDay AoCGlobalLeaderboard = Nothing
+aocDay AoCNextDayTime       = Nothing
 
 -- | A possible (syncronous, logical, pure) error returnable from 'runAoC'.
 -- Does not cover any asynchronous or IO errors.
@@ -313,12 +327,14 @@
     AoCInput  i       -> let _ :<|> r :<|> _ = adventAPIPuzzleClient yr i in r
     AoCSubmit i p ans -> let _ :<|> _ :<|> r = adventAPIPuzzleClient yr i
                          in  r (SubmitInfo p ans) <&> \(x :<|> y) -> (x, y)
-    AoCLeaderboard c  -> let _ :<|> _ :<|> _ :<|> r = adventAPIClient yr
+    AoCLeaderboard c  -> let _ :<|> _ :<|> _ :<|> _ :<|> r = adventAPIClient yr
                          in  r (PublicCode c)
-    AoCDailyLeaderboard d -> let _ :<|> _ :<|> r :<|> _ = adventAPIClient yr
+    AoCDailyLeaderboard d -> let _ :<|> _ :<|> _ :<|> r :<|> _ = adventAPIClient yr
                              in  r d
-    AoCGlobalLeaderboard  -> let _ :<|> r :<|> _ :<|> _ = adventAPIClient yr
+    AoCGlobalLeaderboard  -> let _ :<|> _ :<|> r :<|> _ = adventAPIClient yr
                              in  r
+    AoCNextDayTime        -> let r :<|> _ = adventAPIClient yr
+                             in  r
 
 
 -- | Cache file for a given 'AoC' command
@@ -332,8 +348,9 @@
     AoCInput  d      -> Just $ printf "input/%s%04d/%02d.txt" keyDir yr (dayInt d)
     AoCSubmit{}      -> Nothing
     AoCLeaderboard{} -> Nothing
-    AoCDailyLeaderboard d -> Just $ printf "daily/%04d/%02d.json" yr (dayInt d)
+    AoCDailyLeaderboard d  -> Just $ printf "daily/%04d/%02d.json" yr (dayInt d)
     AoCGlobalLeaderboard{} -> Just $ printf "global/%04d.json" yr
+    AoCNextDayTime         -> Nothing
   where
     keyDir = case sess of
       Nothing -> ""
@@ -443,6 +460,7 @@
             guard $ not evt'        -- only load cache if evt' is false: it was saved in a non-evt time
             pure $ Right lb
         }
+    AoCNextDayTime{} -> noCache
   where
     expectedParts :: Set Part
     expectedParts
diff --git a/src/Advent/API.hs b/src/Advent/API.hs
--- a/src/Advent/API.hs
+++ b/src/Advent/API.hs
@@ -42,38 +42,40 @@
   , FromTags(..)
   , Articles
   , Divs
+  , Scripts
   , RawText
   -- * Internal
   , processHTML
   ) where
 
 import           Advent.Types
+import           Control.Applicative
 import           Control.Monad
 import           Control.Monad.State
 import           Data.Bifunctor
 import           Data.Char
 import           Data.Finite
 import           Data.Foldable
-import           Data.List.NonEmpty         (NonEmpty(..))
-import           Data.Map                   (Map)
+import           Data.List.NonEmpty     (NonEmpty(..))
+import           Data.Map               (Map)
 import           Data.Maybe
 import           Data.Ord
 import           Data.Proxy
-import           Data.Text                  (Text)
-import           Data.Time hiding           (Day)
+import           Data.Text              (Text)
+import           Data.Time hiding       (Day)
 import           GHC.TypeLits
 import           Servant.API
 import           Servant.Client
-import           Text.HTML.TagSoup.Tree     (TagTree(..))
-import           Text.Read                  (readMaybe)
-import qualified Data.ByteString.Lazy       as BSL
-import qualified Data.List.NonEmpty         as NE
-import qualified Data.Map                   as M
-import qualified Data.Text                  as T
-import qualified Data.Text.Encoding         as T
-import qualified Network.HTTP.Media         as M
-import qualified Text.HTML.TagSoup          as H
-import qualified Text.HTML.TagSoup.Tree     as H
+import           Text.HTML.TagSoup.Tree (TagTree(..))
+import           Text.Read              (readMaybe)
+import qualified Data.ByteString.Lazy   as BSL
+import qualified Data.List.NonEmpty     as NE
+import qualified Data.Map               as M
+import qualified Data.Text              as T
+import qualified Data.Text.Encoding     as T
+import qualified Network.HTTP.Media     as M
+import qualified Text.HTML.TagSoup      as H
+import qualified Text.HTML.TagSoup.Tree as H
 
 #if !MIN_VERSION_base(4,11,0)
 import           Data.Semigroup ((<>))
@@ -106,6 +108,9 @@
 -- @since 0.2.3.0
 type Divs     = HTMLTags "div"
 
+-- | Interpret a response as a list of HTML 'T.Text' found in @<script>@ tags.
+type Scripts = HTMLTags "script"
+
 -- | Class for interpreting a list of 'T.Text' in tags to some desired
 -- output.
 --
@@ -205,30 +210,46 @@
               xs' <- forM xs $ \x -> x { glbmRank = Rank currScore } <$ modify succ
               pure (Rank currScore, xs')
 
-
+instance FromTags "script" NextDayTime where
+    fromTags _ = (<|> Just NoNextDayTime) . listToMaybe . mapMaybe findNDT
+      where
+        -- var server_eta = 25112;
+        -- var key = "2020-15-"+server_eta;
+        findNDT body = do
+          eta    <- T.unpack <$> grabKey "server_eta" body
+          yd     <- grabKey "key" body
+          sec    <- readMaybe eta
+          dayStr <- listToMaybe . drop 1 . T.splitOn "-" $ yd
+          dy     <- mkDay =<< readMaybe (T.unpack dayStr)
+          pure $ NextDayTime dy sec
+        grabKey t str =
+            fst . T.breakOn ";\n" <$> T.stripPrefix t' (snd (T.breakOn t' str))
+          where
+            t' = "var " <> t <> " = "
 
 -- | REST API of Advent of Code.
 --
 -- Note that most of these requests assume a "session=" cookie.
 type AdventAPI =
       Capture "year" Integer
-   :> ("day" :> Capture "day" Day
+   :> (Get '[Scripts] NextDayTime
+  :<|> "day" :> Capture "day" Day
              :> (Get '[Articles] (Map Part Text)
             :<|> "input" :> Get '[RawText] Text
             :<|> "answer"
                      :> ReqBody '[FormUrlEncoded] SubmitInfo
                      :> Post    '[Articles] (Text :<|> SubmitRes)
                 )
-  :<|> "leaderboard"
+  :<|> ("leaderboard"
     :> (Get '[Divs] GlobalLeaderboard
    :<|> "day"     :> Capture "day" Day :> Get '[Divs] DailyLeaderboard
    :<|> "private" :> "view"
                   :> Capture "code" PublicCode
                   :> Get '[JSON] Leaderboard
-
-       )
+       ))
       )
 
+
 -- | 'Proxy' used for /servant/ functions.
 adventAPI :: Proxy AdventAPI
 adventAPI = Proxy
@@ -236,7 +257,8 @@
 -- | 'ClientM' requests based on 'AdventAPI', generated by servant.
 adventAPIClient
     :: Integer
-    -> (Day -> ClientM (Map Part Text) :<|> ClientM Text :<|> (SubmitInfo -> ClientM (Text :<|> SubmitRes)) )
+    -> ClientM NextDayTime
+  :<|> (Day -> ClientM (Map Part Text) :<|> ClientM Text :<|> (SubmitInfo -> ClientM (Text :<|> SubmitRes)) )
   :<|> ClientM GlobalLeaderboard
   :<|> (Day -> ClientM DailyLeaderboard)
   :<|> (PublicCode -> ClientM Leaderboard)
@@ -250,7 +272,7 @@
     -> ClientM (Map Part Text) :<|> ClientM Text :<|> (SubmitInfo -> ClientM (Text :<|> SubmitRes))
 adventAPIPuzzleClient y = pis
   where
-    pis :<|> _ = adventAPIClient y
+    _ :<|> pis :<|> _ = adventAPIClient y
 
 userNameNaked :: [TagTree Text] -> Maybe Text
 userNameNaked = (listToMaybe .) . mapMaybe $ \x -> do
diff --git a/src/Advent/Types.hs b/src/Advent/Types.hs
--- a/src/Advent/Types.hs
+++ b/src/Advent/Types.hs
@@ -41,6 +41,7 @@
   , DailyLeaderboardMember(..)
   , GlobalLeaderboard(..)
   , GlobalLeaderboardMember(..)
+  , NextDayTime(..)
   -- * Util
   , mkDay, mkDay_, dayInt
   , _DayInt, pattern DayInt
@@ -246,6 +247,13 @@
   }
   deriving (Show, Eq, Ord, Typeable, Generic)
 
+-- | The next day for a challenge in a given year, and also the number of
+-- seconds until the challenge is released.
+--
+-- @since 0.2.8.0
+data NextDayTime = NextDayTime Day Int
+                 | NoNextDayTime
+  deriving (Show, Eq, Ord, Typeable, Generic)
 
 instance ToHttpApiData Part where
     toUrlPiece = T.pack . show . partInt
