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

# Search Response Models

> Response envelopes for all-photos, BIB search, face search, and video clipping search APIs

## Overview

All search and photo endpoints share the unified envelope `{ responseType, message, data }` documented in [Conventions](/api-reference/conventions#response-envelope). The endpoint-specific payload always lives inside `data` — for photo and BIB search this is `images[]` plus `pagination`; for face search and video clipping it also includes `request_id` and the relevant search/job state.

## Shared Photo Data Shape

Photo list, BIB search, and face search responses all carry the same `data` payload (`images[]` + `pagination`). Face search additionally includes `request_id` and `confidence_percentage` inside `data`.

```json theme={null}
{
  "images": [],
  "pagination": {
    "total": 0,
    "currentPage": 0,
    "totalPages": 0,
    "hasNextPage": false,
    "hasPreviousPage": false,
    "page_size": 32
  }
}
```

| Field        | Type   | Description                                                             |
| ------------ | ------ | ----------------------------------------------------------------------- |
| `images`     | array  | List of <a href="/api-reference/models/image">ImageItem</a> objects.    |
| `pagination` | object | <a href="/api-reference/models/pagination">PaginationInfo</a> metadata. |

## AllPhotosResponse

Returned by [All Photos](/api-reference/all-photos).

```json theme={null}
{
  "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 | Always `"success"` for 2xx responses. See [Conventions](/api-reference/conventions#response-envelope). |
| `message`         | string | Human-readable status message.                                                                         |
| `data.images`     | array  | List of <a href="/api-reference/models/image">ImageItem</a> objects.                                   |
| `data.pagination` | object | <a href="/api-reference/models/pagination">PaginationInfo</a> metadata.                                |

## BibSearchResponse

Returned by [BIB Search](/api-reference/bib-search).

```json theme={null}
{
  "responseType": "success",
  "message": "Records for bib number 1234",
  "data": {
    "images": [],
    "pagination": {
      "total": 0,
      "currentPage": 0,
      "totalPages": 0,
      "hasNextPage": false,
      "hasPreviousPage": false,
      "page_size": 32
    }
  }
}
```

| Field             | Type   | Description                                                             |
| ----------------- | ------ | ----------------------------------------------------------------------- |
| `responseType`    | string | Always `"success"` for 2xx responses.                                   |
| `message`         | string | Human-readable status message.                                          |
| `data.images`     | array  | List of <a href="/api-reference/models/image">ImageItem</a> objects.    |
| `data.pagination` | object | <a href="/api-reference/models/pagination">PaginationInfo</a> metadata. |

## FaceSearchResponse

Returned by [Face Search](/api-reference/face-search) (both POST and GET). The `request_id` and `confidence_percentage` are nested inside `data` so the envelope stays uniform across all endpoints.

```json theme={null}
{
  "responseType": "success",
  "message": "Computed face search",
  "data": {
    "request_id": "ddc661a7-8861-4793-9437-af42a82d12f8",
    "confidence_percentage": 90,
    "images": [],
    "pagination": {
      "total": 0,
      "currentPage": 0,
      "totalPages": 0,
      "hasNextPage": false,
      "hasPreviousPage": false,
      "page_size": 32
    }
  }
}
```

| Field                        | Type           | Description                                                                                                                |
| ---------------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `responseType`               | string         | Always `"success"` for 2xx responses.                                                                                      |
| `message`                    | string         | Human-readable status message.                                                                                             |
| `data.request_id`            | string         | Request UUID associated with the search. See [request\_id idempotency](/api-reference/conventions#request-id-idempotency). |
| `data.confidence_percentage` | number \| null | Confidence threshold used to filter face matches. May be `null` when no threshold is configured.                           |
| `data.images`                | array          | List of <a href="/api-reference/models/image">ImageItem</a> objects.                                                       |
| `data.pagination`            | object         | <a href="/api-reference/models/pagination">PaginationInfo</a> metadata.                                                    |

## TimelineDaysResponse

Returned by `GET /timeline/days` (see [Photo Timeline](/api-reference/photo-timeline)). Days are ordered ascending by date and `hours[]` is ordered ascending by hour.

```json theme={null}
{
  "responseType": "success",
  "message": "Timeline buckets",
  "data": {
    "days": [
      {
        "date": "2024-01-01",
        "total": 125,
        "hours": [
          { "hour": 10, "count": 98 },
          { "hour": 11, "count": 27 }
        ]
      }
    ]
  }
}
```

| Field          | Type   | Description                                                     |
| -------------- | ------ | --------------------------------------------------------------- |
| `responseType` | string | Always `"success"` for 2xx responses.                           |
| `message`      | string | Human-readable status message.                                  |
| `data.days`    | array  | List of <a href="#timelinedayitem">TimelineDayItem</a> objects. |

## TimelineDayItem

Each entry inside `data.days[]` for `GET /timeline/days`.

```json theme={null}
{
  "date": "2024-01-01",
  "total": 125,
  "hours": [
    { "hour": 10, "count": 98 },
    { "hour": 11, "count": 27 }
  ]
}
```

| Field   | Type   | Description                                                                                     |
| ------- | ------ | ----------------------------------------------------------------------------------------------- |
| `date`  | string | UTC calendar date in `YYYY-MM-DD` format.                                                       |
| `total` | number | Total photos captured on this date.                                                             |
| `hours` | array  | List of <a href="#timelinehourbucket">TimelineHourBucket</a> entries ordered by ascending hour. |

## TimelineHourBucket

Each entry inside `data.days[].hours[]` for `GET /timeline/days`.

```json theme={null}
{
  "hour": 10,
  "count": 98
}
```

| Field   | Type   | Description                             |
| ------- | ------ | --------------------------------------- |
| `hour`  | number | Hour of the day in UTC (`0`–`23`).      |
| `count` | number | Number of photos captured in this hour. |

## TimelineImagesResponse

Returned by `GET /timeline/images` (see [Photo Timeline](/api-reference/photo-timeline)). Reuses the shared photo data shape (`images[]` + `pagination`) ordered by ascending capture timestamp.

```json theme={null}
{
  "responseType": "success",
  "message": "Records for timeline window",
  "data": {
    "images": [],
    "pagination": {
      "total": 0,
      "currentPage": 0,
      "totalPages": 0,
      "hasNextPage": false,
      "hasPreviousPage": false,
      "page_size": 32
    }
  }
}
```

| Field             | Type   | Description                                                             |
| ----------------- | ------ | ----------------------------------------------------------------------- |
| `responseType`    | string | Always `"success"` for 2xx responses.                                   |
| `message`         | string | Human-readable status message.                                          |
| `data.images`     | array  | List of <a href="/api-reference/models/image">ImageItem</a> objects.    |
| `data.pagination` | object | <a href="/api-reference/models/pagination">PaginationInfo</a> metadata. |

## VideoClippingStartResponse

Returned by `POST /video-clipping/search-by-selfie` (see [Video Clipping Search](/api-reference/video-clipping-search)). The status is **always** `processing` on success — use the GET endpoint to wait for the terminal status.

```json theme={null}
{
  "responseType": "success",
  "message": "Video clipping search started. Poll GET /api/v1/ext/{org_id}/event/{event_id}/video-clipping/{request_id} for results.",
  "data": {
    "status": "processing",
    "request_id": "ddc661a7-8861-4793-9437-af42a82d12f8"
  }
}
```

| Field             | Type   | Description                                                                                           |
| ----------------- | ------ | ----------------------------------------------------------------------------------------------------- |
| `responseType`    | string | Always `"success"` for 2xx responses.                                                                 |
| `message`         | string | Human-readable status message describing what happens next.                                           |
| `data.status`     | string | Always `"processing"` when the request was accepted.                                                  |
| `data.request_id` | string | UUID for this video clipping search. Reuse with the GET endpoint and to download the resulting clips. |

## VideoClippingResultResponse

Returned by `GET /video-clipping/{request_id}` (see [Video Clipping Search](/api-reference/video-clipping-search)).

```json theme={null}
{
  "responseType": "success",
  "message": "Video clipping completed",
  "data": {
    "request_id": "ddc661a7-8861-4793-9437-af42a82d12f8",
    "status": "completed",
    "videos": [],
    "error": null,
    "clip_errors": []
  }
}
```

| Field              | Type           | Description                                                                                                                         |
| ------------------ | -------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `responseType`     | string         | Always `"success"` for 2xx responses.                                                                                               |
| `message`          | string         | Human-readable status message.                                                                                                      |
| `data.request_id`  | string         | Request UUID echoed from the original `POST`.                                                                                       |
| `data.status`      | string         | One of `processing`, `completed`, `no_matches`, `no_segments`, `clip_failed`, `failed`.                                             |
| `data.videos`      | array          | List of <a href="#videoclipitem">VideoClipItem</a> objects. Empty until `status` is `completed`.                                    |
| `data.error`       | string \| null | Top-level error message when the job fails before any clips are generated (status `failed`).                                        |
| `data.clip_errors` | array          | List of <a href="#videocliperroritem">VideoClipErrorItem</a>. Populated for `clip_failed` and partially-failed `completed` results. |

## VideoClipItem

Each entry inside `data.videos[]` returned by `GET /video-clipping/{request_id}` when `status` is `completed`.

```json theme={null}
{
  "uuid": "5e9a7cc1-0bcb-4a4d-b6e3-6c0b25c57a08",
  "video_url": "https://photos.9pic.ai/vids/456/motion/selfie/ddc661a7-8861-4793-9437-af42a82d12f8/5e9a7cc1-0bcb-4a4d-b6e3-6c0b25c57a08.mp4",
  "source_video_key": "12",
  "start_time": "00:00:42",
  "end_time": "00:00:52"
}
```

| Field              | Type           | Description                                                                   |
| ------------------ | -------------- | ----------------------------------------------------------------------------- |
| `uuid`             | string         | Unique identifier for the generated video clip.                               |
| `video_url`        | string         | Public URL where the clipped MP4 can be downloaded or streamed.               |
| `source_video_key` | string \| null | Identifier of the configured event video source this clip was extracted from. |
| `start_time`       | string \| null | Clip start timestamp inside the source video (`HH:MM:SS`).                    |
| `end_time`         | string \| null | Clip end timestamp inside the source video (`HH:MM:SS`).                      |

## VideoClipErrorItem

Each entry inside `data.clip_errors[]` when the clipping service rejected one or more segments.

```json theme={null}
{
  "video_key": "34",
  "error": "Clipping service returned 500",
  "status_code": 500
}
```

| Field         | Type           | Description                                                     |
| ------------- | -------------- | --------------------------------------------------------------- |
| `video_key`   | string \| null | Identifier of the event video source that failed to be clipped. |
| `error`       | string \| null | Human-readable error message returned by the clipping service.  |
| `status_code` | number \| null | HTTP status code returned by the clipping service, if any.      |

## Used By

| Model                         | Endpoint                                                                 |
| ----------------------------- | ------------------------------------------------------------------------ |
| `AllPhotosResponse`           | [All Photos](/api-reference/all-photos)                                  |
| `BibSearchResponse`           | [BIB Search](/api-reference/bib-search)                                  |
| `FaceSearchResponse`          | [Face Search](/api-reference/face-search) (POST and GET)                 |
| `TimelineDaysResponse`        | [Photo Timeline](/api-reference/photo-timeline) — `GET /timeline/days`   |
| `TimelineDayItem`             | Inside `TimelineDaysResponse.data.days[]`                                |
| `TimelineHourBucket`          | Inside `TimelineDaysResponse.data.days[].hours[]`                        |
| `TimelineImagesResponse`      | [Photo Timeline](/api-reference/photo-timeline) — `GET /timeline/images` |
| `VideoClippingStartResponse`  | [Video Clipping Search](/api-reference/video-clipping-search) — POST     |
| `VideoClippingResultResponse` | [Video Clipping Search](/api-reference/video-clipping-search) — GET      |
| `VideoClipItem`               | Inside `VideoClippingResultResponse.data.videos[]`                       |
| `VideoClipErrorItem`          | Inside `VideoClippingResultResponse.data.clip_errors[]`                  |
