APIs — the big picture

Status: 🟩 COMPLETE Last updated: 2026-06-19 Plain-English tagline: A contract — “here are the things you can ask me to do, here’s how to ask, here’s what I’ll send back” — that lets two pieces of software talk to each other without needing to know how the other is built inside.


In plain English

API stands for Application Programming Interface. It’s a piece of jargon that hides a beautifully simple idea:

When two computers (or two parts of one computer) need to cooperate, they need a shared, predictable language. The API is that language. It defines:

  • What you can ask the other side to do
  • How to phrase the ask
  • What the response will look like
  • What can go wrong, and how errors get reported

The classic analogy: a restaurant menu. You don’t go into the kitchen. You don’t tell the chef which pots to use. You look at the menu, point at “Caesar salad #3,” and out it comes. The menu is the API — a stable contract between you and the kitchen, with the messy details hidden behind it.

In software, almost every interesting cooperation happens through an API:

  • Your webapp’s frontend asks the backend for data → through an API
  • Your backend asks Stripe to charge a card → through Stripe’s API
  • Your backend asks a database to find rows → through the database’s API
  • An LLM service exposes its model behind an API
  • Your phone’s weather app gets forecasts → through a weather service’s API

Crucially, an API is a promise about behavior, not about implementation. If Stripe rewrites their charge-card code in a different language tomorrow, your code keeps working — as long as the API contract is preserved.


Why it matters

Three reasons APIs are everywhere:

  1. Decoupling. The team building the API can rewrite the inside whenever they want, as long as the contract holds. The team CONSUMING the API doesn’t have to know or care. This is the single most important enabler of large software systems.

  2. Specialization. You don’t have to build a payment processor — you call Stripe’s API. You don’t have to host a model — you call Anthropic’s API. The modern internet economy is built on companies offering capabilities via APIs that other companies compose into their own products.

  3. Reusability. Once an API exists, infinite consumers can use it. A weather API serves a thousand apps. A login provider’s API powers millions of “Sign in with Google” buttons.

Without APIs, every piece of software would have to re-implement everything itself. With them, you compose. That’s the entire business model of modern SaaS.


The families of APIs you’ll meet

Not all APIs look the same. The main families:

1. REST APIs (the dominant style)

HTTP requests in a specific shape:

  • A method (GET, POST, PUT, DELETE)
  • A URL identifying a resource (/users/123, /posts/47/comments)
  • A request body (for create/update operations) — usually JSON
  • A status code and JSON body in the response

This is what 80% of “an API” means in 2026. The Stripe API, GitHub API, Supabase REST API, your own Next.js API routes — all REST-shaped.

Deep dive: REST APIs.

2. GraphQL

A client describes the EXACT data shape it wants in a single query; the server returns only that. One endpoint, no over-fetching, schema-typed. More expressive than REST, more complex to operate.

Deep dive: GraphQL.

3. RPC and gRPC

“Remote procedure call.” Treat the network call as if it were a normal function call. userService.getUser({ id: 123 }) invokes code on a remote server. gRPC is Google’s modern RPC framework using HTTP/2 + Protobuf. Common in microservice backends; rare in browser-facing APIs.

4. WebSockets / realtime APIs

A persistent two-way connection. Either side can send messages at any time. Used for chat, presence, multiplayer games, live dashboards, Supabase Realtime, Pusher, Ably.

5. Webhooks

Inverted direction. Instead of you calling the service, the service calls a URL you registered. “When a Stripe payment succeeds, POST to https://yourapp.com/api/webhooks/stripe.” See Webhooks.

6. SDK-style “API”

Sometimes “API” refers to a programming-language-level interface — the public methods of a library. The DOM API. The fetch API. The Anthropic SDK exposes a TypeScript “API” that wraps Anthropic’s HTTP REST API underneath. This is the original meaning of API; the HTTP usage came later.

7. Specialized protocols

GraphQL Subscriptions, Server-Sent Events (SSE), JSON-RPC, SOAP (legacy), GraphQL Federation, OpenAPI/Swagger as a spec format. Each fills a niche.

For the kinds of projects George builds, REST + occasional webhooks covers ~95% of cases. GraphQL appears with some specific tools (Hasura, Shopify Storefront API). Realtime APIs appear when building chat-like or live-update features.


