API versioning best practices
Release breaking changes without disrupting existing clients.
Problem statement
It's typical for an API product to evolve over time as new features are added, existing features are modified, bugs are fixed, and occasionally existing features are retired.
In most cases it is best practice to make changes to an API in a backwards-compatible way, to not disrupt existing customer integrations. Sometimes making a breaking change is unavoidable. In these circumstances, it is desirable to release the change to a controlled subset of customers, without impacting the rest.
Success criteria
Versioning helps improve trust and confidence in an API by avoiding breaking client integrations.
Of course, the purpose of each new version is to deliver greater value to customers. Metrics should measure adoption to assess whether the benefit provided by a new version outweighs the migration cost for consumers.
Goal | Signal | Metric |
---|---|---|
Improve trust | Pass rate | Reduce the number of invalid requests that were previously successful to 0. |
Increase product usage | Adoption rate | The proportion of clients using the new version is above X% after Y weeks/months since release. |
Product requirements
API versioning provides a mechanism to deliver previous and new functionality and behavior to different customers simultaneously.
Strictly speaking, the API's version only needs to change when a breaking change is introduced. For non-breaking changes, the API can be updated without increasing the version. This approach keeps the API version relatively stable.
The downside of only increasing the API's version for breaking changes is that it makes communicating other enhancements less clear as they are no longer tied to new version numbers. This can be mitigated by using semantic versioning, where only the major version number needs to be specified, but the changelog can describe the full version number.
User stories
An API contract is a promise, which developers expect to be upheld. There are a number of valid strategies for upholding this promise:
- Never change the API contract for a given version. Always increment the API version for each change to the contract.
- Only release backwards-compatible changes to a given API version. Only increment the API version for breaking changes.
- If using Semantic Versioning, advise developers that changes to minor and patch versions will always be backwards compatible, and that they can pin their integration to the major version only.
Major version updates should always be opt-in. Allow the client to specify the API version that their integration uses, either through internal configuration or through the API request itself.
If the client's version is determined from an internal configuration, your customer will have to coordinate with you to perform migrations. This can be costly and time consuming for both the API consumer and provider. Prefer to provide a self-serve mechanism for specifying the API version.
Allowing the client to specify which version of the API to use on a per-request basis gives the client the most amount of flexiblity and control:
- The client can opt-in to new versions at will.
- The client can specify different versions in different environments. For example, the client can specify version 1 in their Production environment, and version 2 in their QA environment.
- The client can migrate a portion of their integration to a new version, while keeping the rest of the integration the same.
This approach can be combined with a default version configuration, such that the per-request version acts as an optional override.
When returning errors due to version mismatch, it is a good idea to include links to the any relevant versioning policies, changelogs, and migration guides in the human-readable portion of the error response.
Once an API is deprecated, existing clients should be able to continue access it, but new clients should not.
You can store the minimum supported version per API client, so that client credentials issued after a certain date cannot be used to access APIs that were deprecated on that date.
Any mechanism that allows the client to specify which version of an API to use needs to consider whether clients should opt in or opt out of receiving security updates.
If your API uses Semantic Versioning, an opt-out approach could be to allow clients to specify only the major and minor version to use, without a patch version, i.e.
major.minor
. This ensures that when you release a security update as a patch, all customers will receive it automatically. Customers can opt out of this behavior by specifying the full semantic version, i.e.major.minor.patch
.If your reference documentation is backed by OpenAPI, Operation and Paramter objects support a
deprecated
field to declare them as deprecated.If a release that doesn't include any breaking changes, the developer may decide to migrate to it immediately since it is a lower risk.
All APIs should have a deprecation policy, which stipulates that deprecated APIs will continue to be supported for a period of time, such as 12 months.
During the deprecation period, notify customers who haven't migrated on a periodic basis with increasing frequency.
Customers may need to incorporate the effort in their overall backlog, staff on-call resources and notify customers. Sometimes a customer cannot prioritize the appropriate resources before the sunset date. Depending on the relationship you have with your customers, you may need to be prepared to extend the migration deadline for the last few customers.
Explore design patterns
Semantic versioning
Distinguish between major, minor and patch versions.
Date-based versioning
Easily understand when a version was released.
URI versioning
Clearly segment different versions of the API.
Header versioning
Enable versioning on a per-request basis.
API examples
API | Format | How specified |
---|---|---|
Salesforce Platform APIs | XX.X | Base URI, https://MyDomainName.my.salesforce.com/services/data/vXX.X/resource/ |
Notion API | YYYY-MM-DD | Notion-Version header |
WhatsApp Cloud API | v17.0 | Base URI, https://graph.facebook.com/v17.0 |
MicrosoftGraph | v1.0 or beta | Base URI, https://graph.microsoft.com/{version} |
PayPal APIs | v2 | Base URI, https://api-m.sandbox.paypal.com/v2 |
Stripe API | YYYY-MM-DD | Stripe-Version header |
Razorpay APIs | v1 | Base URI, https://api.razorpay.com/v1 |
MongoDB Data API | v1 or beta | Base URI, https://data.mongodb-api.com/app/<Data API App ID>/endpoint/data/v1 |
Zoho CRM REST APIs | v4 | Base URI, https://www.zohoapis.com/crm/v4 |
Datadog REST API | v1 | Base URI, https://api.datadoghq.com/api/v1 |
Meraki Dashboard API | v1 | Base URI, https://api.meraki.com/api/v1 |
Pipedrive API | v1 | Base URI, https://{COMPANYDOMAIN}.pipedrive.com/api/v1 |
Twilio Messaging / SMS API | YYYY-MM-DD | Base URI,https://api.twilio.com/2010-04-01 |
GitHub API | YYYY-MM-DD | X-GitHub-Api-Version header |
Further reading
- https://restfulapi.net/versioning/
- https://www.infoq.com/articles/roy-fielding-on-versioning/
- https://zuplo.com/blog/2022/05/17/how-to-version-an-api
- https://labs.qandidate.com/blog/2014/10/16/using-the-accept-header-to-version-your-api/
- https://cloud.google.com/blog/products/api-management/api-design-which-version-of-versioning-is-right-for-you