---
title: "Migrating from Azure API Management to Zuplo: Policy Mapping, Architecture Translation, and Step-by-Step Guide"
description: "A practical guide to migrating from Azure API Management to Zuplo — covering XML policy mapping, architecture translation, and a phased migration plan."
canonicalUrl: "https://zuplo.com/learning-center/migrating-from-azure-api-management-to-zuplo"
pageType: "learning-center"
authors: "nate"
tags: "API Gateway, API Best Practices"
image: "https://zuplo.com/og?text=Migrating%20from%20Azure%20API%20Management%20to%20Zuplo"
---
Azure API Management is a natural choice for teams already invested in the
Microsoft Azure ecosystem. But as API programs grow, the operational overhead of
managing APIM instances, navigating XML-based policy expressions, and dealing
with complex tier-based pricing becomes a real burden. If your team is
evaluating a move to a modern, edge-native API gateway, this guide walks you
through every step of migrating from Azure API Management to Zuplo.

## What This Guide Covers

1. [Why Migrate from Azure API Management?](#why-migrate-from-azure-api-management)
2. [Architecture Comparison](#architecture-comparison)
3. [Policy Mapping Reference](#policy-mapping-reference)
4. [Translating Azure APIM Configuration to Zuplo](#translating-azure-apim-configuration-to-zuplo)
5. [Authentication Migration](#authentication-migration)
6. [Developer Portal Migration](#developer-portal-migration)
7. [Step-by-Step Migration Playbook](#step-by-step-migration-playbook)
8. [Post-Migration Benefits](#post-migration-benefits)
9. [Frequently Asked Questions](#frequently-asked-questions)
10. [Next Steps](#next-steps)

---

## Why Migrate from Azure API Management?

Azure APIM is a capable platform, but several pain points drive teams to explore
alternatives:

- **Complex, tier-based pricing** — Azure APIM's pricing model spans Developer
  (~$30/month), Basic v2 (~$150/month), Standard v2 (~$700/month), and Premium
  v2 (~$2,800/month) tiers. Each environment (dev, staging, production) requires
  its own instance, and production workloads often need the Standard or Premium
  tier for features like VNET integration. Costs escalate quickly in
  multi-environment setups.
- **Slow deployment cycles** — Azure APIM deployments can take 15–45 minutes to
  propagate. Creating a new APIM instance can take 30–60 minutes. This creates
  painful feedback loops during development and testing.
- **XML policy complexity** — Azure APIM policies are written in XML with inline
  C# expressions. The syntax is verbose, error-prone, and difficult to test or
  debug outside the Azure portal. Version control is awkward because XML policy
  definitions don't merge cleanly in Git.
- **Azure lock-in** — APIM is tightly integrated with the Azure ecosystem. Using
  it with non-Azure backends or in multi-cloud architectures adds friction —
  VNET peering, private endpoints, and Azure AD integration all assume you're
  running on Azure.
- **Limited GitOps support** — While Azure APIM supports ARM templates, Bicep,
  and Terraform for infrastructure-as-code, the policy definitions remain
  embedded XML that doesn't fit naturally into a Git-based development workflow.
  There's no native "push to Git, deploy to gateway" experience.
- **Regional deployment model** — Azure APIM instances are deployed to specific
  Azure regions. Multi-region deployment requires the Premium tier and manual
  configuration. There's no built-in global edge distribution.

## Architecture Comparison

Understanding the architectural differences helps you plan a clean migration.

### Azure APIM's Architecture

Azure API Management uses a **gateway + management plane** model. A typical
setup involves:

- An **APIM instance** deployed to one or more Azure regions
- **API definitions** stored as OpenAPI specs with XML policy overlays
- **Products** that bundle APIs with subscription keys and usage quotas
- **Policies** written in XML with C# expressions, applied at global, product,
  API, or operation scope
- A **developer portal** deployed alongside the APIM instance
- **VNET integration** (Standard or Premium tier) for private backend
  connectivity
- **ARM templates or Bicep** for infrastructure-as-code deployment

### Zuplo's Architecture

Zuplo takes a fundamentally different approach:

- **Edge-native deployment** — Your gateway runs across 300+ data centers
  worldwide automatically. There's no single-region deployment to manage or
  scale.
- **OpenAPI-native routing** — Routes are defined in a standard
  `routes.oas.json` file using the OpenAPI specification format, extended with
  `x-zuplo-route` for gateway behavior.
- **TypeScript policies** — Instead of XML with C# expressions, you write
  policies in TypeScript with full IDE support, type safety, and access to npm
  packages.
- **Git-native CI/CD** — Every configuration change is version-controlled. Push
  to GitHub, and your gateway deploys to the edge automatically. Every branch
  gets its own isolated preview environment.
- **Fully managed** — No instances to provision, no VNET complexity, no capacity
  planning. Zuplo handles all infrastructure, scaling, and updates.
- **Built-in developer portal** —
  [Auto-generated from your OpenAPI spec](https://zuplo.com/docs/dev-portal/introduction),
  with API key management and an API explorer included.

## Policy Mapping Reference

One of the biggest questions during migration is: "What replaces my Azure APIM
policies?" Below is a mapping of Azure APIM's most common policies to their
Zuplo equivalents.

### Authentication and Authorization

- **validate-jwt** →
  [JWT Auth Policy (OpenID)](https://zuplo.com/docs/policies/open-id-jwt-auth-inbound)
  — Validates JWT tokens from any OpenID-compliant provider, including Microsoft
  Entra ID, Auth0, Okta, and others. Zuplo also has
  [provider-specific JWT policies](https://zuplo.com/docs/policies/auth0-jwt-auth-inbound)
  for Auth0, Cognito, Clerk, Supabase, Firebase, and more.
- **authentication-basic** →
  [Basic Auth Policy](https://zuplo.com/docs/policies/basic-auth-inbound) —
  Built-in support for HTTP Basic authentication.
- **Subscription keys** →
  [API Key Authentication](https://zuplo.com/docs/policies/api-key-inbound) —
  Zuplo's managed API key service replaces Azure APIM subscription keys with
  globally distributed validation, self-serve key management through the
  developer portal, and consumer metadata.

### Traffic Control

- **rate-limit / rate-limit-by-key** →
  [Rate Limiting Policy](https://zuplo.com/docs/policies/rate-limit-inbound) —
  Supports per-user, per-IP, per-API-key, or custom attribute-based rate limits.
  Unlike Azure APIM's regional rate limiting, Zuplo's rate limiter is globally
  distributed across all edge locations. No Redis or separate infrastructure
  required.
- **quota / quota-by-key** →
  [Quota Policy](https://zuplo.com/docs/policies/quota-inbound) — Enforces
  long-term usage quotas per consumer.
- **ip-filter** →
  [IP Restriction Policy](https://zuplo.com/docs/policies/ip-restriction-inbound)
  — Allow/deny lists with CIDR range support. For advanced scenarios, write
  custom IP filtering logic using a
  [Custom Code Policy](https://zuplo.com/docs/policies/custom-code-inbound).
- **cors** →
  [Built-in CORS Configuration](https://zuplo.com/docs/programmable-api/custom-cors-policy)
  — Configure allowed origins, methods, and headers per route. Supports wildcard
  subdomains and environment variables.

### Request and Response Transformation

- **set-header** →
  [Set Headers Policy](https://zuplo.com/docs/policies/set-headers-inbound) —
  Add or overwrite request headers before forwarding to your backend.
- **remove-header** →
  [Remove Headers Policy](https://zuplo.com/docs/policies/remove-headers-inbound)
  — Strip headers from requests or responses.
- **set-body** →
  [Set Body Policy](https://zuplo.com/docs/policies/set-body-inbound) — Replace
  the request or response body.
- **set-query-parameter** →
  [Set Query Params Policy](https://zuplo.com/docs/policies/set-query-params-inbound)
  — Add or modify query parameters.
- **rewrite-uri** →
  [URL Rewrite Handler](https://zuplo.com/docs/handlers/url-rewrite) — Rewrite
  incoming request URLs using template interpolation with access to path
  parameters, query strings, headers, and environment variables.
- **find-and-replace** →
  [Replace String Policy](https://zuplo.com/docs/policies/replace-string-outbound)
  — Find and replace content in response bodies.
- **xml-to-json / json-to-xml** →
  [XML to JSON Policy](https://zuplo.com/docs/policies/xml-to-json-outbound) —
  Convert between XML and JSON formats, especially useful for modernizing legacy
  SOAP backends.

### Security and Validation

- **check-header** →
  [Custom Code Policy](https://zuplo.com/docs/policies/custom-code-inbound) —
  Write a TypeScript function to validate any header, returning an error
  response if the check fails.
- **validate-content** →
  [Request Validation Policy](https://zuplo.com/docs/policies/request-validation-inbound)
  — Validates request bodies, query parameters, path parameters, and headers
  against your OpenAPI schema definitions automatically.

### Caching and Performance

- **cache-lookup / cache-store** → Zuplo supports
  [caching policies](https://zuplo.com/docs/policies/caching-inbound) for
  response caching at the edge.

### Custom Logic

- **C# policy expressions** →
  [Custom Code Policy](https://zuplo.com/docs/policies/custom-code-inbound) —
  Any inline C# expression or complex policy logic translates to a TypeScript
  function. You get full IDE support, type safety, npm package access, and
  standard Web APIs (`Request`, `Response`, `Headers`, `fetch`).

## Translating Azure APIM Configuration to Zuplo

### XML Policies → JSON + TypeScript

In Azure APIM, policies are XML documents with inline C# expressions:

```xml
<policies>
  <inbound>
    <rate-limit-by-key
      calls="100"
      renewal-period="60"
      counter-key="@(context.Subscription.Id)" />
    <set-header name="X-Request-Id" exists-action="skip">
      <value>@(Guid.NewGuid().ToString())</value>
    </set-header>
  </inbound>
</policies>
```

In Zuplo, the equivalent is declarative JSON policies in `policies.json`:

```json
{
  "policies": [
    {
      "name": "rate-limit",
      "policyType": "rate-limit-inbound",
      "handler": {
        "export": "RateLimitInboundPolicy",
        "module": "$import(@zuplo/runtime)",
        "options": {
          "rateLimitBy": "user",
          "requestsAllowed": 100,
          "timeWindowMinutes": 1
        }
      }
    }
  ]
}
```

Key differences to note:

- **JSON instead of XML** — Zuplo policies are standard JSON, which merges
  cleanly in Git and is easy to lint and validate.
- **TypeScript instead of C#** — Custom logic is written in TypeScript with full
  IDE support, not embedded as inline expressions in XML attributes.
- **Everything in Git** — Both `routes.oas.json` and `policies.json` are files
  in your repository. There's no separate management plane or portal state to
  keep in sync.

### Azure APIM C# Expressions → Zuplo TypeScript

Azure APIM's inline C# expressions handle complex logic like conditional
routing, header manipulation, and error handling. In Zuplo, these become clean
TypeScript functions:

**Azure APIM (XML + C#):**

```xml
<policies>
  <inbound>
    <choose>
      <when condition="@(context.Request.Headers
        .GetValueOrDefault("Authorization","")
        .Length == 0)">
        <return-response>
          <set-status code="401" reason="Unauthorized" />
          <set-body>{"error": "Missing authorization"}</set-body>
        </return-response>
      </when>
    </choose>
  </inbound>
</policies>
```

**Zuplo (TypeScript):**

```typescript
import { ZuploContext, ZuploRequest, HttpProblems } from "@zuplo/runtime";

export default async function (
  request: ZuploRequest,
  context: ZuploContext,
  options: never,
  policyName: string,
) {
  const authHeader = request.headers.get("authorization");

  if (!authHeader) {
    return HttpProblems.unauthorized(request, context, {
      detail: "Missing authorization",
    });
  }

  return request;
}
```

The TypeScript version is easier to read, easier to test, and provides full type
checking in your IDE.

### Named Values → Environment Variables

Azure APIM's named values (plain, secret, and Key Vault references) map directly
to Zuplo environment variables:

- **Plain named values** → Standard environment variables, referenced as
  `$env(VARIABLE_NAME)` in route configuration or `environment.VARIABLE_NAME` in
  TypeScript code.
- **Secret named values and Key Vault references** → Secret environment
  variables in Zuplo, which are encrypted at rest and never exposed in logs or
  the UI after creation.

### Concept Mapping Reference

Here is a quick reference for how Azure APIM concepts translate to Zuplo:

- **API** → Routes in your OpenAPI spec (`routes.oas.json`)
- **Operation** → Route (path + method) in your OpenAPI spec
- **Backend** → URL Forward or URL Rewrite handler target
- **Inbound policy (XML)** → Inbound policy (TypeScript/JSON)
- **Outbound policy (XML)** → Outbound policy (TypeScript/JSON)
- **Named value** → Environment variable
- **Subscription key** → API key authentication
- **Product** → API key with consumer metadata
- **APIM instance** → Zuplo environment (automatically provisioned)
- **Developer portal** →
  [Zuplo Developer Portal](https://zuplo.com/docs/dev-portal/introduction)
  (auto-generated from your OpenAPI spec)
- **Application Insights** → Logging integrations (Datadog, Splunk,
  OpenTelemetry, and others)
- **API revision** → Git branch with
  [branch-based deployment](https://zuplo.com/docs/articles/branch-based-deployments)
- **Self-hosted gateway** →
  [Self-hosted Zuplo](https://zuplo.com/docs/self-hosted/overview)

## Authentication Migration

Authentication is typically the most critical part of any gateway migration.
Here's how to approach each Azure APIM authentication mechanism.

### Azure AD (Entra ID) JWT Validation

If you're using Azure APIM's `validate-jwt` policy with Microsoft Entra ID
(formerly Azure AD), migration is straightforward. Configure Zuplo's
[JWT Auth Policy](https://zuplo.com/docs/policies/open-id-jwt-auth-inbound) with
your Entra ID tenant details:

```json
{
  "name": "entra-id-auth",
  "policyType": "open-id-jwt-auth-inbound",
  "handler": {
    "export": "OpenIdJwtInboundPolicy",
    "module": "$import(@zuplo/runtime)",
    "options": {
      "issuer": "https://login.microsoftonline.com/{tenant-id}/v2.0",
      "audience": "api://your-app-id",
      "jwkUrl": "https://login.microsoftonline.com/{tenant-id}/discovery/v2.0/keys"
    }
  }
}
```

Zuplo validates the JWT signature, expiration, issuer, and audience at the edge.
The authenticated user's claims are available in your policies and handlers via
`request.user`.

### Subscription Keys → Zuplo API Keys

Azure APIM subscription keys are tied to products and require manual management
through the Azure portal or ARM templates. Zuplo's
[API Key Authentication policy](https://zuplo.com/docs/policies/api-key-inbound)
provides a more developer-friendly approach:

1. Add the `api-key-inbound` policy to your routes
2. Create consumers with metadata (plan tier, organization, rate limit
   overrides) through the Zuplo Portal or
   [Developer API](https://zuplo.com/docs/articles/api-key-management)
3. Optionally enable the
   [Developer Portal](https://zuplo.com/docs/dev-portal/introduction) for
   self-serve key management

To migrate existing subscription keys, use Zuplo's Developer API to
programmatically create consumers and import keys, so existing client
integrations continue working without changes.

### OAuth 2.0 Flows

If Azure APIM is validating OAuth 2.0 bearer tokens from any identity provider,
Zuplo's JWT policies handle this directly. Your identity provider (Entra ID,
Auth0, Okta, etc.) continues to issue tokens — Zuplo validates them at the edge.
Provider-specific policies are available for
[Auth0](https://zuplo.com/docs/policies/auth0-jwt-auth-inbound),
[AWS Cognito](https://zuplo.com/docs/policies/cognito-jwt-auth-inbound),
[Clerk](https://zuplo.com/docs/policies/clerk-jwt-auth-inbound),
[Supabase](https://zuplo.com/docs/policies/supabase-jwt-auth-inbound),
[Firebase](https://zuplo.com/docs/policies/firebase-jwt-inbound), and
[Okta](https://zuplo.com/docs/policies/okta-jwt-auth-inbound).

## Developer Portal Migration

Azure APIM's built-in developer portal requires significant effort to customize
and maintain. It runs as a separate application alongside your APIM instance,
requires manual content management, and doesn't automatically stay in sync with
your API definitions.

Zuplo's developer portal is
[auto-generated from your OpenAPI spec](https://zuplo.com/docs/dev-portal/introduction).
When you update your routes in `routes.oas.json`, the portal updates
automatically. Features include:

- **Automatic API documentation** — generated directly from your OpenAPI
  specification, always in sync
- **API Explorer** — developers can test your API directly from the docs
- **Self-serve API key management** — consumers sign up, create keys, and manage
  them without admin intervention
- **Custom branding and pages** — add custom documentation pages with MDX
  (Markdown + JSX components)
- **Custom domains** — host your portal on `docs.yourcompany.com`

### Migration Steps

1. **Export your OpenAPI specs from Azure APIM** — use the Azure portal (APIs →
   Export → OpenAPI v3) or the Azure CLI
2. **Import into Zuplo** — your specs become both your gateway configuration and
   your portal documentation
3. **Add custom pages** — migrate any guides or tutorials from your Azure APIM
   portal to MDX custom pages
4. **Configure developer sign-in** — set up authentication using your identity
   provider
5. **Update DNS** — point your developer portal domain to Zuplo

The biggest win is eliminating the separate portal management overhead. No more
manually syncing documentation with your actual API behavior.

## Step-by-Step Migration Playbook

A typical Azure APIM-to-Zuplo migration follows four phases.

### Phase 1: Audit and Setup (Week 1)

**Inventory your Azure APIM setup:**

- List every API and its operations (routes)
- Document all policies at each scope (global, product, API, operation)
- Catalog named values and their types (plain, secret, Key Vault)
- Export OpenAPI specs for all APIs
- List all products, subscriptions, and their configurations
- Note any custom C# policy expressions

**Set up Zuplo:**

- Create a Zuplo account at [portal.zuplo.com](https://portal.zuplo.com)
- Connect your
  [GitHub repository](https://zuplo.com/docs/articles/source-control)
- Import your OpenAPI specs to bootstrap route configuration
- Configure authentication policies to match your Azure APIM auth setup
- Set up rate limiting and quota policies
- Add environment variables for backend URLs and secrets

### Phase 2: Policy Translation and Testing (Week 2)

- Translate XML policies to Zuplo JSON policies using the mapping reference
  above
- Rewrite C# policy expressions as TypeScript custom code policies
- Configure the developer portal
- Test all routes against your backend services
- Validate authentication flows end-to-end
- Run load tests to verify rate limiting behavior
- Review logging and observability configuration

### Phase 3: Parallel Running and Cutover (Week 3)

Run Zuplo alongside Azure APIM to validate behavior:

1. Deploy your Zuplo project by pushing to your Git repository
2. [Set up a custom domain](https://zuplo.com/docs/articles/custom-domains) for
   your Zuplo gateway
3. Route a subset of traffic to Zuplo using DNS-based routing or Azure Front
   Door
4. Compare response behavior between Azure APIM and Zuplo
5. Gradually increase traffic to Zuplo as you validate correctness
6. Complete the full cutover once you're confident

**DNS-based blue-green cutover:**

1. Lower your API domain's DNS TTL to 60 seconds (24 hours before cutover)
2. Switch the DNS record to point at Zuplo's edge network
3. Monitor error rates and latency
4. Roll back by reverting DNS if anything goes wrong

### Phase 4: Validation and Cleanup (Week 4)

After full cutover:

- Verify all routes return expected response codes
- Confirm authentication works for all methods (JWT, API keys)
- Validate rate limiting triggers at correct thresholds
- Check that logs flow to your monitoring system
- Update developer portal URLs and API documentation
- Decommission Azure APIM instances

### Rollback Strategy

Because Zuplo deployments are Git-based, rollback is straightforward:

- **Configuration rollback** — Revert the Git commit and push. The previous
  gateway configuration deploys automatically.
- **DNS rollback** — If you kept Azure APIM running during the parallel phase,
  switch DNS back to your APIM endpoints.

## Post-Migration Benefits

Once you've completed the migration, you'll immediately benefit from
capabilities that Azure APIM doesn't offer:

### Global Edge Performance

Your APIs now run at 300+ edge locations worldwide. Requests are automatically
routed to the nearest point of presence, reducing latency for users everywhere —
not just in the Azure regions where APIM was deployed. There's no Premium tier
upgrade needed for multi-region, no VNET configuration, and no capacity
planning.

### GitOps-Native Workflow

Every change to your API gateway is a Git commit. Open a pull request and get an
isolated preview environment automatically. Review policy changes in code
review, just like application code. Roll back by reverting a commit. If you're
new to this approach, see our overview of
[what GitOps is and why it matters](/learning-center/what-is-gitops). This is a
fundamentally better workflow than managing ARM templates, Bicep files, and XML
policy documents through Azure DevOps pipelines.

### TypeScript Programmability

Custom gateway logic is standard TypeScript — not XML with embedded C#
expressions. You get full IDE support with autocomplete and type checking,
access to the entire npm ecosystem, and the ability to unit test your policies
like any TypeScript function. Your team's existing TypeScript skills transfer
directly.

### Transparent Pricing

Instead of navigating Azure APIM's multi-tier pricing model where each
environment costs hundreds or thousands per month, Zuplo offers straightforward
[pricing](https://zuplo.com/pricing) with a free tier to get started. Preview
environments are free, and you don't pay per-instance for dev and staging.

### Faster Deployments

Azure APIM deployments take 15–45 minutes. Zuplo deployments complete in under
20 seconds to all 300+ edge locations. That's a significant improvement for
development velocity and incident response.

## Frequently Asked Questions

### Can I migrate from Azure APIM without changing my backend services?

Yes. Zuplo proxies requests to your existing backends — including Azure App
Service, Azure Functions, AKS, and Azure Container Apps — using the
[URL Rewrite](https://zuplo.com/docs/handlers/url-rewrite) or
[URL Forward](https://zuplo.com/docs/handlers/url-forward) handler. Your backend
services do not need any changes.

### How long does an Azure APIM migration typically take?

For most teams, a complete migration takes two to four weeks. Teams with
straightforward configurations (standard authentication, rate limiting, and
proxying) can often complete the migration in under a week. Complex setups with
many custom C# policy expressions may take longer.

### What replaces Azure APIM subscription keys in Zuplo?

Zuplo's built-in
[API key authentication policy](https://zuplo.com/docs/policies/api-key-inbound)
provides managed API key services. You can create consumers with metadata, issue
keys validated at the edge, and let developers self-manage keys through the
developer portal. Existing keys can be imported via the
[Zuplo Developer API](https://zuplo.com/docs/articles/api-key-management).

### Does Zuplo work with Azure AD (Entra ID) for authentication?

Yes. Zuplo's
[JWT Auth Policy](https://zuplo.com/docs/policies/open-id-jwt-auth-inbound)
validates tokens from any OpenID-compliant provider, including Microsoft Entra
ID. Configure the policy with your Entra ID issuer URL and audience, and Zuplo
validates tokens at the edge automatically.

### Can I run Zuplo alongside Azure APIM during migration?

Yes. The recommended approach is to run both gateways in parallel during the
cutover phase, using DNS or Azure Front Door to gradually shift traffic from
Azure APIM to Zuplo.

## Next Steps

**Ready to migrate?**
[Sign up for a free Zuplo account](https://portal.zuplo.com) and follow the
[Getting Started Guide](https://zuplo.com/docs/articles/step-1-setup-basic-gateway)
to set up your first gateway in minutes.

For planning your migration:

- [Azure API Management vs Zuplo](/learning-center/azure-api-management-vs-zuplo)
  — a detailed comparison of architecture, developer experience, pricing, and
  when to choose each platform.
- [Compare Zuplo and Azure API Management](https://zuplo.com/api-gateways/azure-api-management-alternative-zuplo)
  — see a feature-by-feature comparison at a glance.
- [Azure APIM's 2026 resource limits](/blog/azure-api-management-new-service-limits-migration-guide)
  — understand the new hard limits on API operations, products, and
  subscriptions that Microsoft began enforcing in March 2026.
- [Azure APIM Migration Guide (Zuplo Docs)](https://zuplo.com/docs/articles/migrate-from-azure-apim)
  — the technical reference for Azure APIM migration in the Zuplo documentation.
- [Policy Catalog](https://zuplo.com/docs/policies/overview) — browse all
  available built-in policies.
- [Custom Policies Documentation](https://zuplo.com/docs/policies/custom-code-inbound)
  — learn how to write your own TypeScript policies.
- [Best API Management Platforms for 2026](/learning-center/best-api-management-platforms-2026)
  — for a broader comparison of the API management landscape.
- [Migrating from Self-Hosted to Managed API Gateway](/learning-center/migrate-self-hosted-to-managed-api-gateway)
  — for general migration strategies and zero-downtime patterns.