API Management : Customize throttling message
Tags: azure, apim, policy
Published: 2021-10-20 01:52:19

We can use ``rate-limit`` policies of Azure API Management to throttle API calls. See [Advanced request throttling with Azure API Management](https://docs.microsoft.com/en-us/azure/api-management/api-management-sample-flexible-throttling) for more detail.

In this article, I explain how we can customize the response message.

# Default behavior

This is a sample policy which limits up to 5 calls per a minute.

```xml
<policies>
    <inbound>
        <rate-limit calls="5" renewal-period="60" />
        <base />
        <mock-response status-code="200" content-type="application/json" />
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
</policies>
```

If we send 6th request in a minute, we can see the following message back from the service.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n6z9kp9gdnuk7mctzo9o.png)

# Change Response Header

``rate-limit`` policy has many attributes to customize the behavior. See [here](https://docs.microsoft.com/en-us/azure/api-management/api-management-access-restriction-policies#attributes-1) for the list.

For example, we can add ``remaining-calls-header-name`` to let caller know how many more calls then can send before they get throttled. Following policy will add additional response header to include the number.

```xml
<rate-limit calls="5" renewal-period="60" remaining-calls-header-name="RemainingCalls"/>
```
And this is the response header on my first call, which let me know I can do four more calls before throttled.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5lj8zquxr44b1c8vdgty.png)

Following policy adds total calls number in response header.

```xml
<rate-limit calls="5" renewal-period="60" remaining-calls-header-name="RemainingCalls" total-calls-header-name="TotalCalls" />
```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3hqyskfsz2ofammma6zd.png)
 
# Customize the response

If we want to change the response message from default one, we can use ``on-error`` policy. We can use [on-error policy](https://docs.microsoft.com/en-us/azure/api-management/api-management-error-handling-policies) to control error behavior, including 429 too many requests.

## Simple example

This policy overrides behavior for **RateLimitExceeded** error. It still returns 429 status code, but message body is overwritten. 

```xml
<policies>
    <inbound>
        <rate-limit calls="5" renewal-period="60" remaining-calls-header-name="RemainingCalls" total-calls-header-name="TotalCalls" />
        <base />
        <mock-response status-code="200" content-type="application/json" />
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <choose>
            <when condition="@(context.LastError.Reason == "RateLimitExceeded")">
                <return-response>
                    <set-status code="429" />
                    <set-header name="Content-Type" exists-action="override">
                        <value>application/json</value>
                    </set-header>
                    <set-body>{
                        "Don't call anymore"
                 }</set-body>
                </return-response>
            </when>
        </choose>
        <base />
    </on-error>
</policies>
```

The response I see after 6th call is this.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3brfbj80cqegb5k7mb6a.png)

## Use LastError context

APIM provide error detail in [context.LastError](https://docs.microsoft.com/en-us/azure/api-management/api-management-error-handling-policies#lasterror) which we can use to enrich the message. Following example append error message to previous custom message.

```xml
<policies>
    <inbound>
        <rate-limit calls="5" renewal-period="60" remaining-calls-header-name="RemainingCalls" total-calls-header-name="TotalCalls" />
        <base />
        <mock-response status-code="200" content-type="application/json" />
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <choose>
            <when condition="@(context.LastError.Reason == "RateLimitExceeded")">
                <return-response>
                    <set-status code="429" />
                    <set-header name="Content-Type" exists-action="override">
                        <value>application/json</value>
                    </set-header>
                    <set-body>@{
                        return String.Format("Don't call anymore because you get {0}",
                        context.LastError.Message);
                    }</set-body>
                </return-response>
            </when>
        </choose>
        <base />
    </on-error>
</policies>
```

The result looks like below.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jt56jeeqlrectan23gdw.png)
 
# Summary

I explain how we can use ``on-error`` to control error behavior. If you are interested in more detail about policy expression, see [API Management policy expressions](https://docs.microsoft.com/en-us/azure/api-management/api-management-policy-expressions).