The shape of every HTTP API call

Whatever the style, an HTTP API call is structurally always the same:

REQUEST
─────────────────────────────────────────────────
Method:  GET
URL:     https://api.example.com/users/123
Headers: Authorization: Bearer abc123
         Content-Type: application/json
         Accept: application/json
Body:    (none for GET; JSON for POST/PUT)

RESPONSE
─────────────────────────────────────────────────
Status:  200 OK
Headers: Content-Type: application/json
         X-RateLimit-Remaining: 4998
Body:    { "id": 123, "name": "George", "email": "..." }

Four parts each way:

RequestResponse
Method — verbStatus code — outcome (200, 404, 500…)
URL — targetHeaders — metadata about the response
Headers — auth, content type, etc.Body — usually JSON payload
Body — input payload (or none)

Every HTTP-based API call you ever make follows this shape. Once you internalize it, every API just becomes “what verbs + URLs + payloads does this particular API understand?”


A concrete example: hitting two real APIs

// GitHub API — list repos for a user
const githubResponse = await fetch("https://api.github.com/users/geo-au/repos", {
  headers: { Accept: "application/vnd.github+json" },
});
const repos = await githubResponse.json();
console.log(repos.map(r => r.name));
 
// Anthropic API — ask Claude something
const claudeResponse = await fetch("https://api.anthropic.com/v1/messages", {
  method: "POST",
  headers: {
    "x-api-key": process.env.ANTHROPIC_API_KEY!,
    "anthropic-version": "2023-06-01",
    "content-type": "application/json",
  },
  body: JSON.stringify({
    model: "claude-sonnet-4-6",
    max_tokens: 1024,
    messages: [{ role: "user", content: "What is an API?" }],
  }),
});
const reply = await claudeResponse.json();
console.log(reply.content[0].text);

Both calls have the same overall shape. They differ in:

  • The URL (the service you’re talking to)
  • The method (GET vs POST)
  • The headers (each API has its own auth header convention)
  • The body shape (each API has its own payload schema)
  • The response shape (each API returns its own JSON structure)

Once you’ve used a couple of APIs, hitting a new one is mostly reading the docs to learn its specific URL/method/payload conventions.


API contracts and how to express them

A well-documented API publishes a specification so consumers can integrate confidently. Common formats:

  • OpenAPI (formerly Swagger) — YAML/JSON describing every endpoint, parameter, request shape, response shape. Tools generate SDKs from it. Industry standard.
  • GraphQL SDL (Schema Definition Language) — strongly-typed schema describing all queries and types. Native to GraphQL; powerful introspection.
  • Protocol Buffers (Protobuf) — binary format + IDL used by gRPC.
  • TypeScript types — for TS-to-TS APIs (like Next.js Server Actions or tRPC), the types ARE the contract.
  • A README plus examples — the unfortunately common minimum.

When picking which APIs to consume, well-specified APIs (OpenAPI / GraphQL / Protobuf) are dramatically easier to integrate than ad-hoc ones. You can autogenerate the client SDK, type-check at compile time, and stay synced with the upstream as it evolves.


Authentication — how the API knows who’s asking

An API endpoint exposed to the public internet must answer: “is this caller authorized?” The common patterns:

PatternHow it works
API keyA long secret string sent in a header (Authorization: Bearer ... or x-api-key: ...). Identifies the calling app. Stripe, OpenAI, Anthropic.
OAuth 2.0 access tokenA token issued by a separate auth flow (the user logged in once and granted permission). Used when the calling app acts on behalf of a USER. GitHub PATs, Slack tokens, Google APIs.
JWT (signed)A self-describing token the server can verify without a database lookup. Common for first-party APIs (your own frontend → your own backend).
Session cookieA cookie sent with each request. The typical pattern for “user logged into a webapp talking to its own backend.”
mTLS / client certsCertificate-based auth between machines. Enterprise / financial / healthcare integrations.
No auth (public)Read-only public endpoints — e.g. weather APIs, public stats.

Often combined with rate limiting (X requests per minute per key) and scopes (an API key has permission to read but not write, or only to access certain resources).


Versioning — APIs change without breaking consumers

