The gh api command is the Swiss Army knife of GitHub CLI. It makes authenticated HTTP requests to the GitHub API — both REST (v3) and GraphQL (v4) — directly from your terminal.

Why Use gh api?

  • You get automatic authentication — no need to pass tokens manually
  • Placeholders like {owner} and {repo} are resolved from the current repo
  • Built-in pagination, caching, and output formatting
  • Access any GitHub API endpoint, even ones that gh doesn't have a dedicated command for

Basic REST Requests

# Get the current user
gh api user
 
# Get a repository
gh api repos/cli/cli
 
# Placeholders — resolved from the current repo's git remote
gh api repos/{owner}/{repo}
gh api repos/{owner}/{repo}/issues
 
# Specify a different repo
gh api repos/facebook/react/releases

HTTP Methods

# GET (default)
gh api repos/{owner}/{repo}
 
# POST
gh api repos/{owner}/{repo}/issues -f title="Bug" -f body="Details..."
 
# PATCH
gh api repos/{owner}/{repo}/issues/42 -f state=closed
 
# PUT
gh api repos/{owner}/{repo}/topics -f names[]="cli" -f names[]="golang"
 
# DELETE
gh api repos/{owner}/{repo}/issues/42/lock -X DELETE
 
# Explicit method
gh api repos/{owner}/{repo} -X PATCH -f description="Updated"

Passing Parameters

Raw Fields (-f / --raw-field)

All values are treated as strings:

gh api repos/{owner}/{repo}/issues -f title="Bug report" -f body="It crashes"

Typed Fields (-F / --field)

Values are auto-converted to the correct JSON type (boolean, integer, null):

gh api repos/{owner}/{repo}/issues -F title="Bug" -F labels[]="bug" -F milestone=3
gh api repos/{owner}/{repo}/issues -F draft=true -F assignees[]=octocat

Nested Parameters

# Nested objects: key[subkey]=value
gh api repos/{owner}/{repo}/rulesets -F rules[0][type]="commit_message_pattern"
 
# Arrays: key[]=value
gh api repos/{owner}/{repo}/topics -f names[]="cli" -f names[]="go"
 
# Empty array
gh api repos/{owner}/{repo}/topics -f names[]=""

Reading from Files

# Request body from a file
gh api repos/{owner}/{repo}/rulesets --input ruleset.json
 
# From stdin
cat payload.json | gh api repos/{owner}/{repo}/rulesets --input -
 
# Field value from a file
gh api repos/{owner}/{repo}/issues -f body=@issue-body.md

GraphQL Queries

# Simple query
gh api graphql -f query='
  query {
    viewer {
      login
      name
    }
  }
'
 
# With variables
gh api graphql -f query='
  query($owner: String!, $name: String!) {
    repository(owner: $owner, name: $name) {
      stargazerCount
      description
    }
  }
' -f owner=cli -f name=cli
 
# From a file
gh api graphql --input query.graphql

Pagination

REST Pagination

# Automatically fetch all pages
gh api repos/{owner}/{repo}/issues --paginate
 
# Combine paginated results into one array
gh api repos/{owner}/{repo}/issues --paginate --slurp

GraphQL Pagination

For GraphQL, your query must:

  1. Accept a $endCursor: String variable
  2. Fetch pageInfo { hasNextPage, endCursor }
gh api graphql --paginate -f query='
  query($endCursor: String) {
    viewer {
      repositories(first: 100, after: $endCursor) {
        nodes { nameWithOwner }
        pageInfo { hasNextPage, endCursor }
      }
    }
  }
'

Output Formatting

JSON Filtering with --jq

# Extract specific fields
gh api repos/{owner}/{repo}/issues --jq '.[].title'
 
# Filter by condition
gh api repos/{owner}/{repo}/issues --jq '[.[] | select(.labels | length > 0)]'
 
# Format as tab-separated values
gh api repos/{owner}/{repo}/issues --jq '.[] | [.number, .title] | @tsv'

Go Templates with --template

gh api repos/{owner}/{repo}/issues --template '
{{range .}}#{{.number}} {{.title}}
{{end}}'

Include HTTP Headers

# Show status line and headers
gh api repos/{owner}/{repo} --include
 
# Full request and response
gh api repos/{owner}/{repo} --verbose

Silent Mode

# Suppress response body (useful for write operations)
gh api repos/{owner}/{repo}/issues/42 -f state=closed --silent

Caching

# Cache the response for 1 hour
gh api repos/{owner}/{repo} --cache 1h
 
# Cache for 5 minutes
gh api repos/{owner}/{repo}/issues --cache 5m

Custom Headers

gh api repos/{owner}/{repo} -H "Accept: application/vnd.github.v3+json"
gh api repos/{owner}/{repo} -H "X-GitHub-Api-Version: 2022-11-28"

API Previews

gh api repos/{owner}/{repo}/topics -p mercy

Real-World Examples

Star a Repository

gh api user/starred/cli/cli -X PUT

Create a Repository Dispatch Event

gh api repos/{owner}/{repo}/dispatches -f event_type="deploy" -f client_payload[environment]="production"

Get Rate Limit Status

gh api rate_limit --jq '.resources.core'

List All Organization Members

gh api orgs/my-org/members --paginate --jq '.[].login'

Delete a Repository (not available as a gh repo subcommand)

gh api repos/owner/repo -X DELETE

Exercises

  1. Get your user info: gh api user --jq '.login'
  2. List issues with pagination: gh api repos/cli/cli/issues --paginate --jq '.[].title' | head -20
  3. Make a GraphQL query for star count: gh api graphql -f query='{ repository(owner:"cli", name:"cli") { stargazerCount } }'
  4. Check your rate limit: gh api rate_limit --jq '.resources.core'