Chapter 26Lesson 01~185 minutes

REST API, GraphQL API, gh api, Pagination, Rate Limits, and Automation Clients: Concepts, Architecture, and Mental Model

GitHub APIs are contracts, not shortcuts around authorization. Build a beginner-first mental model for REST resources, GraphQL connections, authentication, version headers, pagination, rate limits, conditional requests, retry boundaries, and observable automation.

RESTGraphQLgh apiPaginationRate limits

Learning objectives

  • Distinguish REST resources/endpoints from GraphQL schema, connections, queries, and mutations.
  • Explain authentication versus authorization, REST API versions/media types, and resource ownership.
  • Traverse complete REST and GraphQL result sets without silently accepting a first page.
  • Interpret primary/secondary rate-limit evidence and use bounded backoff/conditional requests.
  • Classify writes by idempotence and design observable duplicate-resistant mutations.
Availability: REST, GraphQL, and GitHub CLI are available on GitHub.com without a paid plan. The mandatory path uses a disposable public personal repository. GitHub Enterprise Server has its own hostname, release/version line, feature set, and administrator-configured rate limits.

1. The problem: API automation needs more than a successful request

Earlier chapters used gh api to inspect workflows, packages, security findings, environments, and attestations. Those examples were deliberately narrow. A production client has to preserve correctness when the collection spans many pages, when the resource is private, when GitHub returns a throttling response, when GraphQL returns partial data, or when a create request succeeds on the server but the client loses the response.

Start with a durable rule: HTTP success is not the same as complete business truth. A first-page 200 OK can still omit most repositories. A GraphQL 200 can include an errors array. A timed-out POST can still have created the object. The client must model the hosted resource, authorization boundary, traversal contract, and desired state before it mutates anything.

2. Mental model: identity → contract → traversal → decision → mutation

Concept / workflow diagram
              flowchart TD
                I["Automation identity"] --> A["Authentication"]
                A --> Z["Authorization"]
                Z --> R["REST resource + version"]
                Z --> G["GraphQL schema + query"]
                R --> P["Link/page traversal"]
                G --> C["Connection/cursor traversal"]
                P --> E["Complete normalized evidence"]
                C --> E
                E --> D["Policy / dry-run decision"]
                D --> N{"Mutation needed?"}
                N -->|no| O["No-op evidence"]
                N -->|yes| M["Duplicate/idempotence guard"]
                M --> W["Bounded write"]
                W --> V["Read-after-write verification"]
                R -. status/rate headers .-> B["Backoff controller"]
                G -. errors/rateLimit .-> B
            

Authentication answers “who is calling?” Authorization answers “what may that identity do to this owner/resource?” REST and GraphQL then expose two different contracts over GitHub objects. Pagination is part of correctness, not a performance afterthought. Only a complete, error-checked input set should reach a mutation decision.

3. REST resources versus GraphQL schema

Dimension REST GraphQL
Model Resource-oriented endpoints such as GET /repos/OWNER/REPO/issues. Typed schema queried through one GraphQL endpoint.
Fields Endpoint defines response shape; parameters filter/sort where supported. Client selects exactly the fields it needs.
Pagination Follow response Link relations; do not invent page URLs. Connections use opaque cursors and pageInfo.
Versioning Date-versioned contract; this chapter uses 2026-03-10. Schema/changelog/deprecation process; REST version header is not GraphQL versioning.
Errors HTTP status + JSON body + headers. HTTP can be 200 while errors and partial data coexist.

REST is often clearer for one resource or collection. GraphQL can reduce calls when a client needs related fields across several objects, but query depth, node counts, cost, and partial-error handling become part of the design.

4. Authentication, API versions, media types, and ownership

Current GitHub REST integrations should send Accept: application/vnd.github+json and an explicit supported X-GitHub-Api-Version. The course baseline is 2026-03-10. GitHub currently documents that a REST request without the version header falls back to 2022-11-28; do not make that implicit fallback part of a production contract.

gh api   -H "Accept: application/vnd.github+json"   -H "X-GitHub-Api-Version: 2026-03-10"   repos/OWNER/REPO --jq '{full_name,visibility,default_branch}' 

gh api uses the authenticated GitHub CLI identity. If you intentionally want to observe public unauthenticated behavior, use a client that omits Authorization, such as curl; do not manipulate or expose the CLI credential store.

