A Practical Guide to API Types and When to Actually Use Each One
Every job posting says "experience with APIs," but that phrase covers a lot of ground. REST, GraphQL, gRPC, SOAP, WebSocket, they're all "APIs," yet they solve different problems and make different trade-offs. I've shipped code against all five, and picking the wrong one for a project is the kind of mistake you don't feel until six months later when you're stuck maintaining it.
This post covers what an API actually is, then breaks down the major styles you'll run into on the job and how each one works, what a request/response actually looks like, and when you'd reach for it over the others.
What Is an API
An API (Application Programming Interface) is a interface that allows one piece of software to communicate to another without either side needing to know how the other one is implemented internally. You send a request in an conventional format, you get a response back in an conventional format, done.
The differences between API styles come down to a handful of design decisions:
- What does a request/response actually look like on the wire?
- Is communication one-shot (request → response) or ongoing (a persistent connection)?
- How strict is the interface between client and server?
- How is data structured,documents,remote procedure calls, or something else?
Let's go through the major styles.
1. REST (Representational State Transfer)
REST is quite common API that most people interact with either knowingly or unknowingly. It's not a protocol like HTTP or TCP,it's an architectural style, a set of conventions for using HTTP the way it was originally intended.
How it works:
- Everything is a resource, addressed by a URL (
/users/42,/orders/107/items) - You interact with resources using standard HTTP methods:
GET(read),POST(create),PUT/PATCH(update),DELETE(remove) - Responses are usually JSON (though XML is technically fine too)
- REST is meant to be stateless ,each request carries everything the server needs; the server doesn't remember previous requests
Example:
GET /api/users/42 HTTP/1.1
Host: example.com
Response:
{
"id": 42,
"name": "Apiyo Okafor",
"email": "apiyo@example.com"
}
Creating a resource looks similar, just with a different verb and a body:
POST /api/users HTTP/1.1
Host: example.com
Content-Type: application/json
{
"name": "Apiyo Okafor",
"email": "apiyo@example.com"
}
Strengths:
- Simple, widely understood, huge tooling ecosystem
- Plays nicely with HTTP caching, load balancers, and CDNs
- Easy to debug with just a browser or
curl
Weaknesses:
-
Over-fetching / under-fetching:
GET /users/42might return fields you don't need, and you might need three more requests to get related data - No built-in strict schema,contracts often live only in documentation (or Swagger/OpenAPI specs)
- Versioning (
/v1/,/v2/) can get messy over time
Best for: public APIs, CRUD-heavy applications, anything where cacheability and simplicity matter more than flexibility.
2. GraphQL
GraphQL came out of Facebook, built specifically to fix REST's over-fetching and under-fetching problem. Instead of many fixed endpoints, you get a single endpoint, and the client describes exactly the shape of data it wants back.
How it works:
- One endpoint, typically
POST /graphql - The client sends a query describing the fields it needs, and the server returns exactly that, no more, no less
- A strongly typed schema defines every possible query, mutation, and type up front
Example:
query {
user(id: 42) {
name
orders(last: 3) {
total
date
}
}
}
This single request replaces what might be two or three REST calls, and it returns only the fields listed. Writes work through mutations, which look similar:
mutation {
updateUser(id: 42, input: { name: "Apiyo N. Okafor" }) {
id
name
}
}
Strengths:
- Client controls exactly what data comes back,no over-fetching
- One request can pull nested/related data in one round trip
- Strong typing means better tooling (autocomplete, validation, introspection)
Weaknesses:
- Caching is harder since there's no URL-per-resource for HTTP caches to key off
- More complex to set up on the server (resolvers, schema design, query cost limits)
- A single expensive/deeply nested query can strain the server if not guarded against
Best for: applications with complex, nested, or highly variable data needs, think mobile apps pulling data from many related sources, or frontends built by teams who don't want to wait on backend changes for every new field.
3. gRPC (Google Remote Procedure Call)
gRPC works differently from both. Instead of resources (REST) or queries (GraphQL), you're calling a function on another machine, and it feels almost like calling a local function.
How it works:
- Built on HTTP/2, using Protocol Buffers (protobuf),a compact binary format instead of JSON
- You define a
.protofile describing services and message types, and gRPC generates client/server code in your language of choice - Supports streaming: client-streaming, server-streaming, and full bidirectional streaming, not just request/response
Example (.proto definition):
service UserService {
rpc GetUser (UserRequest) returns (UserResponse);
}
message UserRequest {
int32 id = 1;
}
Once you generate code from that .proto file, calling it from Python looks like a normal function call:
response = user_service_stub.GetUser(UserRequest(id=42))
print(response.name)
No manual JSON parsing, no guessing field names,the client is generated straight from the contract.
Strengths:
- Very fast and bandwidth-efficient (binary format, HTTP/2 multiplexing)
- Native streaming support,great for real-time or high-throughput data
- Strict, generated contracts reduce integration bugs between services
Weaknesses:
- Not human-readable on the wire and it is harder to debug without tooling
- Poor browser support natively (needs gRPC-Web + a proxy)
- Steeper learning curve, less friendly for public-facing APIs
Best for: internal microservice-to-microservice communication where speed and strict contracts matter more than human readability.
4. SOAP (Simple Object Access Protocol)
SOAP is the oldest style on this list, and you won't see it much in modern startups — but it's still running plenty of banking, insurance, and government systems, so it's worth knowing.
How it works:
- Messages are strictly formatted XML, wrapped in an "envelope"
- Comes with a rigid contract called WSDL (Web Services Description Language) that precisely defines every operation, input, and output
- Includes built-in standards for security (WS-Security), transactions, and reliable messaging
Example:
<soap:Envelope>
<soap:Body>
<GetUser>
<UserId>42</UserId>
</GetUser>
</soap:Body>
</soap:Envelope>
Strengths:
- Extremely strict, well-defined contracts — great for formal enterprise integrations
- Built-in support for security and transactional guarantees that heavily regulated industries need
- Protocol-agnostic (can technically run over more than just HTTP)
Weaknesses:
- Verbose XML payloads — slower and heavier than JSON or binary alternatives
- Rigid and ceremonial; slower to develop against than REST or GraphQL
- Considered legacy by most modern web/mobile developers
Best for: enterprise systems, financial services, healthcare, and other domains where formal contracts, security standards, and reliability guarantees are non-negotiable.
5. WebSocket
Everything above is request-then-response. WebSocket breaks that pattern — it opens a persistent, two-way connection between client and server, and either side can send a message whenever it wants, without waiting to be asked.
How it works:
- Starts as an HTTP request that "upgrades" to a WebSocket connection
- After the handshake, both client and server can push messages over the same open connection, with much lower overhead than repeated HTTP requests
Example (JavaScript client):
const socket = new WebSocket("wss://example.com/chat");
socket.onmessage = (event) => {
console.log("New message:", event.data);
};
socket.send("Hello, server!");
Strengths:
- True real-time, bidirectional communication
- Low overhead once the connection is open,no repeated HTTP headers per message
- Ideal for anything that needs to "push" data without the client asking
Weaknesses:
- Not resource-oriented — doesn't fit naturally with typical CRUD operations
- Harder to scale/load-balance (connections are stateful and long-lived)
- Overkill for anything that isn't genuinely real-time
Best for: chat apps, live dashboards, multiplayer games, collaborative editing tools, stock tickers, anywhere the server needs to talk first.
Quick Comparison
| Style | Data Format | Communication | Best For |
|---|---|---|---|
| REST | JSON/XML | Request/response | General-purpose, public APIs |
| GraphQL | JSON | Request/response (flexible shape) | Complex, nested, client-driven data needs |
| gRPC | Protobuf (binary) | Request/response + streaming | Internal microservices, high performance |
| SOAP | XML | Request/response | Enterprise, regulated industries |
| WebSocket | Any (often JSON) | Persistent, bidirectional | Real-time, push-based apps |
How to Choose
A few rules of thumb that tend to hold up in practice:
- Building a public API for third parties? Start with REST. It's the lowest common denominator everyone understands.
- Frontend team constantly needs new field combinations and you're tired of versioning endpoints? GraphQL earns its complexity.
- Talking service-to-service inside your own infrastructure, and performance matters? gRPC.
- Integrating with a bank, hospital system, or government agency? You'll probably be handed a WSDL file and told to use SOAP and that's fine, it's built for that world.
- Need the server to push updates the moment something happens? WebSocket (or a managed alternative like Server-Sent Events for one-directional push).
None of these is objectively "better." They're solving for different constraints, and picking one is really about matching the constraint you actually have,public consumers, internal services, complex nested data, regulatory requirements, or real-time updates.
Wrapping up
You'll end up using more than one of these in a typical career, sometimes in the same project,REST for a public-facing API, gRPC between internal services, WebSocket for the live notification feed. Knowing why each one exists, not just the syntax, is what makes it easy to pick the right one instead of defaulting to whatever you used last time.
What do you reach for most often, and why? Curious to hear what other people are running in production.
Top comments (0)