Research & Analysis · Software engineering · 18 min read

How Do You Retire a Public API Endpoint Safely?

Retire an API endpoint when necessary consumers have a demonstrated replacement or an explicitly accepted loss of service, admitted work has a defined outcome, and the old capability can no longer be invoked through the routes you claim to have closed. A deprecation announcement, a quiet access log or a successful deployment is not enough by itself. You also need to know what happens to retries, cached responses, alternate addresses and data written under the old contract.

I would treat retirement as a change in permitted behaviour, not a deletion task. The useful question is whether a particular operation can stop being available under a particular contract, for a defined population, while preserving the business obligations that survive it. That framing makes the evidence more demanding, but also makes the decision easier to explain.

This paper develops a proposed acceptance method from twelve primary specifications and authoritative technical documents. It is technical synthesis, not an empirical migration study. Provider examples illustrate different mechanisms; they do not establish a universal notice period, version policy or safe observation window. The worked scenario is explicitly fictional, and no live endpoint was changed or tested.

Define the capability that is ending

Start with a retirement statement precise enough to test. “Remove v1” is incomplete if v1 contains read operations, irreversible writes, asynchronous jobs and a shared authentication mechanism. Identify the operation, accepted methods, version-selection rule, consumer population and intended terminal behaviour. Decide whether the business capability disappears, moves to a successor, or remains available only to a smaller authorized population.

These choices produce different acceptance criteria. Moving a read operation may require equivalent selection, pagination and data interpretation. Ending a write operation requires deciding whether previously accepted writes finish, are cancelled or need individual resolution. Removing one public route while keeping the same backend for an internal workflow requires proof about the remaining authorization boundary, not a claim that the backend no longer exists.

OWASP's API inventory guidance calls for accounting for hosts, environments, versions, integrated services and sensitive data flows. It also highlights the danger of protecting the current production interface while neglecting other exposed versions (OWASP API Security Project, 2023). I would use that scope to build the retirement boundary. A list of documented paths is only one input; it does not establish that undocumented deployments or older integrations are absent.

Assign ownership to each decision that survives the shutdown: who accepts a client's unresolved migration, who owns outstanding work, who authorizes a temporary exception and who controls the remaining infrastructure. These can be responsibilities held by the same person in a small team. The important point is that deleting the application must not delete accountability for its unfinished obligations.

Identify the version the caller actually receives

A version is selected through runtime behaviour, not merely through the name printed in documentation. Selection may depend on a path, request header, query parameter or default. Record the effective selection for each relevant route, including what happens when the identifier is missing, malformed or no longer supported. An unversioned request deserves its own test case rather than being silently counted as use of the newest API.

Google's AIP-185 describes its major-version conventions and requires different API versions to coexist for a reasonable transition period, with a communicated deprecation period before an older version is shut down. These are Google API design rules, not universal HTTP requirements (Google, n.d.-a). Their value here is the distinction between providing a successor and proving that consumers have actually selected it. A new version existing beside the old one does not move a client's traffic.

Azure API Management makes a different boundary visible. Its documentation permits path, query-string or header version selection and explains that adding versioning to an existing non-versioned API automatically creates an Original version that continues responding at the default URL (Microsoft, 2026). That compatibility feature can be useful during migration. It also means that reviewing only explicitly numbered versions can miss the interface existing callers still use.

I would construct a small routing matrix from actual configuration: entry point, supplied version selector, effective operation, authentication policy and backend. Include explicit old and new selectors, absent selectors and supported aliases. The matrix is a proposed review artifact, not an assumption that every product exposes the same controls. Its purpose is to prevent an apparently complete migration from leaving a default route that still invokes the retired behaviour.

Establish consumer coverage before interpreting silence

Access records describe observed requests within their collection boundary. They do not enumerate every consumer that might need the service later. A quarterly report generator, a disaster-recovery process and a temporarily offline partner may remain legitimate dependencies despite an uneventful week. Choose an observation period by examining those operating cycles and the consequence of missing them, not by selecting an arbitrary number that makes the graph reach zero.

Build the consumer inventory from more than traffic. Reconcile registered integrations, accountable owners, issued application identities, SDK configurations, scheduled jobs and the relevant operational documentation. These are proposed evidence sources, not a claim that every organization has reliable copies of them. Where a shared credential serves several clients, one identity in a log does not prove that only one dependency exists. Record that ambiguity until it is resolved or explicitly accepted.

Kubernetes provides an instructive implementation example: its deprecation policy describes response warnings, an audit annotation and a metric indicating requests to deprecated APIs (Kubernetes Authors, n.d.). Those signals can help locate dependencies, but they remain observations from the instrumented system. A metric showing that a deprecated interface was requested is different from a complete list of callers, their owners and their migration status. Instrumentation is an input to the decision, not a substitute for the inventory.

