Calling the API

The gateway speaks the OpenAI chat-completions protocol, so an OpenAI client library talks to it after you change one setting. Everything below was run against the live endpoint before it was written here.

LINK ESTABLISHED · IN-DELHICATALOGUE LOADED · EVERY LANE BETANO SLA · DEV AND TEST ONLY

Base URL and key

One base URL, one header. The key is issued with your account and is shown once, when it is created — nothing can print it again, so a lost key is rotated rather than recovered.

Settings
Base URL   https://api.synavion.in/v1
Header     Authorization: Bearer sk-syn-live-…
Body       application/json

Keys are issued, rotated and revoked on the API keys page. A rotated key keeps working for a stated grace period so a running deployment can be moved across; a revoked key stops within a second, everywhere.

Issue or rotate a key

The OpenAI SDK, unmodified

The stock OpenAI Python library works against this endpoint with no patching and no wrapper. Version 2.24.0 was used for every example on this page, streamed and not.

Python
pip install openai

from openai import OpenAI

client = OpenAI(
    base_url="https://api.synavion.in/v1",
    api_key="sk-syn-live-…",
)

Any client that lets you set a base URL works the same way — the wire protocol is the only thing this depends on.

A first call

Name a model by its slug. The slugs are the ones in the catalogue, and a slug is the only model identifier this API accepts.

The same call, without a library

Python
resp = client.chat.completions.create(
    model="synavion/deepseek-v4-flash",
    messages=[{"role": "user", "content": "Say OK."}],
    max_tokens=64,
)
print(resp.choices[0].message.content)
curl
curl https://api.synavion.in/v1/chat/completions \
  -H "Authorization: Bearer $SYNAVION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"synavion/deepseek-v4-flash","messages":[{"role":"user","content":"Say OK."}],"max_tokens":64}'

What comes back

The ordinary chat-completions shape. Two things about it are worth stating because they are the two a client is most likely to depend on.

JSON
{
  "id": "chatcmpl-…",
  "object": "chat.completion",
  "model": "<the slug you asked for>",
  "choices": [{ "index": 0, "message": { "role": "assistant", "content": "OK." },
                "finish_reason": "stop" }],
  "usage": { "prompt_tokens": 7, "completion_tokens": 3, "total_tokens": 10 }
}
  • `model` is the slug you sent, not the name the machine behind it uses for its own weights. Feeding it back into the next request is safe, which is what a client library does when you ask it to pin the model that answered.
  • `system_fingerprint` is not returned. It described the serving engine's build, which is not part of anything sold here, and a value nobody can act on is worse than an absent one.
  • `usage` is what the request is billed on. The same numbers reach your account history, and the charge is derived from them and the rate that was pinned when the request was admitted.

Streaming

Set `stream` and read the events as they arrive. Tokens are forwarded as the machine produces them; nothing is buffered until the end.

Python
with client.chat.completions.stream(
    model="synavion/deepseek-v4-flash",
    messages=[{"role": "user", "content": "Count to three."}],
    max_tokens=64,
) as stream:
    for event in stream:
        if event.type == "content.delta":
            print(event.delta, end="", flush=True)

A streamed request is billed from the counts the machine reports in its final event. If you disconnect halfway, you are billed for what was generated before you left and nothing after it. Ask for the final counts with `stream_options: {"include_usage": true}` and they arrive in the last event; without it, that event is not sent to you.

Listing what you can call

No key is needed for this one. It is the record of what exists, what each model will accept, and what it costs — this page deliberately copies none of those numbers, because a rate is changed in the database without this page being rebuilt.

curl
curl https://api.synavion.in/v1/models
  • `id` — the slug. The only name a request may use.
  • `context_length` and `max_completion_tokens` — the two ceilings a request is checked against.
  • `supported_parameters` — the sampling knobs that model accepts.
  • `pricing` — `inr_per_mtok_in` and `inr_per_mtok_out`, the two rates a call is charged at, plus the id of the exact rate row your call would be billed against.
  • `available` and `unavailable_reason` — whether a call would be served right now. A model with no `pricing` object reports `no_published_price` here and is refused rather than served unbilled.

Only models carrying a `pricing` object can be called today. The others are listed because they exist and are served, not because they are ready to sell.

Your account, from the API

What is left, what is reserved by requests still running, what your limits are, and what you have spent this month.

curl
curl https://api.synavion.in/v1/me \
  -H "Authorization: Bearer $SYNAVION_API_KEY"
