GH-API(1)

πŸ’… GH-API(1) β€” the GitHub CLI manual, but make it make sense

okay bestie, this is gh-api, and it's from the GitHub CLI manual, no cap. if you've ever wanted your terminal to just talk to GitHub for you β€” authenticated, no fuss β€” this is the one. let's get into it 🫢


✨ NAME

gh-api β€” Make an authenticated GitHub API request

🌸 SYNOPSIS

gh api <endpoint> [flags]

πŸ¦‹ DESCRIPTION

So here's the vibe: it makes an authenticated HTTP request to the GitHub API and prints the response. that's the whole thing. iconic. πŸ’…

the endpoint argument

The endpoint argument should either be a path of a GitHub API v3 endpoint, or literally the word graphql if you're trying to access the GitHub API v4. yes, graphql is the secret password. iykyk ✨

placeholders, aka magic curly braces

Placeholder values {owner}, {repo}, and {branch} in the endpoint argument will get replaced with values from the repository of the current directory, or from the repository specified in the GH_REPO environment variable. chef's kiss, truly πŸŽ€

okay but lowkey heads up: in some shells β€” PowerShell, looking at you πŸ™ˆ β€” you may need to enclose any value that contains {...} in quotes, so the shell doesn't go applying special meaning to your curly braces. don't let the shell steal your moment.

previews, babes

The -p/--preview flag enables opting into previews, which are feature-flagged, experimental API endpoints or behaviors. The API expects opt-in via the Accept header with format application/vnd.github.<preview-name>-preview+json, and this command facilitates that via --preview <preview-name>.

So to send a request for the corsair and scarlet-witch previews, you could use -p corsair,scarlet-witch or --preview corsair --preview scarlet-witch. both work, both slay πŸ’–

method vibes

The default HTTP request method is GET normally, and POST if any parameters were added. Override the method with --method. simple as that 🌈

parameters: -f vs -F

Pass one or more -f/--raw-field values in key=value format to add static string parameters to the request payload. To add non-string or placeholder-determined values, see -F/--field below.

⚠️ note that adding request parameters will automatically switch the request method to POST. if that's not what you want and you need the parameters as a GET query string instead, use --method GET. no cap, this trips people up.

The -F/--field flag has magic type conversion based on the format of the value πŸ‘‘

  • literal values true, false, null, and integer numbers get converted to appropriate JSON types;
  • placeholder values {owner}, {repo}, and {branch} get populated with values from the repository of the current directory;
  • if the value starts with @, the rest of the value is interpreted as a filename to read the value from. Pass - to read from standard input.

For GraphQL requests, all fields other than query and operationName are interpreted as GraphQL variables. periodt. 🧚

nesting & arrays

To pass nested parameters in the request payload, use key[subkey]=value syntax when declaring fields. To pass nested values as arrays, declare multiple fields with the syntax key[]=value1, key[]=value2. To pass an empty array, use key[] without a value. the little things matter πŸ’œ

bodies from a file

To pass pre-constructed JSON or payloads in other formats, a request body may be read from file specified by --input. Use - to read from standard input. When passing the request body this way, any parameters specified via field flags are added to the query string of the endpoint URL. sneaky but correct πŸ“

pagination, the main character

In --paginate mode, all pages of results will sequentially be requested until there are no more pages of results. For GraphQL requests, this requires that the original query accepts an $endCursor: String variable and that it fetches the pageInfo{ hasNextPage, endCursor } set of fields from a collection. Each page is a separate JSON array or object.

Pass --slurp to wrap all pages of JSON arrays or objects into an outer JSON array. boom, one big beautiful array ✨


