GitHub API Client¶
The GitHubAPIClient provides a typed, feature-rich wrapper around the GitHub REST and GraphQL APIs with built-in rate limit handling, pagination support, and GitHub Enterprise Server (GHES) compatibility.
Features¶
Typed API access: Full type hints for better IDE support and type safety
Automatic rate limit handling: Detects rate limits and waits automatically (configurable)
Exponential backoff: Retries failed requests with configurable backoff
Conditional requests: ETag support for efficient caching
GHES support: Custom base URL for GitHub Enterprise Server
Pagination helpers: Simplified iteration over paginated results
GraphQL support: Execute GraphQL queries with typed responses
Basic Usage¶
Initialize the Client¶
from github_action_toolkit import GitHubAPIClient
# Using environment variable (GITHUB_TOKEN)
client = GitHubAPIClient()
# Or with explicit token
client = GitHubAPIClient(token="ghp_your_token_here")
# For GitHub Enterprise Server
client = GitHubAPIClient(
token="ghp_your_token",
base_url="https://github.mycompany.com/api/v3"
)
Get Repository Information¶
# Get a repository
repo = client.get_repo("owner/repo")
print(f"Repository: {repo.full_name}")
print(f"Stars: {repo.stargazers_count}")
print(f"Language: {repo.language}")
Get User Information¶
# Get authenticated user
user = client.get_user()
print(f"Logged in as: {user.login}")
# Get specific user
user = client.get_user("octocat")
print(f"User: {user.name}")
Get Organization Information¶
org = client.get_organization("github")
print(f"Organization: {org.name}")
print(f"Members: {org.public_members_count}")
Pagination¶
The client provides a convenient method to iterate through paginated results:
# Paginate through issues
repo = client.get_repo("owner/repo")
for issue in client.paginate(repo.get_issues):
print(f"Issue #{issue.number}: {issue.title}")
# Paginate with parameters
for issue in client.paginate(repo.get_issues, state="closed", labels=["bug"]):
print(f"Closed bug #{issue.number}: {issue.title}")
# Paginate through pull requests
for pr in client.paginate(repo.get_pulls, state="open"):
print(f"PR #{pr.number}: {pr.title}")
Rate Limit Handling¶
The client automatically handles rate limits:
# Automatic waiting (default behavior)
client = GitHubAPIClient(token="ghp_token", rate_limit_wait=True)
# Without automatic waiting (raises RateLimitError)
client = GitHubAPIClient(token="ghp_token", rate_limit_wait=False)
# Check current rate limit status
rate_limit = client.get_rate_limit()
print(f"Core API: {rate_limit['core']['remaining']}/{rate_limit['core']['limit']}")
print(f"Search API: {rate_limit['search']['remaining']}/{rate_limit['search']['limit']}")
print(f"GraphQL API: {rate_limit['graphql']['remaining']}/{rate_limit['graphql']['limit']}")
Raw HTTP Requests¶
For advanced use cases, you can make raw HTTP requests:
# Simple GET request
response = client.request("GET", "/user/repos")
repos = response.json()
# POST request with data
response = client.request(
"POST",
"/repos/owner/repo/issues",
json={
"title": "Issue title",
"body": "Issue description",
"labels": ["bug"]
}
)
issue = response.json()
GraphQL Queries¶
Execute GraphQL queries for more efficient data fetching:
# Simple query
query = """
query {
viewer {
login
name
email
}
}
"""
result = client.graphql(query)
print(f"User: {result['viewer']['login']}")
# Query with variables
query = """
query($owner: String!, $name: String!) {
repository(owner: $owner, name: $name) {
name
description
stargazerCount
issues(first: 5, states: OPEN) {
nodes {
title
number
}
}
}
}
"""
variables = {"owner": "github", "name": "hub"}
result = client.graphql(query, variables=variables)
repo_data = result["repository"]
print(f"Stars: {repo_data['stargazerCount']}")
for issue in repo_data["issues"]["nodes"]:
print(f"Issue #{issue['number']}: {issue['title']}")
Configuration Options¶
Retry Strategy¶
# Configure retry behavior
client = GitHubAPIClient(
token="ghp_token",
max_retries=5, # Maximum retry attempts
backoff_factor=2.0 # Exponential backoff multiplier
)
Error Handling¶
from github_action_toolkit import GitHubAPIClient, RateLimitError, APIError
client = GitHubAPIClient(token="ghp_token", rate_limit_wait=False)
try:
repo = client.get_repo("owner/repo")
except RateLimitError as e:
print(f"Rate limit exceeded. Reset at: {e.reset_time}")
except APIError as e:
print(f"API error ({e.status_code}): {e}")
Advanced Usage¶
Access Underlying PyGithub Instance¶
For features not directly exposed, you can access the underlying PyGithub client:
# Access PyGithub directly
pygithub_client = client.github
# Use any PyGithub method
gists = pygithub_client.get_gists()
for gist in gists:
print(gist.description)
Custom Headers¶
# Add custom headers to requests
response = client.request(
"GET",
"/repos/owner/repo/contents/file.txt",
headers={
"Accept": "application/vnd.github.v3.raw"
}
)
content = response.text
Best Practices¶
Reuse client instances: Create one client and reuse it across your application
Use pagination: Don’t fetch all results at once for large datasets
Enable rate limit waiting: Set
rate_limit_wait=Truefor long-running scriptsUse GraphQL for complex queries: Fetch multiple related resources in one request
Use conditional requests: Enable ETags for frequently accessed, rarely changing data
Handle errors gracefully: Catch
RateLimitErrorandAPIErrorappropriately
Examples¶
Check if Repository Exists¶
from github_action_toolkit import GitHubAPIClient, APIError
client = GitHubAPIClient()
try:
repo = client.get_repo("owner/repo")
print(f"Repository exists: {repo.full_name}")
except APIError as e:
if e.status_code == 404:
print("Repository not found")
else:
raise
Create an Issue¶
client = GitHubAPIClient()
repo = client.get_repo("owner/repo")
issue = repo.create_issue(
title="Bug report",
body="Description of the bug",
labels=["bug", "high-priority"]
)
print(f"Created issue #{issue.number}: {issue.html_url}")
List Open Pull Requests¶
client = GitHubAPIClient()
repo = client.get_repo("owner/repo")
print("Open Pull Requests:")
for pr in client.paginate(repo.get_pulls, state="open"):
print(f" PR #{pr.number}: {pr.title}")
print(f" Author: {pr.user.login}")
print(f" Branch: {pr.head.ref} → {pr.base.ref}")
print()
Monitor Repository Activity¶
import time
from github_action_toolkit import GitHubAPIClient
client = GitHubAPIClient(rate_limit_wait=True)
# Monitor new issues every 5 minutes
last_check = time.time()
while True:
repo = client.get_repo("owner/repo")
for issue in client.paginate(repo.get_issues, since=last_check):
print(f"New issue: #{issue.number} - {issue.title}")
last_check = time.time()
time.sleep(300) # 5 minutes
API Reference¶
See the module docstrings for complete API documentation:
from github_action_toolkit import GitHubAPIClient
help(GitHubAPIClient)
API Rate Limiting¶
Automatic Rate Limit Handling¶
from github_action_toolkit import GitHubAPIClient, info
client = GitHubAPIClient()
# Automatically handles rate limits with retry
with client.with_rate_limit_handling(wait=True):
repos = client.paginate(lambda: client.github.get_user().get_repos())
for repo in repos:
info(f'Processing {repo.full_name}')
# API calls here are protected
Manual Rate Limit Checking¶
from github_action_toolkit import GitHubAPIClient, warning
import time
client = GitHubAPIClient()
def check_rate_limit():
"""Check and warn about rate limit."""
rate_limit = client.get_rate_limit()
remaining = rate_limit.core.remaining
if remaining < 100:
reset_time = rate_limit.core.reset.timestamp()
wait_seconds = reset_time - time.time()
warning(
f'Only {remaining} API calls remaining. '
f'Resets in {wait_seconds/60:.1f} minutes.',
title='Rate Limit Low'
)