# API reference

> Request and response schemas plus curl examples for the four chat protocol endpoints, GET /v1/models and the balance/billing endpoints, with an index of the media, digital-human and knowledge-base endpoints

来源：https://zhonkemodel.dflop.top/en/docs/reference/api-reference

Every public endpoint on the platform accepts the same `sk-gpushare-*` API key. This page details the four chat protocol endpoints plus `GET /v1/models` and both sets of balance endpoints; the media (image, video, music, speech), digital-human and knowledge-base endpoints have pages of their own.

| Endpoint | Protocol / purpose | Details |
|---|---|---|
| [`POST /v1/chat/completions`](#post-v1-chat-completions) | OpenAI Chat — the most universal, works across vendors | this page |
| [`POST /v1/messages`](#post-v1-messages) | Anthropic Messages — for the Anthropic SDK directly | this page |
| [`POST /v1beta/models/{model}:generateContent`](#gemini-native) | Gemini Native — for Google's `genai` SDK directly | this page |
| [`POST /v1/responses`](#post-v1-responses) | OpenAI Responses — GPT-5.x's native protocol, with built-in tools | this page |
| [`GET /v1/models`](#get-v1-models) | OpenAI model discovery (SDKs and third-party clients call it automatically) | this page |
| [`GET /v1/key/balance`](#get-v1-key-balance) | This key's remaining, used and total credit | this page |
| [`GET /v1/dashboard/billing/*`](#get-v1-dashboard-billing) | OpenAI's own billing endpoints — what third-party clients call to show a balance | this page |
| `POST /v1/images/generations` | Text-to-image and image-to-image (synchronous, or `\"async\": true`), **billed per image** | [Image / video / music APIs](./media-apis.md) |
| `POST /v1/videos/generations` (plus `GET .../{id}` polling and a `GET` list) | Text-to-video and image-to-video (async task), **billed per second** | [Image / video / music APIs](./media-apis.md) |
| `POST /v1/music/generations` (plus `GET .../{id}` polling) | AI music generation (async task), **billed per generation** | [Image / video / music APIs](./media-apis.md) |
| `POST /v1/audio/speech` / `/v1/audio/voices` | Speech synthesis (per character) and voice cloning (per call) | [Image / video / music APIs](./media-apis.md#post-v1audiospeech) |
| `POST /v1/videos/avatars` + `GET /v1/videos/clip-templates` | The avatar library and smart-edit templates | [Digital human / smart edit API](./digital-human-apis.md) |
| `POST /v1/embeddings` | ⚠️ The embedding SKUs were withdrawn in July 2026, so **no model is currently available** (calls return 404 `model_not_found`) | — |
| `POST /v1/transcripts/extract` | Short-video link → spoken script (synchronous), **billed per call** | [Image / video / music APIs](./media-apis.md) |
| `/api/v1/ext/wiki/*` (8 read-only REST endpoints) + `/mcp` (an MCP server) | Knowledge-base search | [Knowledge base API & MCP](./wiki-api.md) |

Base URL: `https://zhonkezhonkeapi.dflop.top`

## Authentication

See [Authentication](./authentication.md) for the full story. Pick any of the four forms; they fall back in this order:

1. the `x-api-key: sk-gpushare-xxx` header (recommended)
2. the `x-goog-api-key: sk-gpushare-xxx` header (Google's `genai` SDK default — Gemini SDK users need change nothing)
3. `?key=sk-gpushare-xxx` query
4. the `Authorization: Bearer sk-gpushare-xxx` header (the OpenAI and Anthropic SDK default)

> **One exception**: [`GET /v1/models`](#get-v1-models) accepts only the `Authorization: Bearer` and `x-api-key` headers and **does not support the `?key=` query**.

---

## POST /v1/chat/completions

The OpenAI Chat Completions-compatible endpoint. **The most universal one**, covering every chat model (101); for the full list see [Models](./models.md) or [`GET /v1/models`](#get-v1-models).

> Image- and video-only SKUs (Seedream, Seedance and so on) do not live here — calling them returns 503 `no_channel_available`. Use their own endpoints instead; see [Image / video APIs](./media-apis.md).

### The request

```json
{
  "model": "claude-sonnet-4-6",
  "messages": [
    {"role": "system", "content": "You are helpful."},
    {"role": "user", "content": "Hello"}
  ],
  "stream": false,
  "max_tokens": 1024,
  "temperature": 0.7,
  "tools": [
    {"type": "function", "function": {...}}
  ]
}
```

| Field | Required | Type | Notes |
|---|---|---|---|
| `model` | ✓ | string | the model ID; see [Models](./models.md) |
| `messages` | ✓ | array | conversation history, with role ∈ {system, user, assistant, tool} |
| `stream` | | bool | `true` turns on SSE streaming |
| `max_tokens` | | int | the cap on generated tokens |
| `temperature` | | float | 0-2 |
| `tools` | | array | function tools, or the built-in `{type:"web_search"}` / `{type:"image_generation"}` (constraints below) |
| `tool_choice` | | string\|object | `auto` / `none` / `{type:"function","function":{...}}` |
| `stream_options` | | object | while streaming, `{"include_usage": true}` puts token counts in the trailing chunk |
| `response_format` | | object | `{"type":"json_object"}` forces JSON output |

> **Constraints on the built-in tools** (`{type:"web_search"}` and `{type:"image_generation"}`):
> - Both run through the WebSocket V2 adapter on this endpoint, so **`stream: true` is mandatory** — non-streaming returns 400 `invalid_request` straight away (message: ``Tools `web_search` and `image_generation` require `stream: true` ``)
> - `image_generation` is supported **only on GPT-5.x** (the x1 source); other models return 400 `tool_not_supported`
> - `web_search` is gated on what each model and channel supports, and an unsupported combination returns 400 `tool_not_supported`. Per-model support is in [Models](./models.md)
>
> Function tools (`{type:"function",...}`) are exempt from all of the above and work either streaming or not.

### The response (non-streaming)

```json
{
  "id": "chatcmpl-...",
  "object": "chat.completion",
  "created": 1715845200,
  "model": "claude-sonnet-4-6",
  "choices": [{
    "index": 0,
    "message": {"role": "assistant", "content": "Hello!"},
    "finish_reason": "stop"
  }],
  "usage": {
    "prompt_tokens": 10,
    "completion_tokens": 5,
    "total_tokens": 15
  }
}
```

### The response (streaming)

With `stream: true` you get an SSE stream where each `data:` line is one chunk. See [Streaming](../guides/streaming.md).

### curl

```bash
curl https://zhonkezhonkeapi.dflop.top/v1/chat/completions \
  -H "Authorization: Bearer $PLATFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5.5",
    "messages": [{"role": "user", "content": "Hello"}]
  }'
```

---

## POST /v1/messages

The Anthropic Messages-compatible endpoint.

### The request

```json
{
  "model": "claude-sonnet-4-6",
  "max_tokens": 1024,
  "system": "You are helpful.",
  "messages": [
    {"role": "user", "content": "Hello"}
  ],
  "stream": false,
  "tools": [...]
}
```

| Field | Required | Type | Notes |
|---|---|---|---|
| `model` | ✓ | string | the model ID |
| `max_tokens` | ✓ | int | required by the Anthropic protocol (unlike OpenAI) |
| `messages` | ✓ | array | the conversation, with role ∈ {user, assistant} |
| `system` | | string | the system prompt (a top-level field, not part of messages) |
| `stream` | | bool | |
| `tools` | | array | Anthropic's tool format (`name` / `description` / `input_schema`) |
| `tool_choice` | | object | `{"type":"auto"\|"any"\|"tool", "name": "..."}` |

### The response (non-streaming)

```json
{
  "id": "msg_...",
  "type": "message",
  "role": "assistant",
  "model": "claude-sonnet-4-6",
  "content": [
    {"type": "text", "text": "Hello!"}
  ],
  "stop_reason": "end_turn",
  "usage": {"input_tokens": 10, "output_tokens": 5}
}
```

### curl

```bash
curl https://zhonkezhonkeapi.dflop.top/v1/messages \
  -H "x-api-key: $PLATFORM_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-4-6",
    "max_tokens": 1024,
    "messages": [{"role": "user", "content": "Hello"}]
  }'
```

### Limitations

- For the mainstream `gemini-2.5` and `gemini-3.x` SKUs, prefer the [Gemini Native endpoint](#gemini-native); some `gemini-*` SKUs are wired up here too. The exact model × endpoint support is in the [compatibility matrix](./compatibility-matrix.md)
- When the model exists but no channel serves it on this endpoint, you get 503 `no_channel_available`
- The SDK injects the `anthropic-version` header for you; with raw curl, send `2023-06-01`

---

## Gemini Native

Two related endpoints:

- `POST /v1beta/models/{model}:generateContent` — non-streaming
- `POST /v1beta/models/{model}:streamGenerateContent` — streaming

The `{model}` placeholder goes straight into the URL, e.g. `/v1beta/models/gemini-2.5-pro:generateContent`.

### The request

```json
{
  "contents": [
    {"role": "user", "parts": [{"text": "Hello"}]}
  ],
  "systemInstruction": {
    "parts": [{"text": "You are helpful."}]
  },
  "generationConfig": {
    "maxOutputTokens": 1024,
    "temperature": 0.7
  },
  "tools": [...]
}
```

### The response

```json
{
  "candidates": [{
    "content": {
      "role": "model",
      "parts": [{"text": "Hello!"}]
    },
    "finishReason": "STOP",
    "index": 0
  }],
  "usageMetadata": {
    "promptTokenCount": 10,
    "candidatesTokenCount": 5,
    "totalTokenCount": 15
  }
}
```

### curl

```bash
curl "https://zhonkezhonkeapi.dflop.top/v1beta/models/gemini-2.5-pro:generateContent?key=$PLATFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contents": [{"parts": [{"text": "Hello"}]}]
  }'
```

### Limitations

- GPT-5.x isn't supported here yet (the model exists but has no channel, so you get 503 `no_channel_available`); the exact matrix is in the [compatibility matrix](./compatibility-matrix.md)

---

## POST /v1/responses

The OpenAI Responses API-compatible endpoint — GPT-5.x's **native protocol**. Compared with `/v1/chat/completions` it adds two native tools, **web_search** and **image_generation** (drawing with GPT-image-2), and its responses carry full structured `reasoning`, `output_text` and tool-call fields.

### Supported models

The GPT-5 family (natively) plus the Claude SKUs on the x1 source; the exact matrix is in the [compatibility matrix](./compatibility-matrix.md):

| Model | Notes |
|---|---|
| `gpt-5.5` | |
| `gpt-5.5` | recommended — supports reasoning summaries and the built-in tools |
| `claude-opus-4-6` / `claude-opus-4-7` / `claude-opus-4-8` | X1 |
| `claude-sonnet-4-6` | X1 |
| `claude-haiku-4-5-20251001` | X1 |

What happens when you call something else:

- a model id that isn't in the catalogue → 400 `model_not_found`
- the model exists but no channel serves it here → 503 `no_channel_available` (use `/v1/chat/completions` or that model's native endpoint instead)
- `model_not_allowed` appears **only** when your key has an `allowed_models` allowlist that omits the model

### The request

```json
{
  "model": "gpt-5.5",
  "instructions": "You are concise.",
  "input": [
    {"role": "user", "content": "Say hello in 5 words."}
  ],
  "stream": false,
  "max_output_tokens": 1024,
  "temperature": 1.0,
  "top_p": 0.98,
  "reasoning": {"effort": "medium"},
  "tools": [
    {"type": "web_search"},
    {"type": "image_generation", "size": "1024x1024"},
    {"type": "function", "name": "get_weather", "description": "...", "parameters": {...}}
  ],
  "tool_choice": "auto",
  "parallel_tool_calls": true
}
```

| Field | Required | Type | Notes |
|---|---|---|---|
| `model` | ✓ | string | see [Supported models](#supported-models) |
| `input` | ✓ | array \| string | **always use the message array form** (below). The GPT-5.x upstream requires `input` to be an array and rejects the string form |
| `instructions` | | string | the system prompt. **A top-level field, not messages[0]** (unlike `/v1/chat/completions`). The GPT-5.x upstream requires it, so always send it |
| `stream` | | bool | `true` returns an SSE event stream |
| `max_output_tokens` | | int | the generation cap (the Responses API uses `_output_`, not `max_tokens`) |
| `reasoning` | | object | `{"effort": "low"\|"medium"\|"high"}` controls how hard the model thinks internally |
| `temperature` / `top_p` / `frequency_penalty` / `presence_penalty` | | float | the standard sampling parameters |
| `tools` | | array | see [Tools](#tools) |
| `tool_choice` | | string \| object | `auto` / `none` / `{type:"function","name":"..."}` |
| `parallel_tool_calls` | | bool | true by default |

#### The `input` array form (multi-turn)

```json
"input": [
  {"role": "user", "content": "What is 2+2?"},
  {"role": "assistant", "content": "4"},
  {"role": "user", "content": "What was my first question?"}
]
```

An element's `content` may be shortened to a plain string; **vision** requires content parts:

```json
{"role": "user", "content": [
  {"type": "input_text", "text": "Describe this image."},
  {"type": "input_image", "image_url": "https://...", "detail": "auto"}
]}
```

> **Note**: `input_image.image_url` must be a publicly reachable URL (the upstream fetches it itself); CDN thumbnails and authenticated URLs may come back as `upstream_error`.

#### Tools

```json
// 1. built-in web search — the model decides whether to use it and returns output_text with the answer
{"type": "web_search"}

// 2. built-in image generation — output carries an image_generation_call item (with a base64 result)
{"type": "image_generation", "size": "1024x1024"}

// 3. your own function — output carries a function_call item; you run it and send back function_call_output
{
  "type": "function",
  "name": "get_weather",
  "description": "Get current weather",
  "parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}
}
```

### The response (non-streaming)

```json
{
  "id": "resp_0a430185e6bd1abb016a1576c7bbb08198be6868b655d19349",
  "object": "response",
  "created_at": 1779791559,
  "status": "completed",
  "model": "gpt-5.5",
  "instructions": "...",
  "output": [
    {
      "type": "message",
      "role": "assistant",
      "content": [{"type": "output_text", "text": "Hello, hope you are well."}]
    }
  ],
  "reasoning": {"context": "current_turn", "effort": "medium", "summary": null},
  "usage": {
    "input_tokens": 25,
    "input_tokens_details": {"cached_tokens": 0},
    "output_tokens": 51,
    "output_tokens_details": {"reasoning_tokens": 38},
    "total_tokens": 76
  }
}
```

The `output[]` array is in the order things happened, and each item's `type` is one of:

| `type` | Meaning |
|---|---|
| `message` | the assistant's text reply, in `content[].text` |
| `reasoning` | a summary of the internal reasoning (may be empty) |
| `function_call` | the model chose to call your function; fields are `call_id`, `name` and `arguments` (a JSON string) |
| `image_generation_call` | the result of the built-in image_generation tool; `result` is a base64 PNG |

### The response (streaming)

With `stream: true` you get SSE whose event names are prefixed `response.`. The key sequence:

```
event: response.created            // the overall response frame (usage=null)
event: response.in_progress
event: response.output_item.added  // output item N begins
event: response.output_text.delta  // an incremental piece of text; the chunk is in delta
event: response.output_text.done   // item N's text is complete
event: response.output_item.done   // item N is finished
event: response.completed          // everything is done and usage is populated
```

Every `data:` is a single JSON object carrying a monotonically increasing `sequence_number`. See [Streaming](../guides/streaming.md).

### curl

```bash
# a basic call
curl https://zhonkezhonkeapi.dflop.top/v1/responses \
  -H "Authorization: Bearer $PLATFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5.5",
    "instructions": "You are helpful.",
    "input": [{"role": "user", "content": "Hello"}],
    "max_output_tokens": 100
  }'

# web search
curl https://zhonkezhonkeapi.dflop.top/v1/responses \
  -H "Authorization: Bearer $PLATFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5.5",
    "instructions": "You are helpful.",
    "input": [{"role": "user", "content": "What is today date in Shanghai?"}],
    "tools": [{"type": "web_search"}]
  }'

# streaming
curl -N https://zhonkezhonkeapi.dflop.top/v1/responses \
  -H "Authorization: Bearer $PLATFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5.5",
    "instructions": "You are helpful.",
    "input": [{"role": "user", "content": "Count 1 to 3."}],
    "stream": true
  }'
```

### Limitations

- **Multi-turn conversations must maintain their own history in the `input` array.** The upstream doesn't persist conversation items (`store: false` semantics) and `previous_response_id` is unavailable — put the full history in `input` every turn
- **The model range is under [Supported models](#supported-models)**; anything else belongs on `/v1/chat/completions` or its own native endpoint (error semantics above)
- **Vision is limited by what the upstream can fetch** — some CDN thumbnails, authenticated URLs and hotlink-protected URLs fail. Upload the image to your own public R2 / S3 bucket and pass that URL
- **The `image_generation` tool doesn't force streaming here** — this endpoint is an HTTP pass-through, so even with `stream:false` you find `image_generation_call.result` (a base64 PNG) in `output[]`. That differs from `/v1/chat/completions`, where `image_generation` runs through a dedicated WebSocket streaming adapter that forces `stream:true` and embeds the image as markdown inside `delta.content`

---

## GET /v1/models

The OpenAI-compatible model discovery endpoint. It's what the OpenAI SDK's `client.models.list()` calls, and what most third-party clients (Open WebUI, Cline, Continue and so on) use to discover models automatically.

### An authentication difference

This endpoint accepts only two **header** forms and **does not support the `?key=` query** — unlike the four-way fallback elsewhere, a Gemini-style query call gets a 401:

- `Authorization: Bearer sk-gpushare-xxx`
- `x-api-key: sk-gpushare-xxx`

### The response

```json
{
  "object": "list",
  "data": [
    {"id": "claude-sonnet-4-6", "object": "model", "created": 0, "owned_by": "anthropic"},
    {"id": "gpt-5.5", "object": "model", "created": 0, "owned_by": "openai"}
  ]
}
```

- It returns every entry in the pricing registry, **including image-, video- and embedding-only SKUs and placeholder SKUs that aren't callable yet** — filter as needed when building a model menu or scripting against it
- If your key has an `allowed_models` allowlist, only allowlisted entries come back
- There's also an **unauthenticated** `GET /api/v1/models/public` returning the whole registry with pricing and capability fields (including the `callable` flag and `endpoint_type`), handy for comparison and filtering

### curl

```bash
curl https://zhonkezhonkeapi.dflop.top/v1/models \
  -H "Authorization: Bearer $PLATFORM_API_KEY"
```

---

## GET /v1/key/balance

Check the balance of the account behind this key. Free, and not rate-billed.

**It still answers normally at a zero balance** (where every other *billing* endpoint would 402; `GET /v1/models` likewise doesn't check the balance) — which is exactly the point: monitor with it before you run dry.

```bash
curl https://zhonkezhonkeapi.dflop.top/v1/key/balance \
  -H "Authorization: Bearer $PLATFORM_API_KEY"
```

```json
{
  "object": "key.balance",
  "remaining_usd": "12.3456",
  "used_usd": "7.6544",
  "total_usd": "20.0000",
  "expires_at": null
}
```

| Field | Meaning |
|---|---|
| `remaining_usd` | **How much this key can actually still spend** (floored at 0): the lesser of the account balance and whatever remains of the key's spend cap |
| `used_usd` | **This key's lifetime spend.** Keys with a spend cap report the cap's counter; uncapped keys report the total across this key's own call log — either way it only ever counts this one key, never the account's or an enterprise owner's pooled spend |
| `total_usd` | `remaining_usd + used_usd`. A prepaid wallet has no "granted credit" of its own; this is the synthetic denominator the billing endpoints below publish |
| `expires_at` | When the key expires; `null` means never |

The balance itself is **shared by every key** on the account rather than being per-key.

> ⚠️ **When a key has no spend cap, `remaining_usd` *is* the backing wallet's balance** — for an enterprise-issued key, that's the owner's pool. Only a key with a spend cap reports "what this key has left" instead of the whole pool. **If you don't want the holder to see the pool balance, give that key a spend cap** (see [key spend limits](https://zhonkezhonkemodel.dflop.top/dashboard/keys)).

Amounts are JSON **strings** (so no float precision is lost) — `parseFloat` them client-side.

Authentication matches every other endpoint (any of the four forms). This endpoint does not update the key's "last used" time, so polling it regularly won't register as traffic. `used_usd` may lag by up to 60 seconds; `remaining_usd` is always live.

---

## GET /v1/dashboard/billing

```text
GET /v1/dashboard/billing/subscription
GET /v1/dashboard/billing/usage
```

`/v1/key/balance` above is a name this platform invented, and no third-party client knows it. These two are **OpenAI's own billing endpoints**: relay platforms like New API / One API call them behind their "update balance" button, so pasting the key in is enough with **no extra configuration**. Many desktop clients (Cherry Studio, ChatBox, NextChat, LobeChat and others) use the same pair for their "check balance" feature, though that depends on your client's version.

> `GET /v1/dashboard/billing/credit_grants` (OpenAI's other legacy billing endpoint) is **not** implemented — older clients that call it get a 404. Use the two above.

Both are mounted **with and without the `/v1` prefix** (clients differ on how they join the base URL) and return identical payloads.

```bash
curl https://zhonkezhonkeapi.dflop.top/v1/dashboard/billing/subscription \
  -H "Authorization: Bearer $PLATFORM_API_KEY"
```

```json
{
  "object": "billing_subscription",
  "has_payment_method": true,
  "soft_limit_usd": 20.0,
  "hard_limit_usd": 20.0,
  "system_hard_limit_usd": 20.0,
  "access_until": 0
}
```

```bash
curl "https://zhonkezhonkeapi.dflop.top/v1/dashboard/billing/usage?start_date=2026-07-01&end_date=2026-08-01" \
  -H "Authorization: Bearer $PLATFORM_API_KEY"
```

```json
{
  "object": "list",
  "total_usage": 765.44,
  "daily_costs": []
}
```

- How these map to `/v1/key/balance`: `hard_limit_usd` = `total_usd`, and `total_usage` = `used_usd` **× 100** (`total_usage` is in **cents**, OpenAI's original unit). The balance a client derives as `hard_limit_usd - total_usage / 100` is therefore always exactly `remaining_usd`.
- Amounts on these two endpoints are **JSON numbers**, not strings — that's the official schema, and clients parse them as floats.
- `access_until` is the key's expiry as Unix seconds, or `0` when it never expires.
- `start_date` / `end_date` are **accepted and ignored**; `total_usage` is always the lifetime figure. The reason: the `subscription` endpoint receives no date window at all, so a windowed `total_usage` would make the client's subtraction report a wrong balance. New API / One API ignore them for the same reason.
- `daily_costs` is always an empty array. For per-day or per-model detail, use the usage pages in the console.
- Same properties as `/v1/key/balance`: they answer 200 at a zero balance, and they are not billed, not rate-limited, and don't update the key's "last used" time.

> For step-by-step client and relay-platform setup, see [New API and third-party clients](../integrations/new-api.md).

---

## Error responses

Whenever an endpoint returns HTTP 4xx/5xx, the body uses **that protocol's official error schema** — never a mixture:

### OpenAI shape (`/v1/chat/completions`, `/v1/responses`, `/v1/embeddings`)
```json
{"error": {"message": "...", "type": "...", "code": "..."}}
```

### Anthropic Messages (`/v1/messages`)
```json
{"type": "error", "error": {"type": "...", "message": "..."}}
```

### Gemini Native (`/v1beta/...`)
```json
{"error": {"code": 400, "message": "...", "status": "INVALID_ARGUMENT"}}
```

> **Errors mid-stream**: once a streaming request has opened (HTTP 200 is already out), a later upstream error **cannot change the status code** — it shows up as a truncated stream or an SSE error frame, so your client needs a fallback for "the stream never ended cleanly". See [Error codes](./errors.md).

The most common error is an exhausted balance: **HTTP 402**, which in the OpenAI shape is `code: "quota_exceeded"` / `type: "insufficient_quota"` (Anthropic: `type: "billing_error"`; Gemini: `status: "RESOURCE_EXHAUSTED"`). The full truth table is in [Error codes](./errors.md).

## Limits and timeouts

### Rate limits

The platform sets two ceilings **per account** (shared by all of that account's API keys, not one set each):

| Dimension | Meaning |
|---|---|
| Requests per minute | requests in the last 60 seconds (a sliding window) |
| Max concurrency | requests in flight (not yet finished) at any one moment |

**Unlimited by default**; the platform configures them per account when needed, and can loosen or tighten any single account. **Every `/v1/*` request counts**, including status polling for async tasks (video, music, digital human) — if your integration polls heavily, widen the interval or ask us to raise the ceiling.

Hitting a limit returns **429** `rate_limit_exceeded` with these headers:

| Header | Meaning |
|---|---|
| `Retry-After` | how many seconds to wait (just do as it says) |
| `x-ratelimit-limit-requests` / `-remaining-requests` / `-reset-requests` | the per-minute request ceiling, what's left, and seconds until reset |
| `x-ratelimit-limit-concurrency` / `-remaining-concurrency` | the concurrency ceiling and what's left |

When limits are in place, **successful responses** carry the `x-ratelimit-*` headers too, so clients can throttle themselves adaptively. You can look up your account's ceilings at [zhonkemodel.dflop.top/dashboard/keys](https://zhonkezhonkemodel.dflop.top/dashboard/keys) (the section is hidden for accounts with no limits set).

> A `429` may also be an upstream rate limit passed through, unrelated to our own. Tell them apart by the headers: with `x-ratelimit-*` it's ours, without it's the upstream's — either way, retry with exponential backoff.

### Billing and balance

Charges come out of a **single account balance** (a USD wallet) shared by every API key — no key has its own budget pool, and when the balance runs out every key returns 402 at once (creating a new key doesn't help). Sign-up includes 121.32 of trial credit; top up at dflop.top/dashboard/billing (the same account as zhonkemodel.dflop.top). See [Authentication](./authentication.md).

### Timeouts

| Endpoint | Upstream timeout |
|---|---|
| The four chat protocol endpoints (`/v1/chat/completions`, `/v1/messages`, `/v1/responses`, `/v1beta/...`) | **180s in total** (streaming is bound by the same total — it just gets you the first token sooner, it isn't unbounded) |
| `POST /v1/images/generations` | 240s per attempt / 280s across the ladder (set client ≥300s) |
| `POST /v1/videos/generations` | 60s (submission only; generation is async and doesn't hold the request) |
| `POST /v1/embeddings` | 30s |
| `POST /v1/transcripts/extract` | ~55s (synchronously blocking: it creates a task internally and polls upstream to completion) |

Set your SDK timeout to **≥ 200 seconds** (long reasoning or tool-calling turns can run to minutes — prefer streaming). Very long jobs such as video use async polling; see [Image / video / music APIs](./media-apis.md).

### Automatic failover and retries

On an upstream 5xx or connection failure the gateway switches channels and retries, trying at most 3 channels per request — which is why one request occasionally takes twice as long. A 502 `upstream_unreachable` or 504 `upstream_timeout` is safe for your client to retry.
