API Versioning — Complete Guide to Managing Change
In this tutorial, you will learn about API Versioning. We cover key concepts, practical examples, and best practices to help you master this topic.
API versioning manages changes to endpoints without breaking existing consumers, using URI paths, query parameters, custom headers, or content negotiation strategies to evolve APIs safely.
What You'll Learn
- The four main API versioning strategies
- Pros and cons of URI versioning vs header versioning
- How to deprecate old versions and migrate consumers
Why It Matters
Without versioning, every API change risks breaking consumers. Versioning allows the API to evolve while v1 clients continue working unchanged until they are ready to migrate.
Real-World Use
Doda Browser's sync API uses URI path versioning (/v1/sync, /v2/sync). When v2 introduced delta sync, v1 clients continued using full sync until their app updated, giving a 6-month overlap window.
flowchart LR
A["API Versioning"] --> B["URI Path"]
A --> C["Query Parameter"]
A --> D["Custom Header"]
A --> E["Content Negotiation"]
B --> F["/v1/users"]
B --> G["/v2/users"]
D --> H["Accept-Version: v1"]
D --> I["Accept-Version: v2"]
style A fill:#dbeafe,stroke:#2563eb
Code Examples
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/v1/users')
def v1_get_users():
return jsonify({'users': [{'id': 1, 'name': 'Alice'}]})
@app.route('/v2/users')
def v2_get_users():
return jsonify({
'data': [{'id': 1, 'name': 'Alice', 'email': 'alice@example.com'}],
'meta': {'page': 1, 'total': 1}
})
app.run(port=5000)
Expected output: /v1/users returns flat user array; /v2/users returns wrapped response with metadata.
const express = require('express');
const app = express();
function versionRouter(req, res, next) {
const version = req.headers['accept-version'] || '1';
req.apiVersion = version;
next();
}
app.get('/api/users', versionRouter, (req, res) => {
if (req.apiVersion === '1') {
res.json({ users: [{ id: 1, name: 'Alice' }] });
} else if (req.apiVersion === '2') {
res.json({ data: [{ id: 1, name: 'Alice', email: 'alice@example.com' }] });
} else {
res.status(400).json({ error: 'Unsupported version' });
}
});
app.listen(3000);
Expected output: Header Accept-Version: v1 returns v1 format; header v2 returns v2 format.
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/api/users')
def get_users():
accept = request.headers.get('Accept', 'application/vnd.api.v1+json')
if 'vnd.api.v1' in accept:
return jsonify({'users': [{'id': 1, 'name': 'Alice'}]})
elif 'vnd.api.v2' in accept:
return jsonify({'data': [{'id': 1, 'name': 'Alice', 'email': 'a@example.com'}]})
return jsonify({'error': 'Unsupported media type'}), 415
Expected output: Request with Accept: application/vnd.api.v2+json receives v2 response format.
Common Mistakes
1. Not Versioning from the Start
The first breaking change forces an emergency versioning decision. Version from day one even for internal APIs.
2. Supporting Too Many Versions
Maintaining v1 through v5 simultaneously quadruples testing effort. Support at most two active versions.
3. Breaking Changes in Patch Versions
Breaking changes require a major version bump. Never break backward compatibility in a minor or patch release.
4. No Deprecation Headers
Consumers need to know when a version will be retired. Use Sunset and Deprecation HTTP headers.
5. Mixing Versioning Strategies
Some endpoints use URI versioning, others use headers. Pick one Strategy and apply it consistently.
Practice Questions
- What are four common API versioning strategies?
- Why is URI path versioning the most popular approach?
- What is the problem with supporting more than two API versions?
- How do Sunset headers help consumers plan Migration?
- Why should you version an API that has only one consumer?
Answers:
- URI path, query parameter, custom header, and content negotiation.
- It is explicit in the URL, cache-friendly, and easy to test and document.
- Each additional version multiplies testing, maintenance, and documentation effort.
- Sunset headers tell consumers exactly when a version will stop working.
- Even a single consumer may need time to migrate when requirements change.
Challenge: Design a version migration plan for an API with 10 external consumers moving from v1 to v2. Define the deprecation window, communication timeline, and Sunset header implementation.
FAQ
Mini Project
Create a versioned API with two versions of a product catalog endpoint. v1 returns flat product list; v2 supports pagination, filtering, and nested categories. Include deprecation headers in v1 responses.
What's Next
Explore API documentation strategies for documenting multiple API versions, or read about API lifecycle management for end-to-end version governance.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro