Back to Blog
Lesson 38 of the REST API Design: Design Your First Clean REST API course
API ArchitectureAugust 25, 20263 min read

Content Negotiation: Mastering API Format Handling with Accept Headers

Learn how to implement Content Negotiation in your REST API. Master the Accept header to serve JSON, XML, or other formats based on client preference.

REST APIContent NegotiationHTTP HeadersJSONXMLAPI Design
Close-up of software development tools displaying code and version control systems on a computer monitor.

Previously in this course, we covered rate limiting fundamentals to ensure our API remains stable under load. Now that your Task Manager API is protected, we need to make it more flexible by allowing clients to request data in their preferred format through Content Negotiation.

What is Content Negotiation?

Content negotiation is the mechanism in HTTP that allows a client and a server to agree on the best representation of a resource. Instead of forcing every client to parse the same data structure, your API can detect what the client wants—such as application/json or application/xml—and respond accordingly.

This is driven primarily by the Accept request header. When a client sends a request, it includes this header to tell the server: "I am willing to accept these specific media types." If the server can provide one of those types, it does so; otherwise, it should return a 406 Not Acceptable status code.

Implementing Format Handling

In a typical backend implementation, you check the Accept header of the incoming request before serializing your data. While JSON as the Standard Exchange Format is our default, supporting other formats like XML makes your API more versatile for legacy systems or specialized clients.

Worked Example: Handling JSON vs. XML

Imagine our GET /v1/tasks endpoint. We want to check the Accept header and return the appropriate payload.

JAVASCRIPT
// Pseudo-code implementation for a Task route
app.get(CE9178">'/v1/tasks', (req, res) => {
  const tasks = getTasksFromDb(); // Our task data
  const acceptHeader = req.headers[CE9178">'accept'];

  if (acceptHeader === CE9178">'application/xml') {
    res.setHeader(CE9178">'Content-Type', CE9178">'application/xml');
    return res.send(convertToXml(tasks));
  }

  // Default to JSON
  res.setHeader(CE9178">'Content-Type', CE9178">'application/json');
  res.json(tasks);
});

The server sets the Content-Type header in the response so the client knows exactly what format it received. This prevents common issues like MIME-sniffing prevention, which can lead to security vulnerabilities if the browser guesses the file type incorrectly.

Practical Exercise

  1. Modify your existing GET /v1/tasks endpoint to inspect the Accept header.
  2. If the client sends application/json, return the standard JSON response.
  3. If the client sends text/plain, return a simple comma-separated string of task titles.
  4. If the client sends an unsupported format (e.g., application/pdf), return a 406 Not Acceptable response.

Common Pitfalls

  • Ignoring the 406 Status: Many developers default to JSON regardless of the Accept header. Always return 406 Not Acceptable if the client requests a format you don't support; this is the correct RESTful way to signal a mismatch.
  • Over-complicating: You do not need to support every possible format. Start with JSON and one other format (like XML) only if your specific requirements demand it.
  • Missing Content-Type: Always explicitly set the Content-Type header in your response. Without it, clients may fail to parse your response correctly.

FAQ

Q: Should I use Content Negotiation for API versioning? A: Yes, it is a valid strategy. Instead of v1/tasks, you can use header-based versioning to keep your URIs clean, as discussed in our guide on mastering header-based versioning for clean evolution.

Q: What is the difference between Accept and Content-Type? A: Accept is what the client wants to receive, while Content-Type is what the server is actually sending.

Recap

Content negotiation allows your API to evolve alongside client needs. By respecting the Accept header, you provide a better developer experience and ensure your API remains accessible to a wider variety of clients. Remember to explicitly set your Content-Type headers to keep your responses predictable and secure.

Up next: We will begin exploring Resource Relationships and how to link your data models using HATEOAS.

Similar Posts