Authentication and API keys
Every /v1 endpoint except GET /v1/models needs an API key. A key starts with kn-, and one key works for every model and every API format.
Create a key
Section titled “Create a key”Open API Keys in the dashboard and select Create API Key. Add a Label (optional), such as the name of the app that will use the key, so you can tell keys apart and revoke one without touching the others.
kenari shows the key once, right after you create it. It cannot be recovered later. If you lose a key, select Rotate key on its row to get a new one, or create a new key and revoke the old one.
Send the key
Section titled “Send the key”Send the key in one of two headers. Both work on every /v1 endpoint.
| Header | Used by |
|---|---|
Authorization: Bearer kn-... | OpenAI SDKs and most HTTP clients |
x-api-key: kn-... | Anthropic SDKs and Claude Code |
If a request has both headers, Authorization wins. Send the bare key in x-api-key, without a Bearer prefix.
curl https://kenari.id/v1/chat/completions \ -H "Authorization: Bearer $KENARI_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "step-3-7-flash:free", "messages": [{"role": "user", "content": "Hello!"}] }'Sign in from a terminal
Section titled “Sign in from a terminal”The kenari CLI can create and store a key for you. Install it with npm install -g @kenarihq/cli, then run kenari login. It opens your browser to approve the sign-in, then saves a new key, so you never copy it by hand. On a server with no browser, create a key in the dashboard and run kenari login --api-key. It prompts for the key, or reads it from stdin when you pipe it in. Never pass the key as an argument. The guide above has the details.
Call from a browser
Section titled “Call from a browser”The /v1 API can be called straight from browser JavaScript, so a fetch from a web page works without a proxy of your own.
const response = await fetch("https://kenari.id/v1/chat/completions", { method: "POST", headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json", }, body: JSON.stringify({ model: "step-3-7-flash:free", messages: [{ role: "user", content: "Hello!" }], }),});const data = await response.json();console.log(data.choices[0].message.content);/v1 answers requests from any origin and allows GET, POST and OPTIONS. It accepts whatever request headers the browser asks for in its preflight, so Authorization, Content-Type, x-api-key and the Anthropic headers all pass. Cookies are not used: /v1 reads only the Authorization or x-api-key header. Scripts can read the x-request-id and request-id response headers. The dashboard API under /api stays closed to cross-origin requests.
Public endpoints
Section titled “Public endpoints”GET /v1/models is the only /v1 endpoint that works without a key. These endpoints are public:
| Endpoint | Purpose |
|---|---|
GET /v1/models | The model list, including ?modality= filters. See Models and pricing. |
GET /api/public/pricing | Current prices and free-tier limits |
GET /openapi.json | The OpenAPI description |
A request without a key, or with a bad one, gets a 401 with a plain-text body instead of JSON. See Errors for the messages.
Manage and revoke keys
Section titled “Manage and revoke keys”API Keys lists every key with its status, Active or Revoked. Select Revoke key to disable a key for good. A revoked key cannot be turned back on, and the next request that uses it gets a 401. If you revoke the key that kenari login saved, its per-tool keys are revoked with it. Rotate key creates a new key and revokes the old one in one step, so update your app to the new key right after.
kenari checks the account on every request. If an account is suspended, every key on it stops working at once, and creating a new key does not help.
Restrict a key
Section titled “Restrict a key”Open Advanced settings (expiry, model scope, capabilities, spend cap) in the create dialog to narrow what a key can do.
| Setting | What it does |
|---|---|
| Expiry | The key stops working on that date. Choose Never expires, a number of days, or Custom. |
| Model scope | All models, or Specific models. A request for any other model fails with 400 and bad_request, and the message lists the allowed models. |
| Paid capabilities | Which paid features the key can use: Document reading, Web search and page fetching, and X search. |
| Spend cap, Rp (optional) | The most the key can spend, counted at catalog prices and not against your balance. When it is reached, requests fail with 402 and insufficient_balance. |
The capability list works as follows. A key with no model restriction can use every paid capability. A key with a model scope can use only the capabilities you tick, and a request for another one fails with 400 and bad_request, with no charge. Document reading gates OCR only, meaning the ocr engine and /v1/ocr. A restricted key can still attach documents that kenari reads the default way, because those are ordinary input tokens of a model it may use. See Read documents and Web and X search.
A Token cap (optional), the total of prompt and completion tokens, is set when you create a shared API key.
A key with a model scope, a spend cap or a token cap cannot use named routes. A key with a spend cap also cannot use BYOK models that you address as byok/..., and the request fails with 403 and byok_spend_capped_key. A spend cap counts the Rupiah kenari records, and BYOK usage records Rp 0, so the cap would never be reached. To limit BYOK usage on a key, use a token cap. See BYOK.
A restricted key also reads only its own usage from GET /v1/usage, and GET /v1/account/balance and GET /v1/account/quota refuse it with 403 and restricted_key_not_allowed. See Account overview.
Shared API keys
Section titled “Shared API keys”A shared API key lets other people call kenari without an account of their own. Open Share, find Share an API key, and select Create a new share. Choose the Shared models, an Expiry (1-90 days), and optionally a spend cap, a token cap, a note, and the paid capabilities. Then select Create Share. kenari creates a public link and a key. The key is shown once, like any other key.
A shared key spends from your balance or plan, only on the models you chose, and stops on the expiry date or when a cap is reached. When your own balance cannot cover a request, the holder gets a 402 and insufficient_balance that points at you, the owner. Revoke a share from the same page with Revoke, and the link and key stop working at once.
A shared key is a limited grant, so it cannot do these things:
- Read your account data.
GET /v1/account/balance,GET /v1/account/quotaand the Usage endpoints refuse a shared key with403andshared_key_not_allowed, and thekenari_balance,kenari_usageandkenari_quotatools of the MCP server refuse it too. See Account quota. - Reuse a document reading that another key paid for. The request fails with
400and the document is not read. Uploading the same document again does not get around this, because the stored reading stays tied to the key that paid for it. See Read documents. - Use named routes.
Shared keys also have their own pacing. See Rate limits.