A Guide to API Versioning: Comparing URI, Header, and Query Parameters

Dewa MahendraDewa Mahendra
4 min read

When you build an API, you'll eventually encounter this question: How do I handle versioning?

Versioning allows you to update your API without disrupting existing clients. Think of it this way: if your API is a contract, versioning is your way of saying, "I'm making changes, but here's how you can keep using the old version if needed."

There are several ways to version an API, but the three most common methods are:

  • Using the URI (or URL path)

  • Using a Header

  • Using Query Parameters

Let's go through each one.


๐Ÿ”น 1. URI Versioning (a.k.a Path Versioning)

Request:

GET /api/v1/users

Example Controller:

csharpCopyEdit[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
public class UsersController : ControllerBase
{
    [HttpGet]
    public IActionResult GetV1() => Ok(new { version = "v1", users = new[] { "Alice", "Bob" } });
}

โœ… Pros:

  • Very clear: You can immediately see the version from the URL.

  • Easy to manage in routing: Frameworks often make it easy to handle versioned routes.

  • Simple for clients: No need to mess with headers or query strings.

โŒ Cons:

  • Breaks RESTful purity a bit โ€” the resource path (/users) technically hasnโ€™t changed, only the version.

  • Might lead to duplicate code if you donโ€™t manage your controllers or handlers well.


๐Ÿ”น 2. Header Versioning

Request:

GET /api/users
Header: X-API-Version: 2.0

Example Controller:

csharpCopyEdit[ApiController]
[Route("api/[controller]")]
[ApiVersion("2.0")]
public class UsersController : ControllerBase
{
    [HttpGet]
    public IActionResult GetV2() => Ok(new { version = "v2", users = new[] { "Alice", "Bob", "Charlie" } });
}

The route remains the same (/api/users), but the version is selected from the header.

โœ… Pros:

  • Clean URL: Keeps the path nice and tidy.

  • More flexible: You can version by resource or operation without changing the URL.

โŒ Cons:

  • Hidden from users: Not as obvious unless you inspect the headers.

  • More complex for clients: Especially for front-end devs using tools like fetch or axios, they need to manually set headers.

  • Might be harder to cache at CDN or proxy level (depending on your setup).


๐Ÿ”น 3. Query Parameter Versioning

Example:

GET /api/users?version=3.0

Example Controller:

csharpCopyEdit[ApiController]
[Route("api/[controller]")]
[ApiVersion("3.0")]
public class UsersController : ControllerBase
{
    [HttpGet]
    public IActionResult GetV3() => Ok(new { version = "v3", users = new[] { "Alice", "Bob", "Charlie", "Diana" } });
}

You can access the correct version by sending ?version=3.0 in the query string.

โœ… Pros:

  • Easy to try out: You can test versions directly in the browser or with simple tools.

  • Flexible: Can quickly switch versions during testing.

โŒ Cons:

  • Less RESTful: Versioning is a concern of API behavior, not data filtering โ€” query params are usually for filtering/sorting/searching.

  • Also less visible in API docs and routing logic.


โœ… Tip: Multiple Versions in One Controller

You can even have all versions in the same controller using [MapToApiVersion]:

csharpCopyEdit[ApiController]
[Route("api/users")]
[ApiVersion("1.0")]
[ApiVersion("2.0")]
public class UsersController : ControllerBase
{
    [HttpGet]
    [MapToApiVersion("1.0")]
    public IActionResult GetV1() => Ok(new { version = "v1", users = new[] { "Alice", "Bob" } });

    [HttpGet]
    [MapToApiVersion("2.0")]
    public IActionResult GetV2() => Ok(new { version = "v2", users = new[] { "Alice", "Bob", "Charlie" } });
}

๐Ÿง  So... Which One Should You Use?

Thereโ€™s no one-size-fits-all answer. But hereโ€™s a quick rule of thumb:

Use CaseRecommended Strategy
Public APIsURI versioning
Internal APIs (microservices)Header versioning
Rapid prototyping/testingQuery params (but donโ€™t keep them long-term)

If you want the simplest approach to start, URI versioning is a good bet. Itโ€™s easy to understand, easy to implement, and widely accepted.


๐Ÿ“Š Visual Comparison

Hereโ€™s a quick visual summary to compare the strategies:

FeatureURI VersioningHeader VersioningQuery Param Versioning
๐Ÿ” VisibilityHigh (easy to see)Low (hidden in headers)Medium (in URL)
๐Ÿ›  SimplicityEasy to implementSlightly complexEasy
๐Ÿงผ Clean URLsNoYesNo
๐Ÿ” CDN CachingEasyCan be trickyDepends
๐Ÿงช Testing FriendlyYesNo (needs custom headers)Yes
๐Ÿง˜ RESTful ComplianceMediumHighLow

๐Ÿ Final Thoughts

API versioning is about planning for the future. Even if your API only has one version today, thinking ahead will save you from breaking your clients tomorrow.

Keep it simple. Communicate changes clearly. And choose the versioning style that matches your audience and infrastructure.

Let your API grow โ€” but grow gracefully.

0
Subscribe to my newsletter

Read articles from Dewa Mahendra directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Dewa Mahendra
Dewa Mahendra

I'm a highly motivated and experienced developer expertise in leveraging the power of .NET Core Technology. Currently collaborating with an Australian company based in Nusa Dua, Bali, Indonesia, to deliver innovative application development services that push the boundaries of what technology can achieve, and also contribute to the ever-evolving landscape of the global IT industry