文章/2026 年 GitHub Enterprise API 实操:PAT vs GitHub Apps、15K 速率上限、audit log、EMU 和 GHES 版本错位
工具评测

2026 年 GitHub Enterprise API 实操:PAT vs GitHub Apps、15K 速率上限、audit log、EMU 和 GHES 版本错位

GitHub Enterprise Cloud 和 Server 共用品牌却是两套 API 表面。鉴权方式决定你能拿到 5,000 还是 15,000 次/小时;audit log 只在每席 $21 的档位开放;EMU 账号一半文档里写得能干的事其实不能干;GHES 版本错位会让昨天能跑的代码今天 410。这篇把这些事的实际映射讲清楚。

2026年5月27日阅读时间: 13 分钟0 个主题标签
阅读过渡

上面是文章摘要,下面进入正文深读。可以配合目录逐段阅读,不会丢掉上下文。

工具评测8 个章节

上个月会议上一个 300 工程师公司的 platform engineer 坐我旁边问我,他们新搭的 GitHub Apps 集成为什么每周一早上被限流。他们从一堆 PAT 迁过来(理由是「Apps 速率上限高,文档写 15,000 一小时」),现在每周一次的依赖扫描扫所有仓库时吃 429。烧掉的量大概 9,000 calls/hour,他们以为的上限是 15,000。文档他们没读错,只是没看到限定条件那句。

GitHub 的 Enterprise API 表面属于那种「文档技术上都写了、但顺序完全不是集成工程师真正需要的顺序」的产品。头条数字(PAT 5,000、Apps 15,000)是真的,但限定条件(按用户和仓库扩展、次要限制单独触发、GHEC 和 GHES 行为不同)才是把生产搞挂的部分。这篇走一遍 GitHub Enterprise API 文档技术上覆盖了但埋得很深的那一层:什么鉴权路径真正抗规模、速率上限的悬崖在哪、audit log 给你什么值这每席 $21、EMU 静默打断了什么、怎么写代码挺过 GHES 版本错位。

五种鉴权方式,按企业规模下的耐用度排

GitHub 几年下来攒了五条鉴权路径,企业规模下并不等价:

方式 速率上限 作用域 生命周期 用在什么场景
不鉴权 60/hour/IP 仅公开读 生产环境永远不要
Personal access token (PAT) 5,000/hour 用户级 scope 用户离职就失效 一次性脚本
OAuth App 5,000/hour/app OAuth 流的用户 scope App 级 面向用户的第三方工具
GitHub App (installation token) 5,000~15,000/hour installation 级细粒度 App 级,不随用户 企业集成
Actions 内的 GITHUB_TOKEN 1,000~15,000/hour/repo workflow scope 每次 run Actions 内的 CI/CD

表里反直觉的是 GitHub Apps 这行。5,000 到 15,000 不是单一数字,是两件事的函数。GHES 装的 App 基线 5,000/hour。GHEC 装的 App 基线 15,000/hour。在基线之上,两边都按超过 20 个之后的仓库和用户线性扩展:每多一个仓库 +50/hour,每多一个用户 +50/hour,GHES 封顶 12,500,GHEC 封顶 15,000。GHEC org 上装的 App,400 仓 200 用户,吃到封顶。GHES 实例上 30 仓 25 用户,得到 5,000 +(10 × 50)+(5 × 50)= 5,750。

那个会议上的 platform engineer 公司 300 工程师几百仓但跑的是 GHES,所以实际上限是 GHES 12,500 顶减去那个早上次要 CPU-time 限制吃掉的部分。他们记住的 15,000 是 GHEC 上限,他们没有。

文档没放在一页上的速率上限算术

主要速率上限在官方速率限制页,次要限制零散各处。第一天就该知道的完整清单:

限制 触发条件 看到什么
主要,按身份 5K/15K calls/hour 403 with x-ratelimit-remaining: 0
并发请求 在飞超过 100 429 或 403
端点点数 单端点超过 900 points/min 403 with Retry-After
CPU 时间 60 秒墙时窗口超过 90 秒 CPU 403
内容创建写入 80/min 或 500/hour 403
OAuth token 申请 2,000/hour 403
Search API 不鉴权 10/min、鉴权 30/min 403

