API keys and the Rayu API
Models and providers

API keys and the Rayu API

15. API keys and the Rayu API#

This page is for your own code calling Rayu-hosted models with a Rayu API key. One key works across every model your plan allows — you choose the model on each request.

If you are looking for how to connect an external provider's key (Anthropic, OpenAI, Google, etc.) to the Rayu CLI, see Providers. That is a different credential and is not covered here.

Call Rayu from your own code#

Everything you need for the first request: the base URL, the credential header, and the two wire formats.

SettingValue
Model API base URLhttps://gateway.rayucode.com
OpenAI-compatible basehttps://gateway.rayucode.com/v1
Anthropic-compatible basehttps://gateway.rayucode.com
API key formatrayu_sk_live_…
AuthenticationAuthorization: Bearer <key> or x-api-key: <key>
FormatsOpenAI Chat Completions and Anthropic Messages
Available onPro, Plus, and Max plans

The accounts API lives at https://api.rayucode.com/api (dashboard, billing, key management). Model traffic always goes to https://gateway.rayucode.com; do not send an API key to the accounts API.

Create an API key#

  1. Sign in at https://rayucode.com.
  2. Open Dashboard → API Keys (https://rayucode.com/dashboard/api-keys).
  3. Choose Generate API Key, give it a name such as ci-agent or production-backend, and copy the key.

Copy the key once at creation — it is shown once, and Rayu stores only a SHA-256 hash of it. Export it in your shell:

bash
export RAYU_API_KEY="rayu_sk_live_your_key_here"

A lost key cannot be recovered; rotate it or create a replacement instead.

API access is a paid-plan feature. Free and Basic accounts cannot create keys; see Subscription plans.

Key controls#

A key can be narrower than the account that owns it:

ControlWhat it does
NameA label for dashboards and usage reports.
Model allowlist (allowedModels)Empty means every model the plan allows. A non-empty list restricts the key to those model ids.
Credit cap (creditLimit)Optional per-key credit ceiling per billing period, across every model the key uses.
Rate limit (rateLimitRpm)Optional requests-per-minute ceiling for this key.
Expiry (expiresAt)Optional date after which the key stops working.

You can hold up to 10 active keys. Rotating a key mints a new secret and retires the old one; choose a grace window (up to 7 days) if callers cannot be redeployed at once. Revoking a key takes effect on the next request.

Store it safely#

  • Keep the key in an environment variable or secret manager — never in source code, RAYU.md, settings files, or a commit.
  • Use a separate key per application or environment so one can be revoked without breaking the others.
  • If a key leaks, revoke it immediately and create a replacement.
  • Keys act as the owning user with normal user privileges. They cannot call admin endpoints.

One key, many models#

A Rayu API key is account-scoped, not model-scoped. The same key can call any model that both your plan and the key's allowlist permit. You select the model per request with the model field — there is no separate key, token, or endpoint per model.

What you wantHow to do it
Use every model on your planCreate the key without a model allowlist.
Restrict a key to a few modelsSet allowedModels when creating or editing the key.
Change models between requestsSend a different model id to the same endpoint.
Discover what a key may callGET /v1/models with that key.
Widen or narrow access laterEdit the key's allowlist in the dashboard; changes apply immediately.

GET /v1/models returns the intersection of your plan's catalog and the key's allowlist. A key with no allowlist sees the whole plan catalog; a scoped key sees only its own models. A model outside the allowlist returns 403 — even if the plan includes it.

Example: one key, three models#

The same chat request in three languages, each selecting a different model with the same key. Only the model value changes.

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-v4.1-flash-cloud",
    "messages": [{"role": "user", "content": "Summarize this error log."}]
  }'

Model ids must match an id from GET /v1/models exactly. Short aliases such as sonnet or opus are not accepted by the API.

Example: route by role with one key#

python
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["RAYU_API_KEY"],
    base_url="https://gateway.rayucode.com/v1",
)

MODELS = {
    "fast": "deepseek-v4.1-flash-cloud",
    "long_context": "longcat-2.5",
    "review": "us.anthropic.claude-sonnet-4-6",
}

def complete(prompt: str, role: str = "fast") -> str:
    response = client.chat.completions.create(
        model=MODELS[role],
        messages=[{"role": "user", "content": prompt}],
    )
    return response.choices[0].message.content

This is the entire multi-model design: one credential, model chosen per request. Rayu does not expose a separate auto-router endpoint; if you want fallback or cost-based routing, implement it in your client with the same key.

