Saturday, November 8, 2025

thumbnail

Building Versioned APIs in Python

 ๐Ÿง  What Is API Versioning?


API versioning means managing multiple versions of your API so that changes don’t break existing client applications.


When you update your API — for example, by:


Changing response formats,


Renaming endpoints,


Updating authentication, or


Removing old fields —


you can introduce a new version while keeping older versions active for clients still using them.


⚙️ Why API Versioning Is Important

Benefit Description

Backward Compatibility Old clients can still work while new ones use updated features.

Smooth Upgrades You can gradually migrate users from older to newer versions.

Stability Prevents breaking integrations with external systems.

Testing and Debugging Different versions can run side-by-side for comparison.

Deprecation Strategy You can safely phase out outdated endpoints.

๐Ÿงฉ Common API Versioning Strategies


There are several ways to include versioning in your Python APIs. Let’s look at the most common ones:


1. URL Path Versioning (Most Common)


Add the version number directly to the endpoint path.


Example:


/api/v1/users

/api/v2/users



Pros: Simple, explicit, and easy to route.


Cons: Requires duplicating routes and controllers when upgrading.


✅ Example with FastAPI:


from fastapi import FastAPI


app = FastAPI()


@app.get("/api/v1/users")

def get_users_v1():

    return {"version": "v1", "data": ["Alice", "Bob"]}


@app.get("/api/v2/users")

def get_users_v2():

    return {"version": "v2", "data": [{"id": 1, "name": "Alice"}]}



✅ Example with Flask:


from flask import Flask, jsonify


app = Flask(__name__)


@app.route("/api/v1/users")

def users_v1():

    return jsonify({"version": "v1", "data": ["Alice", "Bob"]})


@app.route("/api/v2/users")

def users_v2():

    return jsonify({"version": "v2", "data": [{"id": 1, "name": "Alice"}]})


2. Header Versioning


Clients specify the version in the HTTP header instead of the URL.


Example Request:


GET /api/users

Accept: application/vnd.company.v2+json



Pros: Keeps URLs clean and RESTful.


Cons: Harder for users to discover versions via browsing.


✅ Example (FastAPI):


from fastapi import FastAPI, Header, HTTPException


app = FastAPI()


@app.get("/api/users")

def get_users(accept: str = Header(default="application/vnd.company.v1+json")):

    if "v2" in accept:

        return {"version": "v2", "data": [{"id": 1, "name": "Alice"}]}

    elif "v1" in accept:

        return {"version": "v1", "data": ["Alice", "Bob"]}

    else:

        raise HTTPException(status_code=400, detail="Unsupported API version")


3. Query Parameter Versioning


Clients specify the version as a query parameter.


Example:


/api/users?version=1

/api/users?version=2



Pros: Simple to implement and test.


Cons: Not as RESTful; versioning appears optional.


✅ Example (Flask):


from flask import Flask, request, jsonify


app = Flask(__name__)


@app.route("/api/users")

def users():

    version = request.args.get("version", "1")

    if version == "2":

        return jsonify({"version": "v2", "data": [{"id": 1, "name": "Alice"}]})

    return jsonify({"version": "v1", "data": ["Alice", "Bob"]})


4. Domain/Subdomain Versioning


Each version is hosted under a different domain or subdomain.


Example:


https://v1.api.myapp.com/users

https://v2.api.myapp.com/users



Pros: Strong separation between versions.


Cons: More infrastructure complexity (requires DNS/routing setup).


This is common in large-scale systems or APIs managed through API gateways (like AWS API Gateway or Kong).


5. Content Negotiation


Clients specify the desired API version in the Content-Type or Accept header.


Example Header:


Accept: application/json; version=2



The server reads the version value and routes accordingly.


✅ Example (FastAPI):


from fastapi import FastAPI, Header


app = FastAPI()


@app.get("/api/users")

def get_users(accept: str = Header(default="application/json; version=1")):

    if "version=2" in accept:

        return {"version": "v2", "data": [{"id": 1, "name": "Alice"}]}

    return {"version": "v1", "data": ["Alice", "Bob"]}


๐Ÿงฑ 6. Organizing Versioned APIs (Best Practices)


For large projects, it’s best to separate code by version to avoid confusion.


