The whole DeepSeek API, from the terminal.
Four wire formats, one binary. Multi-turn that survives the reasoning
round-trip. And it tells you what every call cost.
Badges that could rot are held down by tests:
TestCheckCoversEveryEndpoint fails if an
endpoint is added without deepseek check probing it, and
make cover-gate fails CI if coverage drops below its floor.
Documentation · Install · Quick start · Commands · Docs · Cost · For agents · Config
Unofficial tool. Not affiliated with DeepSeek.
DeepSeek serves the same two models through four different wire formats — OpenAI chat, OpenAI Responses, Anthropic Messages, and FIM. Every other client picks one. This speaks all four, so you can send the same prompt down each and see what actually differs.
It also does three things curl will not:
- Keeps multi-turn correct. With tools in play, DeepSeek returns
400unless every assistant message'sreasoning_contentis replayed on every later request.--continuehandles that; without tools it strips the same field, because replaying it there just burns tokens. - Prices every call. DeepSeek's disk KV-cache makes a cached input
token 50× cheaper than an uncached one. That split is invisible
unless something reads
prompt_cache_hit_tokensand does the arithmetic. This does, on every call, and keeps a local ledger. - Carries the manual.
deepseek docs ask "..."answers questions about the DeepSeek API from DeepSeek's own documentation, offline, with a citation per claim. The tool that talks to an API should be able to explain it.
$ deepseek chat "explain this diff" --file changes.patch
The patch swaps the retry loop for exponential backoff...
· flash · 3.2k in (87% cached) · 412 out (180 think) · ~$0.000178 · 2.1sOne line — detects your platform, installs the binary and both aliases:
curl -sL https://raw.githubusercontent.com/thevibeworks/deepseek-cli/main/install.sh | shOr download a binary from Releases:
# macOS (Apple Silicon)
curl -sL https://github.com/thevibeworks/deepseek-cli/releases/latest/download/deepseek_darwin_arm64.tar.gz | tar xz
sudo mv deepseek /usr/local/bin/
# Linux (x86_64)
curl -sL https://github.com/thevibeworks/deepseek-cli/releases/latest/download/deepseek_linux_amd64.tar.gz | tar xz
sudo mv deepseek /usr/local/bin/Or with Go:
go install github.com/thevibeworks/deepseek-cli/cmd/deepseek@latestdeepseek is eight characters to type many times a day. The binary
answers to ds and dscli as well — not shell aliases, but symlinks to
the same binary, so they work in scripts, cron, Makefiles and
anywhere a shell alias would not:
ds chat "why is the sky blue"
ds usage --since 7dinstall.sh and make install create them. If you installed by hand:
sudo ln -sf deepseek /usr/local/bin/ds
sudo ln -sf deepseek /usr/local/bin/dscliThe binary notices which name invoked it, so ds --help says ds.
export DEEPSEEK_API_KEY=sk-... # https://platform.deepseek.com/api_keys
deepseek check # is everything reachable?
deepseek chat "why is the sky blue"$ deepseek free
The free tier relays your prompts to DeepSeek through a gateway run by
this project. No account, no API key.
gateway https://freeseek.1lm.io
model deepseek-v4-flash
per day 30 requests · 60k input · 20k output tokens
privacy prompts and completions are relayed to DeepSeek and are not
stored or logged by this gateway; only token counts and cost
are recorded
Minting an anonymous token (20 bits of proof-of-work)…
solved 20 bits in 0.4s (1.0M hashes)
Enrolled. Saved to ~/.config/deepseek/free.json
$ deepseek chat "why is the sky blue"
The sky is blue because sunlight is scattered by the atmosphere…
· flash · 93 in · 46 out (7 think) · ~$0.000026 · 1.56sNo email, no card, no dashboard — about a second of CPU stands in for the
signup. Every command then works as normal, and deepseek free status
shows what is left of the day. A real API key always takes precedence,
so this is a fallback for not having one, never a way around having one.
The gateway is in this repository under gateway/ and is
meant to be self-hostable; gateway/DESIGN.md is
the reasoning, including the part where per-user quota is explicitly not
what keeps it solvent. There is also a
browser playground
on the same free tier, which shows you the equivalent deepseek command
for whatever you set up in it.
check calls all six endpoints once and reports which answered. Run it
first when something is wrong and you do not yet know whether the problem
is the key, the balance, the network, a proxy, or one endpoint:
$ deepseek check
https://api.deepseek.com
ok GET /models 141ms deepseek-v4-flash, deepseek-v4-pro
ok GET /user/balance 126ms 18.48 CNY
ok POST /chat/completions 651ms 5 in / 1 out
ok POST /anthropic/v1/messages 590ms 5 in / 1 out
ok POST /responses 595ms 5 in / 9 out
ok POST /beta/completions 379ms 4 in / 1 out
all endpoints reachableOne command per endpoint, named for what it does.
| Command | Endpoint | What it is for |
|---|---|---|
chat |
POST /chat/completions |
The default. OpenAI format, the one most tools speak. |
anthropic |
POST /anthropic/v1/messages |
The format Claude Code and the Anthropic SDKs speak. |
respond |
POST /responses |
The format Codex speaks. JSON Schema output and server-side web_search live only here. |
fim |
POST /beta/completions |
Fill in the middle — the shape editors use for inline completion. |
models |
GET /models |
Available models, joined with the published rate card. |
balance |
GET /user/balance |
What is left, per currency. |
tokens |
POST /beta/completions |
Exact token counts, from the model's own tokenizer. |
docs |
(local) | DeepSeek's own API docs, in the binary. Search, read, and ask. |
usage |
(local) | What this CLI has spent, from its own ledger. |
session |
(local) | The conversations chat --continue replays. |
status |
GET /models, /user/balance |
Is it up, for this key, from here. Costs nothing. |
check |
(all six) | Preflight. |
free |
(gateway) | Use the API with no key: enrol, check quota, opt out. |
raw |
(anything) | Escape hatch — any path, with auth and retries. |
deepseek chat "why is the sky blue"
git diff | deepseek chat "write a commit message"
deepseek chat "explain" --file server.go --file server_test.go
deepseek chat "review this" --model deepseek-v4-pro --effort max
deepseek chat "summarise" --system @house-style.mdArguments are the instruction, pipes and --file are the material.
Answers stream to stdout; the chain of thought, the usage line and any
warnings go to stderr — so redirecting stdout gets you the answer alone.
The API is stateless in every format. Conversations live on your machine.
deepseek chat "walk me through this codebase" --file main.go --continue
deepseek chat "now what would you change first" --continue
deepseek session ls
deepseek session show lastUse --session <name> to keep threads apart. --continue is the session
named last.
-i keeps the conversation open and prompts for the next turn, instead
of you retyping --continue:
$ ds chat -i "walk me through this codebase" --file main.go
The entry point wires three things together...
· flash · 2.1k in · 180 out · ~$0.000302 · 1.8s
› now what would you change first
The retry loop, because...
› /model pro
model deepseek-v4-pro
› ^D
bye — 4 messages saved as "last"; resume with: deepseek chat -cIt is the same session machinery, so quitting loses nothing —
deepseek chat -c picks the conversation back up, and so does
deepseek session show last. /help lists the slash commands:
/model, /think, /effort, /system, /file, /tokens, /docs,
/new, /save. ^C during an answer abandons that answer and keeps
the conversation; ^D leaves.
Interactive mode needs a terminal and refuses to combine with --json —
for scripted multi-turn, use --session, which is what it is built on.
DeepSeek publishes no count-tokens endpoint and no Go tokenizer. But the
FIM endpoint reports prompt_tokens for a raw prompt with no chat
template around it, so subtracting its single BOS token gives an exact
count from the tokenizer that will bill you:
$ ds tokens --file internal/cli/chat.go --file internal/cli/repl.go
TOKENS CHARS CHARS/TOK SOURCE
2082 6940 3.33 internal/cli/chat.go
1140 4187 3.67 internal/cli/repl.go
3222 11127 3.45 total
as a chat request: 3226 in (+4 envelope), 3305 with thinking at default effort (+79 template)
flash input cost: $0.000452 uncached, $0.000009 fully cached
· flash · 3.2k in · 0 out · ~$0.000451 · 1.94sThat last line is the honest part: measuring sends your text to DeepSeek
and is billed as input, exactly as sending it would have been. For a
free local estimate from DeepSeek's published character ratios — an
upper bound, and labelled as one — use --offline.
docs carries every page of api-docs.deepseek.com inside the binary,
plus the FAQ, which lives outside that site as a JSON blob in a
JavaScript bundle and is not otherwise readable as text. About 85KB
compressed, so it works offline:
$ ds docs search "context cache"
guides/kv_cache
Context Caching
The DeepSeek API Context Caching on Disk Technology is enabled by default...
$ ds docs ask "when must I send reasoning_content back?"
Send reasoning_content back only when the model performed a tool call during
that turn. In that case it must be passed back in all subsequent turns, or
the API returns a 400 error (guides/thinking_mode).
answered from guides/thinking_mode, api/create-chat-completion · docs built in, fetched today
· flash · 5.3k in (39% cached) · 116 out · ~$0.000778 · 2.2s
$ ds docs changelog # what DeepSeek shipped, newest first
$ ds docs show guides/kv_cache
$ ds docs sync # refresh from the mirrorask selects pages locally, sends them whole, and instructs the model to
answer only from them and cite the page — so an answer is checkable
against a URL rather than being whatever the model remembers about an API
that changes monthly. It is also the honest demo of the cost accounting:
the same pages lead every request, so a second question about the same
area hits the context cache, and the usage line shows it.
A snapshot ages, so every docs command prints how old it is, and each
page keeps the upstream URL it was converted from.
# Anthropic Messages. Claude model names are accepted and remapped
# server-side; the usage line shows both so cost stays traceable.
deepseek anthropic "hello" --model claude-opus-4-1
# · claude-opus-4-1→pro · 10 in · 8 out · ~$0.000011 · 0.9s
# Responses: JSON Schema output, and a web_search tool DeepSeek runs
deepseek respond "what shipped in Go 1.26" --web-search
deepseek respond "Berlin" -s "Return city and country." --schema @city.json
# FIM: prefix in, suffix optional, the middle comes back
deepseek fim "def add(a, b):" --suffix " return result"Declare a tool once; it works against every format. Both the wrapped
OpenAI shape and the bare input_schema Anthropic shape are accepted.
deepseek chat "weather in Hangzhou?" --tool @weather.json
# tool_call call_00_hUj... get_weather({"city": "Hangzhou"})
deepseek anthropic "weather in Hangzhou?" --tool @weather.json # same fileThis prints the calls the model wants made. It does not run them — executing model-chosen commands is an agent's job and a much larger set of safety questions. This is for developing and debugging tool schemas.
deepseek raw /models
deepseek raw /chat/completions --data @request.jsonEvery other command is a typed convenience over this one, so anything DeepSeek ships tomorrow is reachable today.
Every call prints a usage line to stderr and appends a row to a local JSONL ledger:
$ deepseek usage --since 7d
CALLS IN CACHED OUT COST
deepseek-v4-flash 184 2.1M 78% 94k $0.19
deepseek-v4-pro 12 88k 41% 11k $0.03
total 196 2.2M 77% 105k $0.22
by format: chat 170, anthropic 14, responses 8, fim 4
context cache saved ~$0.23 (1.7M of 2.2M prompt tokens replayed)
costs are estimates from the published USD rate card, not billed amountsThat last line is the point. Cached input costs $0.0028/M against $0.14/M for a miss — structuring prompts so the stable part comes first is worth real money, and this is how you see whether it worked.
Honest limits:
- Costs are estimates from the published USD rate card, not billed amounts. Token counts are exact, and they are what the ledger stores, so old rows can be repriced when the card changes.
- DeepSeek has announced peak/off-peak pricing (2× during 09:00–12:00 and 14:00–18:00 Beijing time) with no effective date. It is deliberately not applied — guessing that a call was billed double would be inventing data.
--no-ledgerskips the write;--no-statshides the line.
Built to be scripted. See AGENTS.md for the full contract and skill/SKILL.md for a drop-in agent skill.
deepseek chat "..." --json | jq -r '.choices[0].message.content'
deepseek chat "..." --jq '.usage'
deepseek models --json- stdout is data, stderr is status. Safe to pipe.
--jsonprints the API's own response, unwrapped and unmodified, so jq recipes written against the OpenAI or Anthropic APIs keep working.- Exit codes carry meaning:
0ok ·1error ·2auth ·3no balance ·4rate limited ·130interrupted. - Errors say what to do, not just what broke:
$ deepseek chat hi
Error: Insufficient Balance (HTTP 402)
out of balance: deepseek balance — top up at https://platform.deepseek.com/top_up| Variable | Purpose |
|---|---|
DEEPSEEK_API_KEY |
API key. |
DEEPSEEK_BASE_URL |
Override the base URL (proxies, gateways). |
DEEPSEEK_CONFIG_DIR |
Where the key file lives. Default ~/.config/deepseek. |
DEEPSEEK_STATE_DIR |
Ledger and sessions. Default ~/.local/state/deepseek. |
Both directories respect XDG_CONFIG_HOME / XDG_STATE_HOME. To keep
the key out of the environment, put it in a file instead:
mkdir -p ~/.config/deepseek && chmod 700 ~/.config/deepseek
printf 'sk-...' > ~/.config/deepseek/api_key && chmod 600 ~/.config/deepseek/api_keyGlobal flags: --api-key --base-url --json --jq --timeout
--verbose/-v (-vv adds bodies) --no-stats --no-ledger.
deepseek free |
enrol this machine (about a second of CPU) |
deepseek free status |
requests, tokens and spend left today |
deepseek free off |
forget the enrolment on this machine |
DEEPSEEK_FREE_URL |
point at a different gateway — yours, or a local one |
~/.config/deepseek/free.json |
where the token is kept, mode 0600 |
Resolution order for a credential is: --api-key, then
DEEPSEEK_API_KEY, then the key file, then the free-tier enrolment. The
free tier is only reached when there is no key at all, and it is skipped
entirely if --base-url or DEEPSEEK_BASE_URL is set — a token minted
for our gateway is not something to send somewhere else.
Things the API does that surprise people, and that this tool surfaces rather than hides:
-
Thinking is on by default, and its cost depends on
--effortin a way nothing documents. The template it adds to your input is a fixed number of tokens, constant regardless of prompt length, but it is not the same number at every level — and at low effort it is not there at all, while the model still reasons:--effortflash pro none+0 (thinking off) +0 (thinking off) minimal,low+0 +0 medium,high,xhigh+79 +0 max+92 +79 Measured against the live API on 2026-08-05 at two prompt lengths, twice each.
deepseek tokens -e lowwill show you the same thing for your own text. Two of those levels —noneandminimal— appear in no DeepSeek documentation at all;nonedisables thinking exactly as--think offdoes. -
Text only. DeepSeek rejects image, document and search-result content blocks in every format.
-
The Responses endpoint is flash-only for now.
-
FIM caps output at 4K tokens and ignores thinking entirely.
-
Slow starts are normal. The API holds the connection with
: keep-alivecomments for up to ten minutes before inference begins. The default--timeoutmatches.
make # build
make check # everything below, plus fmt, vet and the site
make test # 190 tests, no network required
make cover-gate # fails under the coverage floor
make corpus # repack the embedded DeepSeek docs from the mirror
make gateway # build the free-tier gateway
make gateway-test # 112 tests, including the CLI against a real gateway
make price-check # the rate card lives in two modules; catch drift
make site-check # the site, and the playground's three-way puzzle vectorsZero runtime dependencies beyond cobra and golang.org/x/term; the API
client is hand-rolled. --jq shells out to jq if you use it.
The gateway is a separate Go module with no dependencies at
all, so go install .../cmd/deepseek@latest never pulls a line of
server code. The two halves share a documented wire format and no source,
which is why make gateway-test runs the real deepseek binary through
a real gateway rather than trusting that they agree.
Design rulings and the reasons behind them: TASTE.md.
- Documentation — the same material as a browsable site, plus a comparison of DeepSeek's four API formats
- AGENTS.md · skill/SKILL.md — the scripting contract
- TASTE.md — design rejections and their reasons
- DeepSeek API docs — the upstream API
MIT — see LICENSE.