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