Every payment API eventually has to change something your code depends on: a field gets renamed, a status value moves, an endpoint is split in two. How a provider ships those changes decides how much work an upgrade is and how easily it can surprise you. The major providers have settled on two broad answers: pin a dated version, usually through a header and account settings, or put a version number in the URL.

This piece compares how Stripe, Square, Adyen and PayPal document their approaches. For each provider’s current version and dated retirements, use our payment API changelog tracker; this article explains the models rather than repeating those figures.

Model one: a dated version pinned to the account and the request

Stripe names versions by release date. Its versioning reference says that starting with the 2024-09-30.acacia release, it ships new API versions monthly with no breaking changes, and twice a year issues a major release, named like Basil, that starts with a version containing breaking changes. Monthly releases can be adopted without code changes; a new major release may need them.

Three settings decide which version a call actually uses:

  • The account default. Requests made with curl use the account’s default API version unless overridden.
  • The Stripe-Version header, which overrides the default for a single request.
  • The SDK. Stripe’s upgrade guide says a server-side SDK uses the API version that was current when that SDK release came out. In strongly typed languages (Java, Go and .NET) that version is fixed, and Stripe advises against overriding it because responses may not match the SDK’s types.

Stripe’s own recommendation is to specify the version in code rather than rely on the dashboard default. Webhooks have their own setting: events use the account default unless a version is set when the endpoint is created, and for newer event destinations the snapshot_api_version can only be set at creation. Upgrading webhooks therefore means creating a second endpoint on the new version, running both, and switching processing over, which Stripe documents step by step.

Square uses a similar date scheme. Its versioning overview describes a YYYY-MM-DD version that applies to all Square APIs at once, released typically monthly. Each application has a default version, set on its Credentials page, which a request can override with the Square-Version header. The response always returns Square-Version, so a client can log which contract it was served. Square also lists what it counts as breaking: new required fields, retirements, renamed or reshaped fields, stricter validation, type changes and changed HTTP status codes. New endpoints, optional fields and deprecations are not.

The appeal of this model is that the URL never changes and the provider can evolve one resource at a time. The cost is that the effective version lives in more than one place, so an integration that never sets it explicitly can depend on a dashboard setting or an SDK release nobody on the team chose deliberately.

Model two: a version number in the path

Adyen puts the version in the URL. Its versioning page says each API URL carries a suffix of “v” followed by a whole number, that each Adyen API has its own version, and that numbers can be skipped. A Checkout call and a Balance Platform call can therefore sit on unrelated version numbers in the same integration.

Adyen says it ships breaking changes only in a new version and applies non-breaking changes to all versions, and that it maintains older versions until you choose to upgrade. What counts as breaking depends on the API: in its “new” APIs, such as Checkout, adding a field to a response is non-breaking, while in its classic Payment, Recurring, Payout and BinLookup APIs it is breaking. It also publishes an API Diff Tool to compare two versions.

PayPal also versions in the path, but per API family and with larger steps. Its REST endpoints carry /v1/ or /v2/ segments, and moving between them can mean a redesign. The Payments v1 to v2 upgrade guide says the v1 Payments API is deprecated and will be removed, and that v2 splits its work between Orders v2 for checkout and Payments v2 for post-approval operations. A single v1 execute call becomes separate capture or authorize calls; field names change; v1’s list-payments endpoint has no v2 equivalent; and write calls return minimal responses unless the client sends Prefer: return=representation. The same guide notes that the PayPal-Request-Id header carries over, with a retention window reduced to six hours, a detail that matters for the retry logic described in our idempotency keys explainer.

Path versions make the contract visible in every log line and proxy rule, and they let a team move one API at a time. The trade-off is that a major step is a migration project, not a header change.

What this means for an integration

A few practices hold whichever model a provider uses:

  1. Pin the version in code. Send the header or use a pinned SDK rather than inheriting an account default, so a dashboard change cannot alter production behavior.
  2. Record the version you were served. Square returns it in a response header; for path-versioned APIs it is in the URL. Log it alongside request IDs.
  3. Treat webhooks as a separate upgrade. Stripe’s event payloads follow the endpoint’s own version, not the version your code sends, so the event handler and the API client can drift apart.
  4. Read open enums defensively. Stripe warns that some enum values can grow without a version change; a switch statement needs a safe default branch.
  5. Upgrade SDKs deliberately. With Stripe and Square, a new SDK release can move you to a newer API version, so treat an SDK bump as an API upgrade.
  6. Re-check signature and retry code. Version moves can change payloads and headers that webhook signature verification and idempotent retries depend on.

For the version each provider currently ships and any retirement dates, see the tracker entries for Stripe, Square, Adyen and PayPal.

Sources

Documentation reviewed October 10, 2026.