The 9Pic API uses standard HTTP status codes. Endpoint pages list which subset of these codes apply to that endpoint, and link here for the full description.
Error Envelope
Every error response uses the same three-key envelope as success responses, with responseType: "error" and data: null. The HTTP status code carries the error type; the message is a short human-readable description (safe to log, not stable enough to branch on).
{
"responseType": "error",
"message": "BIB number 1234 not found for this event",
"data": null
}
422 Unprocessable Entity errors (request validation failures) flatten Pydantic field errors into a single message string of the form field.path: error description; field.path: error description. The HTTP status remains 422.
Status Codes
| Status | Meaning |
|---|
200 | Success. The body is the documented response model for the endpoint. |
400 | Validation failure. The request was rejected before any work was done. |
401 | Authentication missing. The X-API-Key header was not provided. |
403 | Authentication or authorisation failure. The key is invalid/inactive, the path identifiers do not belong to this org, the host is not approved, or the requested feature is disabled for this event. |
404 | Resource not found. The event, configuration, or request_id does not exist. |
422 | Schema validation failure. The request body or query parameters did not match the documented model (e.g., unknown enum value, missing required field). |
429 | Rate limit exceeded. The platform-wide API Gateway throttle has been hit across the /api/v1/ext/... surface. Safe to retry with exponential backoff. Contact support for the exact rate and burst limits applied to your integration. |
500 | Internal failure. The request could not be recorded, or async work could not be started. Safe to retry; see retry guidance below. |
400 — Bad Request
Returned when the request itself is malformed. Common causes:
- The uploaded selfie file is empty (multipart endpoints).
request_id was provided but is not a valid UUID.
- Path or query parameters fail Pydantic validation (for example,
page=0 or page_size > max).
- Missing or malformed JSON body on POST endpoints that require it.
A 400 means no billable work happened. Fix the request and resend it.
401 — Unauthorized
Returned when the X-API-Key header is missing entirely. Add the header and retry. See Authentication.
403 — Forbidden
Returned for any of the following:
- The API key is unknown, inactive, or has been deleted.
- The
org_id in the URL does not belong to the API key.
- The
event_id in the URL does not belong to the org_id.
- The request originated from a host that is not on the allowlist (see Host Validation).
- The endpoint requires an event-level feature flag (
bib_search, selfie_search, video_search, video_selfie_search) that is currently false for this event. Check the flags from Event Details before calling search endpoints.
A 403 will not become a 200 by retrying. Fix the underlying configuration first.
404 — Not Found
Returned when:
- The event does not exist or its memories configuration is missing.
- A
request_id-scoped GET is called with an unknown UUID. This typically means no POST has been made for that UUID, or the search record was rotated out.
- A BIB number does not exist for the event. (Some endpoints return a
200 with an empty images[] array instead — see the endpoint page for which behaviour applies.)
422 — Unprocessable Entity
Returned when the request body or query parameters fail Pydantic schema validation. Common causes:
- Unknown value for an enum field (for example,
method: "qr" on Download Original Photos, which only accepts "bib" or "selfie").
- A required field is missing from the JSON body.
- A field is the wrong type (for example,
identifier sent as a number instead of a string).
The message flattens all per-field errors into a single string. Fix the request and resend.
429 — Too Many Requests
Returned when API Gateway throttling kicks in. The 9Pic API is fronted by an AWS API Gateway stage with a sustained-rate limit and a burst capacity applied across the entire /api/v1/ext/... surface — every client, every key, and every endpoint share the same token bucket.
Exact rate and burst limits are tuned over time and are not published here. Contact support at hello@9pic.ai if you need the current numbers or want them raised for a specific event or campaign.
Unlike other errors, 429 responses are returned by API Gateway before the request reaches the application layer. The body therefore does not match the unified { responseType, message, data } envelope. Expect a minimal payload such as:
{
"message": "Too Many Requests"
}
Recommended client behaviour:
- Treat
429 as transient and always retryable.
- Use exponential backoff with jitter (for example, start at 500 ms, double up to a cap of 30 s, add ±25% jitter).
- Reuse the same
request_id on selfie-driven endpoints. Successful retries resolve to the cached result, so you do not pay for the same search twice — see request_id idempotency.
- If you sustain bursts above the platform limit (for example, opening the search to large simultaneous audiences), reach out to support so we can review the throttle profile for your integration.
500 — Internal Server Error
Returned when the API could not record the request or start an async job. Examples:
- Database write failed before the request could be persisted.
- An async pipeline (for example, video clipping) could not be enqueued.
For video clipping, when the async job itself fails after the POST has been accepted, the GET endpoint will still return 200 with data.status: "failed" and data.error: "<reason>". The selfie record is marked failed, and a fresh POST is the safest retry path. See retry guidance.
Retry Guidance
| Status | Safe to retry? | Recommendation |
|---|
400 | No | Fix the request body and resend. |
401 | No | Add the X-API-Key header. |
403 | No | Fix the API key, ownership, host, or feature flag. |
404 | Sometimes | A POST with a new selfie is the safest retry for an unknown request_id. Other 404s require fixing the URL. |
422 | No | Fix the request shape (enum, type, or missing field) and resend. |
429 | Yes | Back off and retry with exponential delay plus jitter. Reuse the same request_id on selfie-driven endpoints so retries hit the cache. |
500 | Yes | Retry with the same request_id for selfie-driven endpoints (see request_id idempotency). Use exponential backoff. |
Selfie-driven endpoints are designed to be retried with the same request_id. Successful retries resolve to the cached result, so you do not pay twice for the same search.
Endpoint-Specific Errors
Each endpoint page includes a short table listing only the codes that apply to that endpoint. Use this page for the canonical descriptions; use the endpoint page for which subset to expect.