python
import openai

def complete_with_fallback(prompt: str) -> str:
    for model in MODELS.values():
        try:
            response = client.chat.completions.create(
                model=model,
                messages=[{"role": "user", "content": prompt}],
            )
            return response.choices[0].message.content
        except openai.APIStatusError:
            continue
    raise RuntimeError("all models failed")

Discover models#

GET /v1/models returns the models the calling key may use, in OpenAI list shape.

bash
curl https://gateway.rayucode.com/v1/models \
  -H "Authorization: Bearer $RAYU_API_KEY"

The response is an OpenAI-style list:

json
{
  "object": "list",
  "data": [
    {
      "id": "deepseek-v4.1-flash-cloud",
      "object": "model",
      "created": 1700000000,
      "owned_by": "rayu",
      "label": "Deepseek-v4.1-flash",
      "description": "Fast general-purpose model",
      "supportsReasoning": true,
      "supportsImage": true,
      "supportsTools": true,
      "contextWindow": 1000000
    }
  ]
}
FieldMeaning
idThe exact value to send as model.
labelHuman-readable display name.
descriptionAdmin-authored guidance for choosing the model.
supportsReasoningModel can emit hidden reasoning.
supportsImageModel accepts image input.
supportsToolsModel supports tool/function calling.
contextWindowMaximum context in tokens, or null if unset.

Use this endpoint at runtime instead of hardcoding a catalog — model ids, availability, and per-plan access can change.

Current hosted chat catalog#

Snapshot taken from production on 3 November 2026. What GET /v1/models returns for your key depends on your plan and its allowlist; this table is for orientation only.

Model idLabelContextReasoningImagesToolsExample
longcat-2.5Long Cat 2.51MyesyesyesReview
DeepSeekdeepseek-v4.1-flash-1017-cloudDeepSeek-V4.1-flash1MyesyesyesReview
mistral-large-4-cloudMistral-large-41MyesyesyesReview
Qwenqwen3.8-flashQwen3.8-flash1MyesyesyesReview
MiniMaxminimax-m3.1-flash-cloudMinimax-m3.1-flash1MyesyesyesReview
kimi-k3.1-proKimi-k3.1-pro1MyesyesyesReview
kimi-for-codingkimi-k2.8-for-coding1MyesyesyesReview
longcat-2Long Cat 2.01MyesyesyesReview
DeepSeekdeepseek-v4.1-flash-cloudDeepseek-v4.1-flash1MyesyesyesReview
glm-5.3-flash-cloudGLM5.3 flash1MyesnoyesReview
glm-5.3-cloudGLM-5.31MyesnoyesReview
us.anthropic.claude-sonnet-4-6Sonnet 4.61MyesyesyesReview
MiniMaxminimax-m3Minimax-M31MyesyesyesReview
kimi-k2.7Kimi-k2.7-code256KyesyesyesReview
glm-5.2GLM-5.21MyesnoyesReview
Qwenqwen3.8-maxQwen3.8-max1MyesyesyesReview
us.anthropic.claude-opus-4-6Opus 4.61MyesyesyesReview
poolside-laguna-s-2.1-freeLaguna-s-2.1262KyesyesyesReview
openai-gpt-oss-20bFree200KyesnoyesReview
stepfun-ai-step-3.7-flashStep-3.7-flash256KyesyesyesReview
openai-gpt-oss-120bGPT-OSS-120b200KyesnoyesReview
nvidia-nemotron-3-ultra-550b-a55bNemotron-3-Ultra-550b-a55b1MyesyesyesReview
nvidia-nemotron-3-super-120b-a12bNemotron-3-Super-120b-a12b1MyesyesyesReview

Click Review on any row to jump to that model's runnable example.

Per-model examples#

Each block below uses the same RAYU_API_KEY and changes only the model field. Vision-capable models (Images: yes) also accept images as base64 data: URIs; the models marked no return 400 on image input. A model your plan or key allowlist does not permit returns 403.

longcat-2.5#

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"longcat-2.5","messages":[{"role":"user","content":"Summarize this release note."}]}'

deepseek-v4.1-flash-1017-cloud#

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"deepseek-v4.1-flash-1017-cloud","messages":[{"role":"user","content":"Hello!"}]}'

mistral-large-4-cloud#

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"mistral-large-4-cloud","messages":[{"role":"user","content":"Draft a migration plan for this schema."}]}'

