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

StatusWhenWhat to do
202A paid call was accepted and the job is queued.Poll pollUrl.
200A blocking call (?async=false) finished, or a poll returned the job state.Read the asset URL, or check status / model_version.
400Bad request, for example an unsupported Suede-API-Version.Send Suede-API-Version: 1 or leave the header out.
402No payment was attached, or the payment was not accepted.Read accepts, sign, and retry with PAYMENT-SIGNATURE.
404A music poll for a songId that does not exist.Use the songId from the 202 response.
422The request body failed validation.Fix the fields listed in detail and retry.
429Rate-limited (code: rate_limited).Wait the number of seconds in Retry-After.
5XXA 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} and GET /agent/image/{jobId}.
  • A resend with the same X-Idempotency-Key and 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 a PAYMENT-REFERENCE header.

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:

  1. The signature recovers to authorization.from.
  2. That wallet holds enough USDC on Base.
  3. value matches amount, and the validAfter / validBefore window is valid now.
  4. The token and network match the requirement: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 on eip155: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:

HeaderMeaning
RateLimit-PolicyQuota q per window w seconds (IETF RateLimit header draft).
RateLimitRequests remaining r, and seconds t until the window frees a slot.
X-RateLimit-LimitTotal quota for the window.
X-RateLimit-RemainingRequests left in the window.
X-RateLimit-ResetUnix time, in seconds, when the window resets.
Retry-AfterSeconds 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 Error and ValidationIssue schemas, rate-limit headers, idempotency and info.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.