API reference
The gateway surface: authentication, device login, entitlement, models, and inference.
Base URL
https://api.nib.am/v1
Inference lives on its own host. OpenAI-compatible clients take
https://api.nib.am/v1 as the base URL (so a client appends
/chat/completions to it); Anthropic and Gemini paths hang off the same
host.
Authentication
Send a bearer token on every request except the two device endpoints.
Authorization: Bearer sk-nib-...
API keys are created in the console under API keys. The key is the only credential the gateway accepts; it resolves to your account, plan, and credit balance. Console sessions use a separate token and are what the settings pages call.
Errors
Errors use the OpenAI shape so existing SDKs surface a useful message.
{ "error": { "type": "insufficient_quota", "message": "Monthly credits exhausted" } }
| Status | Type | Meaning |
|---|---|---|
| 401 | authentication_error | Missing, expired, or revoked credential |
| 402 | insufficient_quota | Credits exhausted, including top-up balance |
| 403 | permission_error | Plan does not include the requested model |
| 429 | rate_limit_error | Concurrency or RPM exceeded |
| 5xx | api_error | Gateway or upstream failure |
Device login
POST /api/v1/cli/device/authorize
No authentication. Returns a device code and a short user code.
{
"device_code": "3f0c2a1e-...",
"user_code": "MKGX-36FF",
"verification_uri": "https://nib.am/device",
"verification_uri_complete": "https://nib.am/device?code=MKGX-36FF",
"expires_in": 600,
"interval": 5
}
POST /api/v1/cli/device/token
Poll at interval seconds. Pending responses return HTTP 400 with {"error":"authorization_pending"}. Success returns:
{
"access_token": "nib_dev_...",
"token_type": "Bearer",
"expires_in": 7776000
}
Other error codes: slow_down, access_denied, expired_token.
Entitlement
GET /api/v1/product/entitlement
{
"plan": {
"id": "starter-promo",
"name": "Starter",
"price_usd": 15,
"effective_price_usd": 10,
"currency": "USD",
"promotional": true,
"promotional_until": "2027-01-31T00:00:00Z",
"monthly_credits": 300,
"weekly_credit_cap": 120,
"daily_credit_cap": 45,
"concurrency": 3,
"requests_per_minute": 120
},
"usage": {
"credits_used": 114.0,
"credits_used_this_week": 42.5,
"credits_used_today": 16.2,
"topup_credits_remaining": 0,
"resets_at": "2026-11-14T08:00:00Z"
},
"models": { "available": 42 }
}
All monetary values are numbers in USD, never strings. Caps may be null when unset; the field itself is always present.
Models
GET /v1/models
OpenAI-compatible listing of the models your account can reach. Model ids are the vendor ids
themselves — claude-sonnet-5, gpt-6-sol, gemini-3.8-flash —
so a request written against a vendor's own documentation works unchanged.
{
"object": "list",
"data": [
{ "id": "claude-sonnet-5", "object": "model", "owned_by": "anthropic" },
{ "id": "gpt-6-sol", "object": "model", "owned_by": "openai" }
]
}
The full catalogue is on the Models page.
Inference
| Protocol | Path | Used by |
|---|---|---|
| OpenAI Chat Completions | POST /v1/chat/completions | Most editors and SDKs |
| OpenAI Responses | POST /v1/responses | Codex-style clients |
| Anthropic Messages | POST /v1/messages | Claude Code, Anthropic SDK |
| Gemini | POST /v1beta/models/{model}:generateContent | Google SDK |
Usage accounting is identical across protocols. The same request costs the same credits regardless of which endpoint it arrives on.
Usage and credits
Each response reports the tokens it used in the vendor's own shape (usage for OpenAI,
usage for Anthropic, usageMetadata for Gemini). Charges are recorded server-side
and appear on the console Usage page against the same
credit balance the app shows. Ten credits equal one dollar of usage.
The response also carries the upstream request id, which is what to quote in a support request:
| Header | Meaning |
|---|---|
x-request-id | Upstream request id, for support |
openai-processing-ms | Upstream latency, when the provider reports it |
Versioning
- Breaking changes ship under
/api/v2. v1 stays available for at least six months after v2 ships. - Adding optional response fields is not a breaking change.
- Removing or renaming a field is breaking, including the
usageblock and the request-id header.
Rate limits
Concurrency and RPM follow your plan and are exposed in the entitlement payload. A 429 response includes retry-after in seconds. Client-side, back off exponentially and cap retries at three.