Post

REST vs GraphQL vs gRPC vs WebSocket vs SSE vs SOAP vs Webhooks vs MQTT vs AMQP

A side-by-side comparison of REST, GraphQL, gRPC, WebSocket, SSE, long polling, SOAP, webhooks, MQTT and AMQP, with head-to-head matchups, a decision tree and an architecture that uses them together.

REST vs GraphQL vs gRPC vs WebSocket vs SSE vs SOAP vs Webhooks vs MQTT vs AMQP

REST and GraphQL are styles for client-facing request/response APIs, gRPC is a high-performance RPC framework mostly used between services, WebSocket is a transport for persistent two-way messaging, SSE and long polling push data over plain HTTP, SOAP is the contract-heavy XML predecessor, webhooks are HTTP callbacks between systems, and MQTT and AMQP are broker-based messaging protocols. This post puts them side by side: how they differ, where each one fits and how they are combined in a real system.

This post concludes the API communication protocols series. Each protocol links to its own deep dive.

Index

The three families

Comparing every protocol with every other only makes sense once you notice they solve three different problems:

FamilyQuestion it answersMembers
Request/response APIs“Client, ask me for something and wait for the answer.”REST, GraphQL, gRPC, SOAP
Push and real-time“Server, tell me when something changes.”WebSocket, SSE and long polling, webhooks, GraphQL subscriptions, gRPC streaming
Brokered messaging“Deliver this reliably to whoever needs it, even if they are not ready.”MQTT, AMQP
flowchart LR
    subgraph RR["Request/response"]
        REST
        GraphQL
        gRPC
        SOAP
    end
    subgraph PUSH["Push and real-time"]
        WebSocket
        SSE["SSE / long polling"]
        Webhooks
    end
    subgraph MSG["Brokered messaging"]
        MQTT
        AMQP
    end
    GraphQL -. subscriptions .-> PUSH
    gRPC -. streaming .-> PUSH
    MQTT -. over WebSocket .-> WebSocket

The master comparison table

 RESTGraphQLgRPCSOAPWebSocketSSELong pollingWebhooksMQTTAMQP
What it isArchitectural styleQuery language and runtimeRPC frameworkXML messaging protocolFull-duplex protocolStreaming format and browser APIHTTP techniqueHTTP callback patternPub/sub protocolMessaging protocol
OriginFielding, 2000Facebook, 2012 / 2015Google, 2015Microsoft and partners, 1998IETF RFC 6455, 2011WHATWG, Opera 2006Comet, 2006Jeff Lindsay, 2007IBM and Arcom, 1999JPMorgan Chase, 2003
ModelResources, URLs, verbsOne endpoint, client-written queriesMethods on servicesOperations in an envelopeMessage channelEvent streamHeld requestProvider calls your URLTopics through a brokerExchanges, bindings, queues
TransportHTTP/1.1, 2, 3HTTPHTTP/2HTTP (also JMS, SMTP)TCP via HTTP upgradeHTTPHTTPHTTPTCP (or WebSocket)TCP
PayloadJSON (any media type)JSONProtobuf (binary)XMLText or binaryUTF-8 textAnyJSONAny bytesAny bytes plus properties
ContractOptional (OpenAPI)Required schemaRequired .protoRequired WSDLNoneNoneNoneProvider docsNoneNone
Code generationOptionalCommonBuilt inBuilt inNoNoNoNoNoNo
DirectionRequest/responseRequest/response plus subscriptionsUnary and three streaming typesRequest/responseFull duplexServer to clientServer to clientProvider to consumerPub/subPub/sub and queues
ConnectionShort or keep-aliveShort or keep-aliveLong-lived, multiplexedShortLong-lived, statefulLong-livedRepeatedShortLong-lived to brokerLong-lived to broker
CouplingClient knows URLClient knows schemaClient knows contractClient knows WSDLBoth know message formatClient knows streamClient knows endpointProvider knows URLOnly topic namesOnly exchange and key
Delivery guaranteePer request (retry if idempotent)Per requestPer call, with deadlinesPer request (WS-ReliableMessaging)None beyond TCPResume with Last-Event-IDCursor basedAt least once, retriesQoS 0, 1 or 2At least once with acks
HTTP cachingNativeHard (persisted queries help)NoNoNoNoNoNoNo (retained messages)No
BrowserNativeNativegRPC-Web proxyPossible, rareNativeNativeNativeNot applicableOver WebSocketNot directly
Human-readableYesYesNoYes, verboseDependsYesYesYesDependsDepends

