DeepSeek V4.1 / V4
Call DeepSeek V4.1 and V4 through OpenAI Chat Completions, OpenAI Responses, or Anthropic Messages.
| Item | Value |
|---|---|
| Base URL | https://api.hop-base.com/v1 |
| Chat | POST /v1/chat/completions |
| Responses | POST /v1/responses |
| Anthropic Messages | POST /v1/messages |
Authenticate with a Bearer key and use the exact model ID returned by GET /v1/models. On /v1/messages, the x-api-key header also works.
curl https://api.hop-base.com/v1/chat/completions \
-H "Authorization: Bearer sk-your-key" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4-pro-202606",
"messages": [{
"role": "user",
"content": "Summarize this quarter'\''s risks in five bullets."
}]
}'Models
| Model ID | Context | Max output tokens per call |
|---|---|---|
deepseek-v4.1-flash | 1M | 393,216 |
deepseek-v4-pro-202606 | 1,048,576 | 393,216 |
deepseek-v4-flash-202605 | 1M | 384,000 |
V4.1 Flash returns its thinking in reasoning_content. It is billed against DeepSeek's official price and follows the official peak/off-peak schedule; see Peak and off-peak pricing.
All three models are in the DeepSeek group, so one key reaches all of them.
Only the IDs in the table above are production models. Read the exact ID from GET /v1/models instead of shortening it.
These short names all return the same 404 model_not_found and never name a replacement ID: deepseek-v4-flash, deepseek-v4-pro, deepseek-chat, deepseek-reasoner, and deepseek-flash (DeepSeek's own short name for V4.1 Flash). The message is localized by the request's Accept-Language, English by default:
The current group does not support the requested model: <model>Peak and off-peak pricing
deepseek-v4.1-flash follows DeepSeek's official peak/off-peak schedule, stated in Beijing time (UTC+8).
- Peak: Monday to Friday, 09:00–12:00 and 14:00–18:00.
- Off-peak: every other hour, including nights, the midday gap between the two windows, and the whole weekend.
- Off-peak unit prices for input, cached input and output are all halved.
- A window includes its opening minute and excludes its closing minute: 09:00:00 is already peak, 12:00:00 is already off-peak.
A request is priced by the moment it finishes, not the moment it starts. One that begins at 17:59 and ends at 18:01 is billed entirely at the off-peak rate; one that begins at 08:59 and ends at 09:01 is billed entirely at the peak rate.
A single request always carries exactly one unit price and is never split across a boundary. That is why two usage records a minute apart can show different unit prices for the same model. The rates that apply to your key are in the signed-in model catalog.
Endpoint and request parameters
POST /v1/chat/completions, POST /v1/responses and POST /v1/messages all work for all three models, and streaming works normally on each. Codex CLI uses Responses; Claude Code uses Anthropic Messages, covered in Claude Code and Anthropic Messages.
- Fill-in-the-middle (FIM) completion is not available: it lives on a path HopBase does not route.
- Prefix continuation is available; see Prefix continuation.
The table covers POST /v1/chat/completions. The gateway only rejects empty messages; every other limit comes from the model service.
| Parameter | Required | Type & limits | Default | Description |
|---|---|---|---|---|
model | Required | One of the three IDs above | — | Exact match; short names return 404 |
messages | Required | Non-empty array | — | role values below |
max_tokens | Optional | Non-negative integer; ceiling in Models | Service default | Shared by thinking and answer |
temperature | Optional | Number 0–2 | Service default | — |
thinking | Optional | {"type": …} | On for V4.1 Flash | Values below |
reasoning_effort | Optional | low / medium / high work as tested | Service default | Applies only when thinking is on |
tools, tool_choice | Optional | As in OpenAI | auto | See Tool calling |
stream | Optional | Boolean | false | true returns server-sent events |
stream_options.include_usage | Optional | Boolean | false | Must be true to get usage when streaming |
prefix | Optional | Boolean | false | Prefix continuation |
image_url.url | Optional | data: URI or https:// URL | — | deepseek-v4.1-flash only |
messages[].roleissystem/user/assistant/tool/developer.thinking.typeisenabled/disabled/adaptive; other values (such asauto) return 400.- A negative
max_tokensreturns 400; a value above the ceiling is not rejected and output is cut silently (see Reasoning output). stream_options.include_usagedoes not affect billing.prefixgoes only on the lastassistantmessage.- With
image_url.url, the whole request body is ≤ 60 MB.
Error text quoted from production records (2026-09):
400 missing messages field messages missing (gateway)
400 messages must not be an empty array messages empty (gateway)
400 expected a value <= 2 temperature above 2On /v1/responses, output is capped with max_output_tokens, which must be 1–393,216. temperature is again 0–2, and a request that sets caching must also set store: true. Above the cap:
400 max_output_tokens must be less than or equal to 393216For every parameter, see the Chat Completions API reference and pick the DeepSeek model family.
Image input
deepseek-v4.1-flash reads images natively, on this same endpoint, in the standard OpenAI content-part form. image_url.url takes either a data: URI or a publicly reachable https:// URL, and several images may travel in one message.
The whole request body is capped at 60 MB. Base64 adds about a third, so the images together must stay under roughly 45 MB; send large images as https:// URLs. Above the cap:
413 Request body exceeds the size limit (60 MB)Images are ordinary input: they are counted in prompt_tokens.
deepseek-v4-pro-202606 and deepseek-v4-flash-202605 cannot see images. A request that carries one is rejected with 400 before it reaches the model and is not billed, on Chat Completions, Responses and Anthropic Messages alike. That includes screenshots pasted into Claude Code and image files it reads; use deepseek-v4.1-flash for anything with images.
400 Model deepseek-v4-pro-202606 does not accept image input. Remove the image from the request, including earlier turns of the conversation, or use a model that supports images.{
"model": "deepseek-v4.1-flash",
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "What is in each of these two images?" },
{ "type": "image_url", "image_url": { "url": "data:image/png;base64,iVBORw0KGgo..." } },
{ "type": "image_url", "image_url": { "url": "https://example.com/chart.png" } }
]
}
]
}Reasoning output
DeepSeek returns its thinking trace in a separate reasoning_content field, not inside content. Read it explicitly if you want it.
completion_tokens already includes reasoning tokens, so treat it as the real output length and do not add reasoning_tokens on top. This differs from Grok, where the chat endpoint reports them separately and they must be added.
Thinking draws from the same max_tokens budget as the answer, and it comes first. Set the budget too low and thinking consumes all of it; the call still returns 200, with this signature:
finish_reasonislength.contentis an empty string.completion_tokens_details.reasoning_tokensequals the budget you gave.
Read that combination as "raise max_tokens", not as a failed request. At max_tokens: 16 this model returns a truncated thought and no answer at all.
That budget rule holds for deepseek-v4.1-flash. On deepseek-v4-pro-202606 and deepseek-v4-flash-202605, max_tokens does not cap thinking while thinking is on: in tests, max_tokens: 64 returned 144–161 output tokens on Chat Completions and up to 1,299 on Anthropic Messages, all billed. With thinking turned off, the cap is exact.
The two ceilings do not behave alike. Input beyond the context window (1,048,576 tokens as measured) is rejected with a 400 before anything is generated, and nothing is billed. On Chat Completions, a max_tokens above the output ceiling is not rejected: the call returns 200 and the output is silently cut at the real ceiling, so a successful response is no proof that your budget was accepted.
Tool calling and prefix continuation
Tool calling
Standard OpenAI function calling works end to end: one call or several in parallel, tool_choice set to auto, none, required or a named function, and role: "tool" results fed back in.
This is a reasoning model and it still accepts required. Thinking stays on, and the reply comes back with finish_reason: "tool_calls".
Prefix continuation
Hand the model the opening of its own reply and it carries on from there: make the last entry of messages an assistant message and set prefix: true on it. The completion holds only the continuation; your text is not repeated.
In this mode the model produces no thinking trace: reasoning_content is absent, reasoning_tokens is 0, and the whole budget goes to the continuation.
{
"model": "deepseek-v4.1-flash",
"messages": [
{ "role": "user", "content": "List three primary colors, comma separated, nothing else." },
{ "role": "assistant", "content": "red, green,", "prefix": true }
]
}The content that comes back for that request is blue, one output token.
Prompt caching and reconciliation
Prefix caching is implicit: there is nothing to switch on and no cache-control parameter to send. A hit is reported in the standard OpenAI field usage.prompt_tokens_details.cached_tokens. Hits need at least 256 tokens of shared prefix; below that cached_tokens stays 0.
Your HopBase usage record and the response body count input differently, which matters when you reconcile:
| Field | Meaning |
|---|---|
Response prompt_tokens | Whole prompt, cached portion included |
| Usage record Input tokens | Prompt minus the cached portion |
| Usage record Cached input tokens | The cached portion, listed separately |
So the usage record's input plus cached input equals the response's prompt_tokens. Cache read is priced separately from plain input; see the signed-in model catalog for the rate that applies to your key.
Claude Code and Anthropic Messages
POST /v1/messages accepts standard Anthropic Messages requests for all three models and passes them to the model's own Anthropic-compatible interface, so Claude Code and the Anthropic SDK connect directly. For the SDK, set the base URL to https://api.hop-base.com, without /v1.
With CC Switch, open the key's ··· menu in the console, select Import CCS, then Claude Code. The import fills in the model; keep the default Anthropic format, with no local routing. To set it up by hand:
export ANTHROPIC_BASE_URL="https://api.hop-base.com"
export ANTHROPIC_AUTH_TOKEN="sk-your-key"
export ANTHROPIC_MODEL="deepseek-v4-pro-202606"
export ANTHROPIC_DEFAULT_HAIKU_MODEL="deepseek-v4-flash-202605"
export CLAUDE_CODE_MAX_CONTEXT_TOKENS=1000000
claudeClaude Code does not know these IDs, so CLAUDE_CODE_MAX_CONTEXT_TOKENS tells it the real context window; without it, Claude Code compacts the conversation at 200K tokens. All three models are in the same group, so one key covers them all. More options are in Claude Code.
- Thinking comes back in
thinkingblocks and counts towardoutput_tokens.thinking: {"type": "disabled"}turns it off. - A named tool in
tool_choice({"type": "tool", "name": …}) is called as asked.{"type": "any"}is ignored, and the model may answer in text. - Only
deepseek-v4.1-flashreads images; an image sent to the other two returns400, as described in Image input. - A
max_tokensabove the output ceiling is not rejected. cache_controlbreakpoints are removed before the request goes out, so explicit caching is not available; automatic prefix caching still works. In tests on 2026-10-03, Claude Code read 94%–99% of the prompt from cache from the second turn on.
When you reconcile Anthropic Messages usage, input_tokens excludes the cached part and matches Input tokens in your usage record; cache_read_input_tokens matches Cached input tokens.
Gateway behavior
| Behavior | Effect on your request |
|---|---|
Empty messages | Rejected with a 400 before the request goes out |
| Responses fields the model does not take | Removed silently |
Unsupported input[].type / tools[].type | Not removed — still a 400 |
- The silently removed Responses fields include
stream_options,truncation,user,reasoning.summaryandtext.verbosity. - Unsupported types include
local_shell_call,file_searchandcode_interpreter. - The
modelfield in the response always echoes the public ID you requested, so a client that assertsresponse.model == request.modelkeeps working.
On /v1/responses, previous_response_id and store pass through: a follow-up is routed back to where the earlier response was made. When that is no longer possible you get a 503 asking you to start over; resend the full input without previous_response_id.
For prefix continuation on Responses, set partial: true on the last assistant message instead of prefix: true.
The response model stays the same even when the request is served by a different route.
Next steps
- DeepSeek Harness — the
dshagent runtime's settings file and custom-provider form; this page is the model contract behind it - OpenAI SDK — the general Chat Completions pattern
- Claude Code — direct connection over Anthropic Messages
- Error codes and retries
- Concurrency, timeouts, and billing