✅ Example Folder Structure (FastAPI/Flask)

project/

├── app/

│   ├── v1/

│   │   ├── routes.py

│   │   └── models.py

│   ├── v2/

│   │   ├── routes.py

│   │   └── models.py

│   ├── __init__.py

└── main.py



main.py:


from fastapi import FastAPI

from app.v1.routes import router as v1_router

from app.v2.routes import router as v2_router


app = FastAPI()


app.include_router(v1_router, prefix="/api/v1")

app.include_router(v2_router, prefix="/api/v2")



This allows your API to grow without breaking existing endpoints.


๐Ÿงฐ 7. Managing Versioning with Blueprints (Flask Example)


Flask’s Blueprints are perfect for organizing versioned routes.


from flask import Flask, Blueprint, jsonify


v1 = Blueprint("v1", __name__)

v2 = Blueprint("v2", __name__)


@v1.route("/users")

def users_v1():

    return jsonify({"version": "v1", "data": ["Alice", "Bob"]})


@v2.route("/users")

def users_v2():

    return jsonify({"version": "v2", "data": [{"id": 1, "name": "Alice"}]})


app = Flask(__name__)

app.register_blueprint(v1, url_prefix="/api/v1")

app.register_blueprint(v2, url_prefix="/api/v2")



✅ Advantages:


Each version can have independent routes, models, and logic.


Easier to deprecate or remove an old version cleanly.


๐Ÿงช 8. Deprecating Older Versions


You can’t support every version forever. At some point, older ones should be deprecated.


๐Ÿ› ️ Steps to Deprecate an API Version


Announce deprecation ahead of time (e.g., email, documentation).


Add warnings in responses.


Monitor usage (with logs or analytics).


Disable the version after the grace period.


✅ Example Warning Header:


from fastapi import Response


@app.get("/api/v1/users")

def users_v1(response: Response):

    response.headers["Warning"] = "299 - 'API v1 is deprecated. Use v2 instead.'"

    return {"version": "v1", "message": "This version will be removed soon."}


๐Ÿ“‹ 9. Documentation and OpenAPI Integration


When you version your APIs, document each version clearly.

FastAPI and Flask (via Swagger) make this easy.


Each version can have its own OpenAPI docs, e.g.:


/docs/v1


/docs/v2


✅ FastAPI Example:


app_v1 = FastAPI(title="My API v1", version="1.0")

app_v2 = FastAPI(title="My API v2", version="2.0")



This helps developers know which endpoints belong to which version.


๐Ÿงพ Summary

Strategy How It Works Pros Cons

URL Path Versioning /api/v1/resource Simple, explicit Duplicate routes

Header Versioning Custom HTTP headers Clean URLs Harder to discover

Query Parameter /api/resource?version=2 Easy to implement Less RESTful

Subdomain Versioning v1.api.example.com Strong separation Complex setup

Content Negotiation Accept: version=2 RESTful Harder for clients

๐Ÿ’ก Best Practices


✅ Keep old versions functional for a while.


⚙️ Automate version routing (e.g., via routers or blueprints).


๐Ÿงฑ Separate codebases per version.


๐Ÿ“œ Update documentation for every release.


๐Ÿšจ Use headers or warnings to announce deprecation.


๐Ÿ”’ Apply consistent authentication across versions.


๐Ÿงช Test all versions independently.


๐Ÿง  Final Thoughts


Building versioned APIs in Python ensures your application remains scalable, maintainable, and future-proof.

It gives you the freedom to evolve your system — improving performance and structure — without breaking existing clients.


Whether you’re using Flask, FastAPI, or Django REST Framework, versioning is a best practice that helps your full-stack app grow smoothly and sustainably.

Learn Fullstack Python Training in Hyderabad

Read More

How to Handle Errors and Responses in Full Stack Python APIs

Integrating Third-Party APIs with Full Stack Python

Creating APIs for Mobile Applications with Python

How to Handle API Requests in Python: Methods and Best Practices

At Our Quality Thought Training Institute in Hyderabad

Get Directions

Subscribe by Email

Follow Updates Articles from This Blog via Email

No Comments

About

Search This Blog

Powered by Blogger.

Blog Archive