Your scripts, our models.
One key, one endpoint that existing OpenAI clients already speak, and your Vena Token balance as the single billing unit. Included with the Pro and Expert plans.
What to know before writing a line.
- You need a Pro or Expert plan
- The key is included from Pro upwards. A free or Starter account receives 403 PREMIUM_REQUIRED, both when creating a key and when calling.
- Calls debit YOUR wallet
- Every call spends Vena Tokens from the key owner's balance, at the same multipliers as the chat. Nothing is billed separately.
- The secret is shown once
- We store only its fingerprint: we are technically unable to show it to you again. A lost key is replaced by a rotation.
- An exposed key is bounded, not unlimited
- Per-key rate limit, plus a circuit breaker on volume and on spend. Beyond that the key disables itself and you get an email.
One header, and nothing else.
The key travels in the Authorization HTTP header, Bearer scheme. No other channel is accepted.
Authorization: Bearer vena_sk_VOTRE_CLEFour rules, all of them about security
- Never in a URL. A query parameter ends up in the server's access logs, in the proxy's, in the Referer header and in the browser history. We refuse that authentication mode outright: it answers 401.
- Never in browser JavaScript. The API publishes no CORS configuration, precisely to prevent that use: a key in a web page is a published key.
- One key per environment. 5 active keys per account: enough to separate production, staging and a developer machine, and to cut one without cutting the others.
- Revocation is immediate. It takes effect on the next call, with no propagation delay and no cache.
Two routes, that's all.
https://api.venalabs.example/api/ai/v1| Method | Path | Purpose | Required scope |
|---|---|---|---|
| GET | /api/ai/v1/models | The model catalogue, with the class, multiplier and weights applied. Costs nothing: this is the route you use to estimate before calling. | MODELS |
| POST | /api/ai/v1/chat/completions | One completion, streamed or not depending on the stream field. This is the only route that spends. | CHAT |
Copy, paste, run.
The request format is the OpenAI one: most existing SDKs work by changing the base URL and the key.
curl https://api.venalabs.example/api/ai/v1/chat/completions \
-H "Authorization: Bearer vena_sk_VOTRE_CLE" \
-H "Content-Type: application/json" \
-d '{
"model": "vena-eco",
"messages": [{"role": "user", "content": "Résume ce texte en trois points."}],
"max_tokens": 500
}'Two accountings, served together.
The usage block carries the MODEL's tokens (what any OpenAI client already reads) and, prefixed with vena_, what was actually taken from your wallet. The two numbers differ by a factor of 1 to 80 depending on the class: confusing them is what produces a surprise bill.
{
"id": "3f2a1c8e-…",
"object": "chat.completion",
"model": "vena-eco",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "…" },
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 412,
"completion_tokens": 168,
"total_tokens": 580,
"vena_tokens_charged": 271,
"vena_model_class": "ECO",
"vena_multiplier": 1,
"vena_balance_after": 7999729,
"vena_usage_missing": false
}
}The fields that are ours
- vena_tokens_charged — Vena Tokens actually debited, multiplier applied. This is the number that matters.
- vena_model_class — the class applied (ECO, STANDARD, ADVANCED, FRONTIER).
- vena_multiplier — the multiplier in force when the quote was computed.
- vena_balance_after — your spendable balance after this debit, so you can keep a counter without a second call.
- vena_usage_missing — true when our gateway could not confirm consumption and the debit falls back on an estimate. Those movements are flagged and refundable.
Request fields taken into account
model, messages, max_tokens and stream. Any other field is silently ignored: we would rather ignore it than declare it without honouring it — setting a temperature that changed nothing would be worse than its absence.
data: lines, then [DONE].
With stream set to true, the response arrives as Server-Sent Events, in the protocol OpenAI SDKs expect: data: fragments with no event name, a final fragment carrying finish_reason and the complete usage block, then the data: [DONE] sentinel.
data: {"id":"3f2a…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant"}}]}
data: {"id":"3f2a…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"Trois "}}]}
data: {"id":"3f2a…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"vena_tokens_charged":271,"vena_balance_after":7999729}}
data: [DONE]A keep-alive comment (: hb) is sent every 200 ms. SSE clients ignore it; it is indispensable behind a proxy, which drops an idle connection after 30 to 60 seconds.
If you cut the connection mid-generation, you are charged for what the model actually produced — not for what you received, not for the reservation. The other two bases are wrong, one in each direction.
Estimate your bill before the first call.
A call does not cost a number of tokens: it costs a number of Vena Tokens, which depends on the model class. Here is everything you need to work it out yourself.
The formula, in full
cost = ceiling( (input_tokens × 0.25 + output_tokens × 1.0) × multiplier )
The ceiling is applied once, at the very end. Output weighs four times input, which reflects provider pricing ratios. Reasoning tokens count as output; cache-served tokens count as input.
Multipliers by class
- ECO
- ×1
- STANDARD
- ×5
- ADVANCED
- ×25
- FRONTIER
- ×80
Editable by our administration. A change applies to the next quote, never retroactively: every movement in your ledger freezes the weights and multiplier that were applied to it. The /models route always serves the values in force.
A concrete example
A call with 2,000 input tokens and 800 output tokens costs 1,300 Vena Tokens on Eco, 6,500 on Standard, 32,500 on Advanced and 104,000 on Frontier. Against the Pro monthly quota (8M) that is roughly 6,150 calls on Eco, or 77 on Frontier.
Rounded orders of magnitude, given as guidance: your real consumption depends on how long your exchanges are. Only the vena_tokens_charged field of each response is authoritative.
The order your tokens are spent in
Your balance is made of three pockets: tokens earned by completing courses, your subscription's monthly quota, and purchased top-ups. Spending goes by increasing expiry date — the pocket that expires soonest goes first.
That order is the only one that is never against you: we never let tokens expire that could have been spent.
Purchased top-ups are valid for 365 days and tokens earned from courses for 90 days. The subscription's monthly quota expires at the end of the period, with no rollover.
Per-request caps
A subscriber call is capped at 8,000 output tokens. That cap bounds the cost of the worst call: it protects you from a generation that loops, and us from an unpredictable provider bill. A higher max_tokens is accepted, then brought back to the cap.
What stops a leaked key.
Your balance bounds total spending, never throughput. A key accidentally pushed to a Git repository would respect your quota perfectly while draining it overnight. Three safeguards take care of that.
Per-key rate — 60 calls/minute
Beyond that, 429 RATE_LIMITED with a Retry-After header. The key stays alive: slowing down is not condemning. A correct client backs off on the first refusal.
Per-account rate — 30 calls/minute
It applies to everything the account consumes, chat included. Holding five keys therefore does not multiply the allowed throughput: this is the ceiling that governs.
Circuit breaker — the key disables itself
Over a 10-minute window, three signals are watched and one is enough: call volume, persistence after refusals, and above all spend. Beyond that the key becomes SUSPENDED, subsequent calls receive 401, and you get an email naming the key concerned and the reason.
A disabled key is never re-enabled. If its secret is out there, re-enabling it would simply reopen the door: the correct answer is a rotation, which mints a new secret and kills the old one.
What you receive, and why.
Errors are in the VenaLabs format, not the OpenAI one: {code, message, details}. That is deliberate — a refusal for insufficient balance must be unambiguously distinguishable from any other incident, and the OpenAI format has no such notion.
| Status | Code | When | What to do |
|---|---|---|---|
| 400 | VALIDATION_ERROR | Invalid request body. details carries one message per field. | Fix the reported field. |
| 400 | MODEL_UNKNOWN | Model missing or withdrawn from the catalogue. | Re-read /models: it is the only authoritative list. |
| 401 | API_KEY_INVALID | Key unknown, revoked, disabled or out of scope. All four cases return exactly the same response. | Check the key, then create a new one from your settings. |
| 402 | INSUFFICIENT_TOKENS | The balance does not cover the quote for this call. Nothing is called, nothing is debited. | Buy a top-up, or wait for your quota to renew. |
| 402 | QUOTA_EXCEEDED | A plan ceiling has been reached. | Read details for the limit concerned and its reset date. |
| 403 | PREMIUM_REQUIRED | The account's plan no longer opens the developer API. | Resume a Pro or Expert subscription. Existing keys become usable again straight away. |
| 403 | MODEL_NOT_ALLOWED | The requested class is not open to the account's plan. | Change class, or change plan. |
| 413 | CONTEXT_TOO_LARGE | The conversation exceeds the model's context window. | Shorten the history, or pick a model with a larger context. |
| 429 | RATE_LIMITED | Rate exceeded, or circuit breaker tripped. details.keyDisabled is true in the second case. | Respect Retry-After. If the key was disabled, rotate it. |
| 503 | AI_UNAVAILABLE | The inference gateway is momentarily unavailable. | Retry. Nothing was debited: a call that did not go through is returned in full. |
The limits, stated up front.
- One choice per call: the n field is not supported.
- No tools, no function calling, no structured outputs: the tools field is ignored.
- No images, no input files: messages takes text only.
- No temperature, no top_p: those settings are not exposed while they are not honoured.
- Model identifiers are VenaLabs identifiers. They name no provider.