Skip to main content

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

ParameterTypeDefaultMaxDescription
cursorstringOpaque cursor from a previous response’s next_cursor field
limitinteger30100Number of results to return
Both parameters are optional. If omitted, the API returns the first 30 results.
# 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:
{
  "items": [ ... ],
  "next_cursor": "eyJpZCI6NjB9"
}
FieldTypeDescription
itemsarrayThe result items for this page
next_cursorstring | nullOpaque 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

The Link header is provided for compatibility with tooling that expects RFC 8288 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:
RelationDescriptionPresent when
nextURL for the next page (includes cursor)next_cursor is not null
firstURL for the first page (no cursor)Always
When next is absent, you have reached the last page.
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.

Full Example

Request

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

Response Headers

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

{
  "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

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

JavaScript / TypeScript

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