---
title: "GitHub REST API (unauthenticated): spec vs live"
date: 2026-10-30
canonical: https://apidrifter.com/research/public-api-spec-drift-2026-11/github/
part_of: https://apidrifter.com/research/public-api-spec-drift-2026-11/
---

# GitHub REST API (unauthenticated): spec vs live

Part of [We checked 13 public APIs against their own OpenAPI specs. 9 had hard mismatches.](https://apidrifter.com/research/public-api-spec-drift-2026-11/).

## Summary

- Spec: https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json (OpenAPI 3)
- Base URL: `https://api.github.com`
- Run: 2026-10-30, read-only GET requests, no credentials
- Coverage: 1254 operations in the spec, 40 requests sent, 9 successful, 40 operations observed
- Hard violations: 0 distinct schema locations
- Reviewer note: First run: gist file language is null but not nullable; event payloads match two oneOf branches. The rerun hit the anonymous rate limit (403) after nine successes, so this row understates GitHub; rerun with a token pending.

## Undocumented error responses (28)

3xx and 4xx statuses the spec does not mention, usually auth or not-found cases.

- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/branches/{branch}/protection > response 401`
  - status 401 is not documented for "GET /repos/{owner}/{repo}/branches/{branch}/protection"
  - Request: `GET /repos/octocat/Hello-World/branches/master/protection -> 401 application/json; charset=utf-8`
  - Expected: `["200","404"]`
  - Actual: `401`
  - If the spec is the source of truth: Return one of the documented statuses (200, 404) from GET /repos/{owner}/{repo}/branches/{branch}/protection.
  - If the implementation is the source of truth: Add a "401" response under paths["/repos/{owner}/{repo}/branches/{branch}/protection"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins > response 401`
  - status 401 is not documented for "GET /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins"
  - Request: `GET /repos/octocat/Hello-World/branches/master/protection/enforce_admins -> 401 application/json; charset=utf-8`
  - Expected: `["200"]`
  - Actual: `401`
  - If the spec is the source of truth: Return one of the documented statuses (200) from GET /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins.
  - If the implementation is the source of truth: Add a "401" response under paths["/repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews > response 401`
  - status 401 is not documented for "GET /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews"
  - Request: `GET /repos/octocat/Hello-World/branches/master/protection/required_pull_request_reviews -> 401 application/json; charset=utf-8`
  - Expected: `["200"]`
  - Actual: `401`
  - If the spec is the source of truth: Return one of the documented statuses (200) from GET /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews.
  - If the implementation is the source of truth: Add a "401" response under paths["/repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/branches/{branch}/protection/required_signatures > response 401`
  - status 401 is not documented for "GET /repos/{owner}/{repo}/branches/{branch}/protection/required_signatures"
  - Request: `GET /repos/octocat/Hello-World/branches/master/protection/required_signatures -> 401 application/json; charset=utf-8`
  - Expected: `["200","404"]`
  - Actual: `401`
  - If the spec is the source of truth: Return one of the documented statuses (200, 404) from GET /repos/{owner}/{repo}/branches/{branch}/protection/required_signatures.
  - If the implementation is the source of truth: Add a "401" response under paths["/repos/{owner}/{repo}/branches/{branch}/protection/required_signatures"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks > response 401`
  - status 401 is not documented for "GET /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks"
  - Request: `GET /repos/octocat/Hello-World/branches/master/protection/required_status_checks -> 401 application/json; charset=utf-8`
  - Expected: `["200","404"]`
  - Actual: `401`
  - If the spec is the source of truth: Return one of the documented statuses (200, 404) from GET /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks.
  - If the implementation is the source of truth: Add a "401" response under paths["/repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts > response 401`
  - status 401 is not documented for "GET /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts"
  - Request: `GET /repos/octocat/Hello-World/branches/master/protection/required_status_checks/contexts -> 401 application/json; charset=utf-8`
  - Expected: `["200","404"]`
  - Actual: `401`
  - If the spec is the source of truth: Return one of the documented statuses (200, 404) from GET /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts.
  - If the implementation is the source of truth: Add a "401" response under paths["/repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions > response 401`
  - status 401 is not documented for "GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions"
  - Request: `GET /repos/octocat/Hello-World/branches/master/protection/restrictions -> 401 application/json; charset=utf-8`
  - Expected: `["200","404"]`
  - Actual: `401`
  - If the spec is the source of truth: Return one of the documented statuses (200, 404) from GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions.
  - If the implementation is the source of truth: Add a "401" response under paths["/repos/{owner}/{repo}/branches/{branch}/protection/restrictions"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps > response 401`
  - status 401 is not documented for "GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps"
  - Request: `GET /repos/octocat/Hello-World/branches/master/protection/restrictions/apps -> 401 application/json; charset=utf-8`
  - Expected: `["200","404"]`
  - Actual: `401`
  - If the spec is the source of truth: Return one of the documented statuses (200, 404) from GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps.
  - If the implementation is the source of truth: Add a "401" response under paths["/repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams > response 403`
  - status 403 is not documented for "GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams"
  - Request: `GET /repos/octocat/Hello-World/branches/master/protection/restrictions/teams -> 403 application/json; charset=utf-8`
  - Expected: `["200","404"]`
  - Actual: `403`
  - If the spec is the source of truth: Return one of the documented statuses (200, 404) from GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams.
  - If the implementation is the source of truth: Add a "403" response under paths["/repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users > response 403`
  - status 403 is not documented for "GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users"
  - Request: `GET /repos/octocat/Hello-World/branches/master/protection/restrictions/users -> 403 application/json; charset=utf-8`
  - Expected: `["200","404"]`
  - Actual: `403`
  - If the spec is the source of truth: Return one of the documented statuses (200, 404) from GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users.
  - If the implementation is the source of truth: Add a "403" response under paths["/repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/commits > response 403`
  - status 403 is not documented for "GET /repos/{owner}/{repo}/commits"
  - Request: `GET /repos/octocat/Hello-World/commits?page=1&per_page=30 -> 403 application/json; charset=utf-8`
  - Expected: `["200","400","404","409","422","429","500"]`
  - Actual: `403`
  - If the spec is the source of truth: Return one of the documented statuses (200, 400, 404, 409, 422, 429, 500) from GET /repos/{owner}/{repo}/commits.
  - If the implementation is the source of truth: Add a "403" response under paths["/repos/{owner}/{repo}/commits"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/commits/{commit_sha}/branches-where-head > response 403`
  - status 403 is not documented for "GET /repos/{owner}/{repo}/commits/{commit_sha}/branches-where-head"
  - Request: `GET /repos/octocat/Hello-World/commits/7fd1a60b01f91b314f59955a4e4d4e80d8edf11d/branches-where-head -> 403 application/json; charset=utf-8`
  - Expected: `["200","409","422"]`
  - Actual: `403`
  - If the spec is the source of truth: Return one of the documented statuses (200, 409, 422) from GET /repos/{owner}/{repo}/commits/{commit_sha}/branches-where-head.
  - If the implementation is the source of truth: Add a "403" response under paths["/repos/{owner}/{repo}/commits/{commit_sha}/branches-where-head"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/commits/{commit_sha}/comments > response 403`
  - status 403 is not documented for "GET /repos/{owner}/{repo}/commits/{commit_sha}/comments"
  - Request: `GET /repos/octocat/Hello-World/commits/7fd1a60b01f91b314f59955a4e4d4e80d8edf11d/comments?page=1&per_page=30 -> 403 application/json; charset=utf-8`
  - Expected: `["200"]`
  - Actual: `403`
  - If the spec is the source of truth: Return one of the documented statuses (200) from GET /repos/{owner}/{repo}/commits/{commit_sha}/comments.
  - If the implementation is the source of truth: Add a "403" response under paths["/repos/{owner}/{repo}/commits/{commit_sha}/comments"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/commits/{commit_sha}/pulls > response 403`
  - status 403 is not documented for "GET /repos/{owner}/{repo}/commits/{commit_sha}/pulls"
  - Request: `GET /repos/octocat/Hello-World/commits/7fd1a60b01f91b314f59955a4e4d4e80d8edf11d/pulls?page=1&per_page=30 -> 403 application/json; charset=utf-8`
  - Expected: `["200","409"]`
  - Actual: `403`
  - If the spec is the source of truth: Return one of the documented statuses (200, 409) from GET /repos/{owner}/{repo}/commits/{commit_sha}/pulls.
  - If the implementation is the source of truth: Add a "403" response under paths["/repos/{owner}/{repo}/commits/{commit_sha}/pulls"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/commits/{ref} > response 403`
  - status 403 is not documented for "GET /repos/{owner}/{repo}/commits/{ref}"
  - Request: `GET /repos/octocat/Hello-World/commits/master?page=1&per_page=30 -> 403 application/json; charset=utf-8`
  - Expected: `["200","404","409","422","429","500","503"]`
  - Actual: `403`
  - If the spec is the source of truth: Return one of the documented statuses (200, 404, 409, 422, 429, 500, 503) from GET /repos/{owner}/{repo}/commits/{ref}.
  - If the implementation is the source of truth: Add a "403" response under paths["/repos/{owner}/{repo}/commits/{ref}"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/commits/{ref}/check-runs > response 403`
  - status 403 is not documented for "GET /repos/{owner}/{repo}/commits/{ref}/check-runs"
  - Request: `GET /repos/octocat/Hello-World/commits/master/check-runs?filter=latest&page=1&per_page=30 -> 403 application/json; charset=utf-8`
  - Expected: `["200"]`
  - Actual: `403`
  - If the spec is the source of truth: Return one of the documented statuses (200) from GET /repos/{owner}/{repo}/commits/{ref}/check-runs.
  - If the implementation is the source of truth: Add a "403" response under paths["/repos/{owner}/{repo}/commits/{ref}/check-runs"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/commits/{ref}/check-suites > response 403`
  - status 403 is not documented for "GET /repos/{owner}/{repo}/commits/{ref}/check-suites"
  - Request: `GET /repos/octocat/Hello-World/commits/master/check-suites?app_id=1&page=1&per_page=30 -> 403 application/json; charset=utf-8`
  - Expected: `["200"]`
  - Actual: `403`
  - If the spec is the source of truth: Return one of the documented statuses (200) from GET /repos/{owner}/{repo}/commits/{ref}/check-suites.
  - If the implementation is the source of truth: Add a "403" response under paths["/repos/{owner}/{repo}/commits/{ref}/check-suites"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/commits/{ref}/status > response 403`
  - status 403 is not documented for "GET /repos/{owner}/{repo}/commits/{ref}/status"
  - Request: `GET /repos/octocat/Hello-World/commits/master/status?page=1&per_page=30 -> 403 application/json; charset=utf-8`
  - Expected: `["200","404"]`
  - Actual: `403`
  - If the spec is the source of truth: Return one of the documented statuses (200, 404) from GET /repos/{owner}/{repo}/commits/{ref}/status.
  - If the implementation is the source of truth: Add a "403" response under paths["/repos/{owner}/{repo}/commits/{ref}/status"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/commits/{ref}/statuses > response 403`
  - status 403 is not documented for "GET /repos/{owner}/{repo}/commits/{ref}/statuses"
  - Request: `GET /repos/octocat/Hello-World/commits/master/statuses?page=1&per_page=30 -> 403 application/json; charset=utf-8`
  - Expected: `["200","301"]`
  - Actual: `403`
  - If the spec is the source of truth: Return one of the documented statuses (200, 301) from GET /repos/{owner}/{repo}/commits/{ref}/statuses.
  - If the implementation is the source of truth: Add a "403" response under paths["/repos/{owner}/{repo}/commits/{ref}/statuses"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/issues > response 403`
  - status 403 is not documented for "GET /repos/{owner}/{repo}/issues"
  - Request: `GET /repos/octocat/Hello-World/issues?direction=desc&page=1&per_page=30&sort=created&state=open -> 403 application/json; charset=utf-8`
  - Expected: `["200","301","404","422"]`
  - Actual: `403`
  - If the spec is the source of truth: Return one of the documented statuses (200, 301, 404, 422) from GET /repos/{owner}/{repo}/issues.
  - If the implementation is the source of truth: Add a "403" response under paths["/repos/{owner}/{repo}/issues"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/issues/{issue_number} > response 403`
  - status 403 is not documented for "GET /repos/{owner}/{repo}/issues/{issue_number}"
  - Request: `GET /repos/octocat/Hello-World/issues/1 -> 403 application/json; charset=utf-8`
  - Expected: `["200","301","304","404","410"]`
  - Actual: `403`
  - If the spec is the source of truth: Return one of the documented statuses (200, 301, 304, 404, 410) from GET /repos/{owner}/{repo}/issues/{issue_number}.
  - If the implementation is the source of truth: Add a "403" response under paths["/repos/{owner}/{repo}/issues/{issue_number}"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/issues/{issue_number}/artifacts > response 403`
  - status 403 is not documented for "GET /repos/{owner}/{repo}/issues/{issue_number}/artifacts"
  - Request: `GET /repos/octocat/Hello-World/issues/1/artifacts?page=1&per_page=30 -> 403 application/json; charset=utf-8`
  - Expected: `["200","404","410","503"]`
  - Actual: `403`
  - If the spec is the source of truth: Return one of the documented statuses (200, 404, 410, 503) from GET /repos/{owner}/{repo}/issues/{issue_number}/artifacts.
  - If the implementation is the source of truth: Add a "403" response under paths["/repos/{owner}/{repo}/issues/{issue_number}/artifacts"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/issues/{issue_number}/comments > response 403`
  - status 403 is not documented for "GET /repos/{owner}/{repo}/issues/{issue_number}/comments"
  - Request: `GET /repos/octocat/Hello-World/issues/1/comments?page=1&per_page=30 -> 403 application/json; charset=utf-8`
  - Expected: `["200","404","410"]`
  - Actual: `403`
  - If the spec is the source of truth: Return one of the documented statuses (200, 404, 410) from GET /repos/{owner}/{repo}/issues/{issue_number}/comments.
  - If the implementation is the source of truth: Add a "403" response under paths["/repos/{owner}/{repo}/issues/{issue_number}/comments"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/issues/{issue_number}/dependencies/blocked_by > response 403`
  - status 403 is not documented for "GET /repos/{owner}/{repo}/issues/{issue_number}/dependencies/blocked_by"
  - Request: `GET /repos/octocat/Hello-World/issues/1/dependencies/blocked_by?page=1&per_page=30 -> 403 application/json; charset=utf-8`
  - Expected: `["200","301","404","410"]`
  - Actual: `403`
  - If the spec is the source of truth: Return one of the documented statuses (200, 301, 404, 410) from GET /repos/{owner}/{repo}/issues/{issue_number}/dependencies/blocked_by.
  - If the implementation is the source of truth: Add a "403" response under paths["/repos/{owner}/{repo}/issues/{issue_number}/dependencies/blocked_by"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/issues/{issue_number}/dependencies/blocking > response 403`
  - status 403 is not documented for "GET /repos/{owner}/{repo}/issues/{issue_number}/dependencies/blocking"
  - Request: `GET /repos/octocat/Hello-World/issues/1/dependencies/blocking?page=1&per_page=30 -> 403 application/json; charset=utf-8`
  - Expected: `["200","301","404","410"]`
  - Actual: `403`
  - If the spec is the source of truth: Return one of the documented statuses (200, 301, 404, 410) from GET /repos/{owner}/{repo}/issues/{issue_number}/dependencies/blocking.
  - If the implementation is the source of truth: Add a "403" response under paths["/repos/{owner}/{repo}/issues/{issue_number}/dependencies/blocking"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/issues/{issue_number}/events > response 403`
  - status 403 is not documented for "GET /repos/{owner}/{repo}/issues/{issue_number}/events"
  - Request: `GET /repos/octocat/Hello-World/issues/1/events?page=1&per_page=30 -> 403 application/json; charset=utf-8`
  - Expected: `["200","410"]`
  - Actual: `403`
  - If the spec is the source of truth: Return one of the documented statuses (200, 410) from GET /repos/{owner}/{repo}/issues/{issue_number}/events.
  - If the implementation is the source of truth: Add a "403" response under paths["/repos/{owner}/{repo}/issues/{issue_number}/events"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/issues/{issue_number}/issue-field-values > response 403`
  - status 403 is not documented for "GET /repos/{owner}/{repo}/issues/{issue_number}/issue-field-values"
  - Request: `GET /repos/octocat/Hello-World/issues/1/issue-field-values?page=1&per_page=30 -> 403 application/json; charset=utf-8`
  - Expected: `["200","301","404","410"]`
  - Actual: `403`
  - If the spec is the source of truth: Return one of the documented statuses (200, 301, 404, 410) from GET /repos/{owner}/{repo}/issues/{issue_number}/issue-field-values.
  - If the implementation is the source of truth: Add a "403" response under paths["/repos/{owner}/{repo}/issues/{issue_number}/issue-field-values"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/issues/{issue_number}/labels > response 403`
  - status 403 is not documented for "GET /repos/{owner}/{repo}/issues/{issue_number}/labels"
  - Request: `GET /repos/octocat/Hello-World/issues/1/labels?page=1&per_page=30 -> 403 application/json; charset=utf-8`
  - Expected: `["200","301","404","410"]`
  - Actual: `403`
  - If the spec is the source of truth: Return one of the documented statuses (200, 301, 404, 410) from GET /repos/{owner}/{repo}/issues/{issue_number}/labels.
  - If the implementation is the source of truth: Add a "403" response under paths["/repos/{owner}/{repo}/issues/{issue_number}/labels"].get.responses.

## Data

- Findings: https://github.com/apidrifter/apidrifter/tree/main/tools/survey/results/github.findings.json
- Requests sent: https://github.com/apidrifter/apidrifter/tree/main/tools/survey/results/github.observations.json
- Tool: https://apidrifter.com/docs/cli/ (reproduce with apidrifter run; target definition in tools/survey/targets.json)