最常打人措手不及的两个是端点点数限制和内容创建限制。点数限制把每个 REST 端点当作独立的每分钟桶,所以对单端点(比如 /repos/{owner}/{repo}/issues/comments)每分钟打几千次读调用会触发点数限制,哪怕全局小时计数器还宽裕。内容创建限制打的是自动开 issue、发 status check、批量开 PR 的 bot;从外部追踪器同步状态的话,500/hour 很紧。

X-RateLimit-* 响应头报告所有这些。主要的四个是 X-RateLimit-LimitX-RateLimit-RemainingX-RateLimit-UsedX-RateLimit-Reset(Unix 时间戳)。要埋的隐藏 header 是 X-RateLimit-Resource,告诉你该次调用被哪个桶记账(coresearchcode_searchgraphqlintegration_manifestcode_scanning_uploadactions_runner_registrationdependency_snapshots)。看到限流时第一个问题是「哪个桶」,答案就在这个 header。

audit log API 就是每席 $21 的全部理由

操作上最有价值的 Enterprise-only 端点就一个:audit log。GHEC 上路径是 /enterprises/{ENTERPRISE}/audit-log,接 phrase 查询参数做过滤,返回分页事件列表。值得喂进 SIEM 的事件类型:

  • org.sso_response_succeededorg.sso_response_failed(SSO 登录尝试)
  • repo.add_member / repo.remove_member / repo.change_member_permission
  • org.update_member_repository_creation_permission
  • personal_access_token.access_grantedpersonal_access_token.access_revoked
  • oauth_authorization.createoauth_authorization.destroy
  • protected_branch.updateprotected_branch.destroy
  • repo.transferrepo.destroy

每个事件带 ISO 8601 时间戳、actor login、actor IP 地址、仓库或 org 上下文、事件特定的 metadata。数据密度足以让一个全新的 GHEC 租户每周每活跃工程师产出几百条事件。存储要按这个量规划:500 席 enterprise 每月通常产 5 万~20 万条审计事件,对象存储这个量很小,但如果想塞进关系数据库做查询不分区的话就不轻。

不显眼的坑是 API 的实时性延迟。多数事件几分钟内出现在 audit log API,但高频桶(Actions workflow run、依赖图更新)可能延迟 30~60 分钟。预期亚分钟级到达的 SIEM 规则要按你租户实际的延迟分布调一下。

定价上:首年每席每月 $21,之后按常规价。500 席 enterprise 第一年付 $126,000。audit log 端点是合规和安全团队最常拿来支撑这条预算项的功能。

代码层怎么处理速率限制

生产环境的 GitHub 集成最终都得写两块可复用胶水:一个监控 X-RateLimit-*Retry-After 的 header parser,一个在剩余配额低时节流出站调用的队列。最小可用 Python 形态用 requests

import time
import requests

class GitHubRateLimiter:
    def __init__(self):
        self.remaining = {}
        self.reset = {}

    def update(self, response: requests.Response) -> None:
        resource = response.headers.get("X-RateLimit-Resource", "core")
        remaining = int(response.headers.get("X-RateLimit-Remaining", "5000"))
        reset = int(response.headers.get("X-RateLimit-Reset", "0"))
        self.remaining[resource] = remaining
        self.reset[resource] = reset

    def should_wait(self, resource: str = "core", floor: int = 100) -> int:
        if self.remaining.get(resource, 5000) > floor:
            return 0
        return max(0, self.reset.get(resource, 0) - int(time.time()))