JSON
{
  "object": "user",
  "api_key": { "prefix": "…", "scopes": ["inference"] },
  "wallet": { "currency": "INR", "balance_inr": "…", "held_inr": "…",
              "available_inr": "…", "active_holds": … },
  "limits": { "requests_per_minute": …, "tokens_per_minute": … },
  "usage": { "since": "…", "requests": …, "prompt_tokens": …,
             "completion_tokens": …, "spend_inr": "…" }
}

This endpoint costs one request against your own per-minute limit, so it is not something to call before every completion. The account pages show the same figures without spending a request.

When it refuses

Every refusal is the shape an OpenAI client already understands — an `error` object with a `message`, a `type` and a `code` — and every message names the next step rather than only the problem. The `code` is the field to branch on.

Every refusal this endpoint returns, with the body it returns it in
Status and codeWhat happenedWhat to do
400 invalid_requestThe body is not the shape the endpoint takes — a missing `messages`, a `messages` that is not a list of objects with a `role`.Fix the body. The message names the field it could not read.
401 invalid_api_keyNo key, a key that does not parse, or a key this gateway has never seen. The answer is the same for all three, deliberately.Check the header is `Authorization: Bearer` and the key is the whole string, including the part after the dot.
401 revoked_api_keyThe key existed and was revoked. Revocation takes effect within a second, on every process.Issue a new key. A revoked key never comes back, and rotating one is refused.
402 insufficient_balanceThe reservation for this request is larger than the balance left after other requests still running. The reservation is the worst case: the prompt, plus the whole output allowance.Send a `max_tokens` you actually need — with none, the reservation is the model's entire output ceiling, which is far more than an ordinary answer costs. `available_inr` and `required_inr` on the response are the two numbers compared.
404 model_not_foundThat slug is not in the catalogue. The commonest cause is sending a name that came from somewhere other than this API.Take the exact `id` from `GET /v1/models`.
415 unsupported_media_typeThe request did not arrive as JSON. Form encodings are not accepted.Send `Content-Type: application/json`.
429 rate_limit_exceededA per-minute limit was reached. The message names which one — your key, your account, or the whole gateway.Wait the number of seconds in `Retry-After`. A refused request is never billed and never counted against you a second time.
503 model_not_pricedThe model is real and the request was fine; there is no rate to bill it at, and it will not be served unbilled.Retrying will not help — a rate is published by a person, not by time passing. Use a model that carries a `pricing` object.

Rate limits

Three ceilings apply to every request, narrowest first: your key, your account, and the gateway as a whole. The one you will meet is your key's.

  • A new key is allowed 60 requests and 200,000 tokens a minute. Both are columns on the key, so yours may differ: read the numbers that actually apply to you off the headers below.
  • Every response, including a refusal, carries `x-ratelimit-limit-requests`, `x-ratelimit-remaining-requests` and `x-ratelimit-reset-requests`, and the token trio beside them. Read the limit from there rather than assuming it.
  • The window slides rather than resetting on the minute, so a burst cannot be doubled by sending it across a boundary.
  • `x-request-id` is on every response. Quote it in an email about a request and it can be found.

What the trial credit covers

Every account starts with one grant of trial credit, once per mailbox. It is the only balance an account has: there is no way to add a balance, no card to enter, and nothing on this site takes money. When the grant is spent, requests answer 402 and nothing else about the account changes.

Arithmetic
charge = prompt_tokens / 1,000,000 x inr_per_mtok_in
       + completion_tokens / 1,000,000 x inr_per_mtok_out

rounded to six decimal places, at the rate pinned when the request was admitted

Measured on 2026-08-20, against the rate published that day and the standard grant: about 38,000 calls of roughly 1,000 tokens in and 500 out. Both inputs can change without this page changing, so do the multiplication above with today's numbers if the answer matters — the rates are in `GET /v1/models` and what is left of your grant is in `GET /v1/me`.

It is not a budget to ration. A test suite that runs a few thousand completions will not come close to the end of it.

What this does not do yet

  • There is no way to pay. Pass 3 owns payments and none of that code exists yet, so an account that spends its grant stops and stays stopped.
  • One model has a published rate. The rest of the catalogue is listed, and answers `model_not_priced` until a rate is published for it.
  • Every lane is BETA and there is no SLA. This is scoped to development and testing, and one node in Delhi serves all of it.
  • There is no request log or usage graph in the API beyond `GET /v1/me`. The account pages carry the recent requests and what each was charged.

Every command on this page was run against the live endpoint on 2026-08-20, and every refusal in the table is a body it returned.