> ## Documentation Index
> Fetch the complete documentation index at: https://docs.9pic.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Download Originals

> Generate presigned download links for original (unwatermarked) photos by BIB, selfie request_id, single image_id, or a list of image_ids

## 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](/api-reference/bib-search)).
* **`selfie`** — `identifier` is a `request_id` string returned by **9Pic FaceFind** ([Face Search](/api-reference/face-search)).
* **`image_id`** — `identifier` is a single image ID string (for example, an `image_id` returned by [All Photos](/api-reference/all-photos), [BIB Search](/api-reference/bib-search), or [Face Search](/api-reference/face-search)).
* **`image_ids`** — `identifier` is a non-empty list of image ID strings.

<Note>
  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.
</Note>

## 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`). |

<Note>
  This endpoint returns paginated results. See the [Pagination Model](/api-reference/models/pagination).
</Note>

## Request Body

```json theme={null}
{
  "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

```json theme={null}
{
  "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](#error-responses)).

### `image_ids` body

```json theme={null}
{
  "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](#error-responses)).

## Example Request

<CodeGroup>
  ```bash cURL theme={null}
  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"
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://api.9pic.ai/api/v1/ext/903/event/456/original-photos",
      headers={"X-API-Key": "<your_9pic_api_key>"},
      json={
          "method": "selfie",
          "identifier": "ddc661a7-8861-4793-9437-af42a82d12f8",
      },
      params={"page": 1, "page_size": 60},
  )
  print(response.status_code, response.json())
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    "https://api.9pic.ai/api/v1/ext/903/event/456/original-photos?page=1&page_size=60",
    {
      method: "POST",
      headers: {
        "X-API-Key": "<your_9pic_api_key>",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        method: "selfie",
        identifier: "ddc661a7-8861-4793-9437-af42a82d12f8",
      }),
    }
  );
  console.log(await response.json());
  ```
</CodeGroup>

To download by BIB instead, send `{"method": "bib", "identifier": "1234"}`.

### Example: download by `image_id`

<CodeGroup>
  ```bash cURL theme={null}
  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"
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://api.9pic.ai/api/v1/ext/903/event/456/original-photos",
      headers={"X-API-Key": "<your_9pic_api_key>"},
      json={
          "method": "image_id",
          "identifier": "a4402318-0cf4-4e1e-b8e8-ac8e8f2fc244",
      },
  )
  print(response.status_code, response.json())
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    "https://api.9pic.ai/api/v1/ext/903/event/456/original-photos",
    {
      method: "POST",
      headers: {
        "X-API-Key": "<your_9pic_api_key>",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        method: "image_id",
        identifier: "a4402318-0cf4-4e1e-b8e8-ac8e8f2fc244",
      }),
    }
  );
  console.log(await response.json());
  ```
</CodeGroup>

### Example: download by `image_ids`

<CodeGroup>
  ```bash cURL theme={null}
  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"
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://api.9pic.ai/api/v1/ext/903/event/456/original-photos",
      headers={"X-API-Key": "<your_9pic_api_key>"},
      json={
          "method": "image_ids",
          "identifier": [
              "a4402318-0cf4-4e1e-b8e8-ac8e8f2fc244",
              "f6cb3c08-2c7a-4ff7-8c1b-71b6f7d4d932",
          ],
      },
      params={"page": 1, "page_size": 60},
  )
  print(response.status_code, response.json())
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    "https://api.9pic.ai/api/v1/ext/903/event/456/original-photos?page=1&page_size=60",
    {
      method: "POST",
      headers: {
        "X-API-Key": "<your_9pic_api_key>",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        method: "image_ids",
        identifier: [
          "a4402318-0cf4-4e1e-b8e8-ac8e8f2fc244",
          "f6cb3c08-2c7a-4ff7-8c1b-71b6f7d4d932",
        ],
      }),
    }
  );
  console.log(await response.json());
  ```
</CodeGroup>

## Example Response

<Tabs>
  <Tab title="bib / selfie">
    ```json theme={null}
    {
      "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
        }
      }
    }
    ```
  </Tab>

  <Tab title="image_id (single)">
    When the requested `image_id` exists, the API returns `200 OK` with a single presigned URL. `identifier` echoes the requested image ID.

    ```json theme={null}
    {
      "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`:

    ```json theme={null}
    {
      "responseType": "error",
      "message": "The provided image_id is invalid. The requested image was not found.",
      "data": null
    }
    ```
  </Tab>

  <Tab title="image_ids (mixed)">
    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.

    ```json theme={null}
    {
      "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
        }
      }
    }
    ```
  </Tab>

  <Tab title="image_ids (all invalid)">
    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.

    ```json theme={null}
    {
      "responseType": "error",
      "message": "All provided image_ids are invalid. None of the requested images were found.",
      "data": null
    }
    ```
  </Tab>
</Tabs>

## Response Models

| Model                                                                                                       | Description                                                  |
| ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| <a href="/api-reference/models/downloads#downloadoriginalphotosrequest">DownloadOriginalPhotosRequest</a>   | JSON body sent to this endpoint.                             |
| <a href="/api-reference/models/downloads#downloadoriginalphotosresponse">DownloadOriginalPhotosResponse</a> | Top-level response containing download links and pagination. |
| <a href="/api-reference/models/downloads#presignedurlitem">PresignedUrlItem</a>                             | Successful download-link item inside `presigned_urls[]`.     |
| <a href="/api-reference/models/downloads#failedurlitem">FailedUrlItem</a>                                   | Failed download-link item inside `failed_urls[]`.            |
| <a href="/api-reference/models/pagination">PaginationInfo</a>                                               | 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](/api-reference/errors) for canonical descriptions and retry guidance.