404 is ambiguous: For a private resource, GitHub may return 404 Not Found when the caller cannot see it. Verify host, owner/repository, authenticated identity, repository selection, and documented permissions before concluding that the resource is absent.

5. Pagination is a correctness requirement

Many REST list endpoints return a subset. GitHub documents a default of 30 items for many endpoints and a maximum per_page of 100 for most endpoints, but each endpoint reference is authoritative. Follow the server-provided Link header until rel="next" disappears.

gh api --paginate   -H "X-GitHub-Api-Version: 2026-03-10"   "repos/OWNER/REPO/issues?state=all&per_page=2"   --jq '.[] | {number,title,state}' 

GraphQL connections require first or last from 1 through 100. Request pageInfo { hasNextPage endCursor } and feed the previous cursor into after. gh api graphql --paginate expects an $endCursor: String variable and the relevant pageInfo fields.

6. Primary and secondary rate limits

On GitHub.com, current primary limits include 60 REST requests per hour for unauthenticated public-data requests and generally 5,000 REST requests per hour for an authenticated user. GraphQL generally provides 5,000 points per hour for a user. Rate budget is not authorization.

Secondary rate limits protect service reliability even when primary budget remains. Current guidance discusses concurrency, endpoint/query point pressure, CPU time, and mutation bursts. A REST throttling response can be 403 or 429; GraphQL may surface rate errors with HTTP 200 or 403. Honor Retry-After; if x-ratelimit-remaining reaches zero, wait until x-ratelimit-reset; otherwise back off exponentially and stop after a bounded number of attempts.

gh api -i -H "X-GitHub-Api-Version: 2026-03-10" rate_limit --silent

gh api graphql -f query='query { viewer { login } rateLimit { cost remaining resetAt } }' 

7. Conditional requests, retries, and idempotence

Many REST responses include ETag, and many include Last-Modified. An authenticated conditional GET that returns 304 Not Modified does not count against the primary REST rate limit. This is useful for low-frequency reconciliation when an event is unavailable.

Operation Retry posture Reason
GET/HEAD Usually retryable after bounded transient/rate waits. Read does not create duplicate state.
PUT to a documented desired state Often idempotent, but verify endpoint semantics. Repeated desired representation may converge.
POST create Never blind-retry by default. Re-read for a durable marker first. The server may have created the object before the client timed out.
PATCH State/endpoint dependent; use preconditions where supported and verify after write. Repeated state-sensitive changes can diverge.

8. Read-only inspection before mutation

gh --version
gh auth status
gh api -i -H "X-GitHub-Api-Version: 2026-03-10" user --jq '{login,id}'
gh api -i -H "X-GitHub-Api-Version: 2026-03-10" rate_limit --silent
gh api graphql -f query='query { viewer { login } rateLimit { cost remaining resetAt } }' 

Production logs should capture safe request evidence such as operation/endpoint, host, status, GitHub request ID, duration, page/item counts, and rate metadata. Never log bearer tokens, Authorization headers, whole private response bodies, or sensitive fields the client does not need.

9. Why this matters in DevOps

API clients become control-plane software: they inventory repositories, create issues/deployments, manage policy, and connect GitHub to other systems. A first-page bug can omit half an estate; a blind POST retry can duplicate incidents; an over-scoped identity can turn one parsing flaw into a broad compromise. Treat API automation like production software: explicit contracts, least privilege, complete traversal, bounded resource use, observable decisions, and verified state transitions.

Knowledge check

A REST issue-list response contains 30 items and no code checks the Link header. What can you conclude about completeness?

Why does an authenticated 404 not prove a private repository is absent?

GraphQL returns HTTP 200 plus data and errors. Is this a complete success?

A POST create request times out. What should precede a retry?

Why is per_page=100 not a substitute for pagination?

Summary

Correct GitHub API automation treats REST/GraphQL as permissioned, versioned, paginated, rate-limited contracts. The client names its host/identity/resource, retrieves every page, inspects errors/status/rate metadata, minimizes fields, uses conditional reads or events where appropriate, makes dry-run/desired-state decisions, and verifies the hosted result.

Next lesson

REST API, GraphQL API, gh api, Pagination, Rate Limits, and Automation Clients: Guided Hands-On Workflow and Core Operations

Official references

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.