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_read instead of prompt, and the hit shows up as usage.prompt_tokens_details.cached_tokens on 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 a usage block (with an empty choices array), even if you never set stream_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 by data: [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.