Versioning APIs is never fun but if you provide a public API it's something you'll almost certainly run up against eventually. At Clearbit we've just started adding breaking changes to our API and have had to start thinking about how we're going to approach versioning.
There seems to be a few main approaches that people have tried:
Use the URL
This is a fairly straightforward solution - simply add an incrementing version number in the URL and route different API versions to different endpoints. Segment, for example, takes this approach.
This is definitely the simplest approach, but quickly gets unruly if you need to make lots of API changes.
Use a header
Putting a version in the request header is another good solution depending on your use case. GitHub have gone down this path, with a custom
This approach is probably best for APIs like GitHub's that only create big breaking changes once in a blue moon.
Use a dynamic date
Stripe not only have a very extensive API, but also a fairly stringent definition of what constitutes a backwards incompatible change. Since their versions are changing so often, they've taken the rather novel approach of having a dynamic version based on the date you make your first request.
You can upgrade this version in Stripe's dashboard, or on a per-request level with a custom header. They also provide a convenient changelog.
Although this approach is more complex, it comes with a lot of advantages such as being able to quickly ship new API features without changing an endpoint, and also being able to easily see which customers are using which versions of the API.
We've gone with this approach since we're anticipating quite a few changes to our APIs in the future as we improve them.