Overview
Face Search is the API surface for 9Pic FaceFind, our AI face recognition photo search. Upload a participant’s selfie and the API returns paginated photos that match the selfie above the event’s confidence threshold. Each search is identified by arequest_id (UUID). You can:
- Omit
request_idand let 9Pic generate one. The generated UUID is returned in the response so you can re-fetch the same results later without re-uploading the selfie. - Pass an existing
request_idto retrieve cached results without re-running face matching.
Selfie/face search must be enabled for the event. Check the
selfie_search flag from Event Details before calling this.Why request_id Matters
A face search is expensive on our side and yours. Each POST /faces triggers an image upload, face matching against the event’s face index, confidence filtering, and a persisted result row. The request_id is what lets you do this work exactly once per user-intent and reuse the result everywhere else.
Treat request_id as the canonical identifier for a single selfie search, not as a session token. The full idempotency model is documented in Conventions.
What problem it solves
| Problem | What naive integrations do | What request_id enables |
|---|---|---|
| User refreshes the results screen | Re-upload selfie + re-run face matching | GET /faces/{request_id} returns the cached result instantly |
| User pages through results | POST again per page | One POST, many GET /faces/{request_id}?page=N calls |
Network blip on POST /faces | Treat as “new search” and re-upload | Retry the same POST with the same request_id (idempotent) |
| Download flow needs the same matched set | Re-search before download | Pass request_id as the identifier to Download Original Photos |
How it optimises your system
- Idempotent retries. If
POST /facesfails partway (timeout, 5xx, dropped connection), retrying with the samerequest_idresolves to the cached result on success — no double-charged face-match calls and no duplicate selfie uploads. - Cheap pagination. Hold the
request_idin your UI state. Every page change becomes aGET /faces/{request_id}?page=N&page_size=32instead of a re-search. - Decouple search trigger from result rendering. Your upload screen does the POST and stashes
request_id. Your results screen, share screen, and download screen all read fromGET /faces/{request_id}. None of them need the selfie file. - Stable, shareable references. A
request_idlets your backend log, audit, retry, or reconcile a specific selfie search without storing the user’s selfie image. - Avoid rate limiting. External API calls that perform face matching are subject to stricter limits than read-only calls. Reusing
request_idshifts your traffic fromPOST /faces(rate-limited, billable compute) toGET /faces/{request_id}(cheap reads), keeping integrations comfortably under quota even at peak.
Recommended Integration Sequence — “POST once, GET many”
POST once
Call
POST /faces with the user’s selfie. Do not pass request_id; let the server generate one. Read request_id from the response and persist it (URL state, local storage, your DB).GET many
Every subsequent action that needs results (pagination, refresh, deep-link, share) calls
GET /faces/{request_id}?page=N&page_size=32.Retry safely
On POST failure, retry with the same
request_id you intended for that selfie. The result is cached server-side once any POST succeeds.Hand off to download
Pass
request_id as identifier (with method: "selfie") to Download Original Photos. No new search is needed.Endpoints
POST /faces — Upload a Selfie
Uploads a selfie image and returns the matching event photos.Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
org_id | number | Yes | Your organisation ID. |
event_id | number | Yes | The event to search within. |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
page | number | No | Page number (default: 1). |
page_size | number | No | Images per page (default: 32, max: 100). |
Results are paginated. See the Pagination Model.
Multipart Form Fields
| Field | Type | Required | Description |
|---|---|---|---|
file | file | Yes | Selfie image (JPEG/PNG, max 5 MB recommended). |
request_id | string | No | UUID. If omitted, 9Pic generates one and returns it in the response. |
Example Request
request_id, add -F "request_id=<uuid>" (cURL), include it in data={"request_id": "<uuid>"} (Python), or formData.append("request_id", "<uuid>") (JavaScript).
Example Response
- Matches Found
- No Matches
GET /faces/ — Fetch Cached Search
Returns the cached result for a previous selfie search using itsrequest_id. Useful for paging through results or refreshing a UI without re-uploading the selfie.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
org_id | number | Yes | Your organisation ID. |
event_id | number | Yes | The event the search belongs to. |
request_id | string | Yes | UUID returned by a previous POST /faces call. |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
page | number | No | Page number (default: 1). |
page_size | number | No | Images per page (default: 32, max: 100). |
Results are paginated. See the Pagination Model.
Example Request
Example Response
The response shape is identical toPOST /faces. Use the same model and the same Matches Found / No Matches payloads above.
Response Models
| Model | Description |
|---|---|
| FaceSearchResponse | Top-level face search response envelope. |
| ImageItem | Image object returned inside data.images[]. |
| PaginationInfo | Pagination metadata returned inside data.pagination. |
original_url is null in the standard face search response.
Error Responses
| Status | Meaning |
|---|---|
400 | request_id is not a valid UUID, or the uploaded selfie file is empty. |
401 | API key is missing. |
403 | API key is invalid, inactive, token/event ownership mismatch, or selfie search is disabled for this event. |
404 | Event configuration not found, or request_id is unknown (GET only). |
429 | Rate limit exceeded. Back off and retry with the same request_id to hit the cache. |
500 | Internal failure that prevented the request from being recorded. |