qwen3.8-flash#

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen3.8-flash","messages":[{"role":"user","content":"Translate this changelog to English."}]}'

minimax-m3.1-flash-cloud#

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"minimax-m3.1-flash-cloud","messages":[{"role":"user","content":"Extract the action items from this ticket."}]}'

kimi-k3.1-pro#

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"kimi-k3.1-pro","messages":[{"role":"user","content":"Refactor this function for readability."}]}'

kimi-for-coding#

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"kimi-for-coding","messages":[{"role":"user","content":"Find the bug in this stack trace."}]}'

longcat-2#

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"longcat-2","messages":[{"role":"user","content":"Summarize this incident report."}]}'

deepseek-v4.1-flash-cloud#

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"deepseek-v4.1-flash-cloud","messages":[{"role":"user","content":"Hello!"}]}'

glm-5.3-flash-cloud#

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"glm-5.3-flash-cloud","messages":[{"role":"user","content":"Explain this SQL query."}]}'

glm-5.3-cloud#

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"glm-5.3-cloud","messages":[{"role":"user","content":"Write unit tests for this function."}]}'

us.anthropic.claude-sonnet-4-6#

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"us.anthropic.claude-sonnet-4-6","messages":[{"role":"user","content":"Review this pull request."}]}'

minimax-m3#

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"minimax-m3","messages":[{"role":"user","content":"Condense this meeting transcript."}]}'

kimi-k2.7#

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"kimi-k2.7","messages":[{"role":"user","content":"Explain this diff."}]}'

glm-5.2#

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"glm-5.2","messages":[{"role":"user","content":"Summarize this RFC."}]}'

qwen3.8-max#

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen3.8-max","messages":[{"role":"user","content":"Analyze this dataset schema."}]}'

us.anthropic.claude-opus-4-6#

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"us.anthropic.claude-opus-4-6","messages":[{"role":"user","content":"Design a caching strategy for this service."}]}'

poolside-laguna-s-2.1-free#

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"poolside-laguna-s-2.1-free","messages":[{"role":"user","content":"Complete this TypeScript function."}]}'

openai-gpt-oss-20b#

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"openai-gpt-oss-20b","messages":[{"role":"user","content":"Answer this support question."}]}'

stepfun-ai-step-3.7-flash#

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"stepfun-ai-step-3.7-flash","messages":[{"role":"user","content":"Rewrite this README section."}]}'

openai-gpt-oss-120b#

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"openai-gpt-oss-120b","messages":[{"role":"user","content":"Explain this error message."}]}'

nvidia-nemotron-3-ultra-550b-a55b#

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"nvidia-nemotron-3-ultra-550b-a55b","messages":[{"role":"user","content":"Summarize this legal contract."}]}'

nvidia-nemotron-3-super-120b-a12b#

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"nvidia-nemotron-3-super-120b-a12b","messages":[{"role":"user","content":"Classify this customer feedback."}]}'

Any id returned by GET /v1/models is valid for your key. If a row's example returns 403, the key's allowlist or the plan does not include that model.

Endpoints#

Chat and models#

MethodPathFormat
POST/v1/chat/completionsOpenAI Chat Completions (streaming and non-streaming)
POST/v1/messagesAnthropic Messages (API-key alias)
POST/anthropic/v1/messagesAnthropic Messages (native Rayu route)
POST/v1/messages/count_tokensAnthropic token counting
GET/v1/modelsModels available to the key — OpenAI list shape

Other model surfaces#

The same key also works for non-chat modalities. Their catalogs are selected on GET /v1/models:

MethodPathCatalog query
POST/v1/images/generationsGET /v1/models?modality=image
POST/v1/videos/generationsGET /v1/models?modality=video
POST/v1/audio/transcriptionsGET /v1/models?modality=audio&operation=stt_batch
POST/v1/audio/speechGET /v1/models?modality=audio&operation=tts_batch
POST/v1/ocrGET /v1/models?modality=ocr
POST/v1/realtime/ticketGET /v1/models?modality=audio&operation=stt_realtime

Chat is the default catalog. modality= selects one modality's catalog; media=image|video|all is the older filter that merges the BYO and hosted generation catalogs. Media generation endpoints are billed per image, per second, or per page rather than per token; the catalog entry carries its own pricing unit.

Account and credit metadata#