Operational comparison

 RESTGraphQLgRPCSOAPWebSocketSSEWebhooksMQTTAMQP
Message overheadMedium (HTTP headers + JSON)MediumLow (HPACK + Protobuf)High (XML)Very low (2 to 14 B frames)Very lowMediumVery low (2 B fixed header)Low
LatencyGoodGood, one round tripExcellentFairExcellentExcellentSeconds (event driven)ExcellentGood
Load balancingEasy (stateless)EasyNeeds L7 or client-sideEasyHard (sticky, long-lived)MediumEasyBroker clusterBroker cluster
Scaling concernRequest rateQuery cost, N+1Connection spreadXML CPUOpen connections, fan-outOpen connectionsReceiver burstsConnections, fan-outQueue depth
Debuggingcurl, browserGraphiQLgrpcurl, reflectionSoapUIBrowser DevToolscurl, DevToolsDelivery logs, tunnelsMQTT ExplorerManagement UI
Security modelTLS, OAuth 2.0, API keysSame as REST plus cost limitsTLS or mTLS, metadata tokensTLS plus WS-SecurityTLS, Origin check, token at connectSame as HTTPHMAC signatures, TLSTLS, client certs, topic ACLsTLS, SASL, vhost permissions
Learning curveLowMediumMediumHighLow to start, high to scaleLowLowLowMedium

Head to head

REST vs GraphQL

 RESTGraphQL
EndpointsMany, one per resourceOne
Who shapes the responseServerClient
Round trips for nested dataOften severalOne
CachingHTTP and CDNClient-side normalised cache, persisted queries
Versioning/v1, /v2Evolve the schema, deprecate fields
ErrorsHTTP status codes200 with an errors array
Main riskOver-fetching and chatty clientsExpensive queries, N+1 resolvers

Choose REST for public, cacheable, resource-oriented APIs. Choose GraphQL when many screens need different views of a connected data model, usually behind a BFF. Many companies, GitHub among them, offer both.

REST vs gRPC

 RESTgRPC
ContractOptionalMandatory, with generated code
EncodingJSON textProtobuf binary
StreamingNo (use SSE or WebSocket)Four call types
BrowserNativeNeeds gRPC-Web
SpeedGoodBetter: smaller messages, multiplexing, no JSON parsing
EcosystemUniversalStrong in cloud-native and polyglot back ends

Choose REST at the edge and for partners. Choose gRPC for internal east-west traffic, especially with many languages, high call rates or streaming.

GraphQL vs gRPC

Both have a typed schema, but they optimise for different consumers. GraphQL lets the client decide the shape of each response, which suits front ends. gRPC fixes the shape in the contract and optimises the wire, which suits services. A common architecture uses GraphQL at the edge and gRPC behind it.

SOAP vs REST

SOAP offers formal contracts, message-level security and WS-* transactions at the cost of verbose XML and complexity. REST trades those features for simplicity and the web’s infrastructure. Use SOAP when a partner or regulator requires it; build new APIs as REST or gRPC and wrap legacy SOAP services behind a facade.

WebSocket vs SSE vs long polling

 WebSocketSSELong polling
DirectionBoth waysServer to clientServer to client
ProtocolOwn framing after HTTP upgradePlain HTTP responsePlain HTTP requests
Reconnect and resumeYou build itBuilt inYou build it (cursor)
Binary dataYesNo (text only)Yes
Proxy friendlinessGood on 443, some proxies break itVery good (watch buffering)Excellent
Per-message overhead2 to 14 bytesA few bytesA full HTTP exchange
Typical usesChat, games, collaboration, tradingNotifications, progress, LLM tokensFallback, bot APIs, restrictive networks

Rule of thumb: if the client also sends a lot, use WebSocket; if only the server sends, use SSE; use long polling only when streaming is blocked.

Webhooks vs polling vs SSE

