Calling REST API¶
GitHub's REST API enables you to build automation processes, integrate with GitHub and extend GitHub. See the GitHub REST API documentation for more information.
APIs are fully typed. Type hints in the following examples are just for reference only.
The Basics¶
Calling REST API is simple with githubkit. You just need to create a GitHub
instance with your authentication strategy and call the REST API methods in categories. For example, to get a repository:
from githubkit import GitHub, Response
from githubkit.versions.latest.models import FullRepository
github = GitHub("<your_token_here>")
resp: Response[FullRepository] = github.rest.repos.get("owner", "repo")
repo: FullRepository = resp.parsed_data
from githubkit import Response
from githubkit.versions.latest.models import FullRepository
github = GitHub("<your_token_here>")
resp: Response[FullRepository] = await github.rest.repos.async_get("owner", "repo")
repo: FullRepository = resp.parsed_data
If you are calling an API that requires request body parameters, you can pass the request body as a keyword argument:
from githubkit import GitHub, Response
from githubkit.versions.latest.models import Issue
github = GitHub("<your_token_here>")
resp: Response[Issue] = github.rest.issues.create(
"owner",
"repo",
title="New Issue",
body="This is issue body",
)
issue: Issue = resp.parsed_data
from githubkit import GitHub, Response
from githubkit.versions.latest.models import Issue
github = GitHub("<your_token_here>")
resp: Response[Issue] = await github.rest.issues.async_create(
"owner",
"repo",
title="New Issue",
body="This is issue body",
)
issue: Issue = resp.parsed_data
Tip
By default, githubkit will validate the request body against the API schema. If you want to skip the validation, you can set the client config rest_api_validate_body
to False
. See Configuration for more information.
Or you can pass the json request body as a dictionary:
from githubkit import GitHub, Response
from githubkit.versions.latest.models import Issue
github = GitHub("<your_token_here>")
resp: Response[Issue] = github.rest.issues.create(
"owner",
"repo",
data={"title": "New Issue", "body": "This is issue body"},
)
issue: Issue = resp.parsed_data
from githubkit import GitHub, Response
from githubkit.versions.latest.models import Issue
github = GitHub("<your_token_here>")
resp: Response[Issue] = await github.rest.issues.async_create(
"owner",
"repo",
data={"title": "New Issue", "body": "This is issue body"},
)
issue: Issue = resp.parsed_data
For some APIs, the request body may be raw data. You can pass the raw data directly to the data
parameter:
from githubkit import GitHub
github = GitHub("<your_token_here>")
resp = github.rest.markdown.render_raw(
data="Hello **world**",
)
rendered_html = resp.text
from githubkit import GitHub
github = GitHub("<your_token_here>")
resp = await github.rest.markdown.async_render_raw(
data="Hello **world**",
)
rendered_html = resp.text
Danger
Note that you should hold a strong reference to the githubkit client instance. Otherwise, githubkit client will fail to call the request. For example, you should not do this:
from githubkit import GitHub
def get_client() -> GitHub:
return GitHub()
# This will cause error
get_client().rest.repos.get("owner", "repo")
# This is ok
client = get_client()
client.rest.repos.get("owner", "repo")
Custom Headers¶
In some cases, you may need to pass additional headers to the API request. You can pass the headers through the headers
parameter. For example:
from githubkit import GitHub
github = GitHub("<your_token_here>")
resp = github.rest.repos.get_content(
"owner",
"repo",
"/path/to/file",
headers={"Accept": "application/vnd.github.raw+json"},
)
content = resp.text
from githubkit import GitHub
github = GitHub("<your_token_here>")
resp = await github.rest.repos.async_get_content(
"owner",
"repo",
"/path/to/file",
headers={"Accept": "application/vnd.github.raw+json"},
)
content = resp.text
Reusing Client¶
You can make multiple requests with the same client instance in one context:
from githubkit import GitHub, Response
from githubkit.versions.latest.models import FullRepository
with GitHub("<your_token_here>") as github:
resp: Response[FullRepository] = github.rest.repos.get(owner="owner", repo="repo")
repo: FullRepository = resp.parsed_data
from githubkit import GitHub, Response
from githubkit.versions.latest.models import FullRepository
async with GitHub("<your_token_here>") as github:
resp: Response[FullRepository] = await github.rest.repos.async_get(owner="owner", repo="repo")
repo: FullRepository = resp.parsed_data
Data Validation¶
As shown above, the response data is parsed and validated by accessing the response.parsed_data
property. This ensures that the data type returned by the API is as expected and your code is safe to use it (with static type checking). But sometimes you may want to get the raw data returned by the API, such as when the schema is not correct. You can use the response.text
property or response.json()
method to get the raw data:
from typing import Any
from githubkit import Response
resp: Response[FullRepository] = github.rest.repos.get("owner", "repo")
repo: dict[str, Any] = resp.json()
If you have already got the parsed data and want to dump it into a dict object or JSON string, you can use the pydantic model's dict
or json
method:
from typing import Any
from githubkit import Response
from githubkit.versions.latest.models import FullRepository
resp: Response[FullRepository] = github.rest.repos.get("owner", "repo")
repo: FullRepository = resp.parsed_data
repo_dict: dict[str, Any] = repo.dict(by_alias=True, exclude_unset=True)
repo_json: str = repo.json(by_alias=True, exclude_unset=True)
If you have already got the parsed data and want to dump it into a dict object or JSON string, you can use the pydantic model's model_dump
or model_dump_json
method:
from typing import Any
from githubkit import Response
from githubkit.versions.latest.models import FullRepository
resp: Response[FullRepository] = github.rest.repos.get("owner", "repo")
repo: FullRepository = resp.parsed_data
repo_dict: dict[str, Any] = repo.model_dump(
mode="json", by_alias=True, exclude_unset=True
)
repo_json: str = repo.model_dump_json(by_alias=True, exclude_unset=True)
REST API Versioning¶
githubkit supports multiple versions of GitHub API, you can switch between versions as follows:
github.rest("2022-11-28").repos.get("owner", "repo")
The code above uses the 2022-11-28
version of the GitHub API.
Besides the REST API methods, the versioned models can also be imported from githubkit.versions.<version>.models
module. For example:
from githubkit.versions.v2022_11_28.models import FullRepository
Specially, the latest
module is always linked to the latest version of GitHub API:
from githubkit.versions.latest.models import FullRepository
Note
For backward compatibility, the githubkit.rest
module is linked to the models of latest
version by default.
from githubkit.rest import FullRepository
You can also get the latest version name of GitHub API and version-module mapping of GitHub API:
from githubkit.versions import LATEST_VERSION, VERSIONS
Current supported versions are: (you can find it in the section [[tool.codegen.descriptions]]
of the pyproject.toml
file)
- 2022-11-28 (latest)
- ghec-2022-11-28
REST API Pagination¶
When a response from the REST API would include many results, GitHub will paginate the results and return a subset of the results. In this case, some APIs provide page
and per_page
parameters to control the pagination. See GitHub Docs - Using pagination in the REST API for more information.
githubkit provides a built-in pagination feature to handle this. You can use the github.paginate
method to iterate over all the results:
Pagination typing is checked with Pylance (Pyright).
from githubkit.versions.latest.models import Issue
for issue in github.paginate(
github.rest.issues.list_for_repo, owner="owner", repo="repo", state="open"
):
issue: Issue
print(issue.number)
from githubkit.versions.latest.models import Issue
async for issue in github.paginate(
github.rest.issues.async_list_for_repo, owner="owner", repo="repo", state="open"
):
issue: Issue
print(issue.number)
You can also provide a custom map function to handle complex pagination (such as when the API returns data in a nested field):
from githubkit.versions.latest.models import Repository
for accessible_repo in github.paginate(
github.rest.apps.list_installation_repos_for_authenticated_user,
map_func=lambda r: r.parsed_data.repositories,
installation_id=1,
):
accessible_repo: Repository
print(accessible_repo.full_name)
from githubkit.versions.latest.models import Repository
async for accessible_repo in github.paginate(
github.rest.apps.async_list_installation_repos_for_authenticated_user,
map_func=lambda r: r.parsed_data.repositories,
installation_id=1,
):
accessible_repo: Repository
print(accessible_repo.full_name)
Calling API Directly¶
In some cases, you may want to call the API directly without using the generated methods. You can use the github.request
/ github.arequest
method to make a raw request.
For example, to upload a release asset:
from githubkit import GitHub
from githubkit.versions.latest.models import Release, ReleaseAsset
github = GitHub()
resp = github.rest.repos.get_release_by_tag(
"owner", "repo", "tag_name"
)
release: Release = resp.parsed_data
resp = github.request(
"POST",
release.upload_url.split("{?")[0], # (1)!
params={"name": "test", "label": "description"},
content=b"file content",
headers={"Content-Type": "application/zip"},
response_model=ReleaseAsset,
)
asset: ReleaseAsset = resp.parsed_data
- The release
upload_url
is a template URL. In this example, we simply remove the template part.
from githubkit import GitHub
from githubkit.versions.latest.models import Release, ReleaseAsset
github = GitHub()
resp = await github.rest.repos.async_get_release_by_tag(
"owner", "repo", "tag_name"
)
release: Release = resp.parsed_data
resp = await github.arequest(
"POST",
release.upload_url.split("{?")[0], # (1)!
params={"name": "test", "label": "description"},
content=b"file content",
headers={"Content-Type": "application/zip"},
response_model=ReleaseAsset,
)
asset: ReleaseAsset = resp.parsed_data
- The release
upload_url
is a template URL. In this example, we simply remove the template part.