What Is API Versioning? How AI Coders Manage API Changes Without Breaking Things
You built an API. Your app uses it. Now you need to change how it works — but your mobile app still expects the old format. API versioning is how you solve this without breaking everything at once.
TL;DR
API versioning lets you change your API without breaking clients that depend on the old version. The most common approach: include the version in the URL path (/api/v1/users). When you make a breaking change, you create /api/v2/users with the new behavior and keep /api/v1/users working. Old clients keep working. New clients use the new version.
Why AI Coders Need to Understand API Versioning
When AI builds you an API, it usually generates routes like /api/users or /api/posts with no version in the URL. That's fine when you're the only one calling the API. It becomes a serious problem the moment anyone else depends on it.
Imagine you build an API that your web app and mobile app both use. Then you need to change the response format — maybe the user object now has a displayName field instead of name. If you just change it:
- Your web app breaks (it expected
name) - Your mobile app breaks (same reason)
- Your users see a broken experience
- You have to update and redeploy everything simultaneously
API versioning solves this by running both the old and new versions simultaneously, letting you migrate clients gradually.
Even for solo projects, understanding API versioning helps you: read documentation for popular APIs (OpenAI, Stripe, GitHub all use it), understand deprecation notices, and build professional-quality APIs from the start.
Real Scenario: Upgrading a User API
"I have a REST API with /api/users that returns user data. I need to change the response format — add nested address fields and rename 'name' to 'displayName'. But my mobile app still uses the old format. Set up API versioning so I can have both v1 (old format) and v2 (new format) running at the same time."
What AI Generated (Express.js)
// routes/api.js
// Tested with Express.js 4.x + Node.js 22.x
const express = require('express')
const router = express.Router()
// ===== V1 Routes (original format) =====
router.get('/v1/users/:id', async (req, res) => {
try {
const user = await getUserById(req.params.id)
// V1 format: flat structure, "name" field
res.json({
id: user.id,
name: user.displayName, // Map new field back to old name
email: user.email,
created_at: user.createdAt
})
} catch (error) {
res.status(500).json({ error: 'Internal server error' })
}
})
// ===== V2 Routes (new format) =====
router.get('/v2/users/:id', async (req, res) => {
try {
const user = await getUserById(req.params.id)
// V2 format: nested address, "displayName" field
res.json({
id: user.id,
displayName: user.displayName, // New field name
email: user.email,
address: { // New nested structure
street: user.street,
city: user.city,
country: user.country
},
createdAt: user.createdAt // camelCase (new convention)
})
} catch (error) {
res.status(500).json({ error: 'Internal server error' })
}
})
// Mount all API routes under /api
app.use('/api', router)
// Result:
// GET /api/v1/users/123 → old format (mobile app keeps working)
// GET /api/v2/users/123 → new format (web app updates to use this)
Next.js App Router Equivalent
// app/api/v1/users/[id]/route.js ← v1 route file
export async function GET(request, { params }) {
const user = await getUserById(params.id)
return Response.json({
id: user.id,
name: user.displayName, // V1: old field name
email: user.email
})
}
// app/api/v2/users/[id]/route.js ← v2 route file
export async function GET(request, { params }) {
const user = await getUserById(params.id)
return Response.json({
id: user.id,
displayName: user.displayName, // V2: new field name
email: user.email,
address: { city: user.city, country: user.country }
})
}
The Three Approaches to API Versioning
1. URL Path Versioning (Most Common)
GET /api/v1/users ← Version in the URL path
GET /api/v2/users
Pros: Obvious and explicit. Easy to test in a browser or curl. Works with all HTTP clients. Easy to route in nginx, API gateways, load balancers. Easy to document.
Cons: Technically violates REST principles (the URL should identify a resource, not a version). In practice, nobody cares — it's what 90% of APIs use because it's simple and it works.
When to use: Almost always. This is what AI generates and what you should default to.
2. Header Versioning
GET /api/users
API-Version: 2 ← Version in a request header
# or the Accept header approach:
GET /api/users
Accept: application/vnd.myapi.v2+json
Pros: URL stays clean. Follows REST principles more closely. GitHub uses this approach.
Cons: Not visible in browser address bar. Can't test with a plain URL. Caching gets complicated. Harder for new developers to discover.
When to use: When you care deeply about REST purity and your API consumers are sophisticated (other developers building on your platform, not casual users).
3. Query Parameter Versioning
GET /api/users?version=2 ← Version in query parameter
GET /api/users?v=2
Pros: Optional — clients that don't pass a version get the default. Easy to add without changing URL structure.
Cons: Messy URLs. Confusing when combined with other query parameters. Easy to forget to include.
When to use: Rarely. Usually only for versioning where backward compatibility is the default and versioning is opt-in.
What About Semantic Versioning?
You might have seen version numbers like 2.1.3 on software packages. That's semantic versioning (semver): MAJOR.MINOR.PATCH. APIs typically only include the major version in the URL because:
- Minor and patch changes should be backward-compatible (no need to change the URL)
- A new major version (v1 → v2) is the only time clients break
So /api/v2/ means "major version 2" — it might actually be version 2.4.1 internally, but clients only care about the major version.
Breaking vs. Non-Breaking Changes
Breaking Changes (Require a New Version)
- Removing a field from the response
- Renaming a field
- Changing a field's data type (string → number)
- Changing the URL structure
- Adding a required request parameter
- Changing authentication requirements
- Changing error response format
Non-Breaking Changes (Safe to Ship Without New Version)
- Adding a new optional field to the response
- Adding a new optional request parameter
- Adding a new endpoint entirely
- Performance improvements
- Bug fixes that don't change the contract
The rule: if any existing client could break because of your change, it's a breaking change and needs a new version.
What AI Gets Wrong About API Versioning
1. No Versioning At All
AI's default is to generate /api/users without a version. For early-stage internal projects, this is fine. But AI doesn't always flag the moment you're building something that others will depend on — that's when versioning matters.
Fix: Always prompt with version: "Build me a REST API with URL versioning — all routes should be under /api/v1/."
2. Forgetting to Version All Routes
If you ask AI to "add versioning" to an existing API, it sometimes updates the main resource routes but leaves auth routes, webhook handlers, or utility endpoints unversioned. Do a final check.
3. No Deprecation Strategy
AI builds the v1 and v2 routes but doesn't generate deprecation headers that tell clients when v1 will be retired. Add this:
// Add deprecation headers to v1 routes
router.get('/v1/users/:id', async (req, res) => {
// Tell clients v1 is deprecated and when it'll be removed
res.set('Deprecation', 'true')
res.set('Sunset', 'Sat, 31 Dec 2026 23:59:59 GMT')
res.set('Link', '<https://api.yourdomain.com/v2/users>; rel="successor-version"')
// ... rest of the handler
})
API Versioning Checklist
- All routes include a version prefix (
/api/v1/) - Breaking changes are identified before implementation
- Old versions continue to work after new versions are released
- Deprecation headers added to old versions
- Documentation updated to mark deprecated endpoints
- Clients given a timeline to migrate (usually 6–12 months)
- Error messages reference the correct version in documentation URLs
How to Debug Versioning Issues with AI
Problem: Clients Breaking After API Change
"My mobile app broke after I updated the API. Here's the old response format: [paste]. Here's the new format: [paste]. Help me set up versioning — keep the old format at /api/v1/ and put the new format at /api/v2/ so both clients work while I update the mobile app."
Problem: Adding Versioning to an Existing API
"I have an existing Express API with routes at /api/users, /api/posts, /api/auth. I need to add versioning. Move everything to /api/v1/ and make sure all existing functionality still works. Don't change the business logic — just the routing."
What to Learn Next
Frequently Asked Questions
What is API versioning?
API versioning is a way to manage changes to your API without breaking existing users who depend on the current version. When you release a new version with breaking changes (different response shape, removed fields, changed behavior), you put it at a new URL or version identifier so old clients still work while new clients use the updated API.
What is a breaking change in an API?
A breaking change is any API change that causes existing clients to fail. Examples: removing a field from the response, renaming a field, changing a field's data type, changing the URL structure, requiring a new required parameter. Non-breaking changes (safe without versioning) include adding optional fields or parameters and adding entirely new endpoints.
What are the three ways to version an API?
URL versioning (/api/v1/users), header versioning (API-Version: 1 in request headers), and query parameter versioning (/api/users?version=1). URL versioning is the most common because it's visible, easy to test in browsers, and easy to route in infrastructure. It's what AI generates and what you should default to.
Do I need API versioning for my first project?
If you're building an internal API where you control all the clients, versioning is optional early on. If you're building a public API that external developers or apps will call, version from day one — /api/v1/ — even if you never release v2. It's a convention that signals stability and professionalism to other developers.
What does AI get wrong about API versioning?
AI often generates APIs without any versioning and doesn't always flag when you're crossing the threshold where versioning matters. When adding versioning to existing APIs, AI sometimes misses some routes. AI also rarely generates deprecation headers that help clients understand when old versions will be retired. Prompt explicitly: "Use /api/v1/ URL versioning on all routes."