Guide
Errors & limits
Every error code, the response envelope, and rate limit headers.
Every non-2xx response has the same shape:
{
"error": {
"code": "invalid_tool",
"message": "Unknown tool \"virtual-stagng\". See GET /v1/tools.",
"doc_url": "https://developers.roomagen.com/docs/errors#invalid_tool"
}
}Branch on code. It is stable. message is human-facing and may change without notice, so
never parse it — and doc_url always points at the matching anchor on this page.
Error codes
| Code | Status | Description |
|---|---|---|
invalid_api_key | 401 | The |
key_revoked | 401 | The key was revoked, or the account behind it is disabled. Create a new key in the portal — a revoked key never becomes valid again. |
api_disabled | 404 | The API is switched off server-side. Every route answers this, including requests carrying a valid key, so there is nothing to retry. Email support if you expected access. |
invalid_request | 400 | The request failed validation: a malformed field, an out-of-range value for an option the tool knows, an |
invalid_tool | 400 | The |
invalid_image | 400 | Neither |
image_fetch_failed | 400 | Roomagen could not fetch |
insufficient_credits | 402 | The image balance behind the key cannot cover the job, so no job is created. Top up and retry — see Pricing & credits. |
rate_limited | 429 | Too many requests in the sliding 60-second window. Wait the number of seconds in |
idempotency_conflict | 409 | The same |
job_not_found | 404 | No job with that id belongs to your account. A job that belongs to someone else reports the same code — the API never confirms that another account’s id exists. |
webhook_url_rejected | 400 | The |
payload_too_large | 413 | The request body is over the parser limit — 30 MB with |
generation_failed | 422 | The model could not produce a result. Credits for the job are refunded automatically, and the failure shows on the job as |
internal_error | 500 | Something failed on Roomagen’s side. Retry with the same |
Rate limits
| Endpoint | Per minute |
|---|---|
POST /v1/jobs | 60 req/min |
GET /v1/jobs/{id} | 300 req/min |
GET /v1/tools, GET /v1/account | 120 req/min |
Per API key, in a sliding 60-second window, counted separately per route group. Counters live in the API process, so they reset on deploy.
IP backstop
600 req/min
Per client IP across all /v1 routes, applied before the key is identified. A fleet behind one NAT egress IP shares this budget; contact support if you need it raised.
| Header | Description |
|---|---|
X-RateLimit-Limit | Requests allowed in the current window for this route and key. |
X-RateLimit-Remaining | Requests left in the current window. |
Retry-After | Seconds to wait, sent only with a 429. |
When exceeded
429 · rate_limited
Retry after Retry-After seconds. The window slides rather than resetting on a fixed boundary, so a caller that keeps a steady rate under the limit never sees a 429.
Retry-After
A 429 carries Retry-After with whole seconds to wait. Honour it rather than backing off on
your own schedule: the window slides instead of resetting on a fixed boundary, so a steady
rate recovers without backing off further.
X-RateLimit-Limit and X-RateLimit-Remaining are sent on successful responses too, so you
can slow down before you hit the limit rather than after.
Next step
Pricing & credits — what an image costs and how to buy more.