Errors, rate limits and retries
Suede's x402 endpoints use standard HTTP status codes and a small JSON error body. This page lists what each code means, which requests are never charged, and how to retry safely. Example responses were captured from the live API on 2026-10-11.
Status codes
| Status | When | What to do |
|---|---|---|
202 | A paid call was accepted and the job is queued. | Poll pollUrl. |
200 | A blocking call (?async=false) finished, or a poll returned the job state. | Read the asset URL, or check status / model_version. |
400 | Bad request, for example an unsupported Suede-API-Version. | Send Suede-API-Version: 1 or leave the header out. |
402 | No payment was attached, or the payment was not accepted. | Read accepts, sign, and retry with PAYMENT-SIGNATURE. |
404 | A music poll for a songId that does not exist. | Use the songId from the 202 response. |
422 | The request body failed validation. | Fix the fields listed in detail and retry. |
429 | Rate-limited (code: rate_limited). | Wait the number of seconds in Retry-After. |
5XX | A server or upstream failure. | Retry with backoff, reusing the same X-Idempotency-Key. |
Requests that are never charged
- A paid request that fails validation (
422). - A request that fails with a
5XX. - Every poll:
GET /api/songs/{songId},GET /agent/video/{jobId}andGET /agent/image/{jobId}. - A resend with the same
X-Idempotency-Keyand body within the cache window, which returns the original response.
Error bodies
The 400, 422, 429 and 5XX responses share the Error shape from the OpenAPI spec: detail, a message string, and code, a machine-readable value present where a client should branch on it.
{"detail":"Unsupported Suede-API-Version '9'. Supported: 1.","code":"unsupported_api_version"}
For a 422, detail is a list of issues, each with the path to the field (loc), a message (msg) and a type:
{"detail":[{"loc":["body","prompt"],"msg":"Field required","type":"missing"}]}
The two codes to branch on are rate_limited (429) and unsupported_api_version (400). The music poll's 404 is shaped differently: on 2026-10-11 an unknown songId answered {"error":"Song not found"}.
402 Payment Required
A 402 body is the x402 PaymentRequired object (x402Version, error, resource, accepts, extensions), and the same object is base64-encoded in the PAYMENT-REQUIRED header. The error string says why. Two values seen on 2026-10-11:
PAYMENT-SIGNATURE header is required: the request had no payment.Payment authorization missing: the header was present but could not be read as a payment. That response also carried aPAYMENT-REFERENCEheader.
An unpaid request gets the 402 before the body is validated: an empty body {} also returned 402. Check your body against the endpoint reference before you sign so the paid call does not come back 422.
If a signed payload is refused, check it against the verification steps of the x402 exact scheme:
- The signature recovers to
authorization.from. - That wallet holds enough USDC on Base.
valuematchesamount, and thevalidAfter/validBeforewindow is valid now.- The token and network match the requirement:
0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913oneip155:8453.
Poll states that mean failure
Video and image polls do not answer 404 for an unknown id. An unknown or expired jobId answers 200 with status failed; on 2026-10-11 the body also carried "error": "Generation failed. Please try again.". For music, model_version failed is final, and a 404 means the songId is wrong rather than still rendering.
Rate limits
Responses counted by the limiter carry these headers:
| Header | Meaning |
|---|---|
RateLimit-Policy | Quota q per window w seconds (IETF RateLimit header draft). |
RateLimit | Requests remaining r, and seconds t until the window frees a slot. |
X-RateLimit-Limit | Total quota for the window. |
X-RateLimit-Remaining | Requests left in the window. |
X-RateLimit-Reset | Unix time, in seconds, when the window resets. |
Retry-After | Seconds to wait. Always set on 429; a 402 may carry it when the facilitator is back-pressured. |
Unpaid requests on 2026-10-11 carried RateLimit-Policy: "probe";q=20;w=60, a quota of 20 requests per 60-second window, with RateLimit: "probe";r=19;t=60 counting down from there.
Idempotency
Send X-Idempotency-Key (up to 200 characters) on every paid POST. A render is a non-idempotent job, and the key lets you retry a timeout or a 5XX without paying twice: the same key and body within the cache window return the original response.
Versioning
Every response carries Suede-API-Version. The current version is 1, and a request without the header is served the current version. Additive changes, such as new optional fields or new endpoints, ship within a version; a breaking change ships as a new version. Before a version or operation is withdrawn, its responses carry a Deprecation header (RFC 9745) and a Sunset header (RFC 8594) at least 90 days ahead, with a Link to the notice.
Sources
- app.suedeai.ai/openapi.json: response codes, the
ErrorandValidationIssueschemas, rate-limit headers, idempotency andinfo.x-api-versioning. - x402 exact scheme on EVM: payment verification steps.
- Live requests on 2026-10-11: unpaid and malformed-payment 402s, the 400 for
Suede-API-Version: 9, rate-limit headers, and polls for unknown ids.