For each known consumer, record a concrete disposition: replacement behaviour demonstrated, service no longer needed and approved for removal, bounded exception, or unresolved. Require evidence appropriate to the task. An owner saying “we upgraded” may need confirmation from an actual representative transaction and effective version selection. A consumer with no owner is an unresolved dependency; it does not become disposable simply because the communication channel is inconvenient.

The inventory also needs a change rule during the transition. If new clients can adopt the deprecated interface while existing clients migrate away, a steadily falling traffic total can conceal newly created dependencies. Decide whether new registrations are prohibited, require an exception or remain supported under a separate promise. Record the inventory date and subsequent additions, ownership transfers and removals. A consumer disposition should refer to an identifiable deployed integration and workflow, not just to a company name that may operate several independent clients. This keeps the acceptance population stable enough to review without pretending that production stops changing while the team prepares its shutdown.

Communicate deprecation without pretending it performs migration

Deprecation and shutdown are different events. RFC 9745 defines a Deprecation response field that communicates a resource's lifecycle status without itself changing the resource's behaviour. It also defines a link relation for supporting information. Its date uses Structured Fields syntax, and an accompanying Sunset date must not precede the deprecation date (Dalal & Wilde, 2025, §§2–5). Implement the chosen contract accurately rather than treating any vaguely date-shaped header as equivalent.

RFC 8594 describes Sunset as an indication that a resource is expected to become unavailable. Its normal scope is the responding resource; broader scope must be explained. The document also separates sunset information from cache-control semantics (Wilde, 2019, §§3–5). A header on one discovery page therefore does not prove every client saw a notice covering every operation, and a sunset date does not automatically expire every stored response.

Provider contracts can differ from the newer general specification. The GitHub API-version documentation inspected for this paper describes Deprecation using an HTTP-date representation, whereas RFC 9745 specifies a Structured Fields date. GitHub also documents explicit version selection and changed default selection when an old version leaves support (GitHub, n.d.). A consumer should interpret the provider's actual documented and observed contract; a provider implementing RFC 9745 should not copy an incompatible example without noticing the difference. This is a documentation comparison, not a live interoperability finding.

The human migration notice should identify the affected operation, the replacement task, behavioural differences, required client action, proposed shutdown and a route for unresolved cases. Deliver it through channels connected to accountable consumers as well as runtime signals. Record acknowledgement separately from completion. A delivered message proves communication activity; it does not establish that a deployed integration stopped depending on the old contract.

Test the business contract, not just the new URL

Changing a base URL or version header can make a request syntactically valid while changing its meaning. Compare the work the consumer needs to complete: resource selection, defaults, ordering, pagination, error interpretation, authorization and durable effects. The new endpoint's success response should not be accepted as proof that the consumer received an equivalent result. Define which differences are intended and which would invalidate migration.

AIP-180 distinguishes source, wire and semantic compatibility, and its guidance includes changes to defaults, field formats and serialization that can break consumers even where the operation still exists (Google, n.d.-b). These categories provide a useful way to organize a migration test. They do not remove the need to examine application-specific assumptions, and the document's protobuf/JSON context should not be generalized blindly to every transport.

I would select representative business cases and deliberately include awkward states: empty results, partially populated objects, large collections, permission changes and retries after an uncertain outcome. Use controlled test data with expected results. For a list operation, verify that the client traverses the complete intended collection rather than interpreting the first successful page as the whole export. For a write, verify the resulting state and the absence of an unintended second effect.

A compatibility adapter is a design choice with continuing costs. It may translate an old request into the new backend contract, but then it becomes an interface that needs ownership, authorization, monitoring and its own retirement condition. Calling it a temporary shim does not establish that it is temporary. If the adapter cannot preserve a necessary distinction, record that incompatibility and resolve the consumer's workflow instead of silently mapping two different business operations onto one convenient response.

Drain admitted work without creating a second execution path

Stopping new requests does not decide the fate of requests already accepted. Define an admission boundary and distinguish work accepted before that boundary from work rejected afterward. For an asynchronous operation, an earlier acceptance may have created a job that runs after the public route closes. The shutdown record should state whether that work is allowed to complete, cancelled under an agreed rule or isolated for resolution.

A useful proposed model has separate states for accepting, draining and terminal service. In the accepting state, the old contract still admits authorized work. During draining, new work under that contract is refused while previously admitted obligations are reconciled. In the terminal state, the old operation cannot create new business effects; any retained response exists only to communicate the retirement. These are acceptance states, not a requirement for three particular deployment environments.

