Developer API

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.

In three minutes

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.
Authentication

One header, and nothing else.

The key travels in the Authorization HTTP header, Bearer scheme. No other channel is accepted.

Header example
Authorization: Bearer vena_sk_VOTRE_CLE

Four 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.
Endpoints

Two routes, that's all.

Base URLhttps://api.venalabs.example/api/ai/v1
Endpoints
MethodPathPurposeRequired scope
GET/api/ai/v1/modelsThe 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/completionsOne completion, streamed or not depending on the stream field. This is the only route that spends.CHAT
First call

Copy, paste, run.

The request format is the OpenAI one: most existing SDKs work by changing the base URL and the key.

curl
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
  }'
Response

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.

200 OK
{
  "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.

Streaming

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.

text/event-stream
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.

What it costs

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.

Limits and safeguards

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.

Error codes

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.

Error codes
StatusCodeWhenWhat to do
400VALIDATION_ERRORInvalid request body. details carries one message per field.Fix the reported field.
400MODEL_UNKNOWNModel missing or withdrawn from the catalogue.Re-read /models: it is the only authoritative list.
401API_KEY_INVALIDKey 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.
402INSUFFICIENT_TOKENSThe 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.
402QUOTA_EXCEEDEDA plan ceiling has been reached.Read details for the limit concerned and its reset date.
403PREMIUM_REQUIREDThe account's plan no longer opens the developer API.Resume a Pro or Expert subscription. Existing keys become usable again straight away.
403MODEL_NOT_ALLOWEDThe requested class is not open to the account's plan.Change class, or change plan.
413CONTEXT_TOO_LARGEThe conversation exceeds the model's context window.Shorten the history, or pick a model with a larger context.
429RATE_LIMITEDRate exceeded, or circuit breaker tripped. details.keyDisabled is true in the second case.Respect Retry-After. If the key was disabled, rotate it.
503AI_UNAVAILABLEThe inference gateway is momentarily unavailable.Retry. Nothing was debited: a call that did not go through is returned in full.
What this API does not do

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.