Practice: GitHub API
About GitHub API
Real production API with public and authenticated endpoints.
-
Base URL:
api.github.com -
Documentation: docs.github.com/en/rest
-
Rate Limits:
-
Unauthenticated: 60 requests/hour
-
Authenticated: 5,000 requests/hour
-
Setup
Create Personal Access Token
-
GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic)
-
Generate new token with scopes:
repo,user,read:org -
Store in gopass:
gopass edit v3/dev/github
# Content:
---
token: ghp_xxxxxxxxxxxxxxxxxxxx
username: yourusername
Shell Function
gh_api() {
local endpoint="${1:-user}"
local jq_filter="${2:-.}"
curl -sS \
-H "Authorization: Bearer $(gopass show v3/dev/github | sed '1,/^---$/d' | yq -r '.token')" \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"https://api.github.com/${endpoint}" \
| jq "$jq_filter"
}
Public Endpoints (No Auth)
User Info
# Public user profile
curl -s https://api.github.com/users/torvalds | jq '{name, company, location, public_repos, followers}'
# Your profile (if token set)
gh_api user
Repositories
# User's public repos
curl -s https://api.github.com/users/torvalds/repos | jq '.[0:5] | .[].name'
# Specific repo
curl -s https://api.github.com/repos/torvalds/linux | jq '{name, stars: .stargazers_count, forks: .forks_count}'
Rate Limit Check
# Check remaining requests
curl -s https://api.github.com/rate_limit | jq '.rate'
Authenticated Endpoints
Your Profile
# Full profile
gh_api user
# Your repos
gh_api "user/repos?per_page=5&sort=updated" '.[].full_name'
# Your stars
gh_api "user/starred?per_page=5" '.[].full_name'
Repository Operations
# List your repos
gh_api "user/repos?per_page=10" '[.[] | {name, private, updated_at}]'
# Get specific repo details
gh_api "repos/YOUR_USER/YOUR_REPO" '{name, default_branch, open_issues}'
# List branches
gh_api "repos/YOUR_USER/YOUR_REPO/branches" '.[].name'
# List commits
gh_api "repos/YOUR_USER/YOUR_REPO/commits?per_page=5" '.[].commit.message'
Star a Repository
# Star (PUT with no body)
curl -s -X PUT \
-H "Authorization: Bearer $(gopass show v3/dev/github | sed '1,/^---$/d' | yq -r '.token')" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/user/starred/owner/repo
# Unstar (DELETE)
curl -s -X DELETE \
-H "Authorization: Bearer $(gopass show v3/dev/github | sed '1,/^---$/d' | yq -r '.token')" \
https://api.github.com/user/starred/owner/repo
# Check if starred (204 = yes, 404 = no)
curl -s -o /dev/null -w '%{http_code}' \
-H "Authorization: Bearer TOKEN" \
https://api.github.com/user/starred/torvalds/linux
Issues
# List issues in a repo
gh_api "repos/cli/cli/issues?state=open&per_page=5" '[.[] | {number, title, user: .user.login}]'
# Create issue (your repo only)
curl -s -X POST \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"title": "Test Issue", "body": "Created via API"}' \
https://api.github.com/repos/YOUR_USER/YOUR_REPO/issues | jq
Gists
# List your gists
gh_api gists '[.[] | {id, description, public}]'
# Create gist
curl -s -X POST \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{
"description": "API Test",
"public": false,
"files": {
"test.txt": {"content": "Hello from API"}
}
}' \
https://api.github.com/gists | jq '{id, html_url}'
Search API
Search Repositories
# Search by language and stars
curl -s "https://api.github.com/search/repositories?q=language:rust+stars:>10000&sort=stars" \
| jq '.items[:5] | .[] | {name: .full_name, stars: .stargazers_count}'
Search Code
# Find code patterns (requires auth)
gh_api "search/code?q=filename:Dockerfile+FROM+rust" '.items[:3] | .[].repository.full_name'
Search Users
# Find users by location
curl -s "https://api.github.com/search/users?q=location:tokyo+followers:>1000" \
| jq '.items[:5] | .[] | {login, followers}'
Pagination
GitHub uses Link headers for pagination:
# Check pagination headers
curl -sI "https://api.github.com/users/torvalds/repos?per_page=5" | grep -i link
# Fetch all pages
fetch_all_repos() {
local user="$1"
local page=1
local all_repos="[]"
while true; do
local response
response=$(curl -s "https://api.github.com/users/$user/repos?per_page=100&page=$page")
local count
count=$(echo "$response" | jq 'length')
[[ "$count" -eq 0 ]] && break
all_repos=$(echo "$all_repos" "$response" | jq -s 'add')
((page++))
done
echo "$all_repos"
}
# Usage
fetch_all_repos torvalds | jq 'length'
GraphQL API (Advanced)
GitHub also has GraphQL:
# GraphQL query
curl -s -X POST \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "query { viewer { login name repositories(first: 5) { nodes { name stargazerCount } } } }"
}' \
https://api.github.com/graphql | jq '.data.viewer'
Advanced Challenges
Challenge 1: Repo Statistics
# Get repo stats: commits, contributors, languages
repo="cli/cli"
echo "=== $repo ==="
echo "Stars: $(gh_api "repos/$repo" '.stargazers_count')"
echo "Forks: $(gh_api "repos/$repo" '.forks_count')"
echo "Open Issues: $(gh_api "repos/$repo" '.open_issues_count')"
echo "Languages:"
gh_api "repos/$repo/languages" 'to_entries | .[] | " \(.key): \(.value)"' -r
Challenge 2: Activity Feed
# Your recent activity
gh_api "users/YOUR_USER/events?per_page=10" '.[] | "\(.type) on \(.repo.name) at \(.created_at)"' -r
Challenge 3: PR Review
# Open PRs needing review
gh_api "repos/cli/cli/pulls?state=open&per_page=5" '[.[] | {
number,
title,
author: .user.login,
created: .created_at,
draft: .draft
}]'
Shell Function (Complete)
github() {
local cmd="${1:-help}"
shift
local token
token=$(gopass show v3/dev/github | sed '1,/^---$/d' | yq -r '.token')
_gh_curl() {
curl -sS \
-H "Authorization: Bearer $token" \
-H "Accept: application/vnd.github+json" \
"$@"
}
case "$cmd" in
me)
_gh_curl https://api.github.com/user | jq '{login, name, public_repos, followers}'
;;
repos)
_gh_curl "https://api.github.com/user/repos?per_page=${1:-10}&sort=updated" | jq '.[].full_name' -r
;;
repo)
_gh_curl "https://api.github.com/repos/$1" | jq "${2:-.}"
;;
star)
_gh_curl -X PUT "https://api.github.com/user/starred/$1" && echo "Starred $1"
;;
unstar)
_gh_curl -X DELETE "https://api.github.com/user/starred/$1" && echo "Unstarred $1"
;;
search)
_gh_curl "https://api.github.com/search/repositories?q=$1&per_page=5" | jq '.items[] | {name: .full_name, stars: .stargazers_count}'
;;
*)
echo "Usage: github {me|repos|repo|star|unstar|search} [args]"
;;
esac
}
Exercises Checklist
-
Get your profile info via API
-
List your 10 most recently updated repos
-
Star and unstar a repository via API
-
Search for Rust repos with 1000+ stars
-
Create a private gist via API
-
Fetch all pages of a user’s repos (handle pagination)
-
Build a function to show repo stats (stars, forks, issues)