# Pagination

> How to page through long lists in the Virlo API: which parameters each endpoint takes and how to tell when you have reached the last page.

Source: https://dev.virlo.ai/docs/pagination
Markdown: https://dev.virlo.ai/docs/pagination.md
Section: How the API works

## About the Virlo API (applies to every page)

- Base URL: `https://api.virlo.ai/v1`. Every request needs the header `Authorization: Bearer YOUR_API_KEY` (keys start with `virlo_tkn_`).
- Responses are JSON inside a `data` field, except the `/v1/webhooks` endpoints, which return the object or array directly. Field names are snake_case.
- Prices are in US dollars from a prepaid balance. 1 credit = $0.01. The `X-Cost` response header on each successful response is the exact charge. Errors are free.
- Slow jobs return an ID. Check its status every 15 seconds (or whatever `retry_after_seconds` says) until `finalized` is `true`.
- All docs pages: https://dev.virlo.ai/llms.txt. Every page in one file: https://dev.virlo.ai/llms-full.txt. MCP server for AI assistants: https://dev.virlo.ai/api/mcp/mcp.

---

Long lists come in pages. Keep asking for the next page until you hit that list's stop rule.

**At a glance**

- **What it does:** Splits long lists, like an agent's videos, into pages.
- **You send:** `limit` (items per page) and `page` (starting at 1). A few lists take `offset` or `cursor` instead.
- **You get back:** One page of items, plus fields that say whether there is another page.
- **Cost:** Each page is its own request. Paid lists charge per page: trending sounds is $0.25 a page.
- **When to stop:** See [the table](https://dev.virlo.ai/docs/pagination#parameters). A short page does not always mean the end.

## Which parameters each list takes

| List | Send | `limit` default / max | Stop when |
| - | - | - | - |
| An agent's videos, slideshows, ads, and creator outliers | `page` | 50 / 100 | [Empty page](https://dev.virlo.ai/docs/pagination#shape-a) |
| An agent's hashtags, trend history, and analysis history | `page` | 50 / 100 | [`has_next_page` is `false`](https://dev.virlo.ai/docs/pagination#shape-b) |
| Sound and hook lists, an agent's sounds and hooks, tracked creators and videos | `page` | 20 / 100 | [`has_next_page` is `false`](https://dev.virlo.ai/docs/pagination#shape-b) |
| A tracked creator's posts | `page` | 50 / 200 (`400` if over) | [`has_next_page` is `false`](https://dev.virlo.ai/docs/pagination#shape-b) |
| Your agents (`GET /v1/agents`) and an agent's runs | `page` | 50 / 100 | [Page shorter than the returned `limit`](https://dev.virlo.ai/docs/pagination#shape-c) |
| Your past lookups (`GET /v1/satellite/runs`) | `offset` | 25 / 100 | [You have `total` items](https://dev.virlo.ai/docs/pagination#shape-a) |
| Videos from one lookup (`.../runs/:run_id/videos`) | `offset` | 25 / 200 (`400` if over) | [You have `total` items](https://dev.virlo.ai/docs/pagination#shape-a) |
| A webhook's delivery log | `cursor` | 50 / 100 | [`next_cursor` is `null`](https://dev.virlo.ai/docs/pagination#cursor) |

- `limit` (integer, optional): Items per page. Over the maximum, most lists use the maximum; those marked "`400` if over" return a `400` error. Below 1 is an error on most lists.
- `offset` (integer, optional): How many items to skip. Page 2 with `limit=25` is `offset=25`.

> **Note:** Only the two lookup lists take `offset`, and they reject `page`. Many others show `offset` in responses but take `page`. Most other lists reject `offset` with a `400`. Your agents, an agent's runs, and an agent's ads ignore it and return page 1 every time, so an `offset` loop never ends.

## Lists with a total

Items sit in `data` (under `videos`, `runs`, and so on) beside `total`, `limit`, and `offset`.

On an agent's lists, stop only at an empty page. A page can come back short with more still to come. `total` is approximate and items can repeat across pages, so skip any `id` you already have.

**GET /v1/agents/:id/videos?limit=50&page=2:**

```json
{
  "data": {
    "agent_id": "2b1f9c3d-...",
    "total": 283,
    "limit": 50,
    "offset": 50,
    "videos": [
      { "id": "e30b40b1-...", "url": "https://...", "views": 11166267 }
    ]
  }
}
```

## Lists with a pagination block

A `pagination` object sits next to `data`. Keep going while `has_next_page` is `true`.

> **Note:** On trending sounds (sorted by `videos_7d` or `videos_30d`), breakout sounds, an agent's sounds and hashtags, and hook trending and search, `total` and `total_pages` can be too low. Below, `total: 21` just means "more than 20". Follow `has_next_page`, and stop if a page comes back empty. Hook trending and search end at result 1,000; a page past that returns `400`.

**GET /v1/sounds/trending:**

```json
{
  "data": [
    { "id": "4d06645e-...", "title": "PASSO BEM SOLTO (Slowed)", "platform": "youtube" }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 21,
    "total_pages": 2,
    "has_next_page": true,
    "has_prev_page": false
  },
  "sort": "videos_7d"
}
```

## Lists with only a count

Only `GET /v1/agents` and `GET /v1/agents/:id/runs` work this way. The list sits in `data.agents` or `data.runs`, with `count` (items on this page) and no `total`. Stop at the first page shorter than the `limit` in the response, not the one you sent: a `limit` over 100 is lowered to 100.

**GET /v1/agents:**

```json
{
  "data": {
    "limit": 50,
    "page": 1,
    "count": 3,
    "agents": [
      { "id": "2b1f9c3d-...", "name": "Skincare Routine Research", "is_recurring": true }
    ]
  }
}
```

## Lists with a cursor

A webhook's delivery log has no `data` wrapper. It returns `endpoint_id`, `deliveries`, `limit`, and `next_cursor`. Send `next_cursor` back as `cursor` until it comes back `null`. Replace its `+` with `%2B` (URL-encoding) or you get a `400`. The last page can be empty. [Full details](https://dev.virlo.ai/docs/webhooks#list-deliveries).

**GET /v1/webhooks/:id/deliveries?limit=2:**

```json
{
  "endpoint_id": "b64573fc-...",
  "deliveries": [
    { "id": "718bdf28-...", "event_type": "content_research_agent.run.completed", "status": "pending" }
  ],
  "limit": 2,
  "next_cursor": "2026-09-24T17:32:27.503726+00:00"
}
```

## Results that aren't paged

These return everything in one response. Some take `limit`, but none has a second page.

- An agent's benchmarks, audience affinity, activity, events, proposals, and similar creators. Activity, events, and proposals show a `count` but ignore `page`, so don't loop on them.
- Ranked lists of up to 100: `GET /v1/hashtags`, `GET /v1/videos/digest` (both also per platform), and `GET /v1/trends`. On hashtags, `page=2` repeats page 1 and is still charged.
- `GET /v1/trends/emerging`: `limit` default 20, max 50 (`400` if over).
- Snapshots of a tracked creator or video (default 30) and a sound's usage history (default 90): `limit` max 365 (`400` if over).
- `GET /v1/webhooks`: a plain array with no `data` wrapper.

---

More in How the API works:

- [How results load](https://dev.virlo.ai/docs/async-data.md)
- [Rate limits](https://dev.virlo.ai/docs/rate-limits.md)
- [Webhooks](https://dev.virlo.ai/docs/webhooks.md)
- [Errors](https://dev.virlo.ai/docs/errors.md)
- [Workflow recipes](https://dev.virlo.ai/docs/recipes.md)
- [API playground](https://dev.virlo.ai/docs/playground.md)