A live API will need to evolve. Done carelessly, every change breaks every consumer. Done carefully, consumers can upgrade on their own schedule. Strategies:

  • URL versioning. /v1/users vs /v2/users. Crude but obvious.
  • Header versioning. Anthropic-Version: 2023-06-01 — the client specifies which behavior set it expects.
  • Date-based versioning. Stripe’s approach. Each API key has a “default API version” that’s pinned to a date; new features require opting in.
  • Backwards-compatible additions. Add new optional fields; never remove or change existing ones. Old clients ignore the new fields; new clients use them.

The mainstream pattern in 2026: never break existing behavior; add new behavior alongside. “Deprecate then remove” with a long sunset period. Stripe’s API has been backwards-compatible for over a decade.


Common gotchas

  • An API is a TRUST surface. Anyone with the URL + auth can use it. Don’t assume your endpoint is “private” because it’s not linked from your homepage — bots will find it. Always require proper auth on anything non-public.

  • Rate limits are real and you will hit them. Every external API has limits. Handle 429 responses with exponential backoff. Cache aggressively. Many APIs return a Retry-After header — respect it.

  • Network errors are normal, not exceptional. Timeouts, connection resets, DNS failures, brief outages — all routine. Your code must retry with backoff and surface meaningful errors when retries fail.

  • Always set timeouts. A fetch call with no timeout can hang forever, holding up your serverless function until the platform kills it. Set explicit timeouts (AbortController in browsers/Node).

  • API responses can be huge and slow. A “list all users” endpoint can return 10GB if you have 10M users. Use pagination. Default page sizes should be small (20-50).

  • Headers and bodies are case-sensitive (kind of). HTTP header names are case-INsensitive by spec but case-sensitive in JavaScript objects. Use a library or normalize.

  • JSON nulls vs missing fields are different. { "name": null } and {} mean different things to most parsers. Be explicit about which you mean.

  • Cross-origin requests have CORS rules. A browser calling api.othersite.com from myapp.com will get blocked unless the API explicitly allows it. CORS is configured on the API server, not the calling code.

  • API keys in client-side code are leaked. Anything in browser JavaScript is visible. For server-to-server keys, keep them server-side (env vars, never NEXT_PUBLIC_*).

  • Idempotency is critical. Network retries can cause duplicate calls. POST endpoints that create resources should accept an Idempotency-Key header — clients send a unique key per logical action; the server dedupes. Stripe pioneered this.

  • OpenAPI specs lie sometimes. The spec says “this field is required”; the real API accepts requests without it. Or vice versa. Always test against the live API.

  • Versioning is the long game. Today’s “we’ll never need this” becomes tomorrow’s mandatory breaking change. Plan for evolution from day one.

  • Pagination styles vary. Page numbers, cursors, offsets, link headers (GitHub style). Read the docs of each API.

  • The Internet is a hostile place. Validate every input. Never trust a header. Never trust a payload. Never trust the caller’s claim about their identity. Always re-verify.

  • Documentation can be outdated. A field was deprecated last year; the docs still mention it. Trust working code (test against the real API) over docs when they disagree.

  • Error responses can be misleading. A 401 might really mean 404 (security-by-obscurity). A 500 might really be a 401 (poorly-coded backend). Treat error responses as suggestive, not authoritative.

  • An “internal API” still needs the same care. Microservices talking to each other across a network are subject to the same failures as external APIs. Same patterns apply (timeouts, retries, idempotency).

  • APIs go away. Twitter v1.1 was discontinued. The old GitHub auth model was removed. Plan for the API you depend on being replaced or sunset.

  • Mocking external APIs in tests is essential. Tests that hit real third-party services are slow, flaky, and incur cost. Use a mocking library (MSW, nock) and re-run real integration tests separately.


SDKs vs raw HTTP

Most popular APIs publish SDKs (Software Development Kits) in major languages. The SDK is a wrapper that handles:

  • Auth header management
  • Retries + backoff
  • Pagination
  • Typed request/response objects
  • Idempotency keys
  • Streaming (for AI APIs, GraphQL subscriptions)
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const message = await client.messages.create({
  model: "claude-sonnet-4-6",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Hello" }],
});

vs the raw HTTP version above. The SDK adds value if you’ll make many calls. For one-off scripts, raw fetch is fine.

The SDK is itself an “API” — a programming-language interface that internally calls an HTTP API. Same concept, different abstraction layer.


See also


Sources