Skip to content

Header Versioning

DodaTech 2 min read

title: "Header Versioning — Accept-Version and Custom Headers for API Versioning" description: "Header versioning uses HTTP request headers like Accept-Version or a custom header to specify the API version, keeping URLs clean and RESTful." date: 2026-06-28 lastmod: 2026-06-28 weight: 14 tags: [apis, versioning] }

Header versioning uses HTTP headers (Accept-Version, X-API-Version) to communicate the desired API version, keeping resource URLs unchanged across versions.

What You'll Learn

  • Accept-Version header
  • Custom version headers
  • Header versioning tradeoffs

Why It Matters

Header versioning keeps URLs clean and RESTful. The same URL serves different versions based on the header, which is useful for caching.

Code Examples

# Accept-Version header versioning
@app.route('/api/users')
def get_users():
    version = request.headers.get('Accept-Version', '1')

    if version == '1':
        data = [{"id": u.id, "name": u.name} for u in users]
    elif version == '2':
        data = [{"id": u.id, "name": u.name, "email": u.email} for u in users]
    else:
        return jsonify({"error": "Unsupported version"}), 400

    response = jsonify(data)
    response.headers['X-API-Version'] = version
    return response

# Custom header versioning
@app.route('/api/products')
def get_products():
    version = request.headers.get('X-API-Version', '1')

    if version == '1':
        return jsonify([p.to_dict_v1() for p in products])
    elif version == '2':
        return jsonify([p.to_dict_v2() for p in products])
// Express header versioning
app.get('/api/users', (req, res) => {
  const version = req.headers['accept-version'] || '1';

  const handlers = {
    '1': () => res.json(users.map(u => ({ id: u.id, name: u.name }))),
    '2': () => res.json(users.map(u => ({ id: u.id, name: u.name, email: u.email })))
  };

  const handler = handlers[version];
  if (!handler) return res.status(400).json({ error: 'Unsupported version' });

  res.set('X-API-Version', version);
  handler();
});
# Middleware-based header versioning
class VersionMiddleware:
    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        version = environ.get('HTTP_ACCEPT_VERSION', '1')
        environ['api.version'] = version
        return self.app(environ, start_response)

# Use middleware
app.wsgi_app = VersionMiddleware(app.wsgi_app)

Common Mistakes

1. No Default Version

Without Accept-Version, default to the latest version.

2. Not Returning Version in Response

Include X-API-Version so clients know which version they received.

3. Headers Not Forwarded by Proxies

Proxies may strip custom headers. Use standard Accept header.

4. Harder to Test and Document

URL-based versions are easier to test in browsers and tools.

5. No Version in Error Responses

Include supported versions in error responses.

Practice Questions

  1. What header is commonly used for versioning?
  2. What is the advantage of header versioning?
  3. What is the disadvantage?
  4. Why return version in response headers?
  5. How do proxies affect header versioning?

Answers:

  1. Accept-Version or X-API-Version.
  2. Clean URLs that don't change with versions.
  3. Harder to test, document, and discover.
  4. So clients know which version they received and can log it.
  5. Proxies may strip custom headers; use Accept-Version which is more standard.

Challenge: Implement header versioning with middleware. Create a test that sends different Accept-Version headers and verifies correct version responses.

FAQ

What header name is most standard?

: Accept-Version is more standard. X-API-Version is custom.

Can I use the Accept header for versioning?

: Yes, through content negotiation (media type versioning).

Do browsers support header versioning for testing?

: Not natively. You need extensions like ModHeader or Postman.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro