Topic Overview

Content Negotiation: Accept Headers, Versioning & Formats

Serve multiple formats safely: Accept/Content-Type, versioning strategies, caching, and pitfalls with clients.

15 min read

Content Negotiation

Why Engineers Care About This

Content negotiation lets clients request their preferred content format (JSON, XML, CSV) and API version through HTTP headers. This enables flexible APIs that serve different clients (web, mobile, third-party) with their preferred formats. But content negotiation adds complexity—header parsing, format selection, and version handling. Understanding content negotiation helps you design flexible APIs.

When clients need different formats, or you want to version APIs through headers, or you need to serve the same data in multiple formats, you're hitting problems that content negotiation solves. These problems compound. Without content negotiation, you need separate endpoints for each format (clutter, maintenance burden). With poor content negotiation (wrong format selection, unclear priorities), clients get unexpected formats. Good content negotiation serves client preferences efficiently.

In interviews, when someone asks "How would you support multiple response formats?", they're really asking: "Do you understand content negotiation? Do you know how to implement Accept headers? Do you understand that content negotiation enables flexible APIs?" Most engineers don't. They create separate endpoints for each format (clutter) or don't support multiple formats at all.

Core Intuitions You Must Build

  • Accept header specifies client preferences, not requirements. Clients send Accept header (Accept: application/json, application/xml;q=0.9) to specify preferred formats. Servers select the best format they support (highest quality score, or first supported format). If no format matches, return 406 (Not Acceptable). Don't treat Accept as requirement—it's a preference. Also, if no Accept header, use default format (usually JSON).

  • Content negotiation enables API versioning through headers. Instead of URL versioning (/v1/users), use content negotiation (Accept: application/vnd.api+json;version=1). This keeps URLs clean (no version in path) and enables version selection per request. But content negotiation versioning is less visible than URL versioning—clients must know to send headers. Choose based on your needs—URL versioning is more visible, content negotiation is cleaner.

  • Quality scores (q-values) prioritize formats. Accept header can include quality scores (application/json;q=1.0, application/xml;q=0.8). Higher scores indicate higher preference. Servers should select format with highest quality score that they support. If no quality scores, treat all formats equally (select first supported). Don't ignore quality scores—they indicate client preferences.

  • Content-Type header specifies response format. When serving content, set Content-Type header (Content-Type: application/json) to indicate response format. This helps clients parse responses correctly. Don't omit Content-Type—clients need it to know how to parse responses. Also, Content-Type should match Accept (serve format client requested, if possible).

  • Content negotiation works with other HTTP features. Content negotiation can specify language (Accept-Language: en, fr), encoding (Accept-Encoding: gzip, deflate), and charset (Accept-Charset: utf-8). These work together—clients can request JSON format, English language, gzip encoding. Servers should honor all preferences when possible. Don't implement content negotiation in isolation—consider all Accept headers.

  • Default format handles missing Accept headers. When clients don't send Accept header, use default format (usually JSON for APIs). This ensures APIs work even without content negotiation. Also, document default format in API documentation. Don't require Accept header—some clients don't send it (simple HTTP clients, browsers).

Subtopics (Taught Through Real Scenarios)

Accept Header Handling

What people usually get wrong:

Engineers often ignore Accept headers or treat them as requirements. But Accept headers specify client preferences—servers should honor them when possible. Parse Accept header, select best format server supports (highest quality score, or first supported), and serve that format. If no format matches, return 406 (Not Acceptable). Don't ignore Accept headers—they indicate client preferences.

How this breaks systems in the real world:

An API ignored Accept headers and always returned JSON. A client requested XML (Accept: application/xml) but received JSON. The client couldn't parse JSON (expected XML), causing errors. The fix? Implement Accept header handling—parse Accept header, select best format server supports, and serve that format. Now clients get their preferred formats. But the real lesson is: Accept headers indicate client preferences. Honor them when possible.

What interviewers are really listening for:

They want to hear you talk about Accept header handling, format selection, and 406 responses. Junior engineers say "just always return JSON." Senior engineers say "parse Accept header, select best format server supports (highest quality score), serve that format, and return 406 if no format matches." They're testing whether you understand that content negotiation is about serving client preferences.

API Versioning Through Content Negotiation

What people usually get wrong:

Engineers often think "API versioning is just URL versioning." But content negotiation enables API versioning through headers (Accept: application/vnd.api+json;version=1). This keeps URLs clean (no version in path) and enables version selection per request. But content negotiation versioning is less visible than URL versioning—clients must know to send headers. Choose based on your needs.

How this breaks systems in the real world:

A service used URL versioning (/v1/users, /v2/users). URLs became cluttered, and clients had to update URLs when upgrading versions. The fix? Use content negotiation for versioning (Accept: application/vnd.api+json;version=1). URLs stay clean, and clients can request versions per request. But the real lesson is: content negotiation enables cleaner versioning. Use it when visibility isn't critical.

What interviewers are really listening for:

They want to hear you talk about API versioning through content negotiation, trade-offs with URL versioning, and when to use each. Junior engineers say "just use URL versioning." Senior engineers say "content negotiation enables API versioning through headers, keeping URLs clean but less visible—choose based on needs (URL versioning more visible, content negotiation cleaner)." They're testing whether you understand that versioning strategies have trade-offs.

Default Format Handling

What people usually get wrong:

Engineers often require Accept headers or return errors when Accept is missing. But some clients don't send Accept headers (simple HTTP clients, browsers). Use default format (usually JSON for APIs) when Accept header is missing. This ensures APIs work even without content negotiation. Also, document default format in API documentation.

How this breaks systems in the real world:

An API required Accept headers and returned 406 (Not Acceptable) when Accept was missing. Simple HTTP clients (curl, scripts) couldn't use the API without sending Accept headers. The fix? Use default format (JSON) when Accept header is missing. Now APIs work without content negotiation. But the real lesson is: don't require Accept headers. Use defaults for compatibility.

What interviewers are really listening for:

They want to hear you talk about default format handling, missing Accept headers, and compatibility. Junior engineers say "just require Accept headers." Senior engineers say "use default format (usually JSON) when Accept header is missing—some clients don't send Accept headers, and APIs should work without content negotiation." They're testing whether you understand that content negotiation should enhance APIs, not break them.


  • Accept header specifies client preferences—honor them when possible, use default if missing
  • Content negotiation enables API versioning through headers—keeps URLs clean but less visible
  • Quality scores (q-values) prioritize formats—select format with highest quality score
  • Content-Type header specifies response format—set it to help clients parse responses
  • Content negotiation works with other HTTP features—language, encoding, charset
  • Default format handles missing Accept headers—use JSON as default for APIs
  • Good content negotiation serves client preferences efficiently without breaking compatibility

  • API Design - Designing APIs with content negotiation
  • API Versioning - Versioning strategies including content negotiation

Key Takeaways

Accept header specifies client preferences—honor them when possible, use default if missing

Content negotiation enables API versioning through headers—keeps URLs clean but less visible

Quality scores (q-values) prioritize formats—select format with highest quality score

Content-Type header specifies response format—set it to help clients parse responses

Content negotiation works with other HTTP features—language, encoding, charset

Default format handles missing Accept headers—use JSON as default for APIs

Good content negotiation serves client preferences efficiently without breaking compatibility


About the author

InterviewCrafted helps you master system design with patience. We believe in curiosity-led engineering, reflective writing, and designing systems that make future changes feel calm.