Retries make the boundary harder. A client may not know whether its final old-version request succeeded and may retry against the successor. If the two paths use unrelated operation identifiers, they can produce two effects even though each path independently deduplicates requests. Decide whether a stable business-operation identity can cross the migration boundary and how the destination determines the earlier outcome. If that evidence is unavailable, designate an uncertain state and a resolution owner instead of inventing an exactly-once guarantee.

Do not mirror production write traffic to a successor merely to demonstrate parity unless the mirror is explicitly designed not to repeat external effects. A copied request can create a second shipment, message or entitlement. Controlled comparisons, read-only observations and isolated test executions answer different questions; label them accordingly. Acceptance requires the intended outcome and accountable handling of uncertainty, not an attractive count of requests successfully replayed against the new service.

Choose terminal responses deliberately

The terminal response should match the service's documented end state. RFC 9110 defines 410 Gone for access that is no longer available and likely permanently unavailable; it recommends 404 when permanence is unknown. It also defines redirect semantics, including method preservation for 307, and notes that 308 and 410 can be heuristically cached (Fielding et al., 2022b, §§15.4.8–15.4.9, 15.5.11). Those semantics do not make a redirect a universal API migration mechanism.

A redirect can change the destination without translating the request's business meaning. A client might not follow it, might apply different credential-forwarding rules, or might reach a successor that interprets the original payload differently. Verify the actual client behaviour and destination authorization before relying on redirection. For an incompatible write, a clear terminal response plus a documented migration procedure may be more defensible than automatic forwarding that appears successful while changing the operation.

RFC 9457 supplies a machine-readable problem format and requires a generated status member to agree with the actual HTTP status. It also warns against exposing sensitive implementation details through error information (Nottingham et al., 2023, §§3.1.2, 5). An API retirement response can use that format to provide a stable problem type and appropriate migration information. It should not reveal internal backend addresses, customer identifiers or debugging material simply because the old business function has ended.

Keep the response path operationally small and subject to ordinary abuse controls. A retained explanatory endpoint can help legitimate consumers diagnose a failure, but it is still a maintained service. Decide who owns it, what it logs and when it may be removed. Avoid claiming that the entire endpoint has disappeared while a sizeable compatibility application remains reachable behind the retirement message.

Account for caches before promising a cutoff time

An origin-side shutdown does not prove that every observer immediately sees the terminal response. A stored successful response can remain reusable under its existing policy. Conversely, a cached permanent redirect or terminal error can survive a later rollback. The retirement design needs to examine the actual cache chain: client, intermediary, gateway and any application-level storage relevant to the operation.

RFC 9111 distinguishes freshness, validation and permitted stale reuse. It constrains when heuristics may determine freshness and when a cache may serve stale responses (Fielding et al., 2022a, §§4.2–4.3). These rules are more precise than “the CDN will update.” They also do not establish which caches exist in your deployment, whether their configuration follows the intended contract or whether an application stores derived results outside HTTP caching.

I would specify the desired behaviour separately for old successful responses and the new terminal response. Determine which stored objects can be invalidated, which must age out, and which remain outside the provider's control. A newly emitted cache directive does not retroactively rewrite a response already held elsewhere. Where the claim is that sensitive data is no longer obtainable, distinguish stopping future origin access from recalling data already delivered to a client. Endpoint retirement cannot promise the latter.

Test both previously used and fresh client contexts, while recording the returned status, cache evidence and observation point. If rollback is part of the plan, test how the client recovers from a cached terminal result rather than only whether the backend starts again. Make any bounded delay an explicit part of the acceptance statement. A single successful request through a clean diagnostic client cannot establish the behaviour of a warmed production population.

Close alternate routes and remove only the authority that is obsolete

The advertised hostname may be only one route to the old operation. Reconcile custom domains, provider-generated addresses, regional or stage-specific routes, gateway mappings and directly reachable backends within the authorized inventory. Check both public admission and the credentials or service roles that could still invoke the operation through another channel. Restrict the review to owned systems; unexplained external assets are investigation leads, not authorization to probe them.

Amazon API Gateway's HTTP API documentation illustrates why the entry-point inventory matters. Its generated execute-api endpoint is enabled by default, and disabling it affects all stages; whether deployment is required depends on automatic-deployment settings (Amazon Web Services, n.d.). Removing a custom domain therefore does not by itself establish the state of that other entry point. This is a specific product example, not a claim that every gateway has the same configuration model.

Use an acceptance matrix linking each known route to its intended result and an observation of the business effect. A front-door denial must not be confused with removal of a direct backend path. Equally, revoking a credential shared by the successor can cause an avoidable outage. Identify which authority is exclusive to the old interface, which is still needed and which should be narrowed. Reusing the same backend makes that distinction more important, not less.

