DeepSeek Says 1M, Claude Code Says 200K: I Measured Both and Neither Number Is the Real Limit
DeepSeek's pricing page lists a 1M context length for both deepseek-flash and deepseek-v4-pro. Point Claude Code at DeepSeek's Anthropic-compatible endpoint (three env vars — I covered the setup and what it costs you separately) and Claude Code reports this for the same model:
"contextWindow": 200000
Two numbers, 5x apart, describing the same model. Which one governs?
I measured it. The answer is neither, and the way it fails is more interesting than either number.
What I measured
Three separate questions that get conflated:
- Does the endpoint accept it? — a hard API limit.
- Does the model actually use it? — retrieval, not just acceptance.
- Does Claude Code let you get there? — the client-side gate nobody documents.
All runs: Claude Code 2.1.270, deepseek-flash, macOS, isolated CLAUDE_CONFIG_DIR. Needle-in-a-haystack uses a unique string planted at position zero (hardest position to retrieve when the haystack is huge) with the question at the very end. Every prompt carries a unique nonce so prompt caching can't contaminate the token counts — the first version of this experiment did not, and produced two rows with identical token counts for payloads differing 2x in size.
Layer 1: DeepSeek's real ceiling is 2^20, not "1M"
Ladder on the raw endpoint, needle at position zero:
| Target | Real input tokens | HTTP | Wall | Needle found |
|---|---|---|---|---|
| 200K | 399,954 | 200 | 8.6s | ✅ |
| 400K | 799,823 | 200 | 15.8s | ✅ |
| 950K | 949,775 | 200 | 19.8s | ✅ |
| 1040K | 1,039,744 | 200 | 19.4s | ✅ |
| 1048K | — | 400 | 3.7s | — |
At 1,039,744 tokens the model still pulled a string planted at the very beginning, in under 20 seconds. The long context is real, not a spec-sheet number.
The rejection tells you the exact limit:
This model's maximum context length is 1048576 tokens.
1,048,576 = 2^20. So "1M" is binary, not decimal — you get 48,576 tokens more than a literal million.

The limit includes your output budget
Worth knowing before you size a request. Same input, only max_tokens changed:
| Input tokens | max_tokens |
Result |
|---|---|---|
| 1,045,710 | 1,000 | ✅ 200 |
| 1,045,707 | 8,000 | ❌ 400 |
The error spells out the arithmetic itself:
you requested 1053707 tokens (1045707 in the messages, 8000 in the completion)
So the budget is input + completion ≤ 1,048,576. Ask for a generous max_tokens on a near-full context and you get rejected on input that would otherwise have been fine.

