Usage and credits API
Everything that happens on your Radient account is metered in credits. 1 credit = $1, credits are deducted at the model's own cost with no markup at inference, and you can read every movement back from the API.
Two credential rules shape this surface:
- Usage summaries (
GET /v1/usage) accept an API key or a user token. - Every other route on this page — organization usage, billing, ledger, auto-reload, transfers and billing sources — requires a user token. An API key gets
401 {"error":"invalid or expired token"}.
#Account usage summary — GET /v1/usage
Returns the calling account's totals. Optional start_time and end_time (RFC 3339) bound the window; a malformed value answers 400 {"error":"Invalid start_time format"}.
curl "https://api.radienthq.com/v1/usage" \
-H "Authorization: Bearer $RADIENT_API_KEY"
{
"total_requests": 31,
"successful_requests": 23,
"failed_requests": 8,
"total_tokens": 1591,
"prompt_tokens": 1464,
"completion_tokens": 127,
"total_cost": 0.034583562799999995,
"average_latency_ms": 653,
"model_breakdown": [
{ "model": "…", "request_count": 8, "total_tokens": 344, "prompt_tokens": 190, "completion_tokens": 127, "total_cost": 0.000334 }
],
"endpoint_breakdown": [
{ "endpoint": "chat/completions", "request_count": 8, "total_tokens": 344, "total_cost": 0.000334 }
]
}
(model_breakdown is trimmed.) total_cost is in USD — the amount deducted from your balance across requests in the window, including tools and media.
#Organization usage — GET /v1/tenants/{tenant_id}/usage
The paginated, per-request view for a tenant, for organizations and their members. Query parameters: page, per_page (default 50, max 200), start_date, end_date (RFC 3339), sort, direction (asc/desc), cursor.
The tenant in the path must be the one your token belongs to, or you get 403 {"error":"Tenant ID in path does not match JWT"}.
{
"msg": "Usage records retrieved",
"result": {
"page": 1, "per_page": 2, "total_pages": 16, "total_records": 31,
"records": [
{
"id": "…", "timestamp": "2026-10-09T16:46:10.046Z",
"account_id": "…", "tenant_id": "…",
"usage_type": "inference", "model": "…", "provider": "…",
"prompt_tokens": 315, "completion_tokens": 0, "total_tokens": 315,
"units": 0, "cost": 0.00001323, "latency": 233, "success": true,
"endpoint": "decisions",
"billing_account_id": "…", "billing_source_tenant_id": "…", "billing_source_kind": "own"
}
]
}
}
Each record carries: usage_type (inference or tool), model, provider (the serving endpoint), token counts, units, cost in USD, latency (ms), success / error_type, endpoint, and — when a team pool pays — billing_account_id with billing_source_tenant_id and billing_source_kind (own or team), so pooled spend is attributable.
#Usage rollup — GET /v1/tenants/{tenant_id}/usage/rollup
Aggregates the same data. rollup is required and must be daily, monthly or annual — anything else answers 400 {"error":"Invalid rollup value (must be 'daily', 'monthly', or 'annual')"}. Optional start_date, end_date, application_id, usage_type, provider.
{
"msg": "Usage rollup data retrieved",
"result": { "data_points": [
{ "timestamp": "2026-10-09T16:46:10.046Z", "total_requests": 31, "total_tokens": 1591,
"prompt_tokens": 1464, "completion_tokens": 127, "units": 56,
"total_cost": 0.0345835628, "success_count": 23, "failure_count": 8 }
] }
}
#Team usage — GET /v1/tenants/{tenant_id}/usage/team
For organizations: team-wide totals broken down per member, readable by any active member of a tenant whose Team plan is entitled (no plan → 403 {"code":"team_plan_required",…}). Query: start_date, end_date, rollup (default daily), members (up to 5 comma-separated account ids).
{
"msg": "Team usage retrieved",
"result": {
"window": { "start": null, "end": null },
"rollup": "daily",
"tenant_id": "…",
"viewer": { "account_id": "…", "role": "owner" },
"totals": { "total_requests": 61, "total_tokens": 6998, "total_cost": 0.469030485 },
"members": [ { "account_id": "…", "name": "…", "email": "…", "role": "owner", "status": "active", "in_roster": true, "is_viewer": true, "totals": { "…": "…" } } ],
"series": { "team": [ "…" ], "viewer": [ "…" ], "others": [ "…" ], "members": {} }
}
}
#Balance and ledger
GET /v1/tenants/{tenant_id}/billing/credits→{"msg":"Credit balance retrieved","result":{"balance": 4.9962311112}}GET /v1/tenants/{tenant_id}/billing/ledger?page=&per_page=(per page max 200) → paginated entries:
{ "id": "…", "billing_account_id": "…", "type": "debit", "amount": 0.00001323,
"balance": 4.965416437200002, "description": "Inference usage: … model, 315 tokens",
"reference_id": "…", "created_at": "2026-10-09T16:46:10.057Z" }
type is credit or debit; source (where present) is one of purchase, registration_bonus, signup_grant, admin, transfer_escrow, transfer_release, transfer_return. Both routes require the path tenant to be your own — a mismatch answers 403 {"error":"tenant ID in path does not match authenticated tenant"}.
GET /v1/tenants/{tenant_id}/billingshows the billing account behind the balance:status,credits,giftable_credits,held_credits(credits escrowed in pending transfers),has_been_funded,member_spend_effectiveandmember_spend_source(see Team pooling).PATCH /v1/tenants/{tenant_id}/billinglets an owner or admin setbilling_emails(up to 10 addresses) andmember_spend_enabled.GET /v1/prices(no auth) returns the current promotional credit amounts:{"msg":"Prices retrieved","result":{"default_new_credits":5,"default_registration_credits":10}}.
#Auto-reload
Top up automatically when the balance drops.
| Route | Purpose |
|---|---|
GET /v1/tenants/{tenant_id}/billing/auto-reload | Current settings |
PUT /v1/tenants/{tenant_id}/billing/auto-reload | Replace the settings wholesale |
POST /v1/tenants/{tenant_id}/billing/auto-reload/charge | Attempt a charge now |
PUT is a full replace and refuses unknown fields:
{
"auto_reload": { "enabled": true, "threshold_usd": 2, "amount_credits": 10, "payment_method_id": "pm_…" },
"alerts": { "enabled": true, "warn_threshold_usd": 2, "rearm_threshold_usd": 3 }
}
Limits and defaults: amount_credits is a whole number between 5 and 10,000; threshold_usd between 0.01 and 10,000; defaults are $2.00 / 10 credits. The charge worker has a 10-minute cooldown between attempts.
GET answers with configured plus the current settings and their state (state, last_charged_at, last_error, disabled_reason, in_flight_started_at). Coded errors include 400 payment_method_required / payment_method_not_found, 409 auto_reload_busy / auto_reload_disabled, and 503 auto_reload_unconfigured — the last one means billing automation is not configured on the deployment.
#Credit transfers and gifting
Send credits to another Radient user as a gift or a plain transfer. The sender's balance is debited (escrowed) at creation.
POST /v1/tenants/{tenant_id}/billing/transfers, with an Idempotency-Key header required — 16 to 128 characters of A-Z a-z 0-9 _ -:
curl https://api.radienthq.com/v1/tenants/{tenant_id}/billing/transfers \
-H "Authorization: Bearer $RADIENT_USER_TOKEN" \
-H "Idempotency-Key: radient-docs-gift-0001" \
-H "Content-Type: application/json" \
-d '{
"amount": 1,
"recipient_email": "[email protected]",
"kind": "gift",
"reason": "thanks",
"message": "Thanks!"
}'
| Field | Rules |
|---|---|
amount | ≥ $0.01, rounded half-up to 2 decimals before the debit |
recipient_email | The recipient's Radient account email — or any address, which receives an invitation |
kind | gift or transfer |
reason | Gift only: thanks, welcome or congrats. Sending it on a transfer → 422 |
message | Gift only, up to 280 characters |
Responses: 201 on creation, 200 when the same Idempotency-Key replays the same request (same key, different body → 409 idempotency_key_reused; no key → 400 idempotency_key_required).
What can be gifted: credits you purchased, and credits received through transfers. Promotional credits (registration_bonus, signup_grant) and operator-granted (admin) credits are not giftable — attempting to gift them answers 422 {"code":"transfer_insufficient_giftable","error":"granted credits can no longer be transferred",…}. A total balance that cannot cover the amount answers 422 transfer_insufficient_credits.
Lifecycle: pending → accepted, declined, revoked, or expired (30 days). Sender routes: GET /v1/tenants/{tenant_id}/billing/transfers (list, filters status, page, per_page), DELETE …/transfers/{transfer_id} (revoke a pending transfer — returns the escrow; 409 transfer_not_pending otherwise), POST …/transfers/{transfer_id}/resend (rotates the invitation token; rate-limited, one resend per 12 hours).
Recipient routes (user token; your verified email must match, else 403 email_mismatch):
GET /v1/transfers,GET /v1/transfers/{transfer_id}— a transfer that is not yours answers404 transfer_not_foundPOST /v1/transfers/{transfer_id}/accept— accepting is idempotent; the credits land in your balancePOST /v1/transfers/{transfer_id}/decline
Invitations (recipient without an account yet): GET /v1/transfer-invites/{token} (anonymous, rate-limited) previews a masked summary, and POST /v1/transfer-invites/{token}/attach (user token) binds the invitation to your account — no money moves; the transfer is still accepted separately.
#Team pooling (billing sources)
On a Team plan, an organization's balance becomes a payer for its members. The billing-sources API shows which balances can pay for your calls and lets you pin a preference.
GET /v1/me/billing-sources→ the caller's sources:
{ "msg": "Billing sources retrieved", "result": { "sources": [
{ "billing_account_id": "…", "tenant_id": "…", "tenant_name": "…", "kind": "own",
"role": "owner", "is_home": true, "available": true, "selected": false, "credits": { "balance": 0 } },
{ "billing_account_id": "…", "tenant_id": "…", "tenant_name": "…", "kind": "team",
"role": "member", "is_home": false, "available": true, "selected": false,
"member_spend_enabled": true, "member_spend_source": "default", "credits": { "balance": 4.531 } }
], "selection": null, "selection_required": true } }
member_spend_enabled is the organization's decision to let members draw on its balance, and member_spend_source records whether that is an explicit setting (explicit) or derived from the plan (default).
PUT /v1/me/billing-source—{"mode":"automatic"}or{"mode":"pinned","billing_account_id":"…"}. Pinning a source that could not currently pay answers400 {"code":"invalid_billing_source",…}.GET /v1/me/billing-sources/usage— consumption broken down by payer;GET /v1/me/billing-sources/capacity— balances, held escrow and total.
Order of charging: a token minted by the OAuth code exchange pools the team first, then your own account; every other token and all API keys charge your own account first, then any team. A still-applicable pinned source goes first. Fall-through to the next source happens only for insufficient credits — an own account that is missing or inactive refuses outright rather than charging a team.
See Team plan for exactly what happens to pooled spend when a plan is cancelled or downgraded, and the credit gate for how admission works.