Skip to content

DocsGet started

Authentication

Authenticate with the INFRO API using sk_infro_ keys and the Authorization header. Create, label, rotate, and revoke keys, and set spend limits.


INFRO is in private early access. Running these examples requires an early-access account, an active trial or subscription entitlement, and your own provider connection. Self-serve purchasing is not generally open. See Early access.

Every INFRO API request is authenticated with an API key sent in the Authorization header. Keys are created in the console, start with sk_infro_, and work identically on every endpoint.

Each key carries its own label and optional spend limit, so you can give every service its own key, see what it spends, and cap the damage if one leaks. If you're making your first request, start with the quickstart.

API keys

Keys are sk_infro_ followed by a random string. Copy the full key into your secrets manager when you create it and treat it like a password. If you lose one, revoke it and create a replacement.

Create keys in the console under Keys; each gets a label, an optional spend limit, a rate-limit tier, and optionally an environment, a project and an expiry. An INFRO key reaches the models served by the provider accounts you have connected under Providers — your provider credentials stay sealed in INFRO, and applications only ever hold the INFRO key.

The Authorization header

Pass the key as a bearer token. The header is the only authentication mechanism — there is no query-parameter or cookie alternative.

curl https://api.infro.io/v1/chat/completions \
  -H "Authorization: Bearer $INFRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "anthropic/claude-sonnet-5",
    "messages": [{"role": "user", "content": "Hello"}]
  }'

A missing, malformed, revoked, or expired key returns 401 with type authentication_error. A key that belongs to an archived project returns 403 permission_denied with code project_archived instead, a key that was disabled returns 403 with code key_disabled, and a key called from outside its allowed networks returns 403 with code ip_not_allowed. The examples above are the standard OpenAI clients pointed at INFRO's base URL; see SDKs for other languages and frameworks.

Labels and spend limits

Labels identify keys in the console and in usage analytics. Spend limits are hard ceilings on estimated provider cost: once a key's usage reaches its limit, its requests return 402 budget_exceeded while your other keys keep working. A limit can reset daily, weekly or monthly, or never (a lifetime cap), and can be changed later with Edit spend limit. Rate limits are also enforced per key — see rate limits.

labelstring
Human-readable name shown in the console, in usage analytics, and in the GET /v1/key response. Use one key per service or environment so spend stays attributable.
limitnumber | null
Hard ceiling in USD on the estimated provider cost of this key's requests, as reported by GET /v1/key. Requests return 402 once usage reaches it. null means no per-key cap, in which case only the organization, project and member budgets apply.

Inspect the current key

GET /v1/key returns the label, spend limit, usage, and remaining budget of whichever key made the request — useful for verifying that a deployment picked up the right key. The full reference (request, response fields, code samples) lives in Rate & spend limits.

Rotating and revoking keys

Revoking a key in the console takes effect immediately — subsequent requests with it return 401. Revoke on any suspected exposure; it can't be undone.

To stop a key for a while instead, turn it off with the switch on its page in the console (or PATCH it with "disabled": true). Requests with a disabled key return 403 with code key_disabled; its secret, scope and limits are kept, and turning it back on makes the same secret work on the next request.

Rotate replaces a key in one step: in the console choose Rotate… on the key, or call POST /console/keys/{id}/rotate (management API: POST /manage/v1/gateway_keys/{id}/rotate). The new key gets a fresh secret, shown once, and every setting of the old one: label, project, limits, rate limit, model and route allowlists, environment, expiry, history access and allowed networks. You choose when the old key stops:

OverlapWhat happens to the old key
Immediately (overlap_seconds: 0)Revoked at once, exactly as Revoke does. Use this if the key leaked.
1 hour, 24 hours (the default) or 7 daysKeeps working until the overlap ends, then returns 401 like any expired key. It never outlives its own expiry.
  1. Rotate the key and copy the new secret.
  2. Deploy it — update the environment variable or secrets manager entry your service reads.
  3. Confirm traffic moved: GET /v1/key from the deployed service answers with the new key. In the console the old key shows how long it has left and links to its replacement.
  4. Let the overlap run out, or revoke the old key early.

