github-apiroute
Python clients for GitHub's REST and GraphQL APIs, generated with apiroute and kept current by a weekly build. The REST client comes from GitHub's published OpenAPI description. The GraphQL client comes from GitHub's schema plus a set of operations written in this repository.
- Two layers: every call works with plain dicts (
gh.repos.get("octocat", "Hello-World")), and.modelstakes and returns typed pydantic models (gh.repos.models.get(...)). - Sync and async: add
.aioto any call for the async version. - Lazy loading: only the sections and models you use are imported. The REST client covers about 1,250 endpoints and 4,100 models, and a script that calls one of them stays fast.
- Typed: the package ships
py.typed, and mypy checks the generated code.
Installation
pip install github-apiroute # or: uv add github-apiroute
It needs Python 3.10 or newer.
Authentication
Both clients take a token: a personal access token, a GitHub App installation token, or a function that returns the current token (so a refreshing helper can be passed directly). It is sent as Authorization: Bearer <token>. Without a token, the REST API allows a few anonymous calls and the GraphQL API none.
For GitHub Enterprise Server, pass base_url: https://HOST/api/v3 for REST and https://HOST/api for GraphQL.
REST API
import os
from github_apiroute.rest import GitHubRestApi
gh = GitHubRestApi(token=os.environ["GITHUB_TOKEN"])
repo = gh.repos.get("octocat", "Hello-World") # a dict
repo = gh.repos.models.get("octocat", "Hello-World") # FullRepository
print(repo.full_name, repo.stargazers_count)
issues = gh.issues.list_for_repo("octocat", "Hello-World", state="open", per_page=100)
issues = await gh.issues.aio.list_for_repo("octocat", "Hello-World")
Sections follow GitHub's REST documentation (repos, issues, pulls, actions and so on), and each method is named after its operation. Every call sends X-GitHub-Api-Version: 2022-11-28; pass api_version= to request another version. REST list endpoints are paged with page and per_page.
GraphQL API
The GraphQL client runs the operations in operations/graphql/: a practical starter set. Each one becomes a typed method, and its result models contain exactly the fields the operation selects.
from github_apiroute.graphql import GitHubGraphQLApi
gh = GitHubGraphQLApi(token=os.environ["GITHUB_TOKEN"])
me = gh.models.viewer().viewer
print(me.login, me.created_at) # created_at is a datetime
for issue in gh.repos.models.issues(owner="octocat", name="Hello-World"): # every page
print(issue.number, issue.title)
for repo in gh.search.models.search_repositories(query="apiroute language:python"):
print(repo.name_with_owner)
await gh.mutations.models.aio.add_star(starrable_id=repo.id)
| Section | Operations |
|---|---|
| (root) | viewer, rate_limit |
repos |
repository, issue, and paginated issues, pull_requests, releases, stargazers |
users |
user, paginated user_repositories |
search |
paginated search_repositories, search_issues |
mutations |
create_issue, add_comment, add_star, remove_star |
Paginated operations return an iterator that follows pageInfo.endCursor until the last page. A response with errors raises apiroute.graphql.GraphQLError.
To add an operation, write it in a .graphql file under operations/graphql/ and regenerate (see Development). The generator checks every operation against GitHub's schema.
APIs
| API | Import | Docs |
|---|---|---|
| REST API | from github_apiroute.rest import GitHubRestApi |
docs |
| GraphQL API | from github_apiroute.graphql import GitHubGraphQLApi |
docs |
Errors raise apiroute.UnexpectedResponse, which carries the .response. For retries, transports and other client options, see apiroute's README.
Retries are off unless you turn them on, for example with gh.with_options(retries=3). Once on, failed connections are always retried. After a read timeout, though, only calls that are safe to repeat are retried: REST GET, PUT and DELETE (GitHub's PUTs set state, such as starring a repository), and GraphQL queries. REST POSTs and GraphQL mutations aren't, so an issue or a comment can't be created twice.
How this repository stays up to date
Nothing under src/github_apiroute/ is written by hand except auth.py and __init__.py. Everything else is generated from the sources listed in apis.yaml:
- Every Monday, the Build workflow does the following:
- Upgrades apiroute to its newest release.
- Downloads GitHub's REST description and introspects the GraphQL schema into
specs/. - Regenerates the clients with
apiroute generate. - Runs the lint and tests.
- If anything changed, it commits the result to
main:feat(specs): ...when GitHub changed a spec or schema, which produces a minor release.fix(codegen): ...when only a newer apiroute changed the generated code, which produces a patch release.
- Release (release-please) then updates its release PR, tests it, merges it and publishes the new version to PyPI. No one has to do anything.
CI checks every push. It lints, runs the tests on Python 3.10 to 3.14, and regenerates from the committed specs to confirm the committed code matches them exactly.
Because specs/ is committed, git log -p specs/ shows exactly what GitHub changed and when.
Development
uv sync
GITHUB_TOKEN="$(gh auth token)" uv run python scripts/generate.py --fetch # latest specs, regenerate
uv run python scripts/generate.py --only graphql # regenerate one API from its committed spec
uv run pytest
uv run ruff check . && uv run ruff format --check .
Fetching the GraphQL schema needs a token in GITHUB_TOKEN; regenerating from the committed specs doesn't. Auth lives in src/github_apiroute/auth.py, which the generated clients extend.
License
MIT
Metadata
Release files for github-apiroute 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| github_apiroute-0.2.0.tar.gz | 912.2 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| github_apiroute-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 4.7 MB
Release files / github_apiroute-0.2.0.tar.gz
| Download URL | github_apiroute-0.2.0.tar.gz |
|---|---|
| Size | 912.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
79a0a6d10aa26ac5f03bdbe6c29ffd574c12d55ceb4200123ddb534e2e93f7ad
|
|
BLAKE2b-256 checksum How to use checksums |
fe94c0ada86fe2c536fc796a11b0d53e445b95e2463efbfba4e38a33c6a28be9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / github_apiroute-0.2.0-py3-none-any.whl
| Download URL | github_apiroute-0.2.0-py3-none-any.whl |
|---|---|
| Size | 3.8 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3f36a15fe1b9c5e90367544b45782b6674cbaa01542af236ab092b397359c061
|
|
BLAKE2b-256 checksum How to use checksums |
8b482a6007aba2c00095d63a1b0d9f35c662e68ddd4b0d68cda4ea4c866dbd1b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for github-apiroute 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.