Let’s see why versioning matters.
Imagine you built a successful API. You have 1000 mobile apps using it. Your API returns user data like this:
Old API:
One day, you realize "Wait! We need separate first and last names!" So you change it to:
New API:
What happens?
All 1000 apps crash instantly! They're expecting name, but now there's first_name and last_name. Their code breaks:
// Old app code
This is called a breaking change, and it's a disaster!
Versioning is like saying "Hey, I'm making changes, but I'll keep the old version running for those who need it."
Let’s see the right way:
Version 1 (Old apps use this):
Version 2 (New apps use this):
Now:
Old apps keep working (using v1)
New apps use the better format (using v2)
You can gradually migrate users from v1 to v2
Eventually, you deprecate v1 (after giving warning)
This is what most APIs do. The version is right in the URL:
Let’s walk through a real scenario:
Your Startup's Journey:
━━━━━━━━━━━━━━━━━━━━━
| 2023: Launch API v1
Returns:
2024: Want to support multiple currencies
Launch API v2
Returns:
Keep v1 running for existing users
2025: Want to add tax calculation
Launch API v3
2026: Announce v1 deprecation
"v1 will shut down in 6 months"
Give users time to upgrade
2026 (6 months later):
Turn off v1
Now only v2 and v3 are running
Pros:
Very clear which version you're using
Easy to test different versions
Can run multiple versions simultaneously
Cons:
URLs change when version changes
Need to maintain multiple codebases
Some APIs keep the URL clean and put the version in headers:
Accept: application/vnd.myapi.v1+json
vs
Accept: application/vnd.myapi.v2+json
Let’s see a conversation:
Client → Server
━━━━━━━━━━━━━━━
Same URL, different version!
Pros:
Clean URLs (always just /products)
RESTful purists prefer this
Clients explicitly request version
Cons:
Harder to test (can't just paste URL in browser)
Not visible in logs easily
This is less common but simple.
The rule of thumb:
Breaking Changes (need new version):
Removing a field
Changing field type
BREAKING
Changing field name
BREAKING
❌ Removing an endpoint
Non-Breaking Changes (same version):
✅ Adding optional fields
v1: {"name": "John"}
v1.1: {"name": "John", "nickname": "Johnny"}
← Extra field, OK
Adding new endpoints
v1: GET /users exists
v1.1: GET /users/123/posts added ← New endpoint, OK\
✅ Making required field optional
v1: email required
v1.1: email optional
More flexible, OK\
✅ Fixing bugs (that don't change contract)
v1: Returns wrong calculation
v1.1: Returns correct calculation ← Same format, OK! |
Let’s see how a professional API (Stripe) does versioning:
Stripe's approach:
━━━━━━━━━━━━━━━━━
Every API call includes version:
Stripe-Version: 2023-10-16
Stripe has DATED versions:
2023-10-16
2024-01-15
2024-06-20
When you create an account, you're locked to that version. You can upgrade when ready!
Here's how to retire an old version responsibly:
Step 1: Announce deprecation (12 months ahead)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Response:
Warning: API v1 is deprecated. It will be removed on 2026-10-19.
Please migrate to v2:
https://docs.myapi.com/v2-migration
Step 2: Remind users (6 months ahead)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Response:Warning: API v1 will be removed in 6 months
Only 20% of users have migrated. Start now
Step 3: Final warning (1 month ahead)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Response:
URGENT: API v1 will be removed in 30 days
Migrate to v2 immediately
Step 4: Shutdown
━━━━━━━━━━━━━━━
Response:410 Gone
| Method | Example | Discoverability | URL cleanliness | Browser testable | Used by |
|---|---|---|---|---|---|
| URL path | /v2/users | High | Low (version in URL) | Yes | Stripe, GitHub, Google |
| Header | Accept: vnd.api.v2+json | Low | High | No | GitHub (also), Azure |
| Query param | /users?version=2 | Medium | Medium | Yes | Amazon, Netflix |