All articles
APIsSystem DesignArchitecture14 min read

Types of APIs Explained: REST, GraphQL, gRPC, WebSockets, Webhooks and More

A practical map of API styles — REST, GraphQL, gRPC, tRPC, SOAP, WebSockets, SSE and webhooks — compared on transport, payload, coupling, latency and where each one actually belongs.

Three axes that decide everything

“Which API should I use” is unanswerable until you place the problem on three axes.

  1. Who initiates? Client-pull (REST, GraphQL, gRPC) or server-push (WebSockets, SSE, webhooks).
  2. How coupled are the two sides? A public API with a thousand unknown consumers must be loose and versioned. Two services deployed by the same team can share a schema and a binary codec.
  3. What dominates the cost? Round trips (mobile on 4G), bytes (high-volume internal traffic), or developer time (an internal admin tool).

The landscape at a glance

Public / partnerREST + JSONGraphQLWebhooksSOAP (legacy)stable, documented, cacheableInternal service-to-servicegRPC + ProtobufMessage queuestRPC (same repo)REST (simple cases)fast, typed, high volumeRealtime / pushWebSocketsServer-Sent EventsgRPC streamingLong pollingserver initiates, connection livesA typical product uses all three columns: gRPC inside, REST/GraphQL at the edge, WebSockets for live UI, webhooks outbound.
Most architectures are not choosing one style — they are placing each style where it fits.

REST

Resources identified by URLs, manipulated with HTTP methods, transferred as JSON. REST's real superpower is that it is uniform: browsers, CDNs, proxies, API gateways and every HTTP client on earth already understand it without knowing anything about your domain.

GET    /v1/orders?status=shipped
POST   /v1/orders            → 201 + Location
PATCH  /v1/orders/8812       → If-Match: "v3"
DELETE /v1/orders/8812       → 204
  • Wins: caching, tooling, discoverability, zero client codegen required.
  • Loses: over-fetching, under-fetching, N+1 round trips on mobile, verbose JSON.

GraphQL

One endpoint, a typed schema, and the client declares exactly the shape it needs. It exists because product teams got tired of waiting for a backend release every time a screen needed one more field.

query Order($id: ID!) {
  order(id: $id) {
    id total
    customer { name tier }
    items { sku qty product { title thumbnail } }
  }
}
  • Wins: no over-fetching, one round trip for a whole screen, introspection, strong typing end-to-end.
  • Loses: HTTP caching is largely gone, N+1 resolvers unless you use DataLoader, query cost must be limited or you have a DoS vector.

gRPC

Contract-first RPC: define services and messages in Protobuf, generate client and server code in a dozen languages, transport over HTTP/2 with binary framing and multiplexed streams.

service Orders {
  rpc Get (GetOrderRequest) returns (Order);
  rpc Watch (WatchRequest) returns (stream OrderEvent);
}
  • Wins: smallest payloads, lowest latency, native bidirectional streaming, deadlines and cancellation built in.
  • Loses: not directly callable from a browser (needs gRPC-Web or a proxy), opaque on the wire, needs a build step.

tRPC and typed RPC

When client and server live in the same TypeScript repo, you can skip the schema language entirely and infer types across the boundary. Zero codegen, instant refactors, compile-time safety on both sides.

The trade is total coupling: both halves must ship together and speak TypeScript. Perfect for a product's own web app, useless as a public contract.

SOAP and why it survives

XML envelopes, WSDL contracts, WS-Security, WS-AtomicTransaction. Verbose and unloved, but banks, insurers, telcos and government systems still run on it because those extensions specify things REST never did: message-level signing and encryption, formal contracts, and distributed transactions.

You will meet SOAP as an integration target, not a design choice. Wrap it in a REST or gRPC adapter at your boundary and never let it leak inward.

WebSockets, SSE and long polling

Long pollingSSEWebSockets
DirectionServer → client (simulated)Server → clientBidirectional
ProtocolPlain HTTPPlain HTTPUpgrade from HTTP, then its own framing
PayloadAnyUTF-8 text onlyText or binary
ReconnectManualAutomatic with Last-Event-IDManual
Proxy friendlinessPerfectGoodSometimes blocked
Best forFallback, low-frequencyFeeds, notifications, LLM token streamsChat, collaboration, games, trading
Reach for SSE more often
Most “we need realtime” requirements are one-directional: a live dashboard, a notification bell, streaming tokens from a model. SSE gives you that over ordinary HTTP with automatic reconnection and no new infrastructure. WebSockets are for when the client also needs to push continuously.

Webhooks and event-driven APIs

A webhook inverts the call: instead of you polling their API, they POST to your URL when something happens. This is how Stripe, GitHub and Slack notify you.

  • Verify signatures. An HMAC of the raw body with a shared secret, compared in constant time.
  • Be idempotent. At-least-once delivery is the norm; store the event id and ignore duplicates.
  • Return 2xx fast. Enqueue and process asynchronously — providers time out in seconds and retry.
  • Expect out-of-order. Compare event timestamps or versions before applying state.

For internal systems the same idea appears as message queues and event streams (Kafka, SQS, NATS): asynchronous, decoupled, buffered against downstream outages.

Comparison table

StyleTransportPayloadSchemaCachingBrowserStreaming
RESTHTTP/1.1 or 2JSONOpenAPI (optional)ExcellentNativeNo
GraphQLHTTP POSTJSONSDL (required)Manual / persisted queriesNativeSubscriptions
gRPCHTTP/2ProtobufProto (required)NoneVia gRPC-WebBidirectional
tRPCHTTPJSONInferred TSLimitedNativeVia subscriptions
SOAPHTTP / MQXMLWSDL (required)NoneAwkwardNo
WebSocketTCP after upgradeAnyYours to defineNoneNativeBidirectional
SSEHTTPText eventsYours to defineNoneNativeServer → client
WebhooksHTTP POSTJSONProvider-definedn/an/aPush

How to choose

SituationPickBecause
Public API for unknown consumersREST + OpenAPIUniversally consumable, cacheable, self-documenting
Mobile app with many screen shapesGraphQLOne round trip per screen, no over-fetch on a slow network
Chatty internal microservicesgRPCBinary payloads, HTTP/2 multiplexing, deadlines
Full-stack TypeScript producttRPCEnd-to-end types without codegen
Live collaborative editingWebSocketsGenuinely bidirectional, low latency
Notifications / token streamingSSEOne-directional over plain HTTP with auto-reconnect
Notifying third parties of eventsWebhooksNo polling, near-real-time, standard practice
Decoupling internal workloadsQueue / event streamBuffering, retries, independent scaling

Interview framing

“We expose REST with OpenAPI at the public edge because it caches at the CDN and any consumer can use it. The mobile clients hit a GraphQL BFF so a screen is one round trip instead of six. Internally, services talk gRPC over HTTP/2 with Protobuf and per-call deadlines, since payload size and tail latency dominate at that volume. Live order status is pushed over SSE, and we emit signed, idempotent webhooks to partners for asynchronous integration.”

Keep reading

Suggested next articles based on this one.

Design it, don't just read it.

Practise LLD and system design problems with structured rubrics and AI feedback.

Start practising free