def call_api(url: str, token: str, limiter: GitHubRateLimiter) -> dict:
    resource = "search" if "/search/" in url else "core"
    wait = limiter.should_wait(resource)
    if wait > 0:
        time.sleep(min(wait, 300))

    headers = {
        "Authorization": f"Bearer {token}",
        "X-GitHub-Api-Version": "2026-03-10",
        "Accept": "application/vnd.github+json",
    }
    r = requests.get(url, headers=headers, timeout=30)
    limiter.update(r)

    if r.status_code == 403 and "rate limit" in r.text.lower():
        retry_after = int(r.headers.get("Retry-After", "60"))
        time.sleep(retry_after)
        return call_api(url, token, limiter)

    r.raise_for_status()
    return r.json()

这段代码里编码的三件事:

  1. 按 resource 跟踪而不是全局。 search 重的工作负载和 core REST 工作负载独立限流,单一全局计数器看不出来谁是瓶颈。
  2. Retry-After 是秒。 和 Meta 的 BUC header 不同,GitHub 的 Retry-After 按 HTTP 标准走。这里的坑反过来:被 Meta 分钟编码烫过的开发者有时会过度纠正。
  3. 403 + body 含 "rate limit" 是次要限制信号。 主要限制返回 403 + X-RateLimit-Remaining: 0。次要限制返回 403 + 另一种 body。两种都可重试,但次要限制往往需要比 Retry-After 建议的更长退避,content-creating 桶尤其如此。

EMU 边界,销售工程师跳过的部分

Enterprise Managed Users(EMU)是 GHEC 把用户身份完全交给你 IdP 管的方案。卖点很干净:每个用户账号通过 SCIM 从 Okta 或 Entra ID 同步过来,用户走 SSO 登录,IdP 删人就账号消失,命名空间公司控制。卖点和集成现实之间的差就是团队没预留的那两周。

影响集成设计的 EMU 硬性约束:

  • 不能有 personal repository。 所有 repo 必须挂在 enterprise 内的 org 下。/user/repos 创建个人 repo 端点返回 403。想要 sandbox 的开发者要么用 org 级 "sandboxes" repo,要么另开一个 GitHub.com 个人账号。
  • 不能 fork 到 enterprise 外。 fork API 在目标不在 enterprise 内时返回 403。依赖 fork 上游开源项目回馈贡献的工作流不行;支持的模式是另开一个非 EMU 账号做 OSS 贡献。
  • 不能跨 org 评论或 star。 EMU 用户没法给 github.com 公开 repo 的 issue 评论或 star。这会打断那种「开发者对外部项目评论时触发 Slack 通知」的集成。
  • 用户名变形。 EMU 用户名带 _short_code 后缀(基于 enterprise short code 衍生)。任何模式匹配 GitHub 用户名的集成(Slack handle、IdP 属性)要知道有这个后缀。
  • 不和 GitHub.com 个人账号合并。 如果开发者已有 GitHub.com 账号,EMU 不合并也不关联,是两个独立实体。

集成设计原则:任何产品特性如果依赖跨 org GitHub 活动(在 OSS 里 @ 人、profile 浏览、公开贡献),就不能只靠 EMU 账号。要么这些特性走非 EMU 服务账号,要么绕开边界设计。

GHES 版本错位与 X-GitHub-Api-Version 头

GitHub Enterprise Server 大约 3 个月一个新版本,每个版本支持 12 个月左右。2026 年生产部署里客户的版本分布大致:

GHES 版本 发布时间 生产里常见?
3.8 2025 下半年 是(最新)
3.7 2025 上半年
3.6 2024 下半年
3.5 2024 上半年 部分
3.4 及以前 2023 及以前 少见但仍存在

GHEC 加的端点带着滞后进入 GHES。audit log 的 Streaming API 是 GHEC 独占。SCIM 增强常常先在 GHEC 落地两个 GHES 版本之后才打入 GHES tag build。要覆盖这个版本跨度的客户群,代码得做两件事。

一,每个 REST 调用带 X-GitHub-Api-Version 头。不带的话 GitHub 默认 2022-11-28(支持到 2028-03-10)。当前的 2026-03-10 是更好的目标,如果客户实例较新。这个选择重要,因为新版本有破坏性变更(重命名字段、删除废弃端点),默认值不会暴露这些。

