This page documents cross-cutting rules every endpoint follows. Endpoint pages link here instead of repeating the same boilerplate. For status codes, see Errors.
Base URL
All endpoints are versioned under /api/v1/ext/....
Authentication
Every request must send the API key in the X-API-Key header:
X-API-Key: <your_9pic_api_key>
Tokens are created and managed from the Developer Zone in the dashboard. See Authentication for the full workflow and token lifecycle.
Identifiers
| Identifier | Type | Where it comes from |
|---|
org_id | number | Shown in the top-left organisation selector at admin.9pic.ai. Must match the org the API key belongs to. |
event_id | number | Shown next to each event in the dashboard event list, or returned by List Events. |
request_id | string (UUID) | Returned by selfie-driven endpoints (Face Search — 9Pic FaceFind, Video Clipping Search — 9Pic Motion). Identifies a single search; reuse it across pagination, retries, and downloads. |
image_id | string (UUID) | Returned inside data.images[].image_id from photo and search endpoints. Identifies a single source photo. |
bib | string | The participant’s BIB number, used by 9Pic BibTrack (BIB Search). Treated as case-insensitive on the server. |
URL Path Conventions
- Org-scoped:
/api/v1/ext/{org_id}/...
- Event-scoped:
/api/v1/ext/{org_id}/event/{event_id}/...
- Request-id-scoped:
.../{request_id} where request_id is a UUID returned by a previous selfie POST.
Response Envelope
Every endpoint — success or error — returns the same three-key envelope:
{
"responseType": "success",
"message": "Records for all images",
"data": {
"images": [],
"pagination": { "total": 0, "currentPage": 0, "totalPages": 0, "hasNextPage": false, "hasPreviousPage": false, "page_size": 32 }
}
}
| Field | Type | Description |
|---|
responseType | string | "success" on any 2xx response, "error" on any 4xx/5xx response. |
message | string | Human-readable status or error message. Safe to log; not a stable contract — never branch on its exact text. |
data | object | null | The endpoint payload on success, null on every error. The payload schema is documented per endpoint. |
This applies to every /api/v1/ext/... endpoint, including List Events, Event Details, All Photos, BIB Search, Face Search, Video Clipping Search, Download Original Photos, and Ping. Endpoint-specific fields like request_id, confidence_percentage, status, videos, pagination, etc. always live inside data.
Error envelope
Errors use the same shape with data: null:
{
"responseType": "error",
"message": "BIB number 1234 not found for this event",
"data": null
}
The HTTP status code carries the error type; see Errors for the full status code table.
All datetime fields are ISO 8601 strings:
Date-only fields are not used. When a datetime field is unset, the value is null.
Listing and search endpoints return a PaginationInfo object. Defaults and maximums vary per endpoint.
See the canonical model and per-endpoint limits on Pagination Model.
Some empty-result responses return currentPage: 0 and totalPages: 0. Treat those values as “no page is available” rather than as a normal page number.
request_id Idempotency
Selfie-driven searches use a POST-once / GET-many pattern. The request_id is the canonical identifier for a single selfie search, not a session token.
| Action | Use |
|---|
| First search | POST /faces (or POST /video-clipping/search-by-selfie) without request_id. The server generates one and returns it. |
| Pagination, refresh, deep-links | GET /{request_id} reads cached results and never re-runs face matching. |
| Retry on POST failure | Retry with the same request_id. Successful retries resolve to the cached result. |
| Cross-feature reuse | Reuse the Face Search request_id when starting Video Clipping Search, and as identifier (with method: "selfie") on Download Original Photos. One selfie upload powers all three. |
A new POST with a fresh request_id (or no request_id) is treated as a brand-new billable search. Avoid issuing fresh POSTs on every page change, refresh, or component remount.
Persist request_id in URL state (for example, /results/:request_id) so refreshes and deep-links survive without re-uploading the selfie.
See Face Search and Video Clipping Search for endpoint-specific behaviour.
Async Endpoints
Video Clipping Search is asynchronous: POST returns immediately with data.status: "processing" and data.request_id. Poll GET /video-clipping/{request_id} until data.status is anything other than processing. Recommended interval: exponential backoff starting at 3s.
Host Validation
Requests are validated against an approved hostname allowlist. Calls from non-approved hosts return 403. The production allowlist includes api.9pic.ai. Talk to support if you need an additional host enabled for testing.
Error Codes
A short error table is included on every endpoint page. The full list of status codes, common causes, and recommended client behaviour lives on the Errors page.