_TIER_DEEPSEEK_PRO and _TIER_DEEPSEEK_FLASH in src/services/pricing.py hold DeepSeek's pre-2026-08-16 rate card. Since 2026-08-16 the published card is peak / off-peak, which get_pricing has no axis for.
The modeling is right; the constants and one axis are not
The comment above those tiers is careful and still correct:
# DeepSeek V4 (USD per million tokens). DeepSeek's automatic prefix cache
# bills cache HITS at the low ``cache_read`` rate and cache MISSES at the
# normal input rate; there is no separate cache-write charge, so
# ``cache_creation`` mirrors ``input`` (a non-cached token is just input).
That mirror still holds under the new card — a miss is still just input — so the shape of both dicts survives. What changed is the six numbers, and the fact that each of them now doubles on a UTC schedule.
Current rates from https://api-docs.deepseek.com/quick_start/pricing/, read 2026-08-24, USD per 1M tokens:
|
input / cache_creation |
output |
cache_read |
deepseek-v4-flash off-peak |
0.22 |
0.66 |
0.007 |
deepseek-v4-flash peak |
0.44 |
1.32 |
0.014 |
deepseek-v4-pro off-peak |
0.66 |
1.98 |
0.022 |
deepseek-v4-pro peak |
1.32 |
3.96 |
0.044 |
Off-peak rates are half of the peak rates. Peak hours are 01:00 - 04:00 and 06:00 - 10:00 UTC, Monday through Friday (all other hours are off-peak).
The Chinese page carries the same table in CNY and converts at a fixed 6.8182 on every v4-pro cell, so the USD column is DeepSeek's own conversion.
Why neither existing tier axis reaches it
get_pricing already selects among tiers on two inputs:
def get_pricing(
model: str,
*,
input_tokens: int = 0,
service_tier: str = "standard",
) -> dict[str, float] | None:
Neither can carry this one:
input_tokens is prompt size. DeepSeek's tier does not depend on prompt size.
service_tier comes from the response — compute_cost reads usage.get("service_tier") and defaults to "standard". DeepSeek's API does not return a service tier, so this always resolves to "standard", and even if it did, peak/off-peak is not something the caller or the provider declares per request. It is a function of the wall-clock time the request was sent.
So this needs a third input — the request timestamp — rather than a new value on either existing axis. That is a design call, which is why I am opening an issue rather than a PR.
If a timestamp axis is more than you want to add, the contained version is to put the off-peak card in the constants and say so:
_TIER_DEEPSEEK_PRO = { # off-peak; peak (Mon-Fri 01:00-04:00, 06:00-10:00 UTC) is 2x
"input": 0.66 / 1_000_000,
"output": 1.98 / 1_000_000,
"cache_creation": 0.66 / 1_000_000,
"cache_read": 0.022 / 1_000_000,
}
Off-peak covers 143 of every 168 hours, so it is the better single answer if only one is available. tests/test_deepseek_prefix_cache.py asserts against these values and would need the same update.
Size of the gap
Weighted by a measured agent token mix — 95.64% cache read / 4.07% cache miss / 0.29% output, from an 8.04B-token public trace (gist) — per 1M tokens:
|
compute_cost returns |
actual off-peak |
actual peak |
deepseek-v4-pro |
$0.0237 |
$0.0536 (2.26×) |
$0.1073 (4.53×) |
deepseek-v4-flash |
$0.0092 |
$0.0176 (1.91×) |
$0.0351 (3.82×) |
cache_read is what drives it. At ~96% of agentic input tokens, 0.003625 versus 0.022 moves the total more than the input and output fields combined — and the docstring's own reasoning about status-bar honesty ("The user picks 'no number' over 'wrong number'") applies here: a number that is 4.5× low reads as authoritative in exactly the way a missing one does not.
One suggestion regardless of which route you take: the neighbouring Anthropic tiers have no checked-on date either, but DeepSeek is the entry most likely to move again, since the values currently in the file were themselves a time-limited card. A # checked YYYY-MM-DD line on the DeepSeek block would make the next drift visible on inspection rather than only in the bill.
Per-vendor cache-read rates I checked these against: https://xyzs996.github.io/llm-api-pricing/
_TIER_DEEPSEEK_PROand_TIER_DEEPSEEK_FLASHinsrc/services/pricing.pyhold DeepSeek's pre-2026-08-16 rate card. Since 2026-08-16 the published card is peak / off-peak, whichget_pricinghas no axis for.The modeling is right; the constants and one axis are not
The comment above those tiers is careful and still correct:
That mirror still holds under the new card — a miss is still just input — so the shape of both dicts survives. What changed is the six numbers, and the fact that each of them now doubles on a UTC schedule.
Current rates from https://api-docs.deepseek.com/quick_start/pricing/, read 2026-08-24, USD per 1M tokens:
input/cache_creationoutputcache_readdeepseek-v4-flashoff-peakdeepseek-v4-flashpeakdeepseek-v4-prooff-peakdeepseek-v4-propeakThe Chinese page carries the same table in CNY and converts at a fixed 6.8182 on every v4-pro cell, so the USD column is DeepSeek's own conversion.
Why neither existing tier axis reaches it
get_pricingalready selects among tiers on two inputs:Neither can carry this one:
input_tokensis prompt size. DeepSeek's tier does not depend on prompt size.service_tiercomes from the response —compute_costreadsusage.get("service_tier")and defaults to"standard". DeepSeek's API does not return a service tier, so this always resolves to"standard", and even if it did, peak/off-peak is not something the caller or the provider declares per request. It is a function of the wall-clock time the request was sent.So this needs a third input — the request timestamp — rather than a new value on either existing axis. That is a design call, which is why I am opening an issue rather than a PR.
If a timestamp axis is more than you want to add, the contained version is to put the off-peak card in the constants and say so:
Off-peak covers 143 of every 168 hours, so it is the better single answer if only one is available.
tests/test_deepseek_prefix_cache.pyasserts against these values and would need the same update.Size of the gap
Weighted by a measured agent token mix — 95.64% cache read / 4.07% cache miss / 0.29% output, from an 8.04B-token public trace (gist) — per 1M tokens:
compute_costreturnsdeepseek-v4-prodeepseek-v4-flashcache_readis what drives it. At ~96% of agentic input tokens,0.003625versus0.022moves the total more than the input and output fields combined — and the docstring's own reasoning about status-bar honesty ("The user picks 'no number' over 'wrong number'") applies here: a number that is 4.5× low reads as authoritative in exactly the way a missing one does not.One suggestion regardless of which route you take: the neighbouring Anthropic tiers have no checked-on date either, but DeepSeek is the entry most likely to move again, since the values currently in the file were themselves a time-limited card. A
# checked YYYY-MM-DDline on the DeepSeek block would make the next drift visible on inspection rather than only in the bill.Per-vendor cache-read rates I checked these against: https://xyzs996.github.io/llm-api-pricing/