API Requests Need Controls? Apply and Test API Management Policies

APIM policies are rules that run at the gateway before or after a backend call. For example, a gateway can reject an incomplete request, limit how often a client calls an API, or change the request format expected by the backend. You can apply these rules without changing the backend application.

Reuse apim-ctgateway-weu, the Consumption tier, and CloudTrips Petstore from the API import trip. No new Azure resource or paid tier upgrade is needed. Keep Subscription required enabled.

We will apply rules to just GET /pet/findByStatus:

Control What you will observe
Existing subscription-key requirement A caller without a valid APIM key cannot use the API
Required lab header Missing or incorrect header → HTTP 400
Rate limit Too many calls with the same APIM subscription → HTTP 429
Request transformation Client sends status=sold; gateway sends status=available to Petstore
Response transformation Gateway adds a header showing the effective status value

The header X-CloudTrips-Client: lab-demo is a simple request-validation example. Anyone can copy it; it is not authentication. The subscription key provides the access check in this lab. User identity and permissions require a suitable authentication and authorization design, such as validated OAuth access tokens.

Choose the Operation Scope

Open API Management → apim-ctgateway-weu → APIs → APIs → CloudTrips Petstore → Design.

Select Finds Pets by status, identified by GET /pet/findByStatus. Select the policy code editor (</>) in Inbound processing.

Apply this policy to the selected operation. Keep the previous trip’s key-removal policy under All operations: the <base /> below inherits it, so your APIM subscription key is still removed before the request reaches the public backend.

In this lab, the operation has no custom policy yet. Replace its policy document with:

<policies>
  <inbound>
    <base />
    <check-header name="X-CloudTrips-Client"
                  failed-check-httpcode="400"
                  failed-check-error-message="Send X-CloudTrips-Client: lab-demo."
                  ignore-case="false">
      <value>lab-demo</value>
    </check-header>
    <rate-limit calls="3" renewal-period="60" />
    <set-query-parameter name="status" exists-action="override">
      <value>available</value>
    </set-query-parameter>
    <set-header name="Accept" exists-action="override">
      <value>application/json</value>
    </set-header>
  </inbound>
  <backend>
    <base />
  </backend>
  <outbound>
    <base />
    <set-header name="X-CloudTrips-Effective-Status" exists-action="override">
      <value>@(context.Request.Url.Query.GetValueOrDefault("status", ""))</value>
    </set-header>
  </outbound>
  <on-error>
    <base />
  </on-error>
</policies>

Select Save and allow a short time for the change to reach the gateway.

Inbound rules run before the backend call; outbound rules modify the response. Statements run in order: the header check comes before the rate limit, so requests rejected by that check do not reach this rate-limit rule. The response expression reads the query parameter after it has been changed by the inbound policy.

The limit is three calls per 60 seconds per APIM subscription, not three per browser or per Azure billing subscription. This uses rate-limit, which supports Consumption. The separate rate-limit-by-key policy is not supported on this tier.

Test 1: Reject a Missing Header

Open Test → GET /pet/findByStatus. Keep the automatically supplied Ocp-Apim-Subscription-Key and the same selected APIM subscription throughout all tests. Leave the key masked.

Set status: sold. Do not add X-CloudTrips-Client yet. Select Send.

Expect 400 Bad Request, with a message containing:

Send X-CloudTrips-Client: lab-demo.

The gateway rejected the request before calling Petstore. If you see 401, the APIM subscription key is missing or invalid; fix that first. Changing the lab header will not fix a key error.

APIM test console returning HTTP 400 because the required lab header is missing.

Test 2: Accept and Transform the Request

Keep status: sold and add this under Request headers:

X-CloudTrips-Client: lab-demo

Select Send. Expect 200 OK with a JSON array. In HTTP response → Headers, find:

X-CloudTrips-Effective-Status: available

HTTP header names may appear in lowercase; that is equivalent.

You asked the gateway for sold pets, but the policy replaced the status with available before forwarding. The response header reports the effective query value, and any returned pet records should have status: available. The public demo’s data can change; if the array is empty, the response header still shows the query value used by the gateway.

The gateway also sets Accept: application/json on the backend request. The response header was added by APIM; no Petstore code was changed. This deliberately changes the client’s requested filter to demonstrate a transformation. Use overrides like this only when they match your API’s intended contract.

Successful gateway call with status sold in the request and X-CloudTrips-Effective-Status available in the response headers.

Test 3: Trigger the Rate Limit

Keep the valid lab header, subscription key, and status: sold. Click Send several more times within 60 seconds. The successful call from Test 2 already counts toward the limit.

Expect 429 Too Many Requests once the allowance is exhausted. Inspect the Retry-After response header for the suggested wait in seconds. Rate limiting is distributed, so do not rely on exactly the fourth click being blocked.

APIM test console returning HTTP 429 Too Many Requests with the Retry-After header.

Stop sending requests, wait at least the indicated interval, and try again. If still throttled, wait a full 60 seconds without sending requests. A successful call should return 200 again.

The outbound custom header may be absent from 400 and 429 responses because those requests stop in the inbound pipeline. That is expected.

If the Results Differ

  • 200 without the required header: confirm you saved the policy on the same operation you are testing and that the request uses your APIM gateway URL.
  • 400 even with the header: use exactly X-CloudTrips-Client: lab-demo; the configured value comparison is case-sensitive.
  • 429 during Test 2: previous requests consumed the allowance. Wait without sending, then retry.
  • No 429: keep the same APIM subscription selected and send the calls within one minute. Confirm rate-limit is saved on the tested operation.
  • Backend 5xx: the public Petstore service may be unavailable. Retry later rather than removing the gateway checks.

Finish

To restore the operation for the next trip, replace this operation’s policy with:

<policies>
  <inbound><base /></inbound>
  <backend><base /></backend>
  <outbound><base /></outbound>
  <on-error><base /></on-error>
</policies>

Save. This removes the lab header requirement, rate limit, and transformations while retaining the inherited key-removal rules under All operations. Keep Subscription required enabled.

Keep rg-cloudtrips-apim-test-weu for the next API trip. If finished with APIM, delete the resource group instead.