Radient API Reference
The Radient API is one HTTP surface over one balance. Inference, tools, usage and billing calls all live under a single base URL and are paid for from your Radient Pass:
https://api.radienthq.com/v1
Everything except the health check is under /v1. Responses are JSON; request bodies are JSON unless stated otherwise.
# The smallest useful call: check the API is up.
curl https://api.radienthq.com/health
# {"msg":"OK"}
#Authentication at a glance
Send exactly one credential in the Authorization header:
Authorization: Bearer YOUR_API_KEY
- API key (
rad-prod-…) — for servers and tools calling inference and tool endpoints. Create one in the console's Applications page. - User token — for anything tenant- or account-scoped: keys, usage, billing, members, plans, transfers.
- Client token — only for the public contact form.
A missing credential answers 401 {"error":"missing Authorization header"}. Full detail, including which credential reaches which routes and the key-revocation behaviour: Authentication.
#Response conventions
Two success shapes exist:
- Raw JSON (OpenAI-shaped, no envelope): chat completions, completions, embeddings, decisions, models, the account usage summary, and the tools endpoints that answer JSON directly (search, media). Transcription answers the
resultenvelope, and speech answers audio withX-Radient-Cost-*headers. - Envelope
{"msg": "…", "result": …}: everything else — applications, tenants, usage records and rollups, billing, ledger, auto-reload, transfers, members, invites, plans, billing sources, agents and teams, notifications, prices, client-token.
Billed calls also answer with a versioned cost object stating what the call charged — top level on raw bodies, inside result on the enveloped tool responses. See Cost reporting.
Error responses are one shape everywhere:
{ "error": "insufficient credits" }
Some errors add a machine-readable code and optional details:
{ "code": "idempotency_key_required",
"error": "an Idempotency-Key header of 16-128 [A-Za-z0-9_-] characters is required" }
{ "code": "transfer_insufficient_giftable", "details": { "giftable": 0 },
"error": "granted credits can no longer be transferred" }
The error string is stable for a given cause; match on status and code where present, and treat error as human-readable text.
#Status codes and common bodies
| Status | Typical bodies (as returned) | Meaning |
|---|---|---|
400 | Invalid request format (unparseable JSON); Validation failed; Invalid start_time format; Invalid rollup value (must be 'daily', 'monthly', or 'annual'); Radient client ID is required; Unsupported grant_type: <x> | Bad request or parameters |
401 | missing Authorization header; invalid Authorization header format; invalid API key format; API key is not a Radient key; invalid API key; invalid or expired token | Credential problem — see Authentication |
402 | insufficient credits; billing account is not active; billing account not found for tenant | The credit gate refused the call (below) |
403 | not_a_member; insufficient_role; team_plan_required; email_mismatch; Tenant ID in path does not match JWT | Not allowed for this account |
404 | agent_not_found, invite_not_found, transfer_not_found, media_not_found, team_not_found, … | Unknown object (unknown and foreign ids answer identically) |
409 | already_member, last_owner, idempotency_key_reused, transfer_not_pending, media_already_completed, … | Conflicting state |
413 | The decision request body is too large; transcription File size exceeds the 25MB limit | Body over the limit |
422 | transfer_insufficient_credits, transfer_insufficient_giftable, … ; the decision model's own rejection | Understood but not acceptable |
429 | Too many publish requests. Wait a moment and try again. + Retry-After | A write rate limit (see Rate limits) — not emitted by inference routes |
500 | Failed to process chat completions request (and the embeddings/transcription equivalents); Internal server error | Server-side failure. Upstream model errors are flattened into these bodies; the detail is logged, not returned |
502 | The decision model upstream failed with status N | A decision-model upstream failure |
503 | billing is temporarily unavailable; auto_reload_unconfigured; media_unavailable | A dependency is down or not configured; the credit gate fails closed |
#The credit gate
Paid routes are admitted by a credit gate before any work is sent to a model. It applies to:
POST /v1/chat/completions,POST /v1/completions,POST /v1/embeddings,POST /v1/decisions- every route under
/v1/tools/…exceptGET /v1/tools/media/status,GET /v1/tools/media/result,POST /v1/tools/media/cancel,GET /v1/tools/media/models, andGET /v1/tools/speech/voicing
Note that read-only-looking tool calls such as GET /v1/tools/search and GET /v1/tools/images/providers are gated too: they are priced calls.
How it works:
- Estimate. The API estimates the maximum cost of the call from the model's published rates, your prompt and your
max_tokens/max_completion_tokens(assumed 8192 when absent). When the cost cannot be estimated — the literalautomodel id, tool submissions, bodies it cannot parse — the reservation is a flat $1.00. Decision requests reserve their own estimate, capped at $0.30. - Reserve. The estimate is held against your account's in-flight budget (released after 10 minutes if the call never settles).
- Admit. The call is admitted only if
balance − (others' in-flight holds) − floor ≥ estimate. The floor is −$1.00 for accounts that have ever been funded with real money, and $0 otherwise. A funded account can therefore go slightly negative once; a promotional-only balance cannot dip below zero.
Example: on an account holding $0.50, model: "auto" (flat $1.00 reservation) answers 402 {"error":"insufficient credits"}, while a named model with a small max_tokens is admitted.
Refusals:
| Status | Body | Cause |
|---|---|---|
402 | {"error":"insufficient credits"} | Balance cannot cover the estimate |
402 | {"error":"billing account is not active"} | Account not in an active state |
402 | {"error":"billing account not found for tenant"} | No billing account provisioned |
503 | {"error":"billing is temporarily unavailable"} | The reservation store is down — the gate fails closed |
After the call, the actual cost is written to your ledger and the reservation is released. Streaming calls bill when the stream ends. Media generations are charged when their result is first observed — poll GET /v1/tools/media/status or /result, because a generation that is never fetched is never charged and its reservation expires after two hours.
#Cost reporting
Billed calls answer with a versioned cost object stating what the call charged, in USD. The object is additive: every field that existed before keeps its meaning, and a client that does not read it sees the same responses as always.
"cost": {
"v": 1,
"amount_usd": 0.0001627,
"basis": "billed",
"units": 1447,
"unit": "token",
"provider": "openrouter",
"model": "openai/gpt-6-luna-pro",
"usage_record_id": "6ac9dfbf705ad8633a87dfc4"
}
(The figure above is a chat completion's charge from a live call.)
#The cost object
| Field | Type | Presence | Meaning |
|---|---|---|---|
v | integer | always | Contract version — 1 today. Additive fields do not bump it; ignore keys you do not recognise. |
amount_usd | number | always | The amount charged for this call; basis says what the amount is. |
basis | string | always | billed, estimated or quote — see below. |
units | integer | when known | The billed quantity: tokens, characters, seconds or searches, according to the surface's meter. |
unit | string | when known | What units counts: token, char, second, image, megapixel, search or email. |
unit_price_usd | number | single-rate charges only | The per-unit price, present only where it reconstructs the amount exactly (amount_usd = units × unit_price_usd). Absent for mixed-rate charges — a chat turn with cached and uncached prompt legs — and for charges clamped by a ceiling. |
provider | string | when known | The provider that served the call, in the usage surface's own vocabulary (openrouter, typesafe, elevenlabs, …). |
model | string | when known | The model this charge is for — the serving model, except on /v1/decisions, where it is the model the amount was priced from. |
tool_type | string | tool charges only | Which tool was billed: image_generation, video_generation, speech, transcription, web_search. |
usage_record_id | string | when a usage row exists | The id of the usage row the charge lands on (24 hex characters) — the join key for the usage and ledger surfaces. Absence never means the call was free or uncharged. |
price_version | string | when a platform rate priced it | Provenance of the figure, <rate-family>@<read-date> — e.g. decisions-rate@2026-09-19, speech-rates@2026-09-30. Omitted when the amount is the vendor's own reported figure; for the rate families that record no read date (search, email); and on the binding-quote echo path for streamed/rewritten bodies. |
quality | string | when a marker applies | A marker on qualified figures — e.g. vendor_cost_suppressed, where an implausible vendor figure was withheld and the amount is the platform's own derivation. Ignore values you do not recognise. |
Two reading rules. Amounts are the stored doubles, never re-rounded for display, so a figure can show representation noise (0.00030000000000000003 is ordinary): round for display and reconcile by numeric value, never by comparing decimal strings. And keys are omitted when they do not apply — absence is not zero; v, amount_usd and basis are always present when the object is.
basis says what the amount is:
| Value | Meaning |
|---|---|
billed | The amount IS the charge for this call: a vendor-quoted figure, a figure derived from a published rate, or a settled quote. |
estimated | The ledger bills this figure, but it came from a fallback estimate because no published rate was available for the call. |
quote | A firm committed price that settles when the request completes — media submissions while unsettled. |
#Where to read the charge, per surface
| Surface | Where the object is |
|---|---|
| Chat and completions — non-streaming | Top level, a sibling of usage — Chat API |
| Chat and completions — streaming | On the usage frames the server annotates. The last usage frame is the one that is billed; a stream that reports no usage frame carries no object anywhere and is reconciled through the usage surface — Chat API |
| Embeddings | Top level — Embeddings API |
| Decisions | Top level. usage.cost on this route is the vendor's own informational figure — read cost — Decisions API |
Media — image and video (generate, status, result, cancel) | Top level; alongside the legacy cost_usd / units / unit pair where it exists (generate; status once settled). The quote while unsettled, the billed figure once settled (0 for a failure) — Image API |
| Speech | X-Radient-Cost-* response headers — the body is audio — Speech API |
| Transcription | Inside the result envelope — Transcription API |
| Search | Top level — Search API |
Email — /v1/email/self/send | Inside the result envelope; no usage_record_id by design — this path writes no usage row |
How the object is emitted, in brief: charged calls carry it; error responses and credit-gate refusals (401 / 402) do not — nothing was admitted, nothing was charged. A settled zero (a failed media generation; a decision answered with no readable usage) is billed with amount 0 — a recorded zero, distinguishable from silence. And absence is never "free": a streamed turn with no usage frame is still billed, and the usage surface is the check.
#Reconciling a charge
cost.usage_record_id joins a response to the server-side record of its charge:
- take the id from the response;
GET /v1/tenants/{tenant_id}/usage— the record whoseidequals it; itscostequalsamount_usd;GET /v1/tenants/{tenant_id}/billing/ledger— the debit whosereference_idequals the same id, withamountequal to the same figure.
Both routes need a user token, and the usage row is written asynchronously after the response — an id can briefly precede its row, and a missing row is not permission to read the call as uncharged. A zero-cost success writes no ledger row, and a team-paid call debits the payer's account rather than yours. The full set of caveats — including what a media quote's id means before settlement — is in Usage and credits API.
#Client correlation headers
Two optional headers let your logs point at the server-side record of a call: X-Radient-Client-Session-Id and X-Radient-Client-Request-Id.
- The grammar is
[A-Za-z0-9._:-]{1,64}; anything else is ignored — never a 400, because an optional telemetry header must not fail a paid request. - The values are opaque: never parsed as money, never used for routing, never logged. A session id means whatever your client means by it.
- A valid value is stored on your own usage row (
client_session_id,client_request_id— additive fields, omitted when absent) and is readable back only through the tenant-scoped usage surface. A media generation captures the values at submission and writes them with the settlement charge.
#Rate cards
GET /v1/prices (no auth) publishes the platform's rate cards for pre-call estimates — per-character speech prices, per-second transcription, per-search and per-email charges — alongside the promotional credit amounts. It is served with an ETag and Cache-Control: public, max-age=300: revalidate with If-None-Match after the TTL instead of pinning figures — a match (including the weak form a proxy may hand you) answers 304 Not Modified. Estimates are advisory; the cost object on the response is the record of what was charged. See Usage and credits API.
#Rate limits
What is enforced today:
- There is no request-rate limit on the paid inference and tools routes (chat, completions, embeddings, decisions, search, speech, transcription, images, media). They are bounded only by the credit gate and by the serving models' own limits. No
X-RateLimit-*headers are emitted; the only rate header isRetry-Afteron the routes below. - Publishing and uploading (agents and hub teams) is rate-limited: per-IP 1 request / 15 s (burst 10) and per-account 1 / 60 s (burst 10), answering
429 {"error":"Too many publish requests. Wait a moment and try again.","code":"rate_limited"}withRetry-After. - Signup verification is rate-limited per source address (claim: 1 per 5 s).
- Invites and transfer invitations: reading an invite token is limited (1/s, burst 5), sending is capped at 3 emails per address per day, and a transfer resend has a 12-hour cooldown.
If you need higher throughput than a serving model allows, use a different model or spread requests across models.
#CORS
The API sets permissive CORS for browser callers:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Expose-Headers: Retry-After
Two consequences for browsers:
PATCHis not in the allowed methods andIdempotency-Keyis not in the allowed headers. A cross-origin browser call using either fails its preflight. Server-to-server callers (which do not preflight) are unaffected.- Credentials are not used: the
Authorizationheader is what authenticates, andAllow-Origin: *means any site can call the API with a key it already has — so never embed your key in a page. Keep keys server-side.
#Versioning
The path prefix /v1 is the version. Within it, changes are additive: new endpoints and new response fields may appear at any time, and clients should ignore fields they do not know. Behaviour that would break an existing integration is shipped under a new prefix.
#The route list
Every client-callable route, grouped by surface. “Gate” marks the credit gate; “Auth” column values: Key = application API key, User = user token, Both = either, None = anonymous, Optional = anonymous allowed with richer results when authenticated.
#Inference
| Method | Path | Auth | Gate | Guide |
|---|---|---|---|---|
| POST | /v1/chat/completions | Both | yes | Chat API |
| POST | /v1/completions | Both | yes | Chat API |
| POST | /v1/embeddings | Both | yes | Embeddings API |
| POST | /v1/decisions | Both | yes | Decisions API |
| GET | /v1/models | Both | — | Chat API |
| GET | /v1/usage | Both | — | Usage and credits |
#Tools
| Method | Path | Auth | Gate | Guide |
|---|---|---|---|---|
| POST | /v1/tools/speech | Both | yes | Speech API |
| GET | /v1/tools/speech/voicing | None | — | Speech API |
| POST | /v1/tools/transcriptions | Both | yes | Transcription API |
| GET | /v1/tools/search | Both | yes | Search API |
| GET | /v1/tools/search/providers | Both | yes | Search API |
| POST | /v1/tools/media/generate | Both | yes | Video and media API |
| GET | /v1/tools/media/status | Both | — | Video and media API |
| GET | /v1/tools/media/result | Both | — | Video and media API |
| POST | /v1/tools/media/cancel | Both | — | Video and media API |
| GET | /v1/tools/media/models | None | — | Video and media API |
| POST | /v1/tools/images/generate | Both | yes | Image API (legacy) |
| GET | /v1/tools/images/status | Both | yes | Image API (legacy) |
| GET | /v1/tools/images/providers | Both | yes | Image API (legacy) |
#Usage and billing
| Method | Path | Auth | Gate | Guide |
|---|---|---|---|---|
| GET | /v1/tenants/{tenant_id}/usage | User | — | Usage and credits |
| GET | /v1/tenants/{tenant_id}/usage/rollup | User | — | Usage and credits |
| GET | /v1/tenants/{tenant_id}/usage/team | User | — | Usage and credits |
| GET | /v1/tenants/{tenant_id}/billing | User | — | Usage and credits |
| PATCH | /v1/tenants/{tenant_id}/billing | User | — | Usage and credits |
| GET | /v1/tenants/{tenant_id}/billing/credits | User | — | Usage and credits |
| GET | /v1/tenants/{tenant_id}/billing/ledger | User | — | Usage and credits |
| GET · PUT | /v1/tenants/{tenant_id}/billing/auto-reload | User | — | Usage and credits |
| POST | /v1/tenants/{tenant_id}/billing/auto-reload/charge | User | — | Usage and credits |
| POST · GET | /v1/tenants/{tenant_id}/billing/transfers | User | — | Usage and credits |
| DELETE | /v1/tenants/{tenant_id}/billing/transfers/{transfer_id} | User | — | Usage and credits |
| POST | /v1/tenants/{tenant_id}/billing/transfers/{transfer_id}/resend | User | — | Usage and credits |
| GET | /v1/transfers, /v1/transfers/{transfer_id} | User | — | Usage and credits |
| POST | /v1/transfers/{transfer_id}/accept, /v1/transfers/{transfer_id}/decline | User | — | Usage and credits |
| GET · POST | /v1/transfer-invites/{token}, /v1/transfer-invites/{token}/attach | None · User | — | Usage and credits |
| GET | /v1/me/billing-sources | User | — | Team pooling |
| PUT | /v1/me/billing-source | User | — | Team pooling |
| GET | /v1/me/billing-sources/usage, /v1/me/billing-sources/capacity | User | — | Team pooling |
| GET | /v1/prices | None | — | — |
#Account, applications and keys
| Method | Path | Auth | Gate | Guide |
|---|---|---|---|---|
| POST · GET | /v1/tenants/{tenant_id}/applications | User | — | Authentication |
| GET · PATCH · DELETE | /v1/tenants/{tenant_id}/applications/{application_id} | User | — | Authentication |
| GET | /v1/me | User | — | Authentication |
| GET | /v1/accounts/{account_id} · PATCH | User | — | — |
| GET | /v1/accounts/{account_id}/agents | User | — | — |
| GET | /v1/accounts/{account_id}/notifications | User | — | — |
| GET · PATCH · DELETE | /v1/notifications/{notification_id} | User | — | — |
| GET · PATCH | /v1/account/notification-preferences | User | — | — |
| POST | /v1/email/self/send | Key | — | — |
#Team and membership
| Method | Path | Auth | Gate | Guide |
|---|---|---|---|---|
| GET | /v1/tenants/{tenant_id} | User | — | — |
| GET | /v1/tenants/{tenant_id}/members | User | — | Team Plan |
| POST · GET | /v1/tenants/{tenant_id}/invites | User | — | Team Plan |
| DELETE | /v1/tenants/{tenant_id}/invites/{invite_id} | User | — | Team Plan |
| PATCH · DELETE | /v1/tenants/{tenant_id}/members/{account_id} | User | — | Team Plan |
| GET | /v1/tenants/{tenant_id}/plan | User | — | Team Plan |
| GET | /v1/me/memberships | User | — | Team Plan |
| POST | /v1/invites/{token}/accept | User | — | Team Plan |
| GET | /v1/invites/{token} | None | — | Team Plan |
#Agents and hub teams
| Method | Path | Auth | Gate | Guide |
|---|---|---|---|---|
| GET | /v1/agents | None | — | Agent Hub |
| GET | /v1/agents/{agent_id} | Optional | — | Agent Hub |
| POST · PATCH · DELETE | /v1/agents, /v1/agents/{agent_id} | Both | — | Agent Hub |
| POST · PUT | /v1/agents/upload, /v1/agents/{agent_id}/upload | Both | — | Agent Hub |
| GET | /v1/agents/{agent_id}/download, /v1/agents/{agent_id}/download/count | Optional | — | Agent Hub |
| POST · PUT | /v1/agents/publish, /v1/agents/publish/preview, /v1/agents/{agent_id}/publish, /v1/agents/{agent_id}/publish/preview | Both | — | Agent Hub |
| POST · DELETE · GET | /v1/agents/{agent_id}/like, /v1/agents/{agent_id}/favourite | User | — | Agent Hub |
| GET | /v1/agents/{agent_id}/like/count, /v1/agents/{agent_id}/favourite/count | Optional | — | Agent Hub |
| POST · GET | /v1/agents/{agent_id}/comments | User · Optional | — | Agent Hub |
| PATCH · DELETE | /v1/agents/{agent_id}/comments/{comment_id} | User | — | Agent Hub |
| GET | /v1/agent-name-availability | Optional | — | Agent Hub |
| GET | /v1/teams, /v1/teams/{team_id} | None · Optional | — | Team Plan |
| POST · PUT | /v1/teams/publish, /v1/teams/publish/preview, /v1/teams/{team_id}/publish, /v1/teams/{team_id}/publish/preview | User | — | Team Plan |
| DELETE | /v1/teams/{team_id} | User | — | Team Plan |
| GET · DELETE | /v1/tenants/{tenant_id}/teams, …/teams/{team_id} | User | — | Team Plan |
| GET · DELETE | /v1/tenants/{tenant_id}/agents, …/agents/{agent_id} | User | — | Agent Hub |
Writes in the last two groups are rate-limited (see Rate limits).
#Sign-in, tokens and web
| Method | Path | Auth | Gate | Guide |
|---|---|---|---|---|
| POST | /v1/auth/google, /v1/auth/microsoft | None | — | Authentication |
| POST | /v1/auth/google/refresh, /v1/auth/microsoft/refresh, /v1/auth/token | None | — | Authentication |
| POST | /v1/auth/revoke | None | — | Authentication |
| GET · POST | /v1/auth/oauth/request, /v1/auth/oauth/code, /v1/auth/oauth/token | None · User · None | — | Authentication |
| GET · POST | /v1/auth/signup/verify, /v1/auth/signup/claim, /v1/auth/signup/resend | None · None · User | — | — |
| POST | /v1/web/client-token | None | — | Authentication |
| POST | /v1/web/contact | Client token | — | — |
| GET | /health | None | — | — |
#Other endpoints
- Personal tunnels (
/v1/tunnels/…) are a separate bearer scheme used by the tunnel connector and your authorized browsers; they are managed from the Tunnels page and documented in Personal Tunnels. They are not part of the API-key surface. - Service-account and internal routes (operator billing, OAuth client registration, plan grants, Stripe webhooks) are intentionally not documented here and are not callable with customer credentials.
- The console (console.radienthq.com) is the supported way to manage plans, payment methods, tunnels and team settings; its screens call a mix of these routes on your behalf.
#Where to go next
- Authentication — keys, tokens and revocation.
- Chat API · Embeddings API · Decisions API · Speech API · Transcription API · Search API · Image API · Video and media API
- Usage and credits API — balances, ledger, auto-reload, transfers, team pooling.
- Radient Automatic — the
automodel and reasoning effort.