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

# List an item's comments

> One page of the comments on one item, named by its natural key or its URL.

`GET /v1/comments` returns one page of the comments on one item inside the request. There is no job to poll. YouTube videos are the only object that answers today.

<CodeGroup>
  ```bash curl theme={null}
  curl -G https://api.refmatter.com/v1/comments \
    -H "Authorization: Bearer $REFMATTER_API_KEY" \
    --data-urlencode "item=youtube:OnoNITE-CLc" \
    --data-urlencode "sort=top" \
    --data-urlencode "maxAge=15m"
  ```

  ```ts TypeScript theme={null}
  const firstPage = await refmatter.comments.list({
    item: 'youtube:OnoNITE-CLc',
    sort: 'top',
  });
  const nextPage =
    firstPage.page.nextCursor === null
      ? null
      : await refmatter.comments.list({ cursor: firstPage.page.nextCursor });
  ```
</CodeGroup>

| Parameter | Required | Values                                                          |
| --------- | -------- | --------------------------------------------------------------- |
| `item`    | no\*     | Natural key such as `youtube:OnoNITE-CLc`, or the item URL      |
| `sort`    | no       | `top`, `newest`. Default `top`                                  |
| `cursor`  | no       | Opaque cursor from a previous page                              |
| `maxAge`  | no       | `0`, or whole minutes, hours, or days up to `1d`. Default `15m` |

`item` is required when `cursor` is absent. A cursor excludes `item` and `sort`; pass it alone, optionally with `maxAge`.

The selector names the platform the same way an account selector does: a natural key, or the item's own URL. See [Naming the account](/guides/list-items#naming-the-account) for the grammar; a bare `OnoNITE-CLc` is refused, because nothing in it says which platform to read.

## What each platform offers

| Platform          | Comments      | `sort`                  | Rows per page      | Paging | `total`                       | Rows carry                                                     | Rows lack                 |
| ----------------- | ------------- | ----------------------- | ------------------ | ------ | ----------------------------- | -------------------------------------------------------------- | ------------------------- |
| `youtube`         | Videos        | `top`, `newest` (`top`) | YouTube's own page | Cursor | The exact count YouTube gives | Author, text, `publishedText`, likes, reply count, pinned flag | `publishedAt`, reply rows |
| `instagram`       | Not available | —                       | —                  | —      | —                             | —                                                              | —                         |
| `tiktok`          | Not available | —                       | —                  | —      | —                             | —                                                              | —                         |
| `meta_ad_library` | Not available | —                       | —                  | —      | —                             | —                                                              | —                         |

An item of any other platform answers `422 unsupported_object` with a hint: no other platform publishes comments to a visitor who is not signed in.

## The page

```json theme={null}
{
  "object": "list",
  "data": [
    {
      "object": "comment",
      "id": "youtube:UgwP24lIAYY5zIRtJdd4AaABAg",
      "itemId": "youtube:OnoNITE-CLc",
      "parentId": null,
      "author": {
        "object": "account",
        "id": "youtube:UC3kQx1mZr8pL0vWn2TgYbHd",
        "platform": "youtube",
        "handle": "skywatcher42",
        "name": null,
        "url": "https://www.youtube.com/channel/UC3kQx1mZr8pL0vWn2TgYbHd"
      },
      "authorIsCreator": false,
      "text": "This was incredible to watch live. The commentary from mission control made it feel like being there. 🚀",
      "publishedAt": null,
      "publishedText": "2 weeks ago (edited)",
      "stats": { "likes": 43000, "replies": 905 },
      "likesApproximate": true,
      "pinned": true
    }
  ],
  "page": {
    "nextCursor": "v2.eyJpdGVtIjoiT25vTklURS1DTGMiLCJwIjozfQ.3a7e9c1b5d0f2a8e6c4b7d9f1a3c5e8b0d2f4a6c9e1b3d5f7a0c2e4b6d8f0a1c",
    "hasMore": true
  },
  "total": 67808,
  "totalApproximate": false,
  "account": null,
  "observedAt": "2026-09-17T12:00:03.120Z",
  "cached": false,
  "usage": { "creditsCharged": 1, "lines": [{ "part": "fetch", "credits": 1 }] }
}
```

## What you get and what you do not

Every page includes `total` when YouTube reports it, and `totalApproximate` is `false` because that figure is the exact count rather than an estimate. The first page can therefore show the exact total, such as `67808`, even when the page contains only a few rows. Likes can be approximate, which is marked by `likesApproximate`.

