Index Ai Academy
Phase 8 · APIs · HTTP from the wire up

API styles

REST, RPC, GraphQL, gRPC and streaming: what each is for.

About 30 minutes · A free account opens the whole lesson

Create a free account Try part of it here

Already have an account? Sign in to open this lesson

Why it matters

"API" covers several quite different designs, in the way that "restaurant" covers both a buffet and a set menu. The Claude Messages API is REST-style JSON over HTTP, and it streams with server-sent events. MCP uses JSON-RPC. Payment providers send webhooks. Big companies wire their internal services together with gRPC. Knowing the styles lets you read any API's documentation quickly, and pick the right one when you design your own.

What you'll be able to do

  • Explain REST's idea of resources and methods, and map actions onto it
  • Describe RPC styles, JSON-RPC and gRPC, and where each is used
  • Explain what GraphQL solves, and build a small field selector
  • Choose between polling, webhooks, server-sent events and WebSockets for live updates

What it covers

  1. REST: nouns and verbs
  2. RPC: call a function over the network
  3. GraphQL: ask for exactly the fields you want
  4. Over-fetching, measured
  5. The same request in three styles
  6. Live updates: four ways to hear about changes
  7. Picking a style, and what it costs
  8. Choose one

REST: nouns and verbs

Think of a library. Every book has its own place, and there are only a few things you can do with one: look it up, add a new one, replace it or throw it out. Once you know how that works, you can use any library.

REST (representational state transfer) organizes an API the same way. The things in your system, such as campaigns, orders and customers, are its resources: the nouns. Each resource has an address. The standard HTTP methods are the verbs:

actionmethod and pathsuccess status
list campaignsGET /campaigns200
read oneGET /campaigns/cmp_103200
create onePOST /campaigns201
replace onePUT /campaigns/cmp_103200
change some fieldsPATCH /campaigns/cmp_103200
delete oneDELETE /campaigns/cmp_103204 (no body)

A few more ideas come with it:

  • Each request stands alone. It carries everything the server needs, so the server keeps no memory of past requests. That's called being stateless. Picture a shop where the staff never remember you, so you bring your receipt on every visit.
  • Responses say whether they can be cached, meaning whether a saved copy may be reused.
  • GET never changes anything. It only reads.

Because every REST API uses the same verbs, a developer who knows one can guess most of another.

Quick check
What does this print?
routes = {("GET", "/items"): "list", ("POST", "/items"): "create"}
print(routes[("POST", "/items")])
  1. /items
  2. create
  3. POST
  4. list
Show the answer

create

The key ("POST", "/items") looks up create. In REST, the same path means different things depending on the method.

RPC: call a function over the network

Not every job is about a noun. "Pause this campaign" or "send this invoice" are actions, and it can feel forced to describe them as a change to a record. RPC (remote procedure call) names the action directly: pause_campaign, send_invoice, tools/call. To you it looks like calling a function, except that the function runs on another computer.

There are two common kinds:

  • JSON-RPC sends {"jsonrpc": "2.0", "method": ..., "params": ..., "id": ...}, usually all to one URL. MCP is built on it.
  • gRPC, from Google, describes each function and its message types in a .proto file. Tools generate client and server code from that file in many languages. Messages travel as compact binary Protocol Buffers over HTTP/2, and calls can stream in both directions. It's popular between a company's internal services, where speed and strict types matter more than traffic a person can read.
Quick check
In a REST API, which request should cancel order ord_88?
  1. POST /cancelOrder?id=ord_88 is the only option
  2. DELETE /orders/ord_88, or a PATCH setting its status to "canceled"
  3. GET /orders?cancel=ord_88
  4. GET /orders/ord_88/cancel
Show the answer

DELETE /orders/ord_88, or a PATCH setting its status to "canceled"

GET must never change anything, because browsers, crawlers and caches may repeat it freely. Use DELETE, or PATCH the order's status if canceled orders are kept.

GraphQL: ask for exactly the fields you want

A REST endpoint is a set menu: you get what's on the plate. That causes two problems. A phone screen showing only campaign names still receives every field, which is over-fetching. And a page needing a customer and their orders has to make two or three calls, which is under-fetching.

GraphQL, from Facebook, is ordering exactly what you want. There's one endpoint. The client sends a query describing the shape it wants, and gets back exactly that:

query {
  customer(id: "c_42") {
    name
    orders(last: 3) { id total }
  }
}

The answer is JSON with the same nesting: a name, and three orders with two fields each.

In plain words: with REST, the server decides what comes back. With GraphQL, the client does.

The cost moves to the kitchen. One query can ask for a huge amount of data, so servers must limit how deep and how expensive a query can be. Caching is also harder, because every request is a POST to the same URL.

Worked example

Over-fetching, measured

A price list on a phone needs two fields per product. REST hands back the whole record, unless you've built a way to ask for less. GraphQL lets the client name the fields it wants.

import json

products = [{"id": n, "name": f"Tent {n}", "price": 89.0, "stock": 14,
             "description": "A roomy two-person tent. " * 10,
             "reviews": list(range(20))} for n in range(20)]

