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

# Pagination

> How to paginate through API results

# Pagination

All list endpoints in the JJHub API return paginated results using **cursor-based** pagination. Each response includes a `next_cursor` field in the body and a `Link` header for navigation.

## Query Parameters

| Parameter | Type    | Default | Max   | Description                                                  |
| --------- | ------- | ------- | ----- | ------------------------------------------------------------ |
| `cursor`  | string  | --      | --    | Opaque cursor from a previous response's `next_cursor` field |
| `limit`   | integer | `30`    | `100` | Number of results to return                                  |

Both parameters are optional. If omitted, the API returns the first 30 results.

```bash theme={null}
# First page, default 30 results
curl "https://api.jjhub.tech/api/repos/owner/repo/issues" \
  -H "Authorization: token jjhub_xxxxx"

# 50 results per page
curl "https://api.jjhub.tech/api/repos/owner/repo/issues?limit=50" \
  -H "Authorization: token jjhub_xxxxx"

# Next page using cursor from previous response
curl "https://api.jjhub.tech/api/repos/owner/repo/issues?limit=50&cursor=eyJpZCI6MTAwfQ" \
  -H "Authorization: token jjhub_xxxxx"
```

If `limit` exceeds 100, the API clamps it to 100 silently (no error).

## Response Body

Every paginated response includes a `next_cursor` field alongside the result items:

```json theme={null}
{
  "items": [ ... ],
  "next_cursor": "eyJpZCI6NjB9"
}
```

| Field         | Type           | Description                                                                      |
| ------------- | -------------- | -------------------------------------------------------------------------------- |
| `items`       | array          | The result items for this page                                                   |
| `next_cursor` | string \| null | Opaque cursor for fetching the next page. `null` when there are no more results. |

When `next_cursor` is `null`, you have reached the end of the result set.

## Response Headers

### Link Header

The `Link` header is provided for compatibility with tooling that expects [RFC 8288](https://tools.ietf.org/html/rfc8288) navigation. It contains cursor-based URLs:

```
Link: <https://api.jjhub.tech/api/repos/owner/repo/issues?limit=30&cursor=eyJpZCI6NjB9>; rel="next",
      <https://api.jjhub.tech/api/repos/owner/repo/issues?limit=30>; rel="first"
```

The `Link` header includes only the relations that are relevant to the current page:

| Relation | Description                             | Present when              |
| -------- | --------------------------------------- | ------------------------- |
| `next`   | URL for the next page (includes cursor) | `next_cursor` is not null |
| `first`  | URL for the first page (no cursor)      | Always                    |

When `next` is absent, you have reached the last page.

<Note>
  Using the `next_cursor` field from the response body is the recommended approach. The `Link` header is provided as a convenience for tools that consume it automatically.
</Note>

## Full Example

### Request

```bash theme={null}
curl -i "https://api.jjhub.tech/api/repos/alice/my-project/issues?limit=10" \
  -H "Authorization: token jjhub_xxxxx"
```

### Response Headers

```http theme={null}
HTTP/2 200 OK
Content-Type: application/json
Link: <https://api.jjhub.tech/api/repos/alice/my-project/issues?limit=10&cursor=eyJpZCI6MTB9>; rel="next",
      <https://api.jjhub.tech/api/repos/alice/my-project/issues?limit=10>; rel="first"
```

### Response Body

```json theme={null}
{
  "items": [
    {
      "number": 1,
      "title": "Add dark mode support",
      "state": "open",
      "labels": [{"name": "enhancement", "color": "0075ca"}],
      "created_at": "2024-01-15T10:30:00Z"
    },
    {
      "number": 2,
      "title": "Fix login timeout on slow connections",
      "state": "open",
      "labels": [{"name": "bug", "color": "e11d48"}],
      "created_at": "2024-01-16T09:15:00Z"
    }
  ],
  "next_cursor": "eyJpZCI6MTB9"
}
```

## Code Examples

### curl

```bash theme={null}
# Fetch the first page
curl -s "https://api.jjhub.tech/api/repos/owner/repo/issues?limit=10" \
  -H "Authorization: token jjhub_xxxxx"
```

### JavaScript / TypeScript

```typescript theme={null}
async function fetchAllIssues(owner: string, repo: string, token: string) {
  const issues = [];
  let cursor: string | null = null;

  while (true) {
    const params = new URLSearchParams({ limit: "50" });
    if (cursor) params.set("cursor", cursor);

    const response = await fetch(
      `https://api.jjhub.tech/api/repos/${owner}/${repo}/issues?${params}`,
      { headers: { Authorization: `token ${token}` } }
    );

    const data = await response.json();
    issues.push(...data.items);

    // No more pages when next_cursor is null
    if (!data.next_cursor) break;
    cursor = data.next_cursor;
  }

  return issues;
}
```

### Using the Link Header (compatibility)

```typescript theme={null}
function getNextPageUrl(linkHeader: string | null): string | null {
  if (!linkHeader) return null;

  const links = linkHeader.split(",").map((part) => part.trim());
  for (const link of links) {
    const match = link.match(/<([^>]+)>;\s*rel="next"/);
    if (match) return match[1];
  }

  return null;
}

async function fetchAllPages(url: string, token: string) {
  const results = [];
  let nextUrl: string | null = url;

  while (nextUrl) {
    const response = await fetch(nextUrl, {
      headers: { Authorization: `token ${token}` },
    });

    const data = await response.json();
    results.push(...data.items);

    nextUrl = getNextPageUrl(response.headers.get("Link"));
  }

  return results;
}
```

## Endpoints That Support Pagination

All list endpoints support pagination, including:

* `GET /api/repos/:owner/:repo/issues`
* `GET /api/repos/:owner/:repo/landings`
* `GET /api/repos/:owner/:repo/changes`
* `GET /api/repos/:owner/:repo/bookmarks`
* `GET /api/repos/:owner/:repo/workflows`
* `GET /api/repos/:owner/:repo/runs`
* `GET /api/repos/:owner/:repo/labels`
* `GET /api/repos/:owner/:repo/milestones`
* `GET /api/repos/:owner/:repo/stargazers`
* `GET /api/user/repos`
* `GET /api/user/starred`
* `GET /api/orgs/:org/repos`
* `GET /api/orgs/:org/members`
* `GET /api/search/repositories`
* `GET /api/search/issues`
* `GET /api/search/code`
* `GET /api/search/users`

## CLI Pagination

The `jjhub` CLI handles pagination automatically. Use `--limit` (short: `-L`) to control how many results are returned. The default is 30 and the maximum is 100, consistent across all commands.

```bash theme={null}
# Default: 30 results
jjhub issue list

# Limit to 10
jjhub issue list --limit 10

# Fetch up to 100
jjhub land list --limit 100

# Works the same for all list commands
jjhub workflow list --limit 50
jjhub run list ci --limit 20
```

The CLI handles cursor iteration internally. When using `--json` output, the response includes a `next_cursor` field so scripts can implement manual cursor-based pagination if needed.
