Management API keys
ImageRouter supports two kinds of API keys:
- Normal keys - used to call the generation, credits and other endpoints.
- Management keys - used only to mint new normal keys via the endpoint below. They cannot generate images, spend credits, or call any other endpoint.
Management keys are useful when you want to provision per-user, per-environment or per-customer normal keys from automation (CI, internal tooling, customer dashboards, …) without exposing your account credentials.
You can create a management key on the API keys page: switch the form to Management key, give it a name, and save the secret.
Create normal API key with Management key
Section titled “Create normal API key with Management key”Mints a fresh normal API key for the account that owns the supplied management key. The new key is returned in plain text only on this response - store it securely.
POST /v1/api-keys
Authorization
Section titled “Authorization”The Authorization header must be Bearer <MANAGEMENT_API_KEY>. Normal keys, temporary tokens and disabled / non-management / expired keys are rejected with 401 Unauthorized.
Request Body
Section titled “Request Body”JSON object:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Human-readable name for the new key (max 200 chars). |
spend_limit | number | null | no | Optional spend cap in USD. Requests are blocked once the key has spent this amount within the current window (see spend_limit_period). May be edited later in the dashboard. |
spend_limit_period | "day" | "week" | "month" | null | no | Optional reset window for spend_limit. day, week and month reset at 00:00 UTC on the corresponding boundary (today, Monday of this ISO week, or the 1st of this calendar month). Omit or null for a lifetime/cumulative cap. Requires spend_limit to also be set. |
expires_at | string (ISO 8601) | null | no | Optional expiration timestamp. The key automatically stops working at that time. Must be in the future. Omit or null for a key that never expires. |
Example - minimal
Section titled “Example - minimal”curl -X POST "https://api.imagerouter.io/v1/api-keys" \-H "Authorization: Bearer YOUR_MANAGEMENT_API_KEY" \-H "Content-Type: application/json" \-d '{"name": "production worker #1"}'const response = await fetch('https://api.imagerouter.io/v1/api-keys', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_MANAGEMENT_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'production worker #1' })})const data = await response.json()console.log(data.key) // the new normal API key - store it nowimport requests
url = "https://api.imagerouter.io/v1/api-keys"headers = { "Authorization": "Bearer YOUR_MANAGEMENT_API_KEY", "Content-Type": "application/json"}payload = {"name": "production worker #1"}
response = requests.post(url, headers=headers, json=payload)print(response.json()["key"]) # the new normal API key - store it nowExample - with a monthly spend limit and expiration
Section titled “Example - with a monthly spend limit and expiration”Cap the key at $5.00 per calendar month of spend, and have it stop working entirely on January 1, 2027. When the key reaches the monthly cap, generation requests using it return an error until the next 1st of the month at 00:00 UTC, or until you raise/remove the limit from the API keys page.
curl -X POST "https://api.imagerouter.io/v1/api-keys" \-H "Authorization: Bearer YOUR_MANAGEMENT_API_KEY" \-H "Content-Type: application/json" \-d '{ "name": "customer-acme-prod", "spend_limit": 5.00, "spend_limit_period": "month", "expires_at": "2027-01-01T00:00:00Z" }'const response = await fetch('https://api.imagerouter.io/v1/api-keys', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_MANAGEMENT_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'customer-acme-prod', spend_limit: 5.00, // USD; null or omit for no limit spend_limit_period: 'month', // "day" | "week" | "month" | null (lifetime) expires_at: '2027-01-01T00:00:00Z' // null or omit for never })})const data = await response.json()console.log(data.key, data.spend_limit, data.spend_limit_period, data.expires_at)import requests
url = "https://api.imagerouter.io/v1/api-keys"headers = { "Authorization": "Bearer YOUR_MANAGEMENT_API_KEY", "Content-Type": "application/json"}payload = { "name": "customer-acme-prod", "spend_limit": 5.00, # USD; None or omit for no limit "spend_limit_period": "month", # "day" | "week" | "month" | None (lifetime) "expires_at": "2027-01-01T00:00:00Z", # None or omit for never}
response = requests.post(url, headers=headers, json=payload)data = response.json()print(data["key"], data["spend_limit"], data["spend_limit_period"], data["expires_at"])Response Example
Section titled “Response Example”201 Created:
{ "id": "8a5cf3a2-7b1e-4d6e-9a2c-13f8e21b7bfa", "name": "customer-acme-prod", "key": "f3b6a1c2d3e4f5061718293a4b5c6d7e8f9001112233445566778899aabbccdd", "created_at": "2026-05-17T10:42:13.901Z", "is_active": true, "is_management": false, "spend_limit": 5.00, "spend_limit_period": "month", "expires_at": "2027-01-01T00:00:00.000Z"}The key field is the plaintext API key - it will not be retrievable again. The newly minted key behaves exactly like keys created in the dashboard, including rate limits.
Spend limit reset windows
Section titled “Spend limit reset windows”When spend_limit_period is set, the cap applies only to the current window of that key, not its lifetime spend:
spend_limit_period | Window resets at |
|---|---|
null (omitted) | Never - the cap is cumulative for the lifetime of the key. |
"day" | 00:00 UTC every day. |
"week" | 00:00 UTC every Monday (ISO week). |
"month" | 00:00 UTC on the 1st of every calendar month. |
The reset is lazy: the next request after a window boundary clears the running total before applying the cap. You can inspect the current window’s spend on GET /v1/credits?by_api_key=true (period_spend, period_start, spend_limit_period).
Changing spend_limit_period for an existing key (from the dashboard) resets the running window so the new cap starts from zero.
Error Responses
Section titled “Error Responses”| Status | Reason |
|---|---|
400 | Body is missing name, name is empty / longer than 200 chars, spend_limit is not a non-negative number, spend_limit_period is not one of day/week/month/null, or expires_at is invalid / in the past. |
401 | No Authorization header, the supplied key is invalid, disabled, expired, or is not a management key. |
500 | Internal error while creating the key. |
Example error when a normal key is used instead of a management key:
{ "error": { "message": "This endpoint requires a management API key. Generate one in your API keys dashboard.", "type": "unauthorized" }}- Management keys themselves never spend credits, do not have rate limits and are rejected by every other endpoint.
- Disabling, expiring, or deleting a management key immediately stops it from minting new keys but does not revoke the keys it has already created - manage those individually on the API keys page.
- Expired keys (any kind) are rejected with
401 Unauthorized. The expiration timestamp is included in the error message.