API key management
Use the management API to operate on API keys from your own code: list, create, update, revoke, and cap the API keys in your workspace — for example one spend-capped key per team member, created by your onboarding tooling. The same management key can also read your workspace's spend by day, key, end user and app (GET /api/v1/usage).
These routes authenticate with a management key, not a regular API key: Authorization: Bearer llmr_sk_.... Workspace admins create one at Settings → Management Keys (the secret is shown once). A management key is rejected on every model endpoint, so a leaked one can't spend your credits directly, and every call is scoped to the workspace that owns it; keys you create spend that workspace's credit balance.
GET/api/v1/keys
List the API keys in your workspace.
Parameters
offsetinteger (query)Optional, default 0.include_disabledboolean (query)Optional, default true — include revoked keys.Up to 100 keys per call, newest first. Each key carries its current usage (see the key object below).
Example response
{ "data": [ { "...": "key object" } ] }
POST/api/v1/keys
Create an API key, optionally with a spend limit. The secret is returned once, in key; store it immediately.
Parameters
namestringA label — e.g. the employee's name. Optional, defaults to "default".limitnumberSpend cap for the key in USD. Omit for no cap.limit_resetstringdaily | weekly | monthly — the window limit applies to. Optional, default monthly. UTC calendar windows: daily resets at 00:00 UTC, weekly on Monday 00:00 UTC, monthly on the 1st.expires_atstringOptional ISO 8601 date; the key stops working after it.model_allowliststring[]Optional — restrict the key to these model slugs.Example request
curl https://videorouter.sh/api/v1/keys \
-H "Authorization: Bearer $VIDEOROUTER_MANAGEMENT_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "jane.doe", "limit": 20, "limit_reset": "daily"}'
Example response
{
"key": "llmr_sk_live_...", // shown once — store it now
"hash": "9f2c4e1a...",
"name": "jane.doe",
"label": "jane.doe",
"disabled": false,
"limit": 20.0,
"limit_reset": "daily",
"limit_remaining": 15.7083,
"usage": 4.2917,
"usage_daily": 4.2917,
"usage_weekly": 18.02,
"usage_monthly": 61.5,
"created_at": "2026-10-20T10:00:00+00:00",
"last_used_at": "2026-10-29T08:14:09+00:00",
"expires_at": null
}
Creation is rate-limited to 20 per minute per management key. An invalid limit_reset returns 400.
GET/api/v1/keys/{hash}
Retrieve one API key's settings and current usage (no secret). hash is the stable identifier returned by list/create. 404 if it isn't one of your workspace's keys.
Example response
{
"hash": "9f2c4e1a...",
"name": "jane.doe",
"label": "jane.doe",
"disabled": false,
"limit": 20.0,
"limit_reset": "daily",
"limit_remaining": 15.7083,
"usage": 4.2917,
"usage_daily": 4.2917,
"usage_weekly": 18.02,
"usage_monthly": 61.5,
"created_at": "2026-10-20T10:00:00+00:00",
"last_used_at": "2026-10-29T08:14:09+00:00",
"expires_at": null
}
Key object
hashstringStable key identifier; also the key_hash in usage rows.limit / limit_resetnumber | null / stringThe cap in USD and its window. limit_reset is always present ("monthly" when never set).limit_remainingnumber | nullWhat is left in the current window; null when uncapped.usagenumberSpend in the key's own limit_reset window, including money held for in-flight video jobs — exactly what the cap is checked against.usage_daily / usage_weekly / usage_monthlynumberSpend in each current UTC window.disabledbooleantrue once revoked.created_at / last_used_at / expires_atstring | nullISO 8601 timestamps.PATCH/api/v1/keys/{hash}
Update an API key — rename it, change its spend limit, or revoke it. Only the fields you send change.
Parameters
namestringOptional.limitnumberOptional. Send 0 to remove the cap.limit_resetstringOptional — daily | weekly | monthly.disabledbooleanOptional. true revokes the key. Revocation is irreversible; false on a revoked key does nothing.Changes take effect on the key's very next request. Returns the updated key object.
DELETE/api/v1/keys/{hash}
Revoke (delete) an API key. Anything using it stops working immediately. Irreversible.
Example response
{ "deleted": true }
GET/api/v1/usage
Read spend, request and token totals for the whole workspace — including keys created through this API — bucketed by UTC day and broken down by the dimensions you choose. Limited to 60 requests per minute per management key.
Parameters
startstring (query)YYYY-MM-DD, first day inclusive (UTC). Optional, default 29 days before end.endstring (query)YYYY-MM-DD, last day inclusive (UTC). Optional, default today. The range may span at most 366 days.group_bystring (query)Comma list of key, end_user, app, model. Optional, default key,end_user. Empty gives per-day totals only.granularitystring (query)day (default) — one row per day per group; none — the whole range collapsed into one row per group.keystring (query)Optional. Only this key — pass its hash.end_userstring (query)Optional. Only requests tagged with this user value.limit / offsetinteger (query)Optional pagination. Default 1000 rows, maximum 5000.Row fields
datestringUTC day, YYYY-MM-DD. Absent when granularity=none.key_hash / key_namestringPresent when grouping by key.end_userstring | nullThe request body user value; null for untagged traffic.appstring | nullThe X-Title request header.modelstringModel served.requestsintegerRequests in the group, including failed ones.errorsintegerHow many of those failed (HTTP 4xx/5xx). Failed requests cost nothing.costnumberUSD charged.input_tokens / output_tokensintegerToken counts (zero for media endpoints).Example request
curl "https://videorouter.sh/api/v1/usage?start=2026-10-01&end=2026-10-28&group_by=key,end_user" \
-H "Authorization: Bearer $VIDEOROUTER_MANAGEMENT_KEY"
Example response
{
"data": [
{ "date": "2026-10-28", "key_hash": "9f2c4e1a...", "key_name": "jane.doe", "end_user": "emp-1042",
"requests": 38, "errors": 1, "cost": 4.2917, "input_tokens": 51200, "output_tokens": 9800 },
{ "...": "more rows" }
],
"start": "2026-10-01", "end": "2026-10-28",
"group_by": ["key", "end_user"], "granularity": "day", "timezone": "UTC",
"total_cost": 112.4031
}
Rows are ordered by date, then cost (highest first). total_cost sums the rows on this page. Rows appear within seconds of a request finishing; a video job's final cost lands when it settles. For a daily per-person tracker, call it once a day with start=end= and group_by=key,end_user.
Spend limits
When a key reaches its limit for the current window, its requests return 402 until the window resets (or you raise the limit):
HTTP 402
{ "error": { "message": "daily spend cap reached ($20.00 of $20.00); resets 2026-10-30 00:00 UTC",
"type": "insufficient_quota", "code": "spend_cap_exceeded" } }
- The same error on every billable endpoint — chat (streaming too), embeddings, images, video, audio, lip-sync, face swap, avatars, 3D, live video, and batches/jobs. The realtime WebSocket is refused at connect time and closes with code
4402. - The cap is checked before a request starts, so the request that crosses the line still completes — a key can end a window over its limit by at most one request's cost. Chat requests near the cap get
max_tokensclamped to what's left. - In-flight video jobs count — the amount held for jobs still running counts against the cap immediately, so a key can't queue unlimited jobs while under the limit.
- A batch or job is checked against the remaining budget as a whole: it is refused if its estimated cost would push the key over the cap.
Tagging requests
Usage can only be split by end user and by app if requests carry the tags. Send them on every request; they are recorded on every model endpoint.
Fields
userbody fieldYour own end-user id, truncated to 64 characters. Same field name as the OpenAI API.X-TitleheaderYour app's display name, truncated to 200 characters.HTTP-RefererheaderYour app's URL, truncated to 500 characters.Advisory only — set by the client, never used for authentication or billing. For a hard per-person boundary, give each person their own key and a limit. See App attribution.
Errors
Status codes
401invalid_provisioning_keyMissing or revoked key, or a regular (non-management) key was used.400invalid_request_errorBad date, range over 366 days, unknown group_by, or an invalid limit_reset.404not_foundThe hash isn't one of this workspace's keys.429rate_limit_error60 usage requests per minute, 20 key creations per minute. Honour Retry-After.