What Is HATEOAS?
Hypermedia as the Engine of Application State (HATEOAS) is the highest level of REST maturity — what Roy Fielding's original REST dissertation actually described. The idea: API responses include links to related resources and available actions, so clients navigate the API like a web browser navigates HTML pages, without needing to hardcode URLs.
The Richardson Maturity Model
Leonard Richardson's model grades APIs on REST compliance:
| Level | Description | Example |
|---|---|---|
| 0 | HTTP as transport (RPC) | SOAP, XML-RPC |
| 1 | Resources | `/users`, `/orders` |
| 2 | HTTP verbs + status codes | `GET /users`, `POST /orders` |
| **3** | **Hypermedia controls** | **Response includes `_links`** |
Most APIs claiming to be RESTful are Level 2. True REST (Level 3) requires hypermedia. In practice, very few public APIs reach Level 3 — but understanding why reveals important API design insights.
The Core Idea
Without HATEOAS (Level 2):
GET /api/orders/456
{
"id": 456,
"status": "pending",
"total": 99.99
}
// Client must know: what URLs exist? What actions are available?
// Client hardcodes: /api/orders/456/cancel, /api/orders/456/ship
With HATEOAS (Level 3):
GET /api/orders/456
{
"id": 456,
"status": "pending",
"total": 99.99,
"_links": {
"self": { "href": "/api/orders/456" },
"cancel": { "href": "/api/orders/456/cancel", "method": "POST" },
"payment": { "href": "/api/payments/789" }
}
}
// Client follows links — no URL knowledge needed
// Server can change URLs without breaking clients
Hypermedia Formats
HAL — Hypertext Application Language
HAL is the most widely adopted hypermedia format. It uses _links for navigation and _embedded for included sub-resources:
{
"id": 456,
"status": "shipped",
"total": 99.99,
"_links": {
"self": { "href": "/orders/456" },
"next": { "href": "/orders/457" },
"customer": { "href": "/customers/123", "title": "Alice Chen" },
"curies": [{
"name": "acme",
"href": "https://docs.example.com/rels/{rel}",
"templated": true
}],
"acme:invoice": { "href": "/invoices/456" }
},
"_embedded": {
"items": [
{
"product_id": "SKU-001",
"quantity": 2,
"_links": { "product": { "href": "/products/SKU-001" } }
}
]
}
}
HAL's Media Type: application/hal+json
JSON:API
JSON:API is a more opinionated specification that standardizes request/response format, pagination, filtering, and relationships:
{
"data": {
"type": "orders",
"id": "456",
"attributes": { "status": "shipped", "total": 99.99 },
"relationships": {
"customer": {
"data": { "type": "customers", "id": "123" },
"links": { "related": "/customers/123" }
}
},
"links": { "self": "/orders/456" }
},
"included": [
{
"type": "customers",
"id": "123",
"attributes": { "name": "Alice Chen" }
}
]
}
JSON:API's Media Type: application/vnd.api+json
Siren
Siren extends HAL with actions — structured descriptions of available operations including required fields, HTTP method, and encoding:
{
"class": ["order"],
"properties": { "id": 456, "status": "pending" },
"actions": [
{
"name": "cancel-order",
"title": "Cancel Order",
"method": "POST",
"href": "/orders/456/cancel",
"type": "application/json",
"fields": [
{ "name": "reason", "type": "text", "required": true }
]
}
],
"links": [
{ "rel": ["self"], "href": "/orders/456" }
]
}
Siren is particularly useful for generic API clients that need to render UI dynamically from API responses — like an auto-generated admin interface.
Link Relations (rel)
The rel attribute on a link describes the relationship between the current resource and the linked resource. The IANA maintains a registry of standard link relations (RFC 8288 — Web Linking):
| Relation | Meaning |
|---|---|
| `self` | The resource itself |
| `next` / `prev` | Pagination |
| `first` / `last` | Pagination |
| `collection` | Parent collection |
| `item` | Item in a collection |
| `related` | Related resource |
| `edit` | Edit form |
Custom relations use absolute URIs: rel="https://docs.example.com/rels/invoice"
Practical Benefits and Drawbacks
Benefits
Evolvability: You can change URLs server-side without breaking clients — clients follow links rather than constructing URLs. This is hypermedia's killer feature.
Self-documentation: The response tells clients what they can do next. A pending order shows a cancel link; a shipped order does not. Clients do not need to check order state to know what actions are valid.
Discoverability: A new client can start from the API root and navigate to any resource by following links — similar to how a human uses a website.
Drawbacks
Client complexity: Clients must parse and follow links rather than constructing URLs — which most HTTP client libraries and developers find unfamiliar.
Payload size: Every response includes metadata (links, embedded resources) that simple clients may ignore.
Caching challenges: Including dynamic links (e.g., available actions based on state) makes responses less cacheable.
When to Use Hypermedia
HATEOAS is most valuable when:
- The API is public and clients are diverse and long-lived
- You expect to evolve URLs and resource structure significantly
- You are building a generic client (auto-generated admin UI, API explorer)
- Clients need to discover available actions dynamically based on state
Pragmatic REST (Level 2) is usually sufficient when:
- You control both the API and all clients
- Your API is internal or has a small number of known clients
- You document the API with OpenAPI and clients generate typed clients from it
Even if you do not adopt full HATEOAS, including pagination links and self links in your responses adopts the most valuable subset of hypermedia design with minimal complexity.