APIs are the backbone of digital transformation, but what happens when your business evolves and your API needs to change? Without a solid versioning strategy, you risk breaking client integrations and losing trust. In this article, Iβll show you how to master API versioning in .NET Core β ensuring your APIs remain robust, flexible, and future-ready.
APIs are contracts. Once published, clients depend on them. As your product grows, so do the demands on your API. Versioning lets you:
- Evolve your API without breaking existing clients.
- Support multiple versions simultaneously, giving clients time to migrate.
- Experiment with new features or designs safely.
π According to a 2024 Postman survey, 89% of developers consider versioning essential for API reliability and client satisfaction.
.NET Core supports several versioning strategies via the Asp.Versioning.Mvc.ApiExplorer package.
dotnet add package Asp.Versioning.Mvc.ApiExplorerExample: /api/v1/products
- Most visible & cache-friendly β the version lives in the route.
// Program.cs
services.AddApiVersioning(o =>
{
o.DefaultApiVersion = new ApiVersion(1, 0);
o.AssumeDefaultVersionWhenUnspecified = true;
o.ReportApiVersions = true;
o.ApiVersionReader = new UrlSegmentApiVersionReader(); // π key line
});
// Controllers
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/products")]
[ApiController]
public class ProductsController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return Ok(new { version = "1.0", data = "Old SKU list" });
}
}
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/products")]
[ApiController]
public class ProductsV2Controller : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return Ok(new { version = "2.0", data = "New SKU list" });
}
}Request examples:
GET /api/v1/productsβ returns v1 payloadGET /api/v2/productsβ returns v2 payload
Example: /api/products?api-version=1.0
- Keeps URLs clean but less discoverable.
// Program.cs
services.AddApiVersioning(o =>
{
o.DefaultApiVersion = new ApiVersion(1, 0);
o.AssumeDefaultVersionWhenUnspecified = true;
o.ReportApiVersions = true;
o.ApiVersionReader = new QueryStringApiVersionReader("api-version");
});
// One controller, two actions mapped to versions
[Route("api/products")]
[ApiController]
public class ProductsController : ControllerBase
{
[HttpGet]
[MapToApiVersion("1.0")]
public IActionResult GetV1()
{
return Ok(new { version = "1.0", data = "Old SKU list" });
}
[HttpGet]
[MapToApiVersion("2.0")]
public IActionResult GetV2()
{
return Ok(new { version = "2.0", data = "New SKU list" });
}
}Request examples:
GET /api/products?api-version=1.0GET /api/products?api-version=2.0
Example: Header: api-version: 1.0
- RESTful and clean but requires client setup.
// Program.cs
services.AddApiVersioning(o =>
{
o.DefaultApiVersion = new ApiVersion(1, 0);
o.AssumeDefaultVersionWhenUnspecified = true;
o.ReportApiVersions = true;
o.ApiVersionReader = new HeaderApiVersionReader("api-version"); // π
});
// Same controller technique as query-string example
[Route("api/products")]
[ApiController]
public class ProductsController : ControllerBase
{
[HttpGet]
[MapToApiVersion("1.0")]
public IActionResult GetV1()
{
return Ok(new { version = "1.0" });
}
[HttpGet]
[MapToApiVersion("2.0")]
public IActionResult GetV2()
{
return Ok(new { version = "2.0" });
}
}Request examples:
GET /api/productswith headerapi-version: 1.0GET /api/productswith headerapi-version: 2.0
Example: Accept: application/json;v=1.0
- Powerful for content negotiation.
// Program.cs
services.AddApiVersioning(o =>
{
o.DefaultApiVersion = new ApiVersion(1, 0);
o.AssumeDefaultVersionWhenUnspecified = true;
o.ReportApiVersions = true;
o.ApiVersionReader = new MediaTypeApiVersionReader("v"); // π
});
[Route("api/products")]
[ApiController]
public class ProductsController : ControllerBase
{
[HttpGet]
[MapToApiVersion("1.0")]
public IActionResult GetV1()
{
return Ok(new { version = "1.0" });
}
[HttpGet]
[MapToApiVersion("2.0")]
public IActionResult GetV2()
{
return Ok(new { version = "2.0" });
}
}Request examples:
GET /api/productswithAccept: application/json;v=1.0GET /api/productswithAccept: application/json;v=2.0
| Approach | Pros | Cons |
|---|---|---|
| URL Path | Easy to use, visible, cacheable | Can clutter routes |
| Query String | Clean URLs, easy to implement | Harder to cache, less visible |
| Header | RESTful, no URL changes | Requires client changes |
| Media Type | Flexible, supports negotiation | Complex, less common |
| Strategy | Visibility | RESTful | Client Config Needed | Cache Friendly |
|---|---|---|---|---|
| URL Path | β High | β Yes | β No | β Yes |
| Query String | β Yes | β No | ||
| Header | β Low | β Yes | β Yes | β Yes |
| Media Type | β Low | β Yes | β Yes |
Visuals simplify complex concepts. Hereβs a diagram to help you visually understand how clients interact with different API versions through an API Gateway in a .NET Core environment:
Key Elements:
-
π₯ Clients: Web, Mobile
-
πͺ API Gateway
-
π Routes:
/api/v1/products/api/v2/products/api/v3/products
-
π§ Versioning Strategies:
- π URL Path
- β Query String
- π© Header
- π° Media Type
A mid-sized SaaS company built their backend using .NET Core Web API. As their customer base grew, so did the demands for new featuresβbut without breaking existing integrations for long-time users.
π Challenge: Early on, they had only one API version (v1). Every changeβnew features, response structure updatesβrisked breaking production clients.
β Solution:
They implemented URL-based API versioning (/api/v1/, /api/v2/) using .NET Core API Versioning library. New features were added in v2, while v1 remained stable for legacy clients. They gradually rolled out v2 with a sunset strategy for v1.
π Results:
- β 0% downtime during rollout
- π 70% client migration to v2 within 6 months
- π¨βπ» Increased developer productivity by isolating logic per version
This strategy gave them the flexibility to evolve their APIs without fear of regressionsβfuture-proofing their system as they scaled.
- π 83% of developers say managing breaking changes is a top challenge
- π¨ APIs without versioning are 3x more likely to cause client failures
- π§ͺ Structured versioning reduces integration issues by 40%+
- π Improves API lifecycle management and developer experience
π How are you handling API changes in your projects?
π€ Faced any challenges with versioning?
π Share your experiences in the comments β or π€ connect with me to discuss best practices and real-world solutions!
β Ready to make your APIs future-proof?
π Start implementing versioning in your .NET Core projects today and build with confidence for tomorrow! π‘
Want to explore the code? Dive into the complete .NET Core API Versioning project here: π https://github.com/sandip-Kalsariya/API-Versioning



