cyankiwi Inference API (api.cyan.kiwi)
The cyankiwi Inference API serves models optimized using cyankiwi’s proprietary methods behind an OpenAI-compatible endpoint: any OpenAI SDK or tool (SDK, curl, LangChain, etc.) works by pointing it at our base URL with your API key.
Base URL: https://api.cyan.kiwi/v1 1. Get an API key
Sign in and open the Dashboard, then click Create API key. The key is shown once, so store it somewhere safe (e.g. an environment variable, never in client-side code).
Every account comes with prepaid credits and per-minute rate limits (60
requests / 100,000 tokens per minute by default), shown on the dashboard.
Inference requests deplete your credits; when they run out, requests return a
budget_exceeded error (HTTP 429). Top up from the dashboard, or contact us.
2. List models
The model catalog is public: no API key required (one sent along is simply ignored), and responses are cacheable for five minutes.
curl https://api.cyan.kiwi/v1/models The response is a standard OpenAI model list whose items carry extra metadata you can rely on programmatically:
| Field | Meaning |
|---|---|
id | the value to pass as model in requests below |
context_length, max_output_length | token budgets of the deployment |
pricing | USD per token as exact decimal strings: prompt, completion, input_cache_read |
quantization | precision of the served weights (fp8, int4, …) |
input_modalities, output_modalities | what the deployment accepts and returns |
- Cached input: when the provider reports a prompt-cache hit, the cached
input tokens bill at
input_cache_readinstead ofprompt, and the hit shows up asusage.prompt_tokens_details.cached_tokenson the response. Caching is block-granular (128-token blocks on current models) and a hit is never guaranteed on any individual request — treat it as an opportunistic discount, not something you can plan against.
Official SDKs parse it as usual (client.models.list()), and the same ids
are shown on the dashboard’s Inference tab.
3. Chat completions
Use any id from the model list as model; the examples below use
deepseek-v4-flash.
curl https://api.cyan.kiwi/v1/chat/completions \
-H "Authorization: Bearer $CYANKIWI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4-flash",
"messages": [{"role": "user", "content": "Hello!"}]
}' Streaming works the standard OpenAI way: add "stream": true and consume
server-sent events:
curl -N https://api.cyan.kiwi/v1/chat/completions \
-H "Authorization: Bearer $CYANKIWI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4-flash",
"messages": [{"role": "user", "content": "Count to five."}],
"stream": true
}' Three wire behaviors are guaranteed on every stream. Official SDKs handle all of them already; hand-rolled SSE parsers should expect them:
- Usage is always reported. The final data chunk before
data: [DONE]carries ausageblock (with an emptychoicesarray), even if you never setstream_options.include_usage. This is standard OpenAI wire format. - Keep-alive comments. During long silent phases (prefill, reasoning)
the stream carries SSE comment lines (
: keepalive). Comments are part of the SSE spec and standard parsers skip them. - No silent cuts. If the engine fails mid-stream you receive a
structured error event (
"code": "mid_stream_error") followed bydata: [DONE]instead of a dropped connection. Treat the reply as truncated.
Reasoning controls
Reasoning models accept the common client spellings for reasoning control —
an OpenRouter-style reasoning object ({"enabled": true|false}, or
{"effort": "low"|"medium"|"high"}), OpenAI-style reasoning_effort, and
include_reasoning — and we translate them into whatever dialect the
underlying deployment actually understands, per model. Reasoning content
comes back untouched in the response’s reasoning field. Two documented
degradations, which never error: reasoning.max_tokens is not honored
(the engine dialect has no token budget for it), and
exclude/include_reasoning: false does not strip reasoning from the
response (we never rewrite response bytes) — if you do not want reasoning
content, disable reasoning rather than asking us to hide it.
Request limits
A request that exceeds one of these is rejected, not trimmed. It never runs and it is never charged.
| limit | applies to | if exceeded |
|---|---|---|
| Request body 2 MiB | all inference routes | 413 request_too_large |
n and best_of at most 8 | chat + completions | 400 too_many_completions |
prompt must be text — a string, or a list of strings | /v1/completions | 400 token_ids_not_supported |
chat_template_kwargs may not carry chat_template or tokenize | chat | 400 template_override_not_permitted |
JSON schemas must be valid: every node an object, every value under properties a schema or a boolean | response_format, tools | 400 invalid_json_schema |
regex, ebnf, guided_json, guided_regex, guided_grammar, guided_choice, guided_whitespace_pattern, guided_decoding_backend, structural_tag, prompt_embeds are not accepted | all | 400 unsupported_parameter |
Every rejection names the offending field in the param key of the error
envelope, so an SDK surfaces it directly.
Text completions are not currently served. /v1/completions answers 400
completions_not_supported unless a model declares support for it, and none
does today. Use /v1/chat/completions. The bounds in the table above still
apply to the route, and are checked before that refusal. /v1/embeddings
works the same way.
Token log-probabilities are not supported. logprobs, top_logprobs and
prompt_logprobs are rejected with 400 logprobs_not_supported rather than
silently ignored, on every model. This is the honest form of something the
model catalog already tells you: logprobs has never
appeared in any model’s supported_features.
JSON mode and structured outputs are unaffected. response_format —
both json_object and json_schema — is forwarded untouched, with no limit
on schema size, depth or keywords, including $ref, $defs and pattern.
The only rule is that the schema is valid JSON Schema. The list above is a set
of engine-specific fields that have no place in the OpenAI API; it is not a
restriction on structured output.
This section is different from the one below. That section is about parameters we forward: an unsupported sampling parameter may be silently dropped, and you get a 200 without the behaviour. The limits here are the exception — these we check ourselves, and we reject rather than forward.
Unsupported parameters may be dropped
The gateway forwards unknown or unsupported sampling parameters toward the
engine, and a parameter the model does not support may be silently
dropped rather than rejected — you get a 200 without the behavior you
asked for, not a 400. The supported_sampling_parameters and
supported_features lists on the model catalog are the
source of truth per model; if an effect matters to your application, verify it
by its effect (a stop sequence stopping, JSON parsing as JSON) rather than by
the absence of an error.
4. Python (OpenAI SDK)
from openai import OpenAI
client = OpenAI(
base_url="https://api.cyan.kiwi/v1",
api_key="sk-...", # your cyankiwi API key
)
print([m.id for m in client.models.list()])
resp = client.chat.completions.create(
model="deepseek-v4-flash",
messages=[{"role": "user", "content": "Hello!"}],
)
print(resp.choices[0].message.content)
# streaming
for chunk in client.chat.completions.create(
model="deepseek-v4-flash",
messages=[{"role": "user", "content": "Count to five."}],
stream=True,
):
if chunk.choices:
print(chunk.choices[0].delta.content or "", end="") Limits and errors
Errors use the standard OpenAI envelope ({"error": {"message", "type", "code"}}), so SDK exception handling works unchanged:
| Situation | Status | identifier |
|---|---|---|
No Authorization header on inference | 401 | code: missing_api_key |
| Revoked / unknown key | 401 | type: token_not_found_in_db (code is the numeric status) |
| Credits exhausted | 429 | type: budget_exceeded (code is the numeric status) |
| Per-minute rate limit hit | 429 | rate-limit message; the window resets within a minute |
| Server at capacity | 429 | code: server_overloaded, with Retry-After |
| Unknown model | 400 | invalid_request_error |
| Body over the size limit | 413 | code: request_too_large |
| Body is not a JSON object | 400 | code: invalid_request_body |
| A request limit exceeded | 400 | too_many_completions, token_ids_not_supported, logprobs_not_supported, unsupported_parameter, template_override_not_permitted, invalid_json_schema |
| Gateway temporarily unreachable | 503 | upstream_unavailable, with Retry-After |
| Upstream request failed | 502 | upstream_error |
| Engine failed mid-stream | 200 (stream) | mid_stream_error event, then [DONE] |
We shed load instead of queueing: at capacity you get an immediate
server_overloaded 429 rather than a slow response. It, and the 5xx rows,
are transient; honor Retry-After and retry.
Key rotation. Rotating on the dashboard revokes the old key immediately and issues a new one; your credits and rate limits carry over. Revoking without rotating just disables API access; remaining credits are kept for your next key.