Content Negotiation
The BookNook phone app wants JSON. A reporting tool wants CSV of the same loans. A French user wants error messages in French. You could build three separate endpoints, but HTTP already has a better way: the client says what it can accept, and the server picks the best match it can produce.
The rule
Content-Type says what I'm sending; Accept says what I'd like back.
Four headers do almost all the work:
Content-Typelabels the body of this message:application/json,text/csv,application/xml.Acceptlists the formats the client can read, optionally ranked withqweights from 0 to 1.Accept-Languagedoes the same for human languages, likefr, en;q=0.5.Accept-Encodinglists compressions the client understands, likegzip, br.
GET /v1/members/7/loans HTTP/1.1
Accept: text/csv, application/json;q=0.8
HTTP/1.1 200 OK
Content-Type: text/csv
Vary: Accept
id,book_id,due
901,42,2026-10-09The client preferred CSV (weight 1, the default) over JSON (0.8). The server can produce CSV, so it sends CSV and labels it with Content-Type. The Vary: Accept header tells caches that this URL has several versions depending on Accept, so they must not hand a CSV response to a JSON client.
When negotiation fails, two status codes name the side that failed:
406 Not Acceptable: the server cannot produce anything on the client'sAcceptlist.415 Unsupported Media Type: the client sent a body in a format the server will not read.
The shortcut
Match the direction: request body → Content-Type on the request; response body → Accept on the request, Content-Type on the response. Then check each side can honour it.
What is being sent?
The request has
Content-Type: application/xmlon a new loan.
The trap is mixing up 406 and 415. Ask: is the server unable to write what you want (406) or unable to read what you sent (415)?
Drills
Answer fast — trust the rule.
A client wants a JSON response. Which request header says so?
With Accept: application/xml, application/json;q=0.5, which format does the client prefer most?
A client asks only for Accept: application/pdf, and BookNook can produce JSON or CSV. Which status fits?
Why should BookNook add Vary: Accept when the same URL can return JSON or CSV?
A client sends a JSON body but forgets the Content-Type header. What is the cleanest server behaviour?
Remember
- Content-Type says what I'm sending; Accept says what I'd like back.
- Shortcut: match the direction of the body to the header, then check both sides can honour it.
- Trap: 406 means "can't write what you want", 415 means "can't read what you sent".