rest = json.dumps(products)
graphql = json.dumps([{"name": p["name"], "price": p["price"]} for p in products])

print("REST bytes:", len(rest))
print("GraphQL bytes:", len(graphql))
print("REST is", round(len(rest) / len(graphql)), "times bigger")
It runs right here in your browser, with nothing to install.

Both answers hold what the page needs, but one is far larger. On a slow connection, that's real waiting. GraphQL pays for this elsewhere: caching gets harder, and any client can write an expensive query.

Quick check
A mobile screen needs only a user's name and avatar, but the REST endpoint returns 40 fields. How does GraphQL help?
  1. It turns the request into a webhook
  2. The client asks for exactly the fields it needs
  3. It caches the response forever
  4. It compresses the 40 fields
Show the answer

The client asks for exactly the fields it needs

GraphQL queries name their fields, which avoids over-fetching. The trade-off is more work on the server and harder caching.

Worked example

The same request in three styles

Fetching one campaign's name and spend, shown as the request each style sends. It's the same question, packaged three ways.

import json

rest = ("GET", "/v1/campaigns/cmp_103", None)

json_rpc = ("POST", "/rpc", {
    "jsonrpc": "2.0", "id": 1,
    "method": "get_campaign",
    "params": {"id": "cmp_103"},
})

graphql = ("POST", "/graphql", {
    "query": 'query { campaign(id: "cmp_103") { name spend } }',
})

for name, (method, path, body) in [("REST", rest), ("JSON-RPC", json_rpc), ("GraphQL", graphql)]:
    print(f"{name:9} {method:4} {path:22} {json.dumps(body) if body else ''}")
In the lesson you can change this and run it.

REST puts the "what" in the URL and the method. JSON-RPC puts it in the body as a function name. GraphQL puts a whole query in the body, including which fields to return. All three travel over the same HTTP you met last lesson.

Live updates: four ways to hear about changes

Say you're waiting for a parcel. You could phone the company every hour. They could text you when it ships. You could watch their live tracking page. Or you could stay on a call with the driver. Those are the four ways an app can hear about changes:

techniquehow it worksgood for
pollingthe client asks "anything new?" every few secondssimple dashboards; wasteful at scale
webhooksthe provider POSTs to your server when something happenspayments, form submissions, CI results
server-sent events (SSE)one long HTTP response that keeps sending eventsone-way streams to a client: LLM tokens, progress
WebSocketsan upgraded connection that both sides can send on at any timechat, multiplayer, live collaboration
  • Polling is phoning every hour. It's simple, and most calls are wasted.
  • Webhooks are the text message. The provider contacts you, at a web address you gave it.
  • Server-sent events are the tracking page: one long reply that keeps adding news, in one direction only.
  • WebSockets are the phone call. Either side can speak at any time.

The Claude API streams tokens with SSE. Each event is a few lines like event: content_block_delta then data: {...}, separated by blank lines. That's why streaming works through ordinary HTTP libraries and proxies: to them, it's a normal response that takes a while to finish.

Try it now

Pick the simplest one that does the job.

Quick check
A payment provider needs to tell your server the moment a payment succeeds. Which technique fits?
  1. A webhook: the provider POSTs an event to your URL
  2. GraphQL
  3. Polling the provider every second
  4. WebSockets from your server to every customer
Show the answer

A webhook: the provider POSTs an event to your URL

Webhooks push server-to-server events as they happen. A later lesson covers verifying and retrying them.

Picking a style, and what it costs

GET must not change anything. Browsers, caches and crawlers all feel free to repeat a GET, and to send one on their own. A "delete" link built on GET can be triggered by a link-preview bot. Anything that changes data needs POST, PUT, PATCH or DELETE.

REST means more than JSON over HTTP. An API with one URL, /api, that takes {"action": "delete_user"} is RPC, whatever its docs call it. That's not automatically wrong, and RPC is a good fit for actions that don't map onto resources. But knowing which one you have sets the right expectations, particularly for caching and retries.

GraphQL moves the cost, rather than removing it. Clients get exactly the fields they want. In exchange, your server has to protect itself from expensive queries, and URL-based caching stops working, since everything is a POST to one address. It pays off when many different clients need different shapes of data. For one web app talking to its own backend, plain REST is often simpler.

Polling is wasteful when the provider can tell you. Asking every second means 86,400 requests a day, and nearly all of them say "nothing new". If webhooks are available, use them. Keep a slow poll as a backup for missed events, since webhooks can fail too.

Choose one

Pick one style for your API, and use it throughout. People learn an API by guessing from the last endpoint they used, and every exception breaks that guess.

Practice what you just read

  • 2 code examples you can edit and run
  • a practice exercise that checks your code
  • a 5-question quiz
  • 5 review cards for spaced repetition

Create a free account

Try a quiz question

Which method should never change data on the server?

  1. GET
  2. POST
  3. PATCH
  4. DELETE

Answer it at the end of the lesson to see if you're right, and why.

Before this lesson