Layer 2: Claude Code refuses client-side, and it never calls the API
Pipe an oversized prompt into claude -p and you get:
result: "Prompt is too long"
is_error: true
duration_ms: 34
input_tokens: 0
34 milliseconds and zero tokens. Claude Code rejected it locally. DeepSeek never saw the request, and you were never charged.
So far so reasonable — it declared a 200,000 window, it is enforcing a window. Except the number doesn't match either.
Binary search on prompt size, repetitive English filler:
| Target user tokens | Result | Real total input |
|---|---|---|
| 30K | ✅ pass | 64,040 |
| 80K | ✅ pass | 114,024 |
| 85K | ✅ pass | 119,022 |
| 90K | ❌ reject | 0 (24ms) |
| 95K / 105K / 112K / 120K / 140K / 150K / 180K / 195K / 205K | ❌ reject | 0 |
The gate fires at about 119K total tokens — roughly 60% of the 200,000 it advertises. (~34K of that total is Claude Code's own system prompt and tool definitions, which you pay on every turn.)
Layer 3: the gate isn't measuring tokens at all
Here is the part that matters. Claude Code cannot know DeepSeek's tokenizer, so it has to estimate. If the estimate is the usual characters ÷ 4 heuristic, then the gate should be fixed in characters and drift arbitrarily in real tokens depending on what your text is made of.
Test: same character count, wildly different tokenization density. Repetitive English runs about 5.4 chars/token here; random hex runs about 1.5.
| Content | Characters | Result | Real tokens |
|---|---|---|---|
| Repetitive English | 459,850 | ✅ pass | 119,022 |
| High-entropy hex | 460,000 | ✅ pass | 298,819 |
| Repetitive English | 486,900 | ❌ reject | — |
Same ~460,000 characters. One carried 119K tokens, the other 298,819 — and both passed. The gate did not notice a 2.5x difference in actual token count.
Narrowing the character threshold:
| Characters | Result | Real tokens |
|---|---|---|
| 470,000 | ✅ pass | 304,808 |
| 478,000 | ✅ pass | 309,567 |
| 482,000 | ❌ reject | — |
The gate is at ~480,000 characters. Divide by 4 and you get 120,000 — the chars÷4 heuristic, hard-coded against a budget that is itself 60% of the declared window.

Two consequences that actually bite:
- The declared 200K window is not a ceiling. 478,000 characters of hex went through carrying 309,567 real tokens, 55% past the advertised window, and the needle came back correctly. Nothing broke — DeepSeek's real limit is 1M, so there was plenty of room.
- The declared window is not a floor either. Ordinary English gets cut off at ~119K real tokens, well under 200K.
If your work is CJK text, minified code, base64 blobs, or logs — anything that does not tokenize at 4 characters per token — the number on your screen is not describing your situation in either direction.
Layer 4: in practice you never get near any of this
I built a 1.55MB file, 300K tokens, needle on the last line, and asked Claude Code to cat it. It answered correctly. But the usage said:
input_tokens: 34,973
The file never entered the context. Tracing the actual tool calls shows why:
[call] Bash: cat tail-big.txt
[result] 1,915 chars — "<persisted-output>
Output too large (1.5MB). Full output saved to: /Users/…"
[call] Bash: tail -c 2000 tail-big.txt
[result] 1,999 chars — "…The maintenance access code … is PELICAN-7731."
Claude Code persists oversized tool output to a file and hands the model a ~2KB preview. The model then worked around it on its own with a targeted tail -c 2000. Good agentic behaviour — and it means the needle test told me nothing about long context until I noticed.
The threshold is exact:
| Bash output | Enters context |
|---|---|
| 29,990 chars | 29,989 chars — inline |
| 30,010 chars | 2,315 chars — persisted |
30,000 characters. Any single Bash output above that is written to disk and summarized. Note this is a Bash threshold — in one run the model chose Read instead, which returned 53,656 characters inline for the same file. (That run briefly made my ladder look inconsistent; the cause was the model picking a different tool, not a different limit. I had to pin the tool choice to get a clean measurement.)

So which number wins?
Neither. Ranked by what actually stops you first:
| Limit | Value | Enforced by |
|---|---|---|
| Bash output inlining | 30,000 chars | Claude Code, per tool call |
| Prompt gate | ~480,000 chars (chars÷4 ≈ 120K est. tokens) | Claude Code, client-side, 25ms, no API call |
| Declared window | 200,000 tokens | nothing — neither floor nor ceiling in practice |
| Model hard limit | 1,048,576 tokens (input + max_tokens) |
DeepSeek, HTTP 400 |
The advertised 1M is real and genuinely usable — against the raw API. Through Claude Code you will hit a character-counting gate at roughly a tenth of it, and hit output persistence long before that.
Practical takeaways
- Want the 1M? Call the endpoint directly. Claude Code will not take you there.
- Don't plan around
contextWindow: 200000. It is not enforced as either a minimum or a maximum. The real client-side gate is ~480,000 characters. - Non-English content makes the estimate worse, not better. Chinese, minified JS, base64 — all tokenize far from 4 chars/token, so the gate fires at a real token count that can be double what you expect, in either direction.
- Large files are not a context problem, they are a tool-output problem. Anything over 30,000 characters from Bash gets persisted. Use
grep/tail/sedto extract what you need instead ofcat-ing and hoping. - Size
max_tokensagainst the same budget as your input when you are anywhere near the ceiling.
Pitfalls, condensed
- Reusing filler across a ladder silently enables prompt caching — my first run reported identical token counts for payloads differing 2x. Add a unique nonce per request.
- A needle at position zero survives truncation — it proves nothing. Put it at the end, then verify the content actually entered context by reading
input_tokens. - The model may pick a different tool between runs (
ReadvsBash cat), which changes the numbers completely. Pin the tool when you are measuring the tool. claude -p "$(cat big.txt)"dies withArgument list too long— that is your shell's ARG_MAX, not a Claude Code limit. Pipe via stdin.- "1M" is 2^20 = 1,048,576, and it includes your completion budget.
Tested 2026-09-20, Claude Code 2.1.270 on macOS/Apple Silicon against deepseek-flash (DeepSeek-V4.1-Flash) via https://api.deepseek.com/anthropic. Total API spend for every experiment in this article: ¥5.36 ≈ $0.75, measured by account balance delta. Raw logs retained.