Skip to content

Content Negotiation

A single resource can have several representations — JSON, CSV, different languages. Content negotiation lets the client state a preference and the server pick the best match, all without changing the URI.

The client sends Accept (which media types it wants) and optionally Accept-Language; the server replies with the chosen representation and a matching Content-Type:

GET /reports/42 HTTP/1.1
Accept: text/csv, application/json;q=0.8
Accept-Language: th, en;q=0.5

The q values are quality weights — here the client prefers CSV, then JSON. The server should honor the best type it supports and echo it in Content-Type.

  • If the server cannot produce any accepted type, respond 406 Not Acceptable.
  • If a request body arrives in a type the server cannot parse, respond 415 Unsupported Media Type.
JavaScript
Advantages (Content Negotiation)Trade-offs
One API supports multiple formats (JSON, XML, CSV)The server has to implement multiple serializers
The client says exactly which format it wantsMore complexity — content-type routing logic
Media-type versioning is the most RESTful optionLittle tooling support for custom media types
Compression via Accept-Encoding cuts bandwidthCache keys must account for the Accept header

Not returning Content-Type in the response Symptom:

  • The server returns JSON but never sets Content-Type: application/json
  • The client doesn’t know how to parse the body — some treat it as plain text
  • Always set Content-Type on every response that has a body

Caching without including Accept in the cache key Symptom:

  • A CDN caches the response to GET /reports requested as JSON
  • The next request asks for CSV — the CDN returns the cached JSON instead
  • Set a Vary: Accept header so the CDN includes Accept in the cache key

💡 Real-world examples

GitHub API:

  • Accept: application/vnd.github+json for standard JSON
  • Accept: application/vnd.github.raw+json for raw file content
  • Different formats via the Accept header, no URI change needed

AWS API:

  • Accept-Encoding: gzip — the response body is compressed automatically
  • Cuts bandwidth for large JSON responses like list operations

Sources: GitHub REST API media types, MDN — Accept-Encoding

Which header does a client use to request a representation format?
The server supports none of the types in Accept. Which status?
A request body arrives in a media type the server cannot parse. Which status?