πŸŽ€ OPTIONS

  • --allow-escape-sequences β€” Allow printing terminal escape sequences
  • --cache <duration> β€” Cache the response, e.g. "3600s", "60m", "1h"
  • -F, --field <key=value> β€” Add a typed parameter in key=value format (use "@" or "@-" to read value from file or stdin)
  • -H, --header <key:value> β€” Add a HTTP request header in key:value format
  • --hostname <string> β€” The GitHub hostname for the request (default "github.com")
  • -i, --include β€” Include HTTP response status line and headers in the output
  • --input <file> β€” The file to use as body for the HTTP request (use "-" to read from standard input)
  • -q, --jq <string> β€” Query to select values from the response using jq syntax
  • -X, --method <string> (default "GET") β€” The HTTP method for the request
  • --paginate β€” Make additional HTTP requests to fetch all pages of results
  • -p, --preview <strings> β€” Opt into GitHub API previews (names should omit '-preview')
  • -f, --raw-field <key=value> β€” Add a string parameter in key=value format
  • --silent β€” Do not print the response body
  • --slurp β€” Use with "--paginate" to return an array of all pages of either JSON arrays or objects
  • -t, --template <string> β€” Format JSON output using a Go template; see "gh help formatting"
  • --verbose β€” Include full HTTP request and response in the output

πŸ’€ EXIT CODES

okay so when things end, here's how they end:

  1. 0: Successful execution
  2. 1: Error
  3. 2: Command canceled
  4. 4: Authentication required
NOTE: Specific commands may have additional exit codes. Refer to the command's help for more information. don't come for me if your exit code isn't on this list πŸ™ˆ

πŸ”₯ EXAMPLE

this is the good part. screenshots-worthy. let's go πŸ’–

List releases in the current repository:

# List releases in the current repository
$ gh api repos/{owner}/{repo}/releases

Post an issue comment:

# Post an issue comment
$ gh api repos/{owner}/{repo}/issues/123/comments -f body='Hi from CLI'

Post nested parameter read from a file:

# Post nested parameter read from a file
$ gh api gists -F 'files[myfile.txt][content]=@myfile.txt'

Add parameters to a GET request:

# Add parameters to a GET request
$ gh api -X GET search/issues -f q='repo:cli/cli is:open remote'

Use a JSON file as request body:

# Use a JSON file as request body
$ gh api repos/{owner}/{repo}/rulesets --input file.json

Set a custom HTTP header:

# Set a custom HTTP header
$ gh api -H 'Accept: application/vnd.github.v3.raw+json' ...

Opt into GitHub API previews:

# Opt into GitHub API previews
$ gh api --preview baptiste,nebula ...

Print only specific fields from the response:

# Print only specific fields from the response
$ gh api repos/{owner}/{repo}/issues --jq '.[].title'

Use a template for the output:

# Use a template for the output
$ gh api repos/{owner}/{repo}/issues --template \
  '{{range .}}{{.title}} ({{.labels | pluck "name" | join ", " | color "yellow"}}){{"\n"}}{{end}}'

Update allowed values of the "environment" custom property in a deeply nested array:

# Update allowed values of the "environment" custom property in a deeply nested array
$ gh api -X PATCH /orgs/{org}/properties/schema \
   -F 'properties[][property_name]=environment' \
   -F 'properties[][default_value]=production' \
   -F 'properties[][allowed_values][]=staging' \
   -F 'properties[][allowed_values][]=production'

List releases with GraphQL:

# List releases with GraphQL
$ gh api graphql -F owner='{owner}' -F name='{repo}' -f query='
  query($name: String!, $owner: String!) {
    repository(owner: $owner, name: $name) {
      releases(last: 3) {
        nodes { tagName }
      }
    }
  }
'

List all repositories for a user:

# List all repositories for a user
$ gh api graphql --paginate -f query='
  query($endCursor: String) {
    viewer {
      repositories(first: 100, after: $endCursor) {
        nodes { nameWithOwner }
        pageInfo {
          hasNextPage
          endCursor
        }
      }
    }
  }
'

Get the percentage of forks for the current user:

# Get the percentage of forks for the current user
$ gh api graphql --paginate --slurp -f query='
  query($endCursor: String) {
    viewer {
      repositories(first: 100, after: $endCursor) {
        nodes { isFork }
        pageInfo {
          hasNextPage
          endCursor
        }
      }
    }
  }
' | jq 'def count(e): reduce e as $_ (0;.+1);
[.[].data.viewer.repositories.nodes[]] as $r | count(select($r[].isFork))/count($r[])'

🌈 SEE ALSO

  • gh(1) β€” the main character herself 🫢

that's the whole page bestie, go forth and hit that API responsibly ✨

with love, from the GitHub CLI crew β€” version v2.102.0, dated Oct 2026. mwah πŸ’‹

πŸ“¦ where this page comes from