二,对每个 GHES 实例的集成会话开始时调 GET /meta 拿到当前安装的 GHES 版本。代码按这个版本分支,不假设最新。response 上的 installed_version 字段是 key。

GraphQL 有自己的版本模型。GitHub 废弃的 mutation 和类型在废弃后至少 12 个月仍可用,客户端在 introspection schema 里看到 @deprecated 注解。如果代码用 GraphQL Code Generator 或类似工具钉到一个 schema 快照,每季度针对客户群支持的最新 GHES 版本刷新一次快照。

企业规模 org 上的 REST vs GraphQL

REST 还是 GraphQL 的判断规则,企业规模下比文档暗示的更微妙。关键是点数系统。GraphQL 每次查询读 1 点、mutation 5 点,每小时上限 5,000 点。REST 等价物每次请求算 1 调用,不管复杂度。

大型 org 的算术常在读重工作流上翻向 GraphQL,因为一次 GraphQL 查询可以拉走否则要几十次 REST 调用的数据。例子:取一个仓库最近的 issue + 其评论 + label + assignee,REST 要 1+N 次(1 次列表,N 次详情)调用,GraphQL 一次查询。5,000 个 issue 时 REST 烧 5,001 调用,GraphQL 烧 1 点。GraphQL 赢。

写重工作流算术反过来。GraphQL mutation 每次 5 点对 REST 1 调用。在 REST 里批量建 1,000 个 issue 是 1,000 调用(小时限制内宽裕但撞内容创建的次要限制 80/min、500/hour)。在 GraphQL 里 1,000 mutation 是 5,000 点,正好是小时上限,内容创建次要限制依然适用。写量大时通常 REST 更好,每请求开销小,点数封顶不是瓶颈。

另一个实际差别是错误处理。REST 错误是 HTTP 状态码 + JSON body。GraphQL 永远返回 200,错误嵌在 response body 的 errors 数组里,单次查询能部分成功——某些字段填了,另一些字段带错误。集成代码要为「REST 4xx 响应」和「GraphQL 200 响应带 errors 数组」分别走两条路径。

上 GHEC 或 GHES 之前的清单

把 enterprise GitHub 集成从原型推到生产前的具体清单:

  1. 第一天就选 GitHub Apps 不选 PAT。 后期从 PAT 迁过去成本高(重发 token、重写鉴权流、重新 onboard 用户)。原型期立 App 多花约两小时,省掉整段迁移。

  2. 每个 REST 调用都钉 X-GitHub-Api-Version 显式指定日期字符串,不靠默认。等 GitHub 最终退役 2022-11-28 默认值时,不带 header 的代码会静默切到次老版本。

  3. 第一个请求就埋全部六个 X-RateLimit-* header。 包括 X-RateLimit-Resource,这是「我们小时配额用完了」和「我们在某个 resource 桶上被限流」两件事的分辨依据。

  4. 如果客户群跨 GHEC 和 GHES,按两种都规划。 特性探测优先于版本判断。集成测试跑客户群用的 GHES 版本,不光跑最新。

  5. 卖到受监管行业的话,按 audit log API ingestion 留预算。 500 席客户每月 5 万~20 万事件,这个量决定 ingestion buffer 设计、分区策略、保留策略的规模。

  6. 销售对话前把数据和用户模型对到 EMU 边界上。 卖完集成等客户铺开 EMU 才发现集成挂掉,是丢六位数年合同的那种错误。

  7. 读重批量用 GraphQL,写重批量用 REST。 点数封顶和每请求开销两个方向相反,匹配协议到工作负载形态能让有效吞吐差一个数量级。

速率上限、鉴权方法、Enterprise 独占端点的合并参考在 GitHub Enterprise API 工具页上。更大的开发者工具 API 目录在开发者工具分类

分享文章

文章概览

读完前先看这几项

分类
工具评测
阅读时间
13 分钟
提到的工具
0
返回文章列表 →

下一步

读完后可以继续回到工具目录,对比具体产品。

去看工具