The existing API object-authorization analysis addresses who may act on a resource. Retirement adds a lifecycle decision about whether an operation remains available at all. Where a hostname itself is being abandoned, use the hostname-retirement analysis for DNS, provider bindings and surviving trust references. Neither task is completed merely by removing an entry from the API documentation.

Make negative closure tests discriminate between a retired operation and an unrelated rejection. An expired credential or malformed request can fail before reaching a handler that remains fully operational for other callers. In an authorized harness, use controlled requests that previously exercised the intended operation, with known identity and input conditions, and verify that retirement now prevents the business effect. Pair that result with a legitimate successor transaction so that a general gateway outage is not misreported as a successful selective shutdown. This is a proposed test design, not permission to reuse customer credentials or issue production writes for this paper.

Separate stored-data compatibility from serving the old interface

Removing a public API version does not necessarily remove data encoded under that version. A successor may still need to decode historical objects, read audit records or complete earlier jobs. Inventory those obligations before deleting conversion code or schema support. The storage reader's continued existence need not imply that an external caller can still invoke the old write contract, but the separation must be implemented and tested.

Kubernetes' deprecation policy explicitly distinguishes stopping service of REST endpoints from remaining able to decode or convert previously persisted data, with stated qualifications in its storage-version rules (Kubernetes Authors, n.d.). That is a useful example of why API retirement and storage migration are separate changes. It is not a claim that your database uses Kubernetes' representation rules or inherits its deprecation schedule.

Rollback also depends on data compatibility. If the successor writes values the old implementation cannot interpret, restoring the old binary may not restore a working service. Define the supported rollback state, including data representations, configuration and authorization, before approving migration writes. A backup existing somewhere is not proof that rollback meets the service's recovery objective or preserves work accepted after that backup.

Sometimes the correct decision is forward repair rather than rollback. If old behaviour is insecure, reopening it may recreate the reason for retirement. Specify who can authorize a restricted exception, which consumers it covers, how long it can last and what evidence ends it. Preserve operational records needed to diagnose migration failures while applying the appropriate retention and privacy rules; an API shutdown is not blanket authorization to delete or indefinitely retain every associated record.

Work through a fictional retirement decision

Consider a fictional logistics service, Alder Dispatch, replacing an old shipment-creation endpoint. Stipulate twenty registered integrations: sixteen demonstrate the replacement workflow, two execute quarterly and have not yet been exercised, one now needs only read access, and one has an approved end-of-service disposition. These are invented inputs for reasoning, not measured migration results. The team must not report twenty migrated clients or equate the absence of quarterly traffic with proof those jobs are unnecessary.

The sixteen demonstrations support their tested workflows. The two quarterly integrations remain unresolved until representative execution or an explicit business decision addresses them. The read-only consumer needs a supported read path and evidence that its old write authority is no longer required. The discontinued integration needs its route and credential disposition recorded. Each category has a different acceptance action, so one overall request-volume percentage would obscure rather than explain readiness.

Now stipulate three jobs admitted before the cutoff. Two have recorded completion, while the third has an uncertain external result. Deleting its queue record would remove the evidence needed to decide whether retrying creates a duplicate shipment. The proposed decision is to reconcile or individually resolve that outcome before declaring admitted work closed. This does not require the entire old API to keep accepting new work; it requires a maintained recovery path for that obligation.

Finally, suppose the custom route returns the intended terminal response but a provider-generated route still reaches the old handler. The capability is not retired within the claimed boundary. The responsible owner must close or explicitly exclude that route, then verify the resulting behaviour. The example demonstrates why consumer migration, work reconciliation and capability closure are separate propositions: evidence for any two does not establish the third.

Make the remaining uncertainty a decision, not a hidden dependency

A long transition is not always safer. A vulnerable legacy operation may need rapid restriction while some consumers remain unready. Conversely, keeping a low-risk compatibility interface briefly may be justified when removal would interrupt an essential workflow. Compare those consequences explicitly and record the scope of any exception. There is no universally correct overlap period derivable from the cited specifications.

The proposed method also has limits. Inventory can be incomplete, logs can miss requests, clients can change after testing and documentation can differ from deployed behaviour. The twelve sources establish component semantics and examples, not the correctness of this combined acceptance method in a particular organization. No empirical estimate of outage probability or security improvement is claimed. A team with only a few controlled consumers can use a smaller evidence record, provided it still answers the necessary questions.

I would approve closure against three linked claims: necessary consumers have an accepted disposition; admitted obligations have known outcomes or named residual cases; and the retired capability is unavailable across the stated access boundary. Keep the routing evidence, migration cases, terminal behaviour, data-compatibility decision and exception owners together. If your shutdown plan stops at “send notice, then delete the route,” request an API retirement review. Bring one sanitized API contract and its consumer map; the work is to identify what must be demonstrated before that endpoint can safely stop doing business.

References