DeepSeek put a search engine on its API today, and Hacker News put it at the top of the front page (521 points) with the only number in the headline: $0.75 per 1,000 successful requests. There is also a Fetch API — give it a URL, get the page back as Markdown — at the same price.
"Per successful request" is doing a lot of work in that sentence, so I read the docs the way I read every pricing page — as a contract with the interesting parts in the footnotes — and then probed the live API with no key and with a fake one to see which parts of the contract I could verify for free.
What the docs actually promise
Both endpoints live on the regular DeepSeek base URL:
POST https://api.deepseek.com/v1/tools/search
POST https://api.deepseek.com/v1/tools/fetch
Search takes a query (1–128 Unicode characters), a count between 1 and 50 (default 10), and old-school search operators forwarded intact: site:, -site:, lang:, after:, before:. Each result carries a rerank_score from 0 to 3 (1.5 and up is "high quality," per the docs), an authority tier from −1 to 4, a snippet of typically 100–200 characters, and — the part your model actually reads — a summary of typically 600–800 characters. Fetch takes one URL, at most 8,192 characters, no embedded credentials, and returns title plus Markdown.
The billing section is the interesting part:
Search $0.75 per 1,000 successful requests
Fetch $0.75 per 1,000 successful requests
Billable only when: HTTP 200 AND a result came back.
Search returning an empty results array: not billed.
Fetch returning no content: not billed.
Failures and timeouts: not billed.
count does not affect the price.
Three consequences fall out of that, none of them in the headline:
1. The per-result price swings 50× depending on a parameter you're ignoring. The request costs $0.00075 whether it returns 1 result or 50. At count=50 you are paying $0.000015 per result — 1.5 cents per thousand results. At count=1 you pay fifty times more for the same inventory.
2. An empty result is free, which makes existence checks free. A search that matches nothing costs nothing. "Does anything exist about X?" is a billable event only when the answer is yes.
3. The context bill is yours, not theirs. Fifty results × a 600–800 character summary is 30,000–40,000 characters of summaries alone, before snippets, that your agent is about to read at model token prices. DeepSeek charges you $0.00075 for the search and nothing for the reading. The reading is where your money goes; it just goes to a different line item.
There is also a ceiling hiding in the rate limits. Search and Fetch are each capped at 5 QPS per account, independently. Run one flat-out for a day and the maximum successful volume is 5 × 86,400 = 432,000 requests — a maximum daily spend of $324 per API, $648 if you max both. That is the entire blast radius of a runaway search loop, which is more than I can say for most agent tooling. And if you call these through DeepSeek Harness, Claude Code, or Codex as server-side tools, they are currently free — per-request billing for harness calls starts November 1, 2026, Beijing time. There is a roughly two-week window where the meter is installed but not running.
What I could verify without a key: auth runs before everything
I don't have a DeepSeek key handy, which turns out to be enough to test the error surface. The docs say invalid requests get a 400, invalid keys a 401, and that the param field is "always null." What they don't say is in which order. So I sent requests that were broken in two ways at once — bad key plus bad body — and recorded which error won:
search, no key -> 401 1.11s
search, fake key -> 401 0.82s
search, fake key + count=999 -> 401 0.78s
search, fake key + empty query -> 401 0.57s
fetch, fake key -> 401 0.63s
fetch, fake key + url="not-a-url" -> 401 0.53s
search, fake key + malformed JSON body -> 401 0.53s
fetch, no key -> 401 0.55s
Eight for eight: authentication wins over every other kind of invalid. A count of 999 (documented range: 1–50), an empty query, a URL that isn't a URL, and a body that isn't even JSON all get the identical 401. Whoever wrote the middleware put auth first, which is the right order — validation error shapes are information, and there's no reason to hand them to strangers — but it means the documented 400 examples, like 'count' must be between 1 and 50, are unreachable until you present a valid key. The docs describe the API as seen by a paying customer. The API as seen by everyone else is one error, repeated.
That error has a wart worth noticing:
{
"error": {
"message": "Authentication Fails, Your api key: ****-000 is invalid (request_id: ...)",
"type": "authentication_error",
"param": null,
"code": "invalid_request_error"
}
}
The type says authentication_error; the code says invalid_request_error. Two fields, one error, contradictory labels — if your client switches on code, an auth failure will be filed as a request bug. The docs do warn that param is always null, and it was, in all eight responses. Credit where due: that part of the contract held exactly.
One more detail in that message: the API echoes back a masked fragment of the key you sent (****-000, the last three characters of my fake). Harmless for a fake key. Slightly less charming if your logging pipeline captures error messages for real keys — the mask keeps only the tail, but the tail is the part people use to identify which key is which.
The honest summary
The pricing model is genuinely good: pay only for 200s that return something, failures and empties free, price independent of count, and a rate limit that doubles as a spend ceiling. The thing to model before you wire it into an agent is not the $0.75 — it's the 30,000–40,000 characters of summaries a single maxed-out call drops into your context, and the fact that until November 1 the same calls through a harness cost nothing at all. And if you integrate it, match on type, not code. I checked: the code lies.
Top comments (0)