From cf4584a312e9004eb657cd4c13eaeb9cc006e864 Mon Sep 17 00:00:00 2001
From: Eric Wang
Date: Tue, 25 Aug 2026 00:06:12 -0700
Subject: [PATCH] fix(site): the weekend rule reached the table and not the
sentence
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
PRICE_SCHEDULE learned on 2026-08-22 that peak is weekdays only. Four
places that a reader (or a search engine) actually reads did not: the
cost page's rate-card blurb and three JSON-LD FAQ answers went on saying
peak runs "01:00-04:00 and 06:00-10:00 UTC daily", and the pricing
strip's not-yet-live branch still described the previous era's flip
because adding a new era silently repointed eras[-1] at it.
Fixed, and then closed the class: price_now_verdict() now derives the
day restriction from the schedule instead of spelling it out, and
check_peak_claims() fails the build when any
section quoting both
peak windows omits the restriction the schedule is actually in.
The escape hatch is a marker, not a turn of phrase — a news entry about
a superseded announcement carries on purpose,
so history cannot be claimed by accident. Verified both directions:
dropping a qualifier fails, and dropping `days` from the schedule fails
five sections that still said Mon-Fri.
---
site/bench/index.html | 7 +-
site/build.py | 180 ++++++++++++++++++++++++++++++++++--------
site/cost/index.html | 3 +-
site/news/index.html | 23 +++---
4 files changed, 169 insertions(+), 44 deletions(-)
diff --git a/site/bench/index.html b/site/bench/index.html
index 64cf120..5c95a90 100644
--- a/site/bench/index.html
+++ b/site/bench/index.html
@@ -226,9 +226,10 @@
What it means in practice
a table cell into a bill – and since the repricing hit that column
hardest, it is worth more attention now, not less.
Schedule what can be scheduled. The same call costs
-half as much outside 01:00–04:00 and 06:00–10:00 UTC. Batch
-evaluation, bulk review and overnight agent runs are exactly the workloads
-that can move; ds pricing says which period you are in.
+half as much outside 01:00–04:00 and 06:00–10:00 UTC, Monday to
+Friday – and the whole weekend is off-peak. Batch evaluation, bulk
+review and overnight agent runs are exactly the workloads that can move;
+ds pricing says which period you are in.
Measure successful-task cost, not per-token price. A
cheaper model that retries five times can cost more than a dearer one that lands
first. The ledger stores exact token counts
diff --git a/site/build.py b/site/build.py
index 40887d2..173f4ea 100644
--- a/site/build.py
+++ b/site/build.py
@@ -18,6 +18,7 @@
import datetime
import html
import pathlib
+import re
import subprocess
import sys
@@ -183,9 +184,15 @@ def price_now_verdict():
Phrased against the era the build lands in, and only ever forwards:
once the flip is behind us it stays behind us, so this copy cannot go
- stale the way "from " wording did the morning after. The date
- and the windows come from the data; the copy cannot drift from the
- table.
+ stale the way "from " wording did the morning after.
+
+ Every fact in both sentences -- the instant, the windows, and which
+ days they apply on -- is read off PRICE_SCHEDULE. The weekend era of
+ 2026-08-22 is why: it repointed eras[-1] at a new instant while the
+ not-yet-live branch went on saying "every hour bills at the flat card",
+ which had been true of the previous last era and was not true of this
+ one. Prose that names a fact the table also holds is prose that will
+ drift; derive it or do not write it.
"""
eras = sorted({r["effective"] for r in PRICE_SCHEDULE})
if len(eras) == 1:
@@ -193,7 +200,8 @@ def price_now_verdict():
"Flat pricing, at every hour. No time-of-day "
"tiers are in effect, and the card below is the price."
)
- flip = eras[-1].replace("T", " ").replace(":00Z", " UTC")
+ latest = eras[-1]
+ flip = latest.replace("T", " ").replace(":00Z", " UTC")
# Deduped: successive eras repeat the same two windows -- the weekend
# era changes which days they apply on, not which hours -- and without
# this the sentence lists each window once per era.
@@ -205,23 +213,36 @@ def price_now_verdict():
if span not in spans:
spans.append(span)
windows = " and ".join(spans)
+ # Which days the newest era's peak rows apply on, and therefore what
+ # "every other hour" has to say to stay true.
+ peak_days = {
+ tuple(r["days"]) if r.get("days") else None
+ for r in PRICE_SCHEDULE
+ if r["effective"] == latest and r["start"] is not None
+ }
+ weekdays_only = peak_days == {tuple(WEEKDAYS)}
+ when = "UTC, Monday to Friday" if weekdays_only else "UTC, every day"
+ rest = (
+ "every other hour, and the whole weekend, is off-peak"
+ if weekdays_only
+ else "every other hour is off-peak"
+ )
+ strip = (
+ " With JavaScript on, this strip reads your clock and names the "
+ "period you are in right now."
+ )
live = _now() >= datetime.datetime.strptime(
- eras[-1], "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=datetime.timezone.utc)
+ latest, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=datetime.timezone.utc)
if live:
return (
"Time-of-day billing is live. Peak hours are "
- f"{windows} UTC, Monday to Friday, at twice the off-peak rate; "
- "every other hour, and the whole weekend, is off-peak. With "
- "JavaScript on, this strip reads your clock and names the "
- "period you are in right now."
+ f"{windows} {when}, at twice the off-peak rate; {rest}."
+ + strip
)
return (
- f"Before {flip}, every hour bills at the flat card "
- f"below. From that instant DeepSeek bills peak/off-peak "
- f"on a new, higher card: peak hours are {windows} UTC daily, at "
- "twice the off-peak rate, and every other hour is off-peak. With "
- "JavaScript on, this strip reads your clock and names the period "
- "you are in right now."
+ f"The schedule below changes at {flip}. From that "
+ f"instant peak hours are {windows} {when}, at twice the off-peak "
+ f"rate, and {rest}." + strip
)
# Theme. The default is whatever the OS says; the toggle overrides it and
@@ -1220,7 +1241,8 @@ def jstr(s):
The rate card
USD per 1M tokens. Peak hours are 01:00–04:00 and 06:00–10:00
-UTC daily; every other hour is off-peak at half the peak rate. The
+UTC, Monday to Friday; every other hour, and the whole weekend, is off-peak
+at half the peak rate. The
pricing page carries the full schedule and
names the period you are in right now:
@@ -1665,9 +1687,10 @@ def jstr(s):
a table cell into a bill – and since the repricing hit that column
hardest, it is worth more attention now, not less.
Schedule what can be scheduled. The same call costs
-half as much outside 01:00–04:00 and 06:00–10:00 UTC. Batch
-evaluation, bulk review and overnight agent runs are exactly the workloads
-that can move; ds pricing says which period you are in.
+half as much outside 01:00–04:00 and 06:00–10:00 UTC, Monday to
+Friday – and the whole weekend is off-peak. Batch evaluation, bulk
+review and overnight agent runs are exactly the workloads that can move;
+ds pricing says which period you are in.
Measure successful-task cost, not per-token price. A
cheaper model that retries five times can cost more than a dearer one that lands
first. The ledger stores exact token counts
@@ -1701,11 +1724,11 @@ def jstr(s):
("Is dsh the same as deepseek-cli?",
"No. dsh (DeepSeek Harness) is DeepSeek's official agent harness: it runs an agent loop, executes tools, and manages plugins, skills and sessions. deepseek-cli is an unofficial single-binary API client: it sends one request in any of DeepSeek's four wire formats, prints the response and its estimated cost, and carries DeepSeek's API documentation offline. They are complementary, not competing: run agents with dsh, and use deepseek-cli to check a key, price a call, debug the wire formats, or query the docs."),
("Did DeepSeek raise its API prices?",
- "Yes. The increase took effect at 16:00 UTC on 2026-08-16 and is live: DeepSeek now bills peak/off-peak, with peak hours 01:00-04:00 and 06:00-10:00 UTC daily at twice the off-peak rate. The flat card is gone from the official pricing page. Against it, deepseek-v4-pro peak is 3x on cache-miss input, 4.6x on output and 12x on cache-hit input; off-peak is half of each. This resolved both the undated broad price rise announced on 2026-08-06 and the undated peak-hour policy announced in June 2026."),
+ "Yes. The increase took effect at 16:00 UTC on 2026-08-16 and is live: DeepSeek now bills peak/off-peak, with peak hours 01:00-04:00 and 06:00-10:00 UTC, Monday to Friday, at twice the off-peak rate; every other hour, and the whole weekend since 2026-08-22, is off-peak. The flat card is gone from the official pricing page. Against it, deepseek-v4-pro peak is 3x on cache-miss input, 4.6x on output and 12x on cache-hit input; off-peak is half of each. This resolved both the undated broad price rise announced on 2026-08-06 and the undated peak-hour policy announced in June 2026."),
("What are DeepSeek's current API prices?",
- "Per 1M tokens (cache hit / cache miss / output), on the card in force since 16:00 UTC on 2026-08-16: deepseek-v4-flash is $0.007 / $0.22 / $0.66 off-peak and $0.014 / $0.44 / $1.32 peak; deepseek-v4-pro is $0.022 / $0.66 / $1.98 off-peak and $0.044 / $1.32 / $3.96 peak. In RMB, pro is 0.15 / 4.5 / 13.5 yuan off-peak and 0.3 / 9 / 27 yuan peak. Peak hours are 01:00-04:00 and 06:00-10:00 UTC; every other hour is off-peak."),
+ "Per 1M tokens (cache hit / cache miss / output), on the card in force since 16:00 UTC on 2026-08-16: deepseek-v4-flash is $0.007 / $0.22 / $0.66 off-peak and $0.014 / $0.44 / $1.32 peak; deepseek-v4-pro is $0.022 / $0.66 / $1.98 off-peak and $0.044 / $1.32 / $3.96 peak. In RMB, pro is 0.15 / 4.5 / 13.5 yuan off-peak and 0.3 / 9 / 27 yuan peak. Peak hours are 01:00-04:00 and 06:00-10:00 UTC, Monday to Friday; every other hour, and the whole weekend, is off-peak."),
("Which hours are cheapest on the DeepSeek API?",
- "Every hour outside 01:00-04:00 and 06:00-10:00 UTC, which bill at half the peak rate. Those peak windows are 09:00-12:00 and 14:00-18:00 Beijing time, the Chinese working day, so the whole European and American working day is off-peak. Batch and agent work that can be scheduled should run off-peak; the saving is exactly 2x."),
+ "Every hour outside 01:00-04:00 and 06:00-10:00 UTC, Monday to Friday, which bill at half the peak rate — and since 2026-08-22 the whole weekend, on the Beijing calendar, so the cheap window opens at 16:00 UTC on Friday. Those peak windows are 09:00-12:00 and 14:00-18:00 Beijing time, the Chinese working day, so the whole European and American working day is off-peak. Batch and agent work that can be scheduled should run off-peak; the saving is exactly 2x."),
("Where can I follow DeepSeek API changes?",
"DeepSeek's own change log lives at api-docs.deepseek.com/updates. The deepseek CLI carries the same documentation inside the binary: `deepseek docs changelog` prints it offline, and `deepseek docs sync` refreshes the snapshot."),
]),
@@ -1782,11 +1805,12 @@ def jstr(s):
Cells read off-peak / peak. Peak hours are
-01:00–04:00 and 06:00–10:00 UTC – 09:00–12:00
-and 14:00–18:00 Beijing, seven hours a day – and every other hour
-is off-peak at half the peak rate. Peak ran seven days a week until
-2026-08-22; see weekends are off-peak
-below. In RMB, pro is
+01:00–04:00 and 06:00–10:00 UTC, Monday to Friday
+– 09:00–12:00 and 14:00–18:00 Beijing, seven hours a
+weekday – and every other hour, plus the whole weekend, is off-peak at
+half the peak rate. Peak ran seven days a week until 2026-08-22; see
+weekends are off-peak above. In RMB,
+pro is
¥0.15 / ¥4.5 / ¥13.5 off-peak and ¥0.3 / ¥9 / ¥27 at
peak.
Against the flat card of 2026-08-02, off-peak / peak:
@@ -1841,15 +1865,18 @@ def jstr(s):
quote, and the difference is a factor of two.
2026-08-13 · the price rise has its date and its numberstook effect 2026-08-16
+
The other shoe drops. Alongside the V4-Pro GA release, DeepSeek's
Models & Pricing page now
carries the repricing that the August 6 notice
promised and the June peak-hour policy
sketched, and this time it is dated: at 16:00 UTC on 2026-08-16
the API moves to peak/off-peak billing. Peak hours are
-01:00–04:00 and 06:00–10:00 UTC daily – the boundaries
-are defined in UTC – at twice the off-peak rate; every other hour is
-off-peak.
+01:00–04:00 and 06:00–10:00 UTC, as announced then
+every day of the week – the boundaries are defined in UTC – at
+twice the off-peak rate; every other hour is off-peak. (Weekends came off
+the peak calendar nine days later; see
+weekends are off-peak.)
The multiplier is the June policy's, but the base card is new and
higher. Off-peak is not a discount on today's prices: a flash cache-miss
input token goes from $0.14 to $0.22 per 1M in the cheapest hour,
@@ -2119,6 +2146,7 @@ def jstr(s):
call can be repriced under it.
announced 2026-06-29 · 2× during peak hoursdated 2026-08-13
+
Update: the date exists now, and one detail below did
not survive it. The 2026-08-13 announcement keeps
the 2× multiplier and the same windows but puts them on a new, higher
@@ -2458,11 +2486,94 @@ def jstr(s):
),
))
+# The peak-window claims tripwire.
+#
+# The rate card has a --check that compares committed HTML against the
+# generator, and `make price-check` compares the CLI's card against the
+# gateway's. Neither can see the sentence a reader actually reads: on
+# 2026-08-22 weekends came off the peak calendar, PRICE_SCHEDULE learned
+# it that day, and three FAQ answers plus the cost page went on telling
+# search engines that peak runs "01:00-04:00 and 06:00-10:00 UTC daily".
+# Nothing was red. The table and the prose disagreed and only the table
+# was checked.
+#
+# So: every section that quotes the peak windows has to say which days
+# they apply on, and the phrase it has to carry is read off PRICE_SCHEDULE
+# rather than typed here. Copy that stops matching the data fails the
+# build instead of shipping.
+#
+# The escape hatch is a marker, not a turn of phrase. A news entry about
+# a superseded announcement is quoting what was said on a date, and is
+# supposed to keep saying it -- but "this sentence is history" is a claim
+# the author has to make on purpose, so it is spelled out in the source
+# and cannot be satisfied by accident.
+HISTORICAL_CLAIM = ""
+
+
+def peak_days_phrase():
+ """How the current era restricts peak, as the copy must spell it."""
+ latest = sorted({r["effective"] for r in PRICE_SCHEDULE})[-1]
+ days = {
+ tuple(r["days"]) if r.get("days") else None
+ for r in PRICE_SCHEDULE
+ if r["effective"] == latest and r["start"] is not None
+ }
+ if days == {tuple(WEEKDAYS)}:
+ return ("Monday to Friday", ("monday to friday", "mon–fri",
+ "mon-fri", "weekday"))
+ if days == {None}:
+ return ("every day", ("every day", "daily"))
+ raise SystemExit(
+ f"claims: PRICE_SCHEDULE's newest era mixes day restrictions {days}; "
+ "the tripwire cannot say what the copy should claim")
+
+
+def check_peak_claims(pages):
+ """Fail the build if a rendered claim outlives the schedule.
+
+ `pages` is (name, html). Sections are split on
, because that is
+ the unit a reader arrives at from a table of contents or a search
+ result -- a caption that names the windows and a heading that dates
+ them belong to the same claim.
+ """
+ want, accepted = peak_days_phrase()
+ spans = [
+ f"{_fmt_minutes(r['start'])}–{_fmt_minutes(r['end'])}"
+ for r in PRICE_SCHEDULE if r["start"] is not None
+ ]
+ if len(spans) < 2:
+ return True
+ # The JSON-LD answers are plain text, so they spell the dash out.
+ forms = [spans, [sp.replace("–", "-") for sp in spans]]
+ bad = []
+ for name, html in pages:
+ for section in re.split(r"(?=
]*>(.*?)
", section, re.S)
+ where = re.sub(r"<[^>]+>", "", head.group(1)) if head else "(top of page)"
+ bad.append(f" {name}: {where.strip()[:80]}")
+ if bad:
+ print(f'claims: a peak-hours claim does not say "{want}", which is '
+ "what PRICE_SCHEDULE is in. Say it, or mark the section "
+ f'{HISTORICAL_CLAIM}:', file=sys.stderr)
+ print("\n".join(bad), file=sys.stderr)
+ return False
+ return True
+
+
def build(check_only=False):
- written, stale = [], []
+ written, stale, rendered = [], [], []
for page in PAGES:
out = ROOT / page["slug"] / "index.html" if page["slug"] else ROOT / "index.html"
html_text = render(page)
+ rendered.append((page["slug"] or "/", html_text))
if check_only:
if not out.exists() or out.read_text() != html_text:
stale.append(str(out.relative_to(ROOT)))
@@ -2543,6 +2654,13 @@ def build(check_only=False):
out404.write_text(notfound)
written.append("404.html")
+ # Claims are checked on what was rendered, not on what is on disk, so
+ # a stale claim fails the build that would have written it as well as
+ # the --check that would have passed it.
+ rendered.append(("404.html", notfound))
+ if not check_peak_claims(rendered):
+ return 1
+
if check_only:
if stale:
print("stale (run `python3 site/build.py`):", ", ".join(stale), file=sys.stderr)
diff --git a/site/cost/index.html b/site/cost/index.html
index 33b41bd..33b996c 100644
--- a/site/cost/index.html
+++ b/site/cost/index.html
@@ -93,7 +93,8 @@
Cost
The rate card
USD per 1M tokens. Peak hours are 01:00–04:00 and 06:00–10:00
-UTC daily; every other hour is off-peak at half the peak rate. The
+UTC, Monday to Friday; every other hour, and the whole weekend, is off-peak
+at half the peak rate. The
pricing page carries the full schedule and
names the period you are in right now: