Local OpenAI-compatible API proxy for DeepSeek Web Chat
Quick start • Features • Examples • Models • Endpoints • Open WebUI
One-line install (Linux/macOS native; Windows under Git Bash):
curl -fsSL https://raw.githubusercontent.com/Swastik36/FreeDeepseekAPI/main/scripts/install.sh | shThis clones the fork, walks you through DeepSeek login, and installs the
systemd service. Update later with npm run update (run it from Git Bash on
Windows) — it refuses dirty trees, fast-forwards only, runs tests before
restarting, and rolls back automatically if tests fail.
*Manual install explained below.
FreeDeepseekAPI runs a local API server for DeepSeek Web Chat (chat.deepseek.com) and lets you connect DeepSeek Web to Open WebUI, LiteLLM, Hermes, Claude Code, OpenAI SDK-style clients, and other OpenAI-compatible tools.
The project works through your regular logged-in DeepSeek account in a separate Chrome profile. The local server accepts API requests and then talks to DeepSeek Web itself using the saved browser session.
⚠️ This is an experimental web-chat proxy. DeepSeek may change its internal Web API without warning. For production use cases, the official paid DeepSeek API is more reliable.⚠️
Fork of ForgetMeAI/FreeDeepseekAPI, maintained at Swastik36/FreeDeepseekAPI.
- What it gives you
- Features
- Quick start
- Windows setup
- Linux / Chromium setup
- VPS / headless setup
- Rootless Podman
- Diagnostics / doctor
- Session reuse and chat resets
- Multi-account pool
- Ideas for console auth
- Smoke test
- Request examples
- Models
- Endpoints
- Open WebUI
- OpenCode
- Refresh the login
- Project status
- Use DeepSeek Web as a local API endpoint.
- Connect DeepSeek to Open WebUI and other OpenAI-compatible clients.
- Get plain JSON responses or SSE streaming.
- Use reasoning models with a separate
reasoning_content. - Work with the Anthropic Messages API shim for Claude Code / Anthropic SDK.
- Use the OpenAI Responses API shim for new OpenAI/Codex-style clients.
- Keep separate web sessions for different agents/users.
- OpenAI-compatible API:
POST /v1/chat/completions - Anthropic-compatible shim:
POST /v1/messages - OpenAI Responses shim:
POST /v1/responses - Streaming: SSE chunks and plain non-stream JSON responses
- Reasoning output: separate
reasoning_contentfor thinking models - Tool calling: parsing OpenAI tools, Anthropic tools, and Responses function tools
- Model capabilities:
GET /v1/model-capabilitieswith alias → real web mode - Agent sessions: a separate DeepSeek session per
user/ agent id - Session recovery: auto-reset of stale chains/sessions
- Zero dependencies: Node.js 18+, no npm dependencies
Manual install:
git clone https://github.com/Swastik36/FreeDeepseekAPI.git
cd FreeDeepseekAPI
npm run auth
npm startnpm run auth opens the authorization menu:
- select item
1; - log in to DeepSeek in a separate Chrome profile;
- send a short message like
ok; - go back to the terminal and press Enter.
npm start shows the launch menu:
1— authorize / refresh the DeepSeek login2— show models and statuses3— start the proxy4— exit
For headless/CI runs without the menu:
NON_INTERACTIVE=1 npm start
# or
SKIP_ACCOUNT_MENU=1 npm startBy default the server listens on:
http://localhost:9655
By default the proxy is reachable only from this machine. For network access, explicitly set a bind address and a separate proxy key:
HOST=0.0.0.0 PROXY_API_KEY='replace-with-a-long-random-value' npm startAfter that, pass the key as Authorization: Bearer <key>. Without
PROXY_API_KEY, non-health endpoints stay unauthenticated, so do not
expose such an instance to the network.
Browser requests are allowed from loopback origins. If the UI is open on a different address,
add its exact origin, comma-separated, e.g.
PROXY_CORS_ORIGINS=https://ui.example.com,http://192.168.1.20:3000.
git clone https://github.com/Swastik36/FreeDeepseekAPI.git
cd FreeDeepseekAPI
npm run auth
npm startIf Chrome is installed in a non-standard location, point to it explicitly:
$env:CHROME_PATH="C:\Program Files\Google\Chrome\Application\chrome.exe"
npm run authIf Chrome is not found, npm run auth now prints ready-made instructions for Windows/macOS/Linux instead of a cryptic stack trace.
git clone https://github.com/Swastik36/FreeDeepseekAPI.git
cd FreeDeepseekAPI
CHROME_PATH=$(which chromium) npm run auth
npm startIf Chromium is named differently:
CHROME_PATH=$(which chromium-browser) npm run auth
# or
CHROME_PATH=$(which google-chrome) npm run authThe most reliable flow without Chrome on the server:
- On your home PC with a GUI/Chrome:
npm run auth- Copy
deepseek-auth.jsonto the VPS:
scp deepseek-auth.json user@your-vps:/opt/FreeDeepseekAPI/deepseek-auth.json- On the VPS, import/verify the file and set safe permissions:
cd /opt/FreeDeepseekAPI
npm run auth:import -- --input ./deepseek-auth.json
npm run doctor -- --offline- Run the proxy without the interactive menu:
NON_INTERACTIVE=1 npm startYou can import not only a ready deepseek-auth.json but also a browser cookie export:
DEEPSEEK_TOKEN="<token>" npm run auth:import -- --input ./cookies.jsonImportant:
deepseek-auth.jsonis access to your DeepSeek Web login. Do not commit or publish it; store it with0600permissions.
The container is intended only for non-interactive proxy runs. Do the
browser-based authorization on the host with npm run auth: the auth scripts and
deepseek-auth.json are not copied into the image.
Run Podman as a regular user, without sudo.
- Build the local image:
podman build --tag localhost/free-deepseek-api:local --file Containerfile .- Pass the DeepSeek auth and a separate proxy key via Podman secrets:
podman secret create --replace free-deepseek-auth ./deepseek-auth.json
printf 'Proxy API key: '
IFS= read -r -s PROXY_API_KEY
printf '\n'
printf '%s' "$PROXY_API_KEY" |
podman secret create --replace free-deepseek-proxy-key -Use a long random key. The value stays in the current shell's
PROXY_API_KEY variable so you can check the API; it does not end up in the image or
the Podman command line.
- Run the container with minimal privileges:
podman run --detach \
--name free-deepseek-api \
--publish 127.0.0.1:9655:9655 \
--secret free-deepseek-auth,target=deepseek-auth.json,uid=1000,gid=1000,mode=0400 \
--secret free-deepseek-proxy-key,target=proxy-api-key,uid=1000,gid=1000,mode=0400 \
--read-only \
--cap-drop=ALL \
--security-opt=no-new-privileges \
localhost/free-deepseek-api:localInside the container, NON_INTERACTIVE=1, HOST=0.0.0.0, and the
paths to both secrets are pre-set. REQUIRE_PROXY_API_KEY=1 prevents the container from starting
if the key secret is missing or empty. On the host, the port is published only on
127.0.0.1; do not remove that address without a separate network firewall/access
policy.
- Check liveness, account readiness, and the protected endpoint:
podman healthcheck run free-deepseek-api
curl --fail http://127.0.0.1:9655/readyz
curl --fail \
-H "Authorization: Bearer $PROXY_API_KEY" \
http://127.0.0.1:9655/v1/modelsThe built-in healthcheck verifies the local /health (whether the process is alive).
/readyz additionally returns 503 when no account is eligible under the
credential/cooldown/quota readiness predicate; it is not a capacity gauge.
Container diagnostics:
podman logs free-deepseek-api
podman inspect --format '{{.State.Health.Status}}' free-deepseek-apiStopping and removing the container together with the saved Podman secrets:
podman stop free-deepseek-api
podman rm free-deepseek-api
podman secret rm free-deepseek-auth free-deepseek-proxy-key
unset PROXY_API_KEYWhen rotating the auth or proxy key, replace the corresponding secret and recreate the container, so behavior does not depend on the Podman version.
npm run doctor
# without network requests to DeepSeek:
npm run doctor -- --offlinedoctor checks:
- whether
deepseek-auth.json/DEEPSEEK_AUTH_DIRis found; - whether the JSON is valid;
- whether
token,cookie,wasmUrlare present; - whether the file permissions are safe on macOS/Linux (
0600); - on a normal run — whether the DeepSeek PoW endpoint is reachable (30-second timeout per account, checked sequentially).
If you see data.biz_data is null, fetch failed, 401/403/429, or Hermes/OpenCode doesn't see the models — run npm run doctor first.
FreeDeepseekAPI does not create a new DeepSeek chat for every HTTP request without reason. The logic is:
- one
x-agent-session,session, oruser→ one DeepSeek chat session; - if the session id already exists — the proxy reuses it and continues the chain via
parent_message_id; - only an explicit
/newor reset endpoint clears a live chat; client compaction and a rate-limit migration are the two sanctioned reset paths; - overlapping turns for the same resolved session are rejected with
409 session_busyrather than racing the parent cursor; - local history is kept as a bounded recovery summary for sanctioned fresh-chat paths;
- long agent requests are capped by
DEEPSEEK_MAX_PROMPT_CHARSbefore sending (default 80,000 chars): the task start, fresh tool results, and the tool adapter are preserved; - if the client already sent multi-turn history, the local recovery history is not appended a second time;
- an empty response is retried at most
DEEPSEEK_MAX_RETRIEStimes (default 2), with a shrinking context on each retry.
To set the agent/session explicitly:
curl -X POST http://localhost:9655/v1/chat/completions \
-H "Content-Type: application/json" \
-H "x-agent-session: my-agent" \
-d '{"model":"deepseek-chat","messages":[{"role":"user","content":"Hello"}]}'To list active sessions:
curl --fail -H "Authorization: Bearer ${PROXY_API_KEY}" \
http://localhost:9655/v1/sessionsTo reset one session:
curl --fail -X POST \
-H "Authorization: Bearer ${PROXY_API_KEY}" \
"http://localhost:9655/reset-session?agent=my-agent"To reset all sessions in the authenticated caller's namespace:
curl --fail -X POST \
-H "Authorization: Bearer ${PROXY_API_KEY}" \
"http://localhost:9655/reset-session?agent=all"With PROXY_API_KEY, logical agent names are isolated by key principal. Without
a key, header-selected names are intentionally disabled: loopback callers share
the dev-agent bucket, while remote callers are isolated by remote IP. Reset-all
never crosses that keyless namespace boundary.
Why chats still show up in DeepSeek Web: the proxy works through the internal Web Chat API, and DeepSeek stores the real chat sessions on its side. That is normal for a web proxy. The point of session reuse is to avoid spawning new chats without need and to reset carefully only when the chain has gone stale/broken.
You can connect multiple auth files. The correct model is one sticky account per agent/session: the proxy never silently switches credentials inside a live DeepSeek chat. If an account returns 401/403 or enters 429 cooldown, an existing remote chat fails fast with its owner and cursor preserved; only chat-less sessions rotate, while the explicit rate-limit migration path performs a recovery-prompt-backed move.
Option 1 — a directory with auth files:
mkdir -p accounts
cp deepseek-auth-main.json accounts/main.json
cp deepseek-auth-backup.json accounts/backup.json
chmod 600 accounts/*.json
DEEPSEEK_AUTH_DIR=./accounts NON_INTERACTIVE=1 npm startOption 2 — a file list:
DEEPSEEK_AUTH_PATH="./accounts/main.json,./accounts/backup.json" NON_INTERACTIVE=1 npm startHow the pool works:
- a new agent/session gets the lowest-scoring ready account (smart routing, see below);
- the chosen account sticks to the session (
sticky); - HTTP
429places the account in timed cooldown; HTTP401/403marks its credentials unavailable; - a live chat never moves to another credential behind the client's back: cooling, quota-spent, saturated, or auth-dead owners fail fast with the remote chat preserved; chat-less sessions may rotate;
- authorized callers can see sanitized account status in
/health; anonymous probes receive only liveness data unlessDEEPSEEK_PUBLIC_STATUS=1is set; - auth files must be stored with
0600permissions.
Admission is bounded globally and per account:
DEEPSEEK_MAX_CONCURRENT=24 npm start # positive integer; concurrent completion turns pool-wide
DEEPSEEK_MAX_PER_ACCOUNT=1 npm start # integer 0-10; 0 disables this per-account ceilingA per-account lease remains held for the complete upstream turn, including token
streaming and in-place retries. Initial/sticky admission returns a short
503 overloaded response when every otherwise-ready account is at its ceiling;
rate-limit migration uses the same classification when peers are merely busy.
Unexpected runtime faults (unhandledRejection/uncaughtException) persist
sessions and exit non-zero so systemd restarts a clean process (default on):
DEEPSEEK_FATAL_ON_UNHANDLED=0 npm start # 0 = log-only, keep running (debugging)Fresh chats are assigned by lowest score: busy accounts shed load, failing accounts are avoided within a strike or two, old failures decay, and a recently successful account gets a small bonus. Preferred account and home affinity are biases, not locks. Tune it with:
DEEPSEEK_ROUTING_CONSECUTIVE_STRIKES=2 npm start # failures before an account is sidelined
DEEPSEEK_ROUTING_ESCALATION_COOLDOWN_MS=60000 npm start # short sideline for repeated soft failures
DEEPSEEK_ROUTING_FAILURE_HALFLIFE_MS=300000 npm start # failure half-life (0 disables decay)
DEEPSEEK_ROUTING_FAILURE_WEIGHT=4 npm start # scorer penalty per failure
DEEPSEEK_ROUTING_TIMEOUT_WEIGHT=12 npm start # scorer penalty per consecutive timeout
DEEPSEEK_ROUTING_HOT_BONUS=2 npm start # bonus for the most recently successful account
DEEPSEEK_ROUTING_HOT_WINDOW_MS=60000 npm start # how recent "recently successful" meansExtra tool-call fallback tags (literal substring sentinels, ; splits starts
from ends, | splits entries — no regexes ever; max 32 tags of 128 chars each,
extra ;-sections ignored with a warning; read once at startup, restart to apply):
DEEPSEEK_TOOL_TAGS="<mytools>|<tool_begin>;</mytools>|<tool_end>" npm startOptional same-chat rate-limit retry (default off — fail-fast 429 preserved):
DEEPSEEK_RETRY_RATELIMIT=1 npm start # one 2s wait + in-place retry, only for unknown/brief backoffsWith the retry on, a rate-limited turn waits once (2s, or the upstream
Retry-After up to a 10s cap — longer backoffs go straight to migration), lifts
the account cooldown for exactly one same-account+chat attempt, and restores it
without extending on failure. When everything is exhausted the proxy answers
429 with a backoff + /compact guidance message (status and Retry-After
unchanged, so clients keep backing off instead of hammering).
To configure the cooldown:
DEEPSEEK_ACCOUNT_COOLDOWN_MS=600000 npm startHourly per-account request quota (anti-mute — upstream quiets accounts that
sustain hundreds of requests/hour; over-quota accounts sit out like cooling
ones, all-spent answers 429 with Retry-After):
DEEPSEEK_HOURLY_QUOTA=60 npm start # 0 disables; sliding 1h window per accountBurst cap (anti-velocity — most turns per sliding 60s window; rejects fast 429 without touching scorer state; off until measured):
DEEPSEEK_BURST_PER_MINUTE=0 npm start # 0 disables; candidate 10 post-measurementTurn-aware pacing (anti-velocity — enforces a minimum gap between consecutive
agent-loop turns on one account; human turns are never delayed). The pacer
sleeps only while enough request-deadline budget remains; otherwise it answers
429 with Retry-After and preserves the chat. Defaults are ON — set the gap to
0 to disable:
DEEPSEEK_AGENT_TURN_GAP_MS=6000 npm start # target minimum gap between agent turns (0 disables; max 60000)
DEEPSEEK_TURN_JITTER_MS=2000 npm start # uniform random jitter added to the gap (max 60000)
DEEPSEEK_MIN_USABLE_UPSTREAM_MS=10000 npm start # min deadline budget required to sleep instead of reject (max: request deadline)Out-of-range values warn and fall back to defaults; a gap at or above the usable minimum logs a boot warning since tight deadlines will reject instead of sleep.
Ambient telemetry (advisory — periodic GET /api/v0/users/current per account
so the login shows normal browser route presence; off by default, fire-and-forget,
401 is logged but never cools the account):
DEEPSEEK_AMBIENT_TELEMETRY=1 npm start # 0 disables
DEEPSEEK_TELEMETRY_INTERVAL_MS=900000 npm start # minimum interval between pings (default 15m)Model discovery (advisory — hourly poll of upstream model flags into /health;
never adds/removes aliases):
DEEPSEEK_MODEL_DISCOVERY=1 npm start # 0 disablesThe password flow from PR #3 is doable, but it is safer not to store the password and not to make it the default. A sane implementation:
npm run auth:consoleasks for email/phone and password via a hidden prompt.- The password lives only in process memory — never written to files/logs/history.
- The script replays the Web login flow via
fetch/CDP: gets a captcha/verify challenge, hands the human a link/code, waits for confirmation. - After a successful login, only the standard-format
deepseek-auth.jsonis saved. - If DeepSeek asks for captcha/2FA — the script honestly says "open the link, pass the check, press Enter" instead of trying to bypass protection.
- For VPS,
auth:console --no-save-password --output deepseek-auth.jsonmode is better.
Minimal safe MVP: console auth is interactive-only, no env password. An acceptable automation option: DEEPSEEK_EMAIL=... npm run auth:console, but the password is still entered via hidden prompt.
curl --fail http://localhost:9655/health
curl --fail http://localhost:9655/readyz
curl --fail -H "Authorization: Bearer ${PROXY_API_KEY}" \
http://localhost:9655/v1/models
curl --fail -H "Authorization: Bearer ${PROXY_API_KEY}" \
http://localhost:9655/v1/model-capabilities/health is a liveness probe and always returns status: "ok" while the
process is running. /readyz is a semantic eligibility probe: it returns HTTP
200 when at least one account has credentials and is not auth-unavailable, probe-active, cooling, or quota/burst limited. Predicate (see isAccountReady in server.js): ready = has-credentials AND NOT (auth-unavailable OR probe-active OR cooling OR quota/burst-limited). It intentionally remains ready during normal streaming
saturation and does not claim that a PoW WASM URL has been validated. Anonymous
status probes omit account, model, and session details; configure
PROXY_API_KEY and send Authorization: Bearer ... for sanitized operational
status, or explicitly opt in with DEEPSEEK_PUBLIC_STATUS=1 on a trusted
network.
curl -X POST http://localhost:9655/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-chat",
"messages": [{"role": "user", "content": "Hello! Reply in one sentence."}],
"stream": false
}'curl -X POST http://localhost:9655/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-reasoner",
"messages": [{"role": "user", "content": "Briefly: why is the sky blue?"}],
"stream": false
}'For reasoning models, the API returns the thinking chain separately from the final answer:
- non-stream:
choices[0].message.reasoning_content - stream:
choices[0].delta.reasoning_content - usage:
usage.completion_tokens_details.reasoning_tokens
reasoning_tokens is a rough estimate from the extracted DeepSeek Web THINK text, because the web stream does not report official per-reasoning token usage separately.
curl -X POST http://localhost:9655/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-chat-search",
"messages": [{"role": "user", "content": "Find a fresh fact about DeepSeek and answer briefly."}],
"stream": false
}'curl -N -X POST http://localhost:9655/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-chat",
"messages": [{"role": "user", "content": "Write a short joke."}],
"stream": true
}'curl -X POST http://localhost:9655/v1/messages \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-chat",
"max_tokens": 512,
"messages": [{"role": "user", "content": "Reply with exactly OK"}],
"stream": false
}'For Claude Code you can point the backend directly:
export ANTHROPIC_BASE_URL="http://127.0.0.1:9655"
export ANTHROPIC_AUTH_TOKEN="dummy-key"
export CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1
claude --model deepseek-chatcurl -X POST http://localhost:9655/v1/responses \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-chat",
"input": "Reply with exactly OK",
"stream": false
}'FreeDeepseekAPI accepts:
- OpenAI
tools; - Anthropic
tools; - Responses API function tools.
The proxy asks DeepSeek to return a strict JSON tool call, but can also parse fallback formats:
TOOL_CALL:- fenced JSON with an explicit
tool_call,tool_calls, orfunction_callenvelope <tool_call>...</tool_call>- DeepSeek DSML (
<|DSML|tool_calls>...) and the Web variant with<||DSML|| Tool Calls>
GET /v1/models returns only aliases that are currently verified to work through this proxy.
| Alias | Web mode | Reasoning | Web search | Comment |
|---|---|---|---|---|
deepseek-chat |
Fast / default |
no | no | base chat |
deepseek-v3 |
Fast / default |
no | no | compatible alias |
deepseek-default |
Fast / default |
no | no | compatible alias |
deepseek-reasoner |
Fast / default |
yes | no | thinking_enabled=true |
deepseek-r1 |
Fast / default |
yes | no | R1-compatible alias |
deepseek-chat-search |
Fast / default |
no | yes | web search |
deepseek-default-search |
Fast / default |
no | yes | web search alias |
deepseek-reasoner-search |
Fast / default |
yes | yes | reasoning + search |
deepseek-r1-search |
Fast / default |
yes | yes | R1-compatible + search |
deepseek-expert |
Expert / expert |
no | no | Expert mode |
deepseek-v4-pro |
Expert / expert |
yes | no | Expert + reasoning |
Full mapping:
curl http://localhost:9655/v1/model-capabilitiesPer the official DeepSeek V4 Preview page, deepseek-chat and deepseek-reasoner currently route to deepseek-v4-flash non-thinking/thinking. In chat.deepseek.com itself, the direct stream does not report the exact checkpoint name (model: ""), so the proxy pins both the web mode (default / Fast) and the current official routing (DeepSeek-V4-Flash).
The current DeepSeek Web remote config output shows these web modes:
default/ UIFast— works; supportsthinking_enabledandsearch_enabled.expert/ UIExpert— works via the current web contract (x-client-version=2.0.0) and supportsthinking_enabled./v1/modelsexposesdeepseek-expertwithout reasoning anddeepseek-v4-proas Expert + reasoning.vision/ UI image recognition — visible in remote config, but right now the direct Web API returnsbackend_err_by_model(Vision is temporarily unavailable). Sodeepseek-visionis hidden from/v1/models.
Search is unavailable for Expert per remote config, so deepseek-expert-search stays unsupported.
| Method | Path | Purpose |
|---|---|---|
GET |
/ or /health |
proxy liveness status |
GET |
/readyz |
account eligibility (HTTP 503 when none is eligible) |
GET |
/v1/models |
list of working OpenAI-compatible aliases |
GET |
/v1/model-capabilities |
full mapping of aliases, real model, capabilities |
POST |
/v1/chat/completions |
OpenAI-compatible Chat Completions |
POST |
/v1/messages |
Anthropic Messages API shim |
POST |
/v1/responses |
OpenAI Responses API shim |
GET |
/v1/sessions |
active local agent sessions |
POST |
/reset-session?agent=<id> |
reset one session |
POST |
/reset-session?agent=all |
reset all sessions |
Base URL for Open WebUI in Docker:
http://host.docker.internal:9655/v1
For local runs without Docker:
http://localhost:9655/v1
If PROXY_API_KEY is not set, the API key can be anything. If the key is set,
the client must pass exactly that key — the proxy checks the bearer token before
granting access to models, sessions, and completions.
OpenCode does not auto-discover arbitrary OpenAI-compatible servers (only
Ollama, LM Studio, and vLLM are probed automatically), so add the proxy as a
custom provider in opencode.jsonc. The proxy already ships OpenCode-specific
handling: session reuse per agent, compaction detection, and separate
reasoning_content for thinking models.
Global config: ~/.config/opencode/opencode.jsonc. Project config:
opencode.jsonc in the project root.
If the proxy has no PROXY_API_KEY set, export any placeholder so the provider
has a credential to send:
export FDS_API_KEY=anythingIf PROXY_API_KEY is set, use the same value:
export FDS_API_KEY="$PROXY_API_KEY"Then pick a model with /models in the TUI, or for one run:
opencode run --model fds/deepseek-reasoner "Explain this stack trace"Notes:
/connectdoes not work here. It only lists catalog providers; custom providers are configured in the file above.reasoningField: "reasoning_content"is what makes thinking visible fordeepseek-reasoner— without it OpenCode will not show the reasoning part.- The
modelskeys are what you select in OpenCode. Only aliases the proxy actually serves are safe:deepseek-chat,deepseek-reasoner,deepseek-chat-search,deepseek-reasoner-search,deepseek-v4-pro,deepseek-expert.deepseek-visionanddeepseek-expert-searchare not supported. - Long agent prompts are capped by
DEEPSEEK_MAX_PROMPT_CHARS(default 80,000 chars) before they reach DeepSeek, so do not assume the OpenCode default context size. Keep prompts within that budget.
npm run auth
npm startIf DeepSeek starts answering 401, 403, or asks for a new PoW/session — repeat npm run auth and refresh the saved browser session.
For multi-account management (add / renew / delete / check with live probes):
npm run auth:cli
npm run auth:cli -- check allLocal auth files must not end up on GitHub:
deepseek-auth.json.chrome-profile-deepseek/.env
They are already in .gitignore.
Syntax-check the project:
npm testLive smoke tests against a running local proxy:
BASE_URL=http://127.0.0.1:9655 MODEL=deepseek-chat npm run test:liveFreeDeepseekAPI is an experimental web-chat proxy for local use and integrations. It depends on the current DeepSeek Web Chat contract, so when DeepSeek changes something, the auth/session logic or model mapping may need an update.
If something stops working:
- refresh the login via
npm run auth; - check
/v1/model-capabilities; - retry the request on a fresh session;
- if the problem persists — DeepSeek likely changed its internal Web API.
ForgetMeAI · Telegram
{ "$schema": "https://opencode.ai/config.json", "providers": { "fds": { "name": "FreeDeepseekAPI", "package": "@opencode/ai/providers/openai-compatible", "settings": { "baseURL": "http://127.0.0.1:9655/v1", "apiKey": "{env:FDS_API_KEY}" }, "models": { "deepseek-chat": { "name": "DeepSeek Chat" }, "deepseek-reasoner": { "name": "DeepSeek Reasoner", "compatibility": { "reasoningField": "reasoning_content" } }, "deepseek-chat-search": { "name": "DeepSeek Chat (Web Search)" }, "deepseek-v4-pro": { "name": "DeepSeek V4 Pro (Expert + reasoning)" } } } }, "model": "fds/deepseek-chat" }