Overview
Generate presigned download URLs for the original, unwatermarked photos belonging to a participant. Choose one of four ways to identify the photos via the method field. The shape of identifier depends on method:
bib — identifier is a BIB number string (from 9Pic BibTrack / BIB Search).
selfie — identifier is a request_id string returned by 9Pic FaceFind (Face Search).
image_id — identifier is a single image ID string (for example, an image_id returned by All Photos, BIB Search, or Face Search).
image_ids — identifier is a non-empty list of image ID strings.
Presigned URLs are time-limited and typically expire within ~1 hour. Generate them on demand rather than caching them in your client storage. Only large/original images are returned.
Endpoint
POST /api/v1/ext/{org_id}/event/{event_id}/original-photos
Path Parameters
| Parameter | Type | Required | Description |
|---|
org_id | number | Yes | Your organisation ID. |
event_id | number | Yes | The event whose photos you want to download. |
Query Parameters
| Parameter | Type | Required | Description |
|---|
page | number | No | Page number (default: 1). |
page_size | number | No | Number of photos per page (default: 30, max: 60). |
Request Body
{
"method": "selfie",
"identifier": "ddc661a7-8861-4793-9437-af42a82d12f8"
}
| Field | Type | Required | Description |
|---|
method | string | Yes | One of bib, selfie, image_id, or image_ids. |
identifier | string OR array of string | Yes | The value to look up. Type depends on method: a string when method is bib, selfie, or image_id; a non-empty array of strings when method is image_ids. |
identifier shape by method
method | identifier type | Example |
|---|
bib | string | "1234" |
selfie | string | "ddc661a7-8861-4793-9437-af42a82d12f8" |
image_id | string | "a4402318-0cf4-4e1e-b8e8-ac8e8f2fc244" |
image_ids | array of string | ["a4402318-...","f6cb3c08-..."] |
image_id body
{
"method": "image_id",
"identifier": "a4402318-0cf4-4e1e-b8e8-ac8e8f2fc244"
}
The server attempts to generate a presigned URL for the single requested image_id. If the image does not exist in storage, the API responds with 400 Bad Request (see Error Responses).
image_ids body
{
"method": "image_ids",
"identifier": [
"a4402318-0cf4-4e1e-b8e8-ac8e8f2fc244",
"f6cb3c08-2c7a-4ff7-8c1b-71b6f7d4d932"
]
}
The server attempts to generate presigned URLs for each requested image_id. Image IDs that do not exist in storage are returned in failed_urls, while valid ones are returned in presigned_urls. If every requested image ID is invalid, the API responds with 400 Bad Request (see Error Responses).
Example Request
curl -i \
-H "X-API-Key: <your_9pic_api_key>" \
-H "Content-Type: application/json" \
-d '{"method":"selfie","identifier":"ddc661a7-8861-4793-9437-af42a82d12f8"}' \
"https://api.9pic.ai/api/v1/ext/903/event/456/original-photos?page=1&page_size=60"
To download by BIB instead, send {"method": "bib", "identifier": "1234"}.
Example: download by image_id
curl -i \
-H "X-API-Key: <your_9pic_api_key>" \
-H "Content-Type: application/json" \
-d '{"method":"image_id","identifier":"a4402318-0cf4-4e1e-b8e8-ac8e8f2fc244"}' \
"https://api.9pic.ai/api/v1/ext/903/event/456/original-photos"
Example: download by image_ids
curl -i \
-H "X-API-Key: <your_9pic_api_key>" \
-H "Content-Type: application/json" \
-d '{"method":"image_ids","identifier":["a4402318-0cf4-4e1e-b8e8-ac8e8f2fc244","f6cb3c08-2c7a-4ff7-8c1b-71b6f7d4d932"]}' \
"https://api.9pic.ai/api/v1/ext/903/event/456/original-photos?page=1&page_size=60"
Example Response
bib / selfie
image_id (single)
image_ids (mixed)
image_ids (all invalid)
{
"responseType": "success",
"message": "Presigned URLs generated",
"data": {
"presigned_urls": [
{
"url": "https://be6b0590ad80e33aa59e6592da2b1bcd.r2.cloudflarestorage.com/raw-memories/imgs/456/large/a4402318-0cf4-4e1e-b8e8-ac8e8f2fc244.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...",
"filename": "a4402318-0cf4-4e1e-b8e8-ac8e8f2fc244.jpg"
}
],
"failed_urls": [],
"total_images": 47,
"successful_count": 47,
"failed_count": 0,
"identifier": "ddc661a7-8861-4793-9437-af42a82d12f8",
"pagination": {
"total": 47,
"currentPage": 1,
"totalPages": 1,
"hasNextPage": false,
"hasPreviousPage": false,
"page_size": 60
}
}
}
When the requested image_id exists, the API returns 200 OK with a single presigned URL. identifier echoes the requested image ID.{
"responseType": "success",
"message": "Presigned URLs generated",
"data": {
"presigned_urls": [
{
"url": "https://be6b0590ad80e33aa59e6592da2b1bcd.r2.cloudflarestorage.com/raw-memories/imgs/456/large/a4402318-0cf4-4e1e-b8e8-ac8e8f2fc244.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...",
"filename": "a4402318-0cf4-4e1e-b8e8-ac8e8f2fc244.jpg"
}
],
"failed_urls": [],
"total_images": 1,
"successful_count": 1,
"failed_count": 0,
"identifier": "a4402318-0cf4-4e1e-b8e8-ac8e8f2fc244",
"pagination": {
"total": 1,
"currentPage": 1,
"totalPages": 1,
"hasNextPage": false,
"hasPreviousPage": false,
"page_size": 30
}
}
}
If the requested image_id does not exist, the API instead responds with 400 Bad Request:{
"responseType": "error",
"message": "The provided image_id is invalid. The requested image was not found.",
"data": null
}
When at least one of the requested IDs is valid, the API returns 200 OK with a per-image summary. identifier is null because the request supplied a list.{
"responseType": "success",
"message": "Presigned URLs generated",
"data": {
"presigned_urls": [
{
"url": "https://be6b0590ad80e33aa59e6592da2b1bcd.r2.cloudflarestorage.com/raw-memories/imgs/456/large/a4402318-0cf4-4e1e-b8e8-ac8e8f2fc244.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...",
"filename": "a4402318-0cf4-4e1e-b8e8-ac8e8f2fc244.jpg"
}
],
"failed_urls": [
{
"image_id": "f6cb3c08-2c7a-4ff7-8c1b-71b6f7d4d932",
"error": "File not found in raw-memories bucket",
"path": "imgs/456/large/f6cb3c08-2c7a-4ff7-8c1b-71b6f7d4d932.jpg"
}
],
"total_images": 2,
"successful_count": 1,
"failed_count": 1,
"identifier": null,
"pagination": {
"total": 2,
"currentPage": 1,
"totalPages": 1,
"hasNextPage": false,
"hasPreviousPage": false,
"page_size": 60
}
}
}
When every requested image ID is invalid, the API responds with 400 Bad Request and the standard error envelope. No partial summary is returned in this case.{
"responseType": "error",
"message": "All provided image_ids are invalid. None of the requested images were found.",
"data": null
}
Response Models
| Model | Description |
|---|
| DownloadOriginalPhotosRequest | JSON body sent to this endpoint. |
| DownloadOriginalPhotosResponse | Top-level response containing download links and pagination. |
| PresignedUrlItem | Successful download-link item inside presigned_urls[]. |
| FailedUrlItem | Failed download-link item inside failed_urls[]. |
| PaginationInfo | Pagination metadata. |
To access the full set, page through the presigned_urls array using page and page_size.
Error Responses
| Status | Meaning |
|---|
400 | Validation failure on the request body: missing or unknown method, identifier type does not match the method (string vs. list of strings), identifier is empty, or every requested image_id is invalid (for method = image_id / image_ids, no matching images were found in storage). |
401 | API key is missing. |
403 | API key is invalid, inactive, or token/event ownership mismatch. |
404 | Event not found, or the supplied identifier (BIB number or request_id) has no associated photos. |
422 | Request body failed schema validation (for example, method is not one of bib, selfie, image_id, or image_ids). |
429 | Rate limit exceeded. Back off and retry. |
500 | Internal failure that prevented presigned URLs from being generated. |
See Errors for canonical descriptions and retry guidance.