Start with the policy pipeline

Almost everything APIM does is a policy, and every policy runs in one of four scopes: inbound, backend, outbound, or on-error. Policies also inherit — global, then product, then API, then operation — and <base /> is where the inherited policy gets spliced in.

Getting that inheritance wrong is the most common APIM problem I see. A policy added at the API level that omits <base /> silently drops every global policy for that API, including the ones doing authentication.

Rule of thumb: put cross-cutting concerns (correlation IDs, security headers, global throttles) at the global scope, product-specific limits at the product scope, and nothing at operation scope unless that single operation genuinely differs.

Keep business logic out of the gateway

A policy can transform payloads, branch on content and call other services. That does not make it a good place for domain rules. Logic in a policy has no unit tests, no type checking, no local debugger and no code review culture around it.

Use policies for concerns that are genuinely about the edge: authentication, throttling, routing, shaping, caching, observability. Anything that would need a business analyst to explain belongs in a service.

Use it when you catch yourself writing a third conditional in a policy — that is the signal to move it.
Version with revisions and versions, deliberately

APIM separates the two on purpose. Revisions are non-breaking changes you can stage and swap without consumers noticing. Versions are breaking changes that consumers opt into, exposed by path, query string or header.

Pick one versioning scheme for the whole estate and stick to it. Mixed schemes mean every consumer integration starts with a conversation about where the version goes.

Use it when you have more than one consumer you cannot deploy in lockstep with.

Authentication and authorisation at the edge

The gateway is the right place to reject a request that was never going to be valid. It is the wrong place to make fine-grained authorisation decisions that depend on domain state.

validate-jwt, with the checks actually filled in

<validate-jwt> is only as good as its configuration. Set openid-config so signing keys rotate automatically, and populate required-claims — audience and issuer at minimum. A token that is validly signed by the right tenant but issued for a different audience is not a token for you.

Validate at the gateway to shed obviously bad traffic, then validate again in the service. The gateway is not the only way in, and a service that trusts its caller blindly is one network mistake from being wide open.

Use it when always, for any API that is not genuinely public.
Subscription keys are identification, not authentication

A subscription key tells you which consumer is calling so you can meter and throttle them. It is a bearer string that ends up in scripts, Postman collections and support tickets.

Treat it as a product-management primitive, not a security boundary. If the answer to “what if this key leaks” is worse than “we rotate it and someone loses some quota”, you need real authentication as well.

Use it when you need per-consumer quotas and analytics — which is most of the time.
Managed identity to the backend

APIM can authenticate to backends with its own managed identity via <authentication-managed-identity>. That removes a stored credential from the picture entirely.

Where the backend cannot take a token, keep the secret in Key Vault and reference it as a named value rather than pasting it into a policy.

Use it when the backend is an Azure resource or anything that accepts Entra tokens.
Named values, backed by Key Vault

Named values are APIM's configuration mechanism. Mark anything sensitive as secret and, better, back it with a Key Vault reference so rotation happens in one place and APIM picks it up.

Plain named values are visible to anyone with reader access on the APIM instance. That is a wider audience than people assume.

Use it when a policy needs any value that differs per environment.

Traffic management

Rate limiting protects your backend. Quotas protect your commercial model. They are different tools and people routinely reach for the wrong one.

rate-limit-by-key for burst, quota-by-key for volume

rate-limit-by-key works over a short window (seconds) and exists to stop a burst flattening your backend. quota-by-key works over a long window (days or months) and exists to enforce what a customer bought.

Key both on something meaningful — subscription id, or a claim from the JWT. Keying on IP address gives you a limit shared by everyone behind a corporate NAT.

Use it when you have any backend that can be overwhelmed, which is all of them.
Return 429 with Retry-After, and mean it

A throttled response without Retry-After teaches clients to retry immediately, which turns a throttle into a retry storm. APIM sets it for you on the built-in policies — do not strip it in an outbound policy.

Document the limits in the developer portal. Clients that know the limit generally respect it; clients that discover it by being cut off generally complain.

Use it when always — it costs nothing and prevents a self-inflicted outage.
Circuit-break with backend retry policy

APIM's backend entity supports a circuit breaker: trip after N failures in a window, stay open for a period, then probe. That stops the gateway hammering a backend that is already down.

Pair it with a sensible <retry> — short, bounded, and only on idempotent operations. Retrying a POST that partially succeeded is how you end up with duplicate orders.

Use it when a backend has any history of transient failure, or is shared.
Cache responses that deserve it

<cache-lookup> and <cache-store> can serve repeat reads from the gateway without touching the backend. Vary by the headers and query parameters that actually change the response, or you will serve one customer's data to another.

Reference data, lookup lists and configuration endpoints are the obvious wins. Anything personalised needs vary-by on the identity, at which point the hit rate often collapses and the cache is not worth it.

Use it when the same read is served repeatedly and staleness of a few minutes is acceptable.

Topology and operations

Self-hosted gateway for data residency and latency

The self-hosted gateway runs the APIM data plane as a container in your own environment — on-premise, another cloud, or a Kubernetes cluster — while the control plane stays in Azure.

It solves two real problems: traffic that legally cannot leave a jurisdiction, and a gateway hop that would otherwise cross a region for no reason. It also adds an upgrade responsibility you now own.

Use it when the backend cannot be reached from Azure, or residency rules forbid the round trip.
Treat APIM configuration as code

Policies, products, named values and API definitions all belong in source control and a pipeline, not in the portal. The portal is for looking, not for changing production.

Export the configuration, review changes in a pull request, and deploy them like any other artefact. It also gives you the diff when someone asks why a limit changed.

Use it when more than one person can touch the instance.
Wire observability up before you need it

Send gateway logs and metrics to Application Insights or Log Analytics, and add a correlation id in an inbound policy if the caller did not supply one. Without it, tracing a request from consumer to backend is guesswork.

Be careful what you log. Request and response bodies are the most useful thing to have and the most dangerous — they routinely contain personal data.

Use it when before go-live, not after the first incident.

Where to read more