﻿![]()

#### 

#### Background

Sitecore Send provides an [API](https://doc.sitecore.com/send/en/developers/api-documentation/introduction-to-the-sitecore-send-api.html) to interact and integrate with your custom system. You can check my articles about various APIs and how to use them: [here](https://www.brimit.com/blog/sitecore-send-apis) and [here](https://www.brimit.com/blog/sitecore-send-3-rest-api).

If the API is used to subscribe and unsubscribe users, you can easily face [API rate limits](https://doc.sitecore.com/send/en/developers/api-documentation/api-rate-limiting.html).

#### API Rate Limits

##### Current Limits

Up-to-date limits can be checked in the [official documentation](https://doc.sitecore.com/send/en/developers/api-documentation/api-rate-limiting.html).

Currently, the limits are the following - **number of requests per 10 seconds**:

| Resource | Method | Endpoint Path | Rate Limit |
| --- | --- | --- | --- |
| [Add subscribers](https://doc.sitecore.com/send/en/developers/api-documentation/add-a-new-subscriber.html) | POST | `/subscribers/{ListID}/subscribe` | 10 |
| [Add multiple subscribers](https://doc.sitecore.com/send/en/developers/api-documentation/add-multiple-subscribers.html) | POST | `/subscribers/{ListID}/subscribe_many` | 2 |
| [Unsubscribe a subscriber from an account](https://doc.sitecore.com/send/en/developers/api-documentation/unsubscribe-a-subscriber-from-an-account.html) | POST | `/subscribers/unsubscribe` | 20 |
| [Unsubscribe a subscriber from an email list](https://doc.sitecore.com/send/en/developers/api-documentation/unsubscribe-a-subscriber-from-a-mailing-list.html) | POST | `/subscribers/{ListID}/unsubscribe` | 20 |
| [Unsubscribe a subscriber from an email list and a campaign](https://doc.sitecore.com/send/en/developers/api-documentation/unsubscribe-a-subscriber-from-a-mailing-list-and-a-campaign.html) | POST | `/subscribers/{ListID}/
            {CmpId}/unsubscribe` | 20 |

*Note*: All resource limits are counted separately, so you can execute *2 x Add multiple subscribers*, *10 x Add subscribers*, and *20 x Unsubscribe a subscriber from an account* simultaneously within a timespan of 10 seconds without being rate limited.

##### Rate Limited Response

If you reach the rate limit, you'll get the following response:

```
{
  "Code": 429,
  "Error": "RATE_LIMITING",
  "Context": null
}
```

Additionally, information about rate limits is included in response headers, e.g.:

```
x-ratelimit-expires:    07/10/2024 14:12:52
x-ratelimit-firstcall:  07/10/2024 14:12:42
```

These headers can be handled to understand when the next call can be executed.

#### Handling Rate Limits in C# with `Polly`

The source for Polly policies can be checked [here](https://github.com/izharikov/SitecoreSend/blob/develop/dotnet/SitecoreSend.SDK.Tests/Limiter/SendRateLimiterPolicy.cs).

##### Rate Limiting Using Polly

This policy will limit number of executions withing specified timespan. If it's executed more often, then `RateLimitRejectedException` is thrown.

```
public static AsyncPolicy<T> ConfigureRateLimit<T>(int numberOfExecutions,
    TimeSpan perTimeSpan)
{
    return Policy
            .RateLimitAsync<T>(numberOfExecutions, perTimeSpan)
        ;
}
```

##### Retry if Polly Rate Limit Failed

If the request is executed more often than configured in the policy above, retry it 5 times with some delay.

```
public static AsyncPolicy<T> RetryRateLimitException<T>(int numberOfExecutions, TimeSpan perTimeSpan)
{
    var delay = Backoff.DecorrelatedJitterBackoffV2(medianFirstRetryDelay: perTimeSpan.Divide(numberOfExecutions) * 1.5,
        retryCount: 5);
    return Policy<T>.Handle<RateLimitRejectedException>()
        .WaitAndRetryAsync(delay);
}
```

##### Retry if HTTP Response is `RATE_LIMITING`

If the request is executed but the HTTP response contains a `RATE_LIMITING` error, retry it after the `x-ratelimit-expires` header date.

```
public static AsyncPolicy<T> RetryRateLimit<T>() where T : SendResponse?
{
    return Policy
        .HandleResult<T>((response) => response?.RateLimitDetails != null)
        .WaitAndRetryAsync(5, (i, result, _) =>
        {
            if (result.Result?.RateLimitDetails?.Expires != null)
            {
                var utcNow = DateTimeOffset.UtcNow;
                return result.Result.RateLimitDetails.Expires.Value.Subtract(utcNow) +
                        TimeSpan.FromMilliseconds(i * 50);
            }

            return TimeSpan.FromMilliseconds(i * 50);
        }, (_, _, _, _) => Task.CompletedTask);
}
```

*Note*: This code is based on the [NuGet package `SitecoreSend.SDK`](https://www.nuget.org/packages/SitecoreSend.SDK).

##### Result Policy

All previously mentioned policies can be combined together:

```
public class SendRateLimiterPolicy<T> where T : SendResponse?
{
    public AsyncPolicy<T> Instance { get; private set; }

    public SendRateLimiterPolicy(int numberOfExecutions,
        TimeSpan perTimeSpan)
    {
        Instance = Policy.WrapAsync(PoliciesHelper.RetryRateLimit<T>(),
            PoliciesHelper.RetryRateLimitException<T>(numberOfExecutions, perTimeSpan),
            PoliciesHelper.ConfigureRateLimit<T>(numberOfExecutions, perTimeSpan));
    }
}
```

And then configured for each endpoint separately:

```
public static class SendRateLimits
{
    public static readonly AsyncPolicy<SendResponse<IList<Subscriber>>?> AddMultipleSubscribers =
        new SendRateLimiterPolicy<SendResponse<IList<Subscriber>>?>(2, TimeSpan.FromSeconds(10)).Instance;

    public static readonly AsyncPolicy<SendResponse<Subscriber>?> AddSubscriber =
        new SendRateLimiterPolicy<SendResponse<Subscriber>?>(10, TimeSpan.FromSeconds(10)).Instance;

    public static readonly AsyncPolicy<SendResponse?> UnsubscribeFromAllLists =
        new SendRateLimiterPolicy<SendResponse?>(20, TimeSpan.FromSeconds(10)).Instance;

    public static readonly AsyncPolicy<SendResponse?> UnsubscribeFromList =
        new SendRateLimiterPolicy<SendResponse?>(20, TimeSpan.FromSeconds(10)).Instance;

    public static readonly AsyncPolicy<SendResponse?> UnsubscribeFromListAndCampaign =
        new SendRateLimiterPolicy<SendResponse?>(20, TimeSpan.FromSeconds(10)).Instance;
}
```

You can automatically use it together with the [`SitecoreSend.SDK` NuGet package](https://www.nuget.org/packages/SitecoreSend.SDK):

```
var client = new SendClient(
    apiConfiguration,
    httpClientFactory, 
    new RateLimiterConfiguration()
    {
        Subscribers = new SubscribersWrapper()
        {
            AddSubscriber = SendRateLimits.AddSubscriber.ExecuteAsync,
            AddMultipleSubscribers = SendRateLimits.AddMultipleSubscribers.ExecuteAsync,
            UnsubscribeFromAllLists = SendRateLimits.UnsubscribeFromAllLists.ExecuteAsync,
            UnsubscribeFromList = SendRateLimits.UnsubscribeFromList.ExecuteAsync,
            UnsubscribeFromListAndCampaign = SendRateLimits.UnsubscribeFromListAndCampaign.ExecuteAsync,
        },
    }
);
```

For more information, check the [README](https://github.com/izharikov/SitecoreSend/tree/develop/dotnet) of the NuGet package.

#### Suggestions to Minimize Rate Limits Errors

##### 1. Use Bulk Actions

Sitecore Send comes with bulk actions by default:

- Import Members into List
- Archive List Members
- Unsubscribe List Members
- Delete List Members
- Copy Members from Another Email List

Maybe you don't need to use the API? Perhaps you can export subscribers into a CSV/XLSX file and perform a bulk operation in the Admin Panel?

##### 2. Use `Add Subscribers` endpoint only to add NEW, not to update EXISTING

You may know that using the `/v3/subscribers/{MailingListID}/subscribe.{Format}` endpoint, you can not only create a new subscriber but also update information about an existing subscriber. **Don't do it or do it very carefully!** 

This may cause API rate limits errors. The better approach is to use two requests:

- `GET /v3/subscribers/{MailingListID}/view.{Format}?apikey={API_KEY}&Email={Email}` - to get a subscriber by email (retrieve `{SubscriberId}` from the response)
- `POST /v3/subscribers/{MailingListID}/update/{SubscriberId}.{Format}?apikey={API_KEY}` - to update a subscriber by ID

##### 3. Handle `RATE_LIMITING` Error Code Response

Always handle `RATE_LIMITING` error code with retries: using Polly as mentioned above or using any other mechanism.

##### 4. Ensure subscribe/unsubscribe endpoints are used correctly and limited

On one of our projects, we had a webhook that sends requests to Sitecore Send with updated subscriber information. However, we did not filter anonymous requests, so many requests were sent incorrectly, leading to numerous `RATE_LIMITING` errors. After excluding anonymous requests, we resolved this issue.

* * *

I hope all the explanations and suggestions will help you avoid issues when using the API.

###### More From Author

[##### Sitecore Marketplace SDK Integration with Vercel AI SDK
February 25, 2026](https://www.brimit.com/blog/sitecore-marketplace-sdl-integration-with-vercel-ai-sdk)[##### Sitecore Marketplace Updates (December 2025)
December 16, 2025](https://www.brimit.com/blog/sitecoreai-marketplace-december-updates)[##### SitecoreAI, Marketer MCP, Agents API, Marketplace — Sitecore Symposium 2025 Recap
November 11, 2025](https://www.brimit.com/blog/sitecoreai-mcp-marketplace-symposium2025-recap)

###### Author

[!\[Igor Zharikov - Senior Sitecore Developer, Sitecore MVP\](https://www.brimit.com/-/jssmedia/feature/blogs/authors/igor-zharikov-2.jpg?h=700&amp;iar=0&amp;w=700&amp;hash=292A84E8CB3E5CE57CA743385CFB1464)
Igor Zharikov
Sitecore MVP / Senior Sitecore Developer](https://www.brimit.com/blog/author?authors=Igor%20Zharikov)

#### More on Sitecore

[!\[How Vercel Will Help You Save Effort When Deploying Sophisticated Sitecore Projects\](https://www.brimit.com/-/jssmedia/project/brimit/blog/2024/vercel_cover-image.png)
#Guides#How-toDXPE-commerce
##### How Vercel Will Help You Save Effort When Deploying Sophisticated Sitecore Projects
Optimize and accelerate the development and deployment of complex multisite Sitecore projects.
Alexei Vershalovich on July 17, 2024](https://www.brimit.com/blog/how-vercel-will-help-you-save-effort-when-deploying-sophisticated-sitecore-projects)

[!\[Training Up Tomorrow's Sitecore MVPs: a Mentoring Success Story\](https://www.brimit.com/-/jssmedia/project/brimit/blog/2023/sitecore-mentoring---cover-image.png)
#How-toDXP
##### Training Up Tomorrow's Sitecore MVPs: a Mentoring Success Story
How to participate in the Sitecore Mentor program and help younger colleagues jump-start a career in Sitecore development.
Sergey Baranov on October 2, 2023](https://www.brimit.com/blog/training-up-tomorrows-sitecore-mvps)

[!\[Going Headless. Part 2: When a Headless CMS Is Your Best Bet (if you have Sitecore)\](https://www.brimit.com/-/jssmedia/project/brimit/blog/2022/headless/adobestock_456986731.jpg)
#How-toDXPE-commerce
##### Going Headless. Part 2: When a Headless CMS Is Your Best Bet (if you have Sitecore)
Discover how a headless CMS can benefit organizations that use Sitecore.
Daniil Raschupkin, Palina Trokhautsava on September 15, 2022](https://www.brimit.com/blog/going-headless-part-2-when-a-headless-cms-is-your-best-bet-if-you-have-sitecore)

![](https://bat.bing.net/action/0?ti=187017043&amp;tm=gtm002&amp;Ver=2&amp;mid=32421894-08a0-4512-ac6d-64c86453437e&amp;bo=2&amp;gtm_tag_source=1&amp;pi=0&amp;lg=en-US&amp;sw=800&amp;sh=600&amp;sc=24&amp;nwd=1&amp;tl=Sitecore%20Send%3A%20API%20Rate%20Limits&amp;kw=sitecore%20send,api,rate%20limits&amp;p=https%3A%2F%2Fwww.brimit.com%2Fblog%2Fsitecore-send-api-rate-limits&amp;r=&amp;lt=271&amp;evt=pageLoad&amp;sv=2&amp;asc=D&amp;cdb=AQAY&amp;rn=623590)