All three answer “how do I learn about changes in another system?”. Polling is simple but slow and wasteful. Webhooks are instant and cheap but need a public endpoint and careful verification. SSE or long polling work when the consumer cannot accept inbound connections (a laptop, a mobile app, a locked-down network). Many providers, such as Telegram, offer both webhooks and long polling for exactly this reason.

MQTT vs AMQP (and Kafka)

 MQTTAMQP 0-9-1Kafka (for reference)
Designed forConstrained devices and unreliable networksReliable business messaging between servicesHigh-throughput, replayable event streams
RoutingTopic hierarchy with + and # wildcardsExchanges (direct, fanout, topic, headers) and bindingsTopics and partitions
StorageRetained message and session queues onlyQueues until acknowledgedLog retained for days or forever
ConsumersEvery subscriber gets a copy (shared subscriptions in MQTT 5)Competing consumers per queue, copies per queueConsumer groups, each can replay
Client footprintTinyModerateHeavier
Typical scaleMillions of device connectionsThousands of service connectionsMillions of messages per second

Choose MQTT at the device edge, AMQP for commands, jobs and business events inside the platform, and Kafka when many consumers need to replay a stream. They are often chained: MQTT from devices, bridged into AMQP or Kafka in the cloud.

gRPC streaming vs WebSocket

Both provide bidirectional streams. gRPC streams are typed by a contract, multiplexed over HTTP/2 and best between services. WebSocket is untyped but native in every browser and best between a browser and a server.

The same operation in each protocol

“Get order 42” makes the differences concrete.

REST

1
2
GET /v1/orders/42 HTTP/1.1
Accept: application/json

GraphQL

1
query { order(id: "42") { id status total } }

gRPC

1
grpcurl -d '{"id": 42}' orders.internal:50051 orders.v1.OrderService/GetOrder

SOAP

1
2
3
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:o="urn:orders">
  <soap:Body><o:GetOrder><o:Id>42</o:Id></o:GetOrder></soap:Body>
</soap:Envelope>

WebSocket (a message on an already open connection; the format is your own)

1
{ "type": "order.get", "id": "req-17", "orderId": 42 }

SSE (you do not ask; updates to order 42 arrive on the stream)

1
2
event: order
data: {"id": 42, "status": "shipped"}

Webhook (the provider calls you when order 42 changes)

1
2
3
4
POST /webhooks/orders HTTP/1.1
Content-Type: application/json

{"type": "order.updated", "data": {"id": 42, "status": "shipped"}}

MQTT and AMQP (publish an event; whoever is subscribed receives it)

1
2
mosquitto_pub -t 'shop/orders/42/status' -m 'shipped' -q 1
rabbitmqadmin publish exchange=orders routing_key=order.shipped payload='{"id": 42}'

Decision tree

flowchart TD
    A["What do you need?"] --> B{"Does the caller need<br/>an answer right now?"}
    B -- "Yes" --> C{"Who is the caller?"}
    C -- "Public or partner client" --> D["REST"]
    C -- "Front end with varied,<br/>nested data needs" --> E["GraphQL"]
    C -- "Internal service,<br/>latency matters" --> F["gRPC"]
    C -- "Legacy system with a WSDL" --> G["SOAP"]
    B -- "No, it is an event<br/>or a notification" --> H{"Who receives it?"}
    H -- "A browser or app,<br/>server to client only" --> I["SSE<br/>(long polling as fallback)"]
    H -- "A browser or app,<br/>both directions" --> J["WebSocket"]
    H -- "Another company's server" --> K["Webhooks"]
    H -- "IoT devices" --> L["MQTT"]
    H -- "Your own back-end services,<br/>must not be lost" --> M["AMQP<br/>(or Kafka for replayable streams)"]

Using them together

