Content Negotiation in REST APIs: A Practical Guide
Content Negotiation in REST APIs: A Practical Guide
Content negotiation lets clients and servers agree on the response format — JSON, XML, CSV, or custom media types. The client says what it wants via the Accept header, the server returns the best match. In practice, most APIs only support JSON. But understanding content negotiation unlocks API versioning, format flexibility, and proper HTTP semantics.
How It Works
Request
GET /api/users/123
Accept: application/json
Response
HTTP/1.1 200 OK
Content-Type: application/json
{"id": 123, "name": "John"}
If the server can't produce the requested format, it returns 406 Not Acceptable.
The Accept Header
Clients specify preferred formats with quality values (0-1):
Accept: application/json, application/xml;q=0.9, text/csv;q=0.5
This means: prefer JSON, XML is acceptable, CSV is last resort. Quality defaults to 1.0 if not specified.
Common Media Types
| Media Type | Use Case |
|---|---|
application/json | Default for APIs |
application/xml | Legacy enterprise APIs |
text/csv | Data export, spreadsheets |
application/pdf | Document generation |
text/html | Browser-readable responses |
application/octet-stream | Binary file download |
multipart/form-data | File uploads |
text/event-stream | Server-Sent Events |
Custom Media Types
Custom media types encode API-specific information:
Accept: application/vnd.yourapi.user.v2+json
Format: application/vnd.{vendor}.{resource}.{version}+{format}
GitHub's Approach
Accept: application/vnd.github.v3+json
Accept: application/vnd.github.v3.raw # Raw file content
Accept: application/vnd.github.v3.html # HTML rendered content
Accept: application/vnd.github.v3.diff # Diff format
Accept: application/vnd.github.v3.patch # Patch format
GitHub uses custom media types to control both the API version and the response format for the same resource.
Content Negotiation for Versioning
Instead of URL path versioning (/v1/users), version via the Accept header:
# Version 1
Accept: application/vnd.yourapi.v1+json
# Version 2
Accept: application/vnd.yourapi.v2+json
Pros: Clean URLs, per-resource versioning, RESTful. Cons: Harder to test (can't paste in browser), less visible, more complex routing.
Implementation Patterns
1. Default Format
Always have a default. If no Accept header is provided, return JSON:
GET /api/users → application/json (default)
2. Format via Extension (Pragmatic)
Some APIs support format via URL extension as a fallback:
GET /api/users.json
GET /api/users.csv
GET /api/users.xml
This isn't proper content negotiation, but it's practical and easy to test.
3. Format via Query Parameter
GET /api/users?format=csv
Also not proper content negotiation, but commonly used alongside Accept header support.
4. Response Format Matching
Your server should:
- Parse the
Acceptheader - Sort by quality value
- Find the first format you support
- Return 406 if no match
Practical Recommendations
For Most APIs
- Support
application/jsonas the only format - Return
Content-Type: application/jsonon all responses - Ignore the
Acceptheader (always return JSON) - Use URL path versioning instead of content negotiation for versions
This is what 95% of APIs do, and it's fine.
For APIs Needing Multiple Formats
- Support
application/json(default) and one or two alternatives (CSV, XML) - Respect the
Acceptheader - Return
406 Not Acceptablefor unsupported formats - Include
Vary: Acceptheader for proper caching
For Enterprise APIs
- Full content negotiation with custom media types
- Version via
Acceptheader - Support JSON, XML, and potentially CSV/PDF
- Document supported media types in OpenAPI spec
Common Mistakes
| Mistake | Impact | Fix |
|---|---|---|
| Ignoring Accept header but returning wrong type | Client parse errors | Respect Accept or always return JSON |
| No 406 response | Client gets unexpected format | Return 406 for unsupported formats |
| Missing Content-Type header | Client can't parse response | Always include Content-Type |
| No Vary: Accept header | CDN caches wrong format | Add Vary: Accept when supporting multiple formats |
| Over-engineering formats | Maintenance burden | Start with JSON only, add formats when needed |
Designing REST APIs? Explore API design patterns and best practices on APIScout — architecture guides, comparisons, and developer resources.