Each row is a top-level comment. Replies are not returned as comment rows yet. `parentId` is `null` in this endpoint's normal projection. Comments provide relative `publishedText`; `publishedAt` is `null` in the normal projection.

## Freshness

`maxAge` defaults to `15m` and accepts up to `1d`. A stored comments page confirmed within the window can be served without contacting YouTube. Observations are immutable.

| `maxAge` | Behavior                          |
| -------- | --------------------------------- |
| `0`      | Always read comments live         |
| `15m`    | Default freshness window          |
| `1d`     | Maximum accepted freshness window |

## Paging

Pass `page.nextCursor` back as `cursor`, alone, optionally with `maxAge`. Cursors expire after 24 hours. A cursor is not tied to your workspace, but each workspace pays for the pages it is served.

## Credits

| Situation                                            | Credits | `cached` |
| ---------------------------------------------------- | ------- | -------- |
| Live page with at least one row                      | 1       | `false`  |
| First serving of a stored page with at least one row | 1       | `false`  |
| Repeat within `maxAge`, served from a stored page    | 0       | `true`   |
| Empty page                                           | 0       | —        |
| `maxAge=0`: a fresh page every time                  | 1       | `false`  |
| Every error                                          | 0       | —        |
| A successful read whose result could not be written  | 0       | `false`  |

`usage.creditsCharged` is the number charged in this response. Errors are always free.

At a zero balance a live page answers `402 insufficient_credits` before the platform is contacted; a repeat within your `maxAge` and an empty page are free and keep answering.

## Headers

| Header                        | On                   | Value                                     |
| ----------------------------- | -------------------- | ----------------------------------------- |
| `Refmatter-Credits-Charged`   | Every response       | Integer, `0` whenever nothing was charged |
| `Refmatter-Credits-Remaining` | Every response       | The workspace balance after this response |
| `Server-Timing`               | Successful responses | Durations for phases that ran             |
| `Cache-Control`               | Successful responses | `no-store`                                |
| `Retry-After`                 | `429`                | Whole seconds                             |

## Rate limits

If no platform request can start, the endpoint returns `429 rate_limited` with `details.retryAfterSeconds`. Wait that many seconds before trying again. Errors are free.

## Errors

| Code                                                             | HTTP      | Means                                                                    |
| ---------------------------------------------------------------- | --------- | ------------------------------------------------------------------------ |
| `invalid_input`                                                  | 400       | Missing item, bad `maxAge`, conflicting cursor fields, or invalid cursor |
| `invalid_input` with `details.reason=invalid_selector`           | 400       | The `item` value names no platform, or is not a natural key or URL       |
| `invalid_input` with `details.reason=not_available_for_platform` | 400       | A `sort` value outside `top` and `newest`                                |
| `invalid_input` with `details.reason=expired_cursor`             | 400       | Cursor expired or its page is no longer available                        |
| `invalid_input` with `details.reason=malformed_cursor`           | 400       | Cursor format is invalid                                                 |
| `invalid_input` with `details.reason=invalid_signature`          | 400       | Cursor signature is invalid                                              |
| `invalid_input` with `details.reason=unsupported_version`        | 400       | Cursor version is not supported                                          |
| `invalid_input` with `details.reason=no_next_page`               | 400       | The cursor has no next page                                              |
| `unauthenticated` / `invalid_credential`                         | 401       | Missing or invalid credential                                            |
| `not_found` / `unavailable_removed`                              | 404 / 410 | Video or comments are unavailable                                        |
| `unsupported_object`                                             | 422       | The item is not a YouTube video                                          |
| `unavailable_private` / `unavailable_restricted`                 | 422       | YouTube will not serve the video publicly                                |
| `rate_limited`                                                   | 429       | Try again after `details.retryAfterSeconds`                              |
| `source_changed` / `network_transport`                           | 502       | The source or upstream connection failed                                 |
| `route_disabled` / `route_degraded`                              | 503       | The route is unavailable                                                 |
| `network_timeout`                                                | 504       | The comments page took too long                                          |

A video with comments turned off is an empty page, not an error, and costs nothing.

## From an agent

Use `list_comments` with `item` for the first page. Pass `page.nextCursor` as `cursor` alone for the next page. The tool returns one page synchronously.

## Not in this endpoint

This endpoint does not return comment replies as rows, full item data, transcripts, or media bytes.

## Next steps

* [List an account's items](/guides/list-items)
* [Search](/guides/search)
* [Look up an item](/guides/look-up-an-item)
* [Look up an account](/guides/look-up-an-account)
* [Resolve a URL](/guides/resolve-a-url)