Real systems mix protocols, each where it is strongest. This animation follows one order from a mobile app through six of them:

  1. Customer taps Pay: The app has one screen to render and wants one round trip for everything it shows.
  2. GraphQL at the edge: A placeOrder mutation goes to the backend-for-frontend, which asks for exactly the fields the screen needs.
  3. gRPC inside: The BFF calls OrderService.CreateOrder over gRPC with a deadline. Typed, binary and fast on the internal network.
  4. Service to service: The Order service reserves stock with another gRPC call. Generated clients mean no hand-written HTTP code.
  5. REST to a third party: Card payment goes to an external provider's public REST API, the lingua franca between organisations.
  6. Webhook back: Seconds later the provider POSTs a signed payment-succeeded event to a webhook endpoint instead of being polled.
  7. Publish an event: The handler verifies the signature and publishes order.paid to an AMQP exchange. Billing, shipping and analytics can all subscribe.
  8. Consume asynchronously: The notification service consumes the message from its queue at its own pace and acknowledges it.
  9. Push to the device: The app holds an open WebSocket, so the confirmation appears instantly. Mobile push or SSE would also fit a one-way update.
HopProtocolWhy this one
Mobile app to BFFGraphQLOne round trip with exactly the fields the screen needs
BFF to Order service, Order to InventorygRPCFast, typed, internal calls with deadlines
Order service to payment providerRESTThe provider’s public API; universal and well documented
Payment provider to webhook handlerWebhookThe provider notifies you; no polling
Webhook handler to other servicesAMQPDurable, decoupled fan-out to billing, shipping and notifications
Notification service to appWebSocket (or SSE, or mobile push)Instant update on an open connection

Add MQTT if warehouse scanners or delivery vans report events, bridged into the same broker.

Where each one is used

ProtocolIndustries and productsWell-known examples
RESTEvery industry; public APIs, cloud control planesGitHub, Stripe, Twilio, Kubernetes API, Azure Resource Manager
GraphQLE-commerce, social, media, developer platformsGitHub API v4, Shopify Storefront API, Netflix and Airbnb internal graphs
gRPCCloud-native infrastructure, microservices, ML servingKubernetes CRI, etcd, Envoy xDS, TensorFlow Serving
SOAPBanking, insurance, telecom, government, ERPEU VIES VAT check, Salesforce SOAP API, carrier and payment gateways
WebSocketMessaging, gaming, trading, collaborationSlack, Discord Gateway, exchange market-data feeds
SSE / long pollingAI products, notifications, feeds; botsLLM streaming APIs (Anthropic, OpenAI), Telegram getUpdates
WebhooksSaaS, payments, DevOps, e-commerceStripe events, GitHub webhooks, Shopify, Slack apps
MQTTIoT, manufacturing, energy, automotive, smart homeAWS IoT Core, Azure IoT Hub, Home Assistant, Facebook Messenger (2011)
AMQPFinance, e-commerce back ends, enterprise integrationRabbitMQ with Celery, Azure Service Bus

Common mistakes

  • Using WebSocket for everything “real-time”, when SSE would be simpler and work with existing HTTP infrastructure.
  • Exposing gRPC to browsers or partners directly instead of putting REST or GraphQL at the edge.
  • Using GraphQL as a thin proxy over REST without batching, cost limits or schema design.
  • Calling services synchronously in long chains (REST or gRPC) where an AMQP event would decouple them and survive outages.
  • Trusting webhooks without verifying signatures, or doing slow work before returning 2xx.
  • Treating MQTT QoS 2 or AMQP acks as end-to-end exactly-once; consumers still need idempotency.
  • Building new SOAP services where REST or gRPC would do.
  • Forgetting the infrastructure: long-lived connections (WebSocket, SSE, gRPC, MQTT) need load balancers, timeouts and capacity planning designed for them.

Summary

  • REST: the universal default for public and CRUD APIs; cacheable and simple, but can be chatty.
  • GraphQL: client-shaped queries for rich front ends; flexible, but moves complexity to the server.
  • gRPC: fast, typed, streaming RPC between services; not for browsers without help.
  • SOAP: formal contracts and WS-Security for enterprise and legacy integration; heavy for new work.
  • WebSocket: full-duplex real time; powerful, but stateful and you design the protocol.
  • SSE and long polling: one-way push over plain HTTP; the simplest way to stream to a browser.
  • Webhooks: event callbacks between systems; verify, deduplicate and acknowledge fast.
  • MQTT: tiny, reliable pub/sub for devices on bad networks.
  • AMQP: durable, acknowledged messaging that decouples services in time.

Pick per interaction, not per system: most architectures use three or four of these at once.

This post is licensed under CC BY 4.0 by the author.