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.
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.
Base URL https://api.synavion.in/v1
Header Authorization: Bearer sk-syn-live-…
Body application/jsonKeys 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.
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.
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
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 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.
{
"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.
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 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 https://api.synavion.in/v1/me \
-H "Authorization: Bearer $SYNAVION_API_KEY"{
"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.
| Status and code | What happened | What to do |
|---|---|---|
| 400 invalid_request | The 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_key | No 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_key | The 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_balance | The 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_found | That 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_type | The request did not arrive as JSON. Form encodings are not accepted. | Send `Content-Type: application/json`. |
| 429 rate_limit_exceeded | A 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_priced | The 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.
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 admittedMeasured 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.