Chapter 03 of 12
Semantic Versioning for APIs
How SemVer maps onto an HTTP API, why consumers only ever feel the major, what an alias like v1 really is, and where date-based numbering fits instead.
Semantic versioning is the default answer to how a version should be numbered, and it was written for libraries. An HTTP API is not a library, so the mapping is close but not exact, and the differences are where teams get into trouble.
What SemVer actually says
The specification at semver.org is short. You increment the major version for incompatible API changes, the minor version when you add functionality in a backward compatible manner, and the patch version for backward compatible bug fixes. Version 1.0.0 defines the public API, and anything below it may change at any time.
That last rule is more useful than it looks. A 0.x API carries no stability promise at all, which is a legitimate and honest state for a product that has not found its shape yet. What is not legitimate is staying on 0.x for three years while enterprise customers build on it.
Consumers only ever feel the major
Map the three digits onto the classification from what counts as a breaking change and something becomes obvious.
| Digit | Contains | Can a consumer opt out |
|---|---|---|
| Major | Breaking changes | Yes, and they must be able to |
| Minor | Additive, backward compatible | No, because there is nothing to opt out of |
| Patch | Fixes with no contract change | No |
If a minor release is genuinely backward compatible, then every caller that worked before still works. Offering 1.3 and 1.4 as separately selectable versions therefore creates a choice with no consequence, and a support matrix with no benefit.
That is why almost every public API exposes only the major to its callers, whether as /v2, as Api-Version: 2 or as a date range. The precise number keeps living in your release notes and in your support conversations, and the caller never has to care.
An alias is not a version
The label v1 in a URL is usually an alias, meaning a stable name that points at whichever precise version is current. Today it resolves to 1.5.0, next month it resolves to 1.6.0, and no consumer changes anything.
This is the mechanism that makes additive evolution work in public. Consumers integrate against a label with a compatibility promise, you keep shipping improvements behind it, and the exact number stays available for anybody who needs it. The promise attached to the label is the important half, so write it down. Something like new fields may appear and existing fields will not change is enough, and it is what tells a consumer how to write their parser.
The trap called latest
A latest selector looks helpful and behaves like a scheduled outage.
Anybody pointing at latest is opted into your next breaking change on the day you ship it, without a decision and usually without noticing. Worse, they are the callers least likely to be watching your changelog, because pointing at latest is what people do when they do not want to think about versions.
Serving the newest contract to callers who send no version is the same trap wearing different clothes. It is a reasonable default while an API is young, and it stops being reasonable the moment you have a consumer you cannot phone.
Date-based numbering
Some APIs replace the counter entirely and name each release by its date. Stripe and GitHub both do this, as covered in versioning strategies, and the reasoning is worth understanding even if you never adopt it.
A date makes no claim about compatibility, which sounds like a weakness and is actually the point. There is no argument about whether a change deserves a major, because the release is simply the contract as it stood on that day. Consumers pin to the date they integrated on and upgrade deliberately.
The bill arrives in the long tail. Customers stay pinned for years, so the number of live contracts grows steadily, and you need automated migration support plus a retirement policy that you actually enforce. Choose dates when your contract changes often and your customers upgrade rarely, and choose numbers when your contract changes rarely in deliberate steps.
Keep the build number out of it
One more distinction saves a great deal of confusion. The version your deployment pipeline stamps on a container is not the version your consumers select, even when both are numbers and both live in the same repository.
Your service can ship forty times a week while the contract stays at 2. When the two share a number, every deploy looks like a contract change from the outside, and the changelog turns into noise that nobody reads. Keep the contract version in the specification, keep the build version in the pipeline, and let the release notes join them where it helps.
In Routebase
Every specification version in Routebase carries a full Major.Minor.Patch number, and the create dialog offers the three bumps with the meaning attached to each. Comparing against the previous version suggests which digit the changes actually justify, so the number follows the classification instead of somebody's mood.

Aliases are a separate layer on top of that number, so v1 can resolve to 1.5.0 while the precise version stays visible everywhere it matters. A published version can carry an alias and a sunset date, and both appear in a table below the versioning strategy once they are set. Only published and deprecated versions take either one, since neither means anything for a draft that is still moving. The Spec Versioning guide covers the numbering dialog and the alias table in detail.
Frequently asked questions
Does semantic versioning work for REST APIs?
It works as a numbering discipline and not as a routing scheme. Semantic versioning tells you which digit to increment, since semver.org defines MAJOR for incompatible changes, MINOR for backward compatible additions and PATCH for backward compatible fixes. What consumers actually select is almost always the major alone, because a caller has no reason to pin to a minor that only added optional fields.
Why do API consumers only see the major version?
Because minor and patch releases are defined as backward compatible, so there is nothing for a consumer to opt into. If v1.4.0 works for every caller that v1.3.0 worked for, then exposing both as selectable versions creates choices with no consequences and a support matrix with no benefit. The internal number stays precise while the public selector stays coarse.
What is an API version alias?
An alias is a stable label pointing at a precise version, so v1 might resolve to 1.5.0 today and 1.6.0 next month. Consumers integrate against the label and receive compatible improvements without changing anything. The precise number stays available for release notes, for support conversations and for anybody who needs to pin exactly.
Should an API version be a number or a date?
Use a number when your contract changes in occasional deliberate steps, since a major number communicates a migration. Use a date when the contract changes often in small compatible increments and customers stay pinned for years, which is the pattern behind Stripe and GitHub. A date says what the contract looked like on a day, and it removes the argument about whether something deserves a major.
Last reviewed by The Routebase Team.