Quickstart
One key and one base URL. Any OpenAI or Anthropic client can then talk to the models zdr.sh serves.
Base URL and key#
| Setting | Value |
|---|---|
| Base URL (OpenAI format) | https://api.zdr.sh/v1 |
| Base URL (Anthropic format) | https://api.zdr.sh |
| API key | a duck- key from the dashboard |
| Models | glm-5.3, glm-5.3-flash, deepseek-v4.1-flash, deepseek-dgx and the other ids on Models |
The dashboard is at zdr.sh/app. The API is a different host, api.zdr.sh. The Anthropic base URL is the origin only, because the Anthropic SDKs append /v1/messages themselves.
Create an account and a key#
- Sign up at zdr.sh/signup and confirm your email.
- Open the dashboard and choose Create key.
- Copy the secret. It starts with
duck-and is shown once; afterwards the dashboard keeps only its prefix.
Store the key in the environment, never in code:
Every request carries it as Authorization: Bearer $ZDR_API_KEY or as x-api-key. When both are present, the first duck- credential wins.
Add credit#
A new account has no balance, so a request is refused with 402 insufficient_balance before it reaches a model. Choose Top up in the dashboard, pay the amount in crypto to the address the screen shows, and the balance moves when the transfer reaches the confirmations that asset requires. There is no subscription and no renewal: the balance is the only limit.
Zero retention explains what is kept, and Errors lists the refusal codes.
Send your first request#
The answer is an OpenAI chat.completion: choices[0].message.content, a usage object and the response headers.
Stream the answer#
Set stream to true and the answer arrives as server-sent events, one JSON chunk per line, closed by data: [DONE].
The Anthropic format#
The same conversation through the Messages endpoint. The base URL is the origin and the request carries anthropic-version:
The endpoints#
| Method | Path | Purpose |
|---|---|---|
| POST | /v1/chat/completions | OpenAI Chat Completions |
| POST | /v1/messages | Anthropic Messages |
| POST | /v1/responses | OpenAI Responses, stateless |
| POST | /v1/embeddings | OpenAI Embeddings |
| POST | /v1/messages/count_tokens | Input token count of a Messages payload |
| POST | /v1/responses/input_tokens | Input token count of a Responses payload |
| GET | /v1/models | The models the key may use |
| GET | /v1/usage | Balance and spend of the day |
| POST | /v1/tools/search | Web search, one to four queries |
| GET | /v1/tools/search | Whether search is available |
| POST | /v1/tools/browse | A public page's readable text, free |
| POST | /mcp | The memory vault over MCP, for a key with MCP on |
The audio, image and rerank endpoints are not served: they answer 404 endpoint_not_supported. The API host also answers GET /api/hello as a warm-up probe, and the dashboard host serves its own /api/* routes.
Headers on the answer#
| Header | Meaning |
|---|---|
x-request-id | The request id, quote it in a support message |
x-token-cost | What the request cost, in EUR (a trailer on a stream) |
x-ratelimit-* | The key's limits, what is left and when it resets |
x-token-cache | hit when the response cache answered |
x-token-notice | What the service changed or left out |
x-ai-generated, x-ai-model | The answer is AI-generated, and the model that wrote it |
retry-after | On a 429 or a 503, the seconds to wait |
Next#
- Chat completions, Messages and Responses for the parameters of each format
- Models for ids, limits and prices
- Errors for the shape of a non-200 answer
- Claude Code, opencode or Hermes Agent to put an agent on the key