关于分页
当 REST API 的响应包含大量结果时,GitHub 会对结果进行分页,只返回其中的一部分。例如,GET /repos/octocat/Spoon-Knife/issues 只会返回 octocat/Spoon-Knife 仓库的 30 条 issue,尽管该仓库有超过 1600 条未关闭的 issue。这样可以让服务器和用户更容易处理响应。
您可以使用响应中的 link 头部来请求额外的数据页面。如果某个端点支持 per_page 查询参数,您可以控制每页返回的结果数量。
本文演示了如何为分页响应请求额外的结果页面、如何更改每页返回的结果数量,以及如何编写脚本获取多个结果页面。
使用 link 头部
当响应被分页时,响应头部会包含一个 link 头部。如果端点不支持分页,或所有结果都能放在单页中,则不会出现 link 头部。
link 头部包含可用于获取额外结果页面的 URL。例如,前一页、下一页、第一页和最后一页的链接。
要查看特定端点的响应头部,您可以使用 curl、GitHub CLI,或使用您所用的请求库。若使用请求库,请参考该库的文档以查看响应头部。若使用 curl 或 GitHub CLI,请在请求时加入 --include 参数。例如
curl --include --request GET \
--url "https://api.github.com/repos/octocat/Spoon-Knife/issues" \
--header "Accept: application/vnd.github+json"
如果响应被分页,link 头部大致如下所示
link: <https://api.github.com/repositories/1300192/issues?page=2>; rel="prev", <https://api.github.com/repositories/1300192/issues?page=4>; rel="next", <https://api.github.com/repositories/1300192/issues?page=515>; rel="last", <https://api.github.com/repositories/1300192/issues?page=1>; rel="first"
link 头部提供了前一页、下一页、第一页和最后一页的 URL
- 前一页的 URL 后面跟着
rel="prev"。 - 下一页的 URL 后面跟着
rel="next"。 - 最后一页的 URL 后面跟着
rel="last"。 - 第一页的 URL 后面跟着
rel="first"。
在某些情况下,这些链接只会出现一部分。例如,如果您位于结果的第一页,则不会包含指向前一页的链接;如果无法计算最后一页,则不会包含指向最后一页的链接。
您可以使用 link 头部中的 URL 来请求另一页结果。例如,根据前面的示例请求最后一页结果
curl --include --request GET \
--url "https://api.github.com/repositories/1300192/issues?page=515" \
--header "Accept: application/vnd.github+json"
link 头部中的 URL 使用查询参数来指示应返回哪一页结果。不同端点的 link URL 可能使用不同的查询参数,但每个分页端点都会使用 page、before/after 或 since 查询参数。(有些端点使用 since 参数来实现除分页之外的功能。)无论如何,您都可以使用 link 头部中的 URL 来获取额外的结果页面。有关查询参数的更多信息,请参阅 REST API 入门指南。
更改每页项目数量
如果端点支持 per_page 查询参数,则您可以控制每页返回的结果数量。有关查询参数的更多信息,请参阅 REST API 入门指南。
例如,下面的请求使用 per_page 查询参数将每页返回两条结果
curl --include --request GET \
--url "https://api.github.com/repos/octocat/Spoon-Knife/issues?per_page=2" \
--header "Accept: application/vnd.github+json"
per_page 参数会自动包含在 link 头部中。例如
link: <https://api.github.com/repositories/1300192/issues?per_page=2&page=2>; rel="next", <https://api.github.com/repositories/1300192/issues?per_page=2&page=7715>; rel="last"
使用分页进行脚本编写
您无需手动复制 link 头部中的 URL,而是可以编写脚本来获取多个结果页面。
以下示例使用 JavaScript 和 GitHub 的 Octokit.js 库。有关 Octokit.js 的更多信息,请参阅 REST API 入门指南以及 Octokit.js README。
使用 Octokit.js 分页方法的示例
要使用 Octokit.js 获取分页结果,您可以调用 octokit.paginate()。octokit.paginate() 会持续获取下一页,直到到达最后一页,然后将所有结果返回为一个数组。有些端点会在对象中返回分页结果数组,而不是直接返回数组。octokit.paginate() 始终返回项目数组,即使原始结果是对象。
例如,以下脚本获取 octocat/Spoon-Knife 仓库的所有 issue。虽然每次请求 100 条 issue,但该函数会一直运行,直到获取完所有页面的数据后才返回结果。
import { Octokit } from "octokit";
const octokit = new Octokit({ });
const data = await octokit.paginate("GET /repos/{owner}/{repo}/issues", {
owner: "octocat",
repo: "Spoon-Knife",
per_page: 100,
headers: {
"X-GitHub-Api-Version": "2026-03-10",
},
});
console.log(data)
import { Octokit } from "octokit";
const octokit = new Octokit({ });
const data = await octokit.paginate("GET /repos/{owner}/{repo}/issues", {
owner: "octocat",
repo: "Spoon-Knife",
per_page: 100,
headers: {
"X-GitHub-Api-Version": "2026-03-10",
},
});
console.log(data)
您可以向 octokit.paginate() 传入可选的映射函数,以在未到达最后一页之前结束分页,或通过仅保留响应的子集来降低内存占用。您也可以使用 octokit.paginate.iterator() 来一次遍历单页而不是一次性请求所有页面。更多信息,请参阅 Octokit.js 文档。
创建分页方法的示例
如果您使用的语言或库没有自带分页方法,您可以自行实现一个分页方法。以下示例仍然使用 Octokit.js 发起请求,但不依赖 octokit.paginate()。
getPaginatedData 函数使用 octokit.request() 向端点发起请求。响应中的数据会由 parseData 进行处理,以应对未返回数据或返回对象而非数组的情况。处理后的数据随后被追加到一个列表中,该列表保存了迄今为止收集的所有分页数据。如果响应包含 link 头部且该头部中有指向下一页的链接,函数会使用正则表达式模式(nextPattern)提取下一页的 URL。然后函数使用此新 URL 重复上述步骤。当 link 头部不再包含指向下一页的链接时,函数返回所有结果。
import { Octokit } from "octokit";
const octokit = new Octokit({ });
async function getPaginatedData(url) {
const nextPattern = /(?<=<)([\S]*)(?=>; rel="next")/i;
let pagesRemaining = true;
let data = [];
while (pagesRemaining) {
const response = await octokit.request(`GET ${url}`, {
per_page: 100,
headers: {
"X-GitHub-Api-Version":
"2026-03-10",
},
});
const parsedData = parseData(response.data)
data = [...data, ...parsedData];
const linkHeader = response.headers.link;
pagesRemaining = linkHeader && linkHeader.includes(`rel=\"next\"`);
if (pagesRemaining) {
url = linkHeader.match(nextPattern)[0];
}
}
return data;
}
function parseData(data) {
// If the data is an array, return that
if (Array.isArray(data)) {
return data
}
// Some endpoints respond with 204 No Content instead of empty array
// when there is no data. In that case, return an empty array.
if (!data) {
return []
}
// Otherwise, the array of items that we want is in an object
// Delete keys that don't include the array of items
delete data.incomplete_results;
delete data.repository_selection;
delete data.total_count;
// Pull out the array of items
const namespaceKey = Object.keys(data)[0];
data = data[namespaceKey];
return data;
}
const data = await getPaginatedData("/repos/octocat/Spoon-Knife/issues");
console.log(data);
import { Octokit } from "octokit";
const octokit = new Octokit({ });
async function getPaginatedData(url) {
const nextPattern = /(?<=<)([\S]*)(?=>; rel="next")/i;
let pagesRemaining = true;
let data = [];
while (pagesRemaining) {
const response = await octokit.request(`GET ${url}`, {
per_page: 100,
headers: {
"X-GitHub-Api-Version":
"2026-03-10",
},
});
const parsedData = parseData(response.data)
data = [...data, ...parsedData];
const linkHeader = response.headers.link;
pagesRemaining = linkHeader && linkHeader.includes(`rel=\"next\"`);
if (pagesRemaining) {
url = linkHeader.match(nextPattern)[0];
}
}
return data;
}
function parseData(data) {
// If the data is an array, return that
if (Array.isArray(data)) {
return data
}
// Some endpoints respond with 204 No Content instead of empty array
// when there is no data. In that case, return an empty array.
if (!data) {
return []
}
// Otherwise, the array of items that we want is in an object
// Delete keys that don't include the array of items
delete data.incomplete_results;
delete data.repository_selection;
delete data.total_count;
// Pull out the array of items
const namespaceKey = Object.keys(data)[0];
data = data[namespaceKey];
return data;
}
const data = await getPaginatedData("/repos/octocat/Spoon-Knife/issues");
console.log(data);