Limits follow the key across a rotation. The old and new key share one spend limit and one lifetime cap: spend recorded on the old key counts against the new one, and while both work their requests are admitted against a single ceiling. Rotating is never a way to get fresh spending room; raise the limit instead. In Analytics, a rotated key's history is shown on its replacement's row. Rotation is recorded in the audit log as api_key.rotated.

IP allowlist

A key can be limited to the networks your servers call from. Set Allowed networks when you create the key or later from its menu, or send allowed_ips on POST / PATCH /console/keys and the management API. Each entry is an IPv4 or IPv6 address or CIDR range — 203.0.113.0/24, 2001:db8::/48 — up to 32 per key. An empty list accepts any address. Every plan includes it.

  • Entries are stored in canonical form: a bare address becomes /32 or /128, IPv6 is lowercased and compressed. A range with host bits set (203.0.113.7/24) is refused with the network it probably meant (203.0.113.0/24).
  • The check uses the full client address the Cloudflare edge reports, never X-Forwarded-For. An IPv4-mapped IPv6 address (::ffff:203.0.113.7) matches IPv4 entries; otherwise IPv4 and IPv6 never match each other.
  • It fails closed: when no trusted client address is available, a key with an allowlist is refused.
  • A refused request returns 403 with type permission_denied and code ip_not_allowed, before any budget, routing or provider call. The response never lists the allowed networks. The console shows how many requests each key's allowlist refused today.
  • A change applies from the key's next request.
  • It applies to every request a gateway key authenticates — inference, /v1/usage, /v1/requests, /v1/key and the FOCUS export. Management tokens and console sign-in are not affected.
  • Use the addresses of the servers that call INFRO. Your browser's address is not one of them.

Providers a key may use

A gateway key is INFRO's, not a provider's: your application sends it to INFRO, and INFRO calls your providers with your own provider keys. By default a gateway key may use every provider you connect, including ones you connect later. To limit it, choose Only selected under Providers when you create the key or later from its menu, or send allowed_providers on POST / PATCH /console/keys and the management API — provider ids such as openai or openrouter, as GET /console/providers/kinds lists them. An empty list means every provider. Every plan includes it.

  • It holds on every path: a model name, a provider's own id (laozhang/<model id>), a route and per-request fallbacks. A route target on another provider is skipped; the route still serves from the rest.
  • It narrows what the key's environment already reaches. A provider key scoped to another environment stays out of reach.
  • When nothing the key may use can serve a request, the gateway answers 503 with code connection_out_of_scope and a sentence naming the providers the key allows and the one that could have served it. No retry clears it.
  • A change applies from the key's next request. A rotated key keeps the same providers.

Security best practices

Never ship an API key in client-side code. A key in a browser bundle, mobile app, or public repo can be extracted by anyone and spent against your account. Treat any key that reaches a client as compromised: revoke it and route those requests through your own backend.

  • Read keys from environment variables or a secrets manager. Never commit them to source control — .env files belong in .gitignore.
  • Use one key per service and per environment. Separate keys make usage attributable and let you revoke one integration without touching the rest.
  • Set a spend limit on every key. A leaked key with a limit is a bounded problem; a leaked key without one can spend against your provider account until you notice.
  • Rotate on any suspicion of exposure. With an overlap both keys work while you deploy, so there's no reason to wait for proof; if the key leaked, rotate with no overlap.
  • Restrict production keys to your servers' networks with an IP allowlist.

401 vs 402

Both statuses mean the request never reached a model, but the fixes differ. Both use the standard error envelope:

Error body
{
  "error": {
    "message": "Invalid API key provided.",
    "type": "authentication_error",
    "code": null
  }
}
StatusError typeWhat it meansWhat to do
401authentication_errorThe key is missing, malformed, revoked, or expiredCheck the Authorization header format and confirm the key is active in the console
402budget_exceededA budget you set — on the key, the member, the project or the organization — has no room for this requestRaise or remove that budget in the console; error.code names which one (for example key_budget or organization_budget). The same status also covers subscription_required and trial_payment_failed — see errors.

Neither is retryable — backing off and retrying a 401 or 402 just burns requests. Handle both as configuration errors and alert on them. The full status list and retry guidance are in errors.

↑ ↓ navigate↵ open/ anywhere in docs