Comprehensive documentation for the GitHub API client including creation, configuration, authentication, rate limiting, pagination, and core operations.
Creates a new GitHub API client.
func NewClient(httpClient *http.Client) *ClientThe httpClient parameter can be nil for unauthenticated requests, or an *http.Client configured with authentication transport.
Usage examples:
// Unauthenticated client (limited to 60 requests/hour)
client := github.NewClient(nil)
// Authenticated client using OAuth2
import "golang.org/x/oauth2"
ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: "your-token"})
tc := oauth2.NewClient(context.Background(), ts)
client := github.NewClient(tc)The Client type provides access to all GitHub API services and core operations.
type Client struct {
// Service fields for accessing specific API areas
Actions *ActionsService
Activity *ActivityService
Admin *AdminService
Apps *AppsService
Authorizations *AuthorizationsService
Billing *BillingService
Checks *ChecksService
Classroom *ClassroomService
CodeScanning *CodeScanningService
CodesOfConduct *CodesOfConductService
Codespaces *CodespacesService
Compute *ComputeService
Copilot *CopilotService
Dependabot *DependabotService
DependencyGraph *DependencyGraphService
Emojis *EmojisService
Enterprise *EnterpriseService
Gists *GistsService
Git *GitService
Gitignores *GitignoresService
Interactions *InteractionsService
IssueImport *IssueImportService
Issues *IssuesService
Licenses *LicensesService
Marketplace *MarketplaceService
Meta *MetaService
Migrations *MigrationService
Organizations *OrganizationsService
Packages *PackagesService
Projects *ProjectsService
PullRequests *PullRequestsService
RateLimit *RateLimitService
Reactions *ReactionsService
Repositories *RepositoriesService
SCIM *SCIMService
Search *SearchService
SecretScanning *SecretScanningService
SecurityAdvisories *SecurityAdvisoriesService
Teams *TeamsService
Users *UsersService
}Configure client with a personal access token.
func (c *Client) WithAuthToken(token string) *ClientReturns a copy of the client configured to use the provided authentication token.
client := github.NewClient(nil).WithAuthToken("ghp_your_token_here")
// Now all requests will be authenticated
user, _, err := client.Users.Get(context.Background(), "")For OAuth2 flows, use the golang.org/x/oauth2 package:
import (
"context"
"github.com/google/go-github/v79/github"
"golang.org/x/oauth2"
)
func authenticateWithOAuth2(accessToken string) *github.Client {
ctx := context.Background()
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: accessToken},
)
tc := oauth2.NewClient(ctx, ts)
return github.NewClient(tc)
}For GitHub App installations, use a JWT-based authentication transport:
import (
"github.com/bradleyfalzon/ghinstallation/v2"
"github.com/google/go-github/v79/github"
)
func authenticateAsGitHubApp(appID, installationID int64, privateKeyPath string) (*github.Client, error) {
itr, err := ghinstallation.NewKeyFromFile(
http.DefaultTransport,
appID,
installationID,
privateKeyPath,
)
if err != nil {
return nil, err
}
return github.NewClient(&http.Client{Transport: itr}), nil
}Basic authentication with username and password (deprecated by GitHub, use tokens instead):
import "net/http"
func basicAuth(username, password string) *github.Client {
tp := &BasicAuthTransport{
Username: username,
Password: password,
}
return github.NewClient(tp.Client())
}
type BasicAuthTransport struct {
Username string
Password string
Transport http.RoundTripper
}
func (t *BasicAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
req2 := cloneRequest(req)
req2.SetBasicAuth(t.Username, t.Password)
return t.transport().RoundTrip(req2)
}
func (t *BasicAuthTransport) Client() *http.Client {
return &http.Client{Transport: t}
}
func (t *BasicAuthTransport) transport() http.RoundTripper {
if t.Transport != nil {
return t.Transport
}
return http.DefaultTransport
}Configure client for GitHub Enterprise Server.
func (c *Client) WithEnterpriseURLs(baseURL, uploadURL string) (*Client, error)client := github.NewClient(nil)
client, err := client.WithEnterpriseURLs(
"https://github.company.com/api/v3/",
"https://github.company.com/api/uploads/",
)
if err != nil {
// Handle error
}Get current rate limit status for all API categories.
func (c *Client) RateLimits(ctx context.Context) (*RateLimits, *Response, error)ctx := context.Background()
rateLimits, _, err := client.RateLimits(ctx)
if err != nil {
// Handle error
}
fmt.Printf("Core API: %d/%d (resets at %v)\n",
rateLimits.Core.Remaining,
rateLimits.Core.Limit,
rateLimits.Core.Reset.Time)
fmt.Printf("Search API: %d/%d\n",
rateLimits.Search.Remaining,
rateLimits.Search.Limit)type Rate struct {
Limit int // Maximum number of requests per hour
Remaining int // Remaining requests in current period
Reset Timestamp // Time when rate limit resets
}
type RateLimits struct {
Core *Rate // Core API endpoints
Search *Rate // Search API
GraphQL *Rate // GraphQL API
IntegrationManifest *Rate // Integration manifest
SourceImport *Rate // Source import
CodeScanningUpload *Rate // Code scanning upload
ActionsRunnerRegistration *Rate // Actions runner registration
SCIM *Rate // SCIM API
DependencySnapshots *Rate // Dependency snapshots
CodeSearch *Rate // Code search
AuditLog *Rate // Audit log
}Every API response includes rate limit information:
repos, resp, err := client.Repositories.List(ctx, "octocat", nil)
if err != nil {
// Handle error
}
// Check rate limit from response
fmt.Printf("Rate: %d/%d, resets at %v\n",
resp.Rate.Remaining,
resp.Rate.Limit,
resp.Rate.Reset.Time)
// Check for token expiration
if !resp.TokenExpiration.IsZero() {
fmt.Printf("Token expires: %v\n", resp.TokenExpiration)
}user, _, err := client.Users.Get(ctx, "octocat")
if err != nil {
// Check for rate limit errors
if rateLimitErr, ok := err.(*github.RateLimitError); ok {
fmt.Printf("Rate limited! Resets at: %v\n", rateLimitErr.Rate.Reset.Time)
// Wait until reset
sleepDuration := time.Until(rateLimitErr.Rate.Reset.Time)
time.Sleep(sleepDuration)
// Retry request
user, _, err = client.Users.Get(ctx, "octocat")
}
// Check for abuse/secondary rate limit
if abuseErr, ok := err.(*github.AbuseRateLimitError); ok {
if abuseErr.RetryAfter != nil {
fmt.Printf("Secondary rate limit! Retry after: %v\n", *abuseErr.RetryAfter)
time.Sleep(*abuseErr.RetryAfter)
}
}
}Control rate limit behavior with context values:
import "context"
// Bypass rate limit check (use with caution)
ctx := context.WithValue(context.Background(),
github.BypassRateLimitCheck, true)
// Auto-sleep until rate limit reset when rate limited
ctx = context.WithValue(context.Background(),
github.SleepUntilPrimaryRateLimitResetWhenRateLimited, true)
repos, _, err := client.Repositories.List(ctx, "user", nil)Every API call returns a Response containing HTTP response details and metadata.
type Response struct {
*http.Response
// Rate limit information for this request
Rate Rate
// Token expiration time (if applicable)
TokenExpiration time.Time
// Pagination links
NextPage int
PrevPage int
FirstPage int
LastPage int
}Embedded in most list operation option types for pagination control.
type ListOptions struct {
Page int // Page number (starting at 1)
PerPage int // Results per page (max 100)
}import "context"
func listAllRepositories(client *github.Client, username string) ([]*github.Repository, error) {
ctx := context.Background()
var allRepos []*github.Repository
opts := &github.RepositoryListOptions{
ListOptions: github.ListOptions{
Page: 1,
PerPage: 100, // Maximum
},
}
for {
repos, resp, err := client.Repositories.List(ctx, username, opts)
if err != nil {
return nil, err
}
allRepos = append(allRepos, repos...)
// Check if there are more pages
if resp.NextPage == 0 {
break
}
opts.Page = resp.NextPage
}
return allRepos, nil
}Send an API request and return the parsed response.
func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Response, error)req, err := client.NewRequest("GET", "users/octocat", nil)
if err != nil {
// Handle error
}
var user github.User
resp, err := client.Do(context.Background(), req, &user)
if err != nil {
// Handle error
}Send an API request without automatic response parsing.
func (c *Client) BareDo(ctx context.Context, req *http.Request) (*Response, error)req, _ := client.NewRequest("GET", "zen", nil)
resp, err := client.BareDo(context.Background(), req)
if err != nil {
// Handle error
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))Create a new API request.
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error)// GET request
req, err := client.NewRequest("GET", "repos/owner/repo", nil)
// POST request with body
createRepo := &github.Repository{
Name: github.Ptr("new-repo"),
Private: github.Ptr(false),
}
req, err := client.NewRequest("POST", "user/repos", createRepo)Create a form-encoded POST request.
func (c *Client) NewFormRequest(urlStr string, body io.Reader, opts ...RequestOption) (*http.Request, error)// Create a form-encoded request
formData := url.Values{}
formData.Set("key", "value")
req, err := client.NewFormRequest("path/to/endpoint", strings.NewReader(formData.Encode()))Create a request for uploading assets.
func (c *Client) NewUploadRequest(urlStr string, reader io.Reader, size int64, mediaType string) (*http.Request, error)file, _ := os.Open("asset.zip")
defer file.Close()
stat, _ := file.Stat()
req, err := client.NewUploadRequest(
"repos/owner/repo/releases/123/assets?name=asset.zip",
file,
stat.Size(),
"application/zip",
)Get the underlying http.Client.
func (c *Client) Client() *http.ClienthttpClient := client.Client()
// Modify timeouts, transports, etc.Get information about GitHub.com API.
func (c *Client) APIMeta(ctx context.Context) (*APIMeta, *Response, error)
type APIMeta struct {
Hooks []string
Git []string
Pages []string
Importer []string
Actions []string
Dependabot []string
VerifiablePasswordAuthentication bool
SSHKeyFingerprints struct {
SHA256_RSA string
SHA256_DSA string
SHA256_ECDSA string
SHA256_ED25519 string
}
}meta, _, err := client.APIMeta(context.Background())
if err != nil {
// Handle error
}
fmt.Printf("GitHub hooks IPs: %v\n", meta.Hooks)
fmt.Printf("SSH fingerprints: %+v\n", meta.SSHKeyFingerprints)Get ASCII art Octocat with optional message.
func (c *Client) Octocat(ctx context.Context, message string) (string, *Response, error)octocat, _, err := client.Octocat(context.Background(), "Hello!")
if err != nil {
// Handle error
}
fmt.Println(octocat)Get a random line from The Zen of GitHub.
func (c *Client) Zen(ctx context.Context) (string, *Response, error)zen, _, err := client.Zen(context.Background())
if err != nil {
// Handle error
}
fmt.Println(zen) // e.g., "Design for failure."Get available GitHub emojis.
func (c *Client) ListEmojis(ctx context.Context) (map[string]string, *Response, error)emojis, _, err := client.ListEmojis(context.Background())
if err != nil {
// Handle error
}
// Map of emoji name to URL
fmt.Println(emojis["+1"]) // URL to thumbs up emoji image
fmt.Println(emojis["smile"]) // URL to smile emoji imageGet a specific code of conduct.
func (c *Client) GetCodeOfConduct(ctx context.Context, key string) (*CodeOfConduct, *Response, error)
type CodeOfConduct struct {
Name *string
Key *string
URL *string
Body *string
}coc, _, err := client.GetCodeOfConduct(context.Background(), "contributor_covenant")
if err != nil {
// Handle error
}
fmt.Printf("Code of Conduct: %s\n", *coc.Name)
fmt.Printf("Content: %s\n", *coc.Body)Get all available codes of conduct.
func (c *Client) ListCodesOfConduct(ctx context.Context) ([]*CodeOfConduct, *Response, error)cocs, _, err := client.ListCodesOfConduct(context.Background())
if err != nil {
// Handle error
}
for _, coc := range cocs {
fmt.Printf("%s: %s\n", *coc.Key, *coc.Name)
}// Standard API error response
type ErrorResponse struct {
Response *http.Response
Message string
Errors []Error
Block *ErrorBlock
DocumentationURL string
}
type Error struct {
Resource string
Field string
Code string
Message string
}
type ErrorBlock struct {
Reason string
CreatedAt *Timestamp
}
// Rate limit exceeded
type RateLimitError struct {
Rate Rate
Response *http.Response
Message string
}
// Secondary/abuse rate limit
type AbuseRateLimitError struct {
Response *http.Response
Message string
RetryAfter *time.Duration
}
// Two-factor authentication required
type TwoFactorAuthError ErrorResponse
// Request accepted but processing asynchronously (HTTP 202)
type AcceptedError struct{}import "errors"
user, resp, err := client.Users.Get(ctx, "username")
if err != nil {
// Check for rate limit error
var rateLimitErr *github.RateLimitError
if errors.As(err, &rateLimitErr) {
fmt.Printf("Rate limited until: %v\n", rateLimitErr.Rate.Reset.Time)
return
}
// Check for abuse rate limit
var abuseErr *github.AbuseRateLimitError
if errors.As(err, &abuseErr) {
if abuseErr.RetryAfter != nil {
fmt.Printf("Retry after: %v\n", *abuseErr.RetryAfter)
}
return
}
// Check for 2FA required
var twoFactorErr *github.TwoFactorAuthError
if errors.As(err, &twoFactorErr) {
fmt.Println("Two-factor authentication required")
return
}
// Check for standard API error
var errResp *github.ErrorResponse
if errors.As(err, &errResp) {
fmt.Printf("API error: %s\n", errResp.Message)
for _, err := range errResp.Errors {
fmt.Printf(" - %s: %s\n", err.Field, err.Message)
}
return
}
// Other error
fmt.Printf("Error: %v\n", err)
return
}Create pointers to literal values for API requests.
// Generic pointer helper (preferred)
func Ptr[T any](v T) *T
// Legacy type-specific helpers (deprecated)
func Bool(v bool) *bool
func Int(v int) *int
func Int64(v int64) *int64
func String(v string) *string// Using the generic Ptr function (preferred)
repo := &github.Repository{
Name: github.Ptr("my-repo"),
Description: github.Ptr("A test repository"),
Private: github.Ptr(false),
AutoInit: github.Ptr(true),
}
// Legacy approach (still works but deprecated)
repo := &github.Repository{
Name: github.String("my-repo"),
Description: github.String("A test repository"),
Private: github.Bool(false),
AutoInit: github.Bool(true),
}Create a string representation of any library type.
func Stringify(message interface{}) stringuser, _, _ := client.Users.Get(ctx, "octocat")
fmt.Println(github.Stringify(user))
// Output: &{Login:"octocat" ID:123 ... }Wrapper around time.Time for proper JSON marshaling with GitHub API.
type Timestamp struct {
time.Time
}
func (t Timestamp) Equal(u Timestamp) bool// Timestamps are used throughout the API
repo, _, _ := client.Repositories.Get(ctx, "owner", "repo")
fmt.Printf("Created: %v\n", repo.CreatedAt.Time)
fmt.Printf("Updated: %v\n", repo.UpdatedAt.Time)
fmt.Printf("Pushed: %v\n", repo.PushedAt.Time)All API methods require a context.Context as the first parameter for cancellation, timeouts, and request metadata.
import (
"context"
"time"
)
// With timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
repos, _, err := client.Repositories.List(ctx, "user", nil)
// With cancellation
ctx, cancel := context.WithCancel(context.Background())
go func() {
// Cancel after some condition
time.Sleep(5 * time.Second)
cancel()
}()
repos, _, err := client.Repositories.List(ctx, "user", nil)
// With deadline
deadline := time.Now().Add(1 * time.Minute)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
defer cancel()
repos, _, err := client.Repositories.List(ctx, "user", nil)Special context keys for controlling request behavior.
const (
// Bypass rate limit checking for this request
BypassRateLimitCheck requestContext = iota
// Automatically sleep until rate limit resets when rate limited
SleepUntilPrimaryRateLimitResetWhenRateLimited
)// Bypass rate limit check (use with caution)
ctx := context.WithValue(context.Background(),
github.BypassRateLimitCheck, true)
// Auto-sleep on rate limit
ctx = context.WithValue(context.Background(),
github.SleepUntilPrimaryRateLimitResetWhenRateLimited, true)const Version = "v79.0.0"fmt.Printf("go-github version: %s\n", github.Version)