MethodPathReturns
GET/v1/creditsPlan, used/remaining credits, top-up balance, reset time, and the key's own limits
GET/v1/credits/topup/quoteCurrent top-up price quote
GET/v1/_whoami{"userId": …, "role": "user"}
GET/v1/_entitlementsPlan, status, allowed models, top-up balance

These are read-only and free.

OpenAI SDK#

Point the SDK's base_url at the versioned gateway root. The same streaming request in three languages:

curl https://gateway.rayucode.com/v1/chat/completions \
  -H "Authorization: Bearer $RAYU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-v4.1-flash-cloud",
    "messages": [{"role": "user", "content": "Hello!"}],
    "stream": true
  }'

OpenAI request support#

Supported: messages, stream, max_tokens / max_completion_tokens, temperature, top_p, stop, tools / tool_choice, and image content on vision models. max_tokens defaults to 4096 if omitted. Images must be sent as base64 data: URIs; remote image_url links are rejected.

Rejected with 400: n greater than 1, logprobs, top_logprobs, seed, and logit_bias.

Add "stream_options": {"include_usage": true} to receive a final usage chunk while streaming.

Anthropic SDK#

Point the Anthropic SDK at the gateway root — the SDK appends /v1/messages itself. The SDK sends the key as x-api-key, which the gateway accepts. The same request in three languages:

curl https://gateway.rayucode.com/v1/messages \
  -H "x-api-key: $RAYU_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-v4.1-flash-cloud",
    "max_tokens": 1024,
    "messages": [{"role": "user", "content": "Hello!"}]
  }'

POST /v1/messages/count_tokens returns {"input_tokens": N} and is free. The count is an estimate; the response carries x-rayu-token-count: estimate.

Responses, cost, and limits#

What a request costs#

The charge is reported differently per surface:

  • Anthropic, non-streaming: a top-level rayu member on the response body.
  • Anthropic, streaming: the message_delta event carries rayu.
  • OpenAI: the standard usage block. For streaming, set stream_options.include_usage to receive the final usage chunk.
  • Anthropic headers: x-rayu-credits-used, x-rayu-credits-remaining (or unlimited), and x-rayu-topup-balance.
  • Both surfaces: GET /v1/credits is the authoritative balance.
json
"rayu": {
  "billableTokens": 2950,
  "tokensPerCredit": 1000,
  "credits": 2.95
}

GET /v1/credits returns the authoritative balance and also echoes the calling key's creditLimit, allowedModels, and rateLimitRpm, so you can show the binding limit rather than guessing.

For the rate math, see How tokens are charged.

Error codes#

StatusMeaning
400Invalid JSON, missing messages, unsupported parameter, or a capability the model lacks (for example, image input on a text-only model).
401Missing, invalid, revoked, or expired API key.
403The model is outside your plan, outside this key's allowlist, or the account is not active.
429A limit was reached. See the reason field.
503The model is temporarily unavailable or the gateway is at capacity; retry after Retry-After.
502The upstream provider failed.

A 401 looks like this:

json
{"error": {"message": "invalid API key", "type": "authentication_error"}}

429 limit reasons#

Every 429 carries a machine-readable reason, so handle the one that actually applies instead of showing a generic "out of credits" message:

json
{
  "error": {"message": "API key rate limit exceeded", "type": "rate_limit_exceeded"},
  "reason": "api_key_rpm_limit",
  "resetSeconds": 60
}
reasonWhat hit the limit
api_key_rpm_limitThis key's requests-per-minute cap. Retry-After: 60.
api_key_credit_limitThis key's credit cap for the billing period. Retry-After: 3600.
daily_turn_limitThe plan's daily turn cap.
concurrencyToo many in-flight requests; transient, retry in about 2 seconds.
requestsShort-window request rate; retry in about a minute.
period_limitThe account's credit allowance for the period is exhausted.
weekly_limit / session_window_limitPlan pacing window reached; credit may remain.
bucket_limit / pool_limitA team wallet limit.

Most 429 responses also send Retry-After. Some carry an x-rayu-limit header with the same reason. Retry with backoff; do not retry 400 or 403.

Security checklist#

  • Keep keys server-side. Never ship one in a browser, mobile app, or public repository.
  • Use a distinct key per service and environment.
  • Set a model allowlist, credit cap, rate limit, and expiry for untrusted or automated callers.
  • Rotate on a schedule and after any suspected exposure.
  • Revoke immediately from the dashboard if a key leaks.