API Limits

  • Applies to Dynamics 365 for Customer Engagement apps version 9.x

  • Applies to Dynamics 365 (online), version 8.x

We limit the number of API requests made by each user, per organization instance, within a five minute sliding window. Additionally, we limit the number of concurrent requests that may come in at one time. When one of these limits is exceeded, an exception will be thrown by the platform.

The limit will help ensure that users running applications cannot interfere with each other based on resource constraints. The limits will not affect normal users of the platform. Only applications that perform a large number of API requests may be affected. The limit will help provide a level of protection from random and unexpected surges in request volumes that threaten the availability and performance characteristics of the Dynamics 365 platform.

If your application has the potential to exceed the limit, please consider the guidance given in the What should I do if my application exceeds the limit? section below.

What happens when the limit is exceeded?

When the limit is exceeded, all requests for the same user will return error responses.

If you use the .NET SDK assemblies, the platform will respond with a FaultException<OrganizationServiceFault> WCF Fault.

Error Code Message
-2147015902 Number of requests exceeded the limit of 4000, measured over time window of 300 seconds.
-2147015903 Combined execution time of incoming requests exceeded limit of 1,200,000 milliseconds over time window of 300 seconds. Decrease number of concurrent requests or reduce the duration of requests and try again later.
-2147015898 Number of concurrent requests exceeded the limit of X

If you use HTTP requests, the response will include the same messages, but with:
StatusCode : 429

All requests will return these error responses until the volume of API requests falls below the limit. If you get these responses, your application should stop sending API requests until the volume of requests is below the limit.

What should I do if my application exceeds the limit?

When your application exceeds the limit, the error response from the server may specify the amount of time you should wait before sending more requests. The response object is slightly different if you are using SDK assemblies or HTTP requests.

For a discussion of best practices, see Azure Architecture Best Practices Transient fault handling

The Polly Project is a library which includes capabilities for dealing with transient faults using execution policies.

HTTP requests

If you are using HTTP requests, you can look for the Retry-After HTTP header included in the error response. This will contain a value in seconds indicating how long you should wait before making a follow-up request. More information MDN web docs Retry-After

SDK assemblies

If you are using the SDK assemblies, you can look for the Retry-After delay in the OrganizationServiceFault.ErrorDetails property, using the key "Retry-After". The value returned is a TimeSpan object.

.NET SDK Assembly Example

The following example uses the Retry class described below to retrieve one account using the IOrganizationService.RetrieveMultiple method. If the request fails because an API limit has been exceeded, the Retry class will wait according to a delay specified by the server and try again.

var qe = new QueryExpression("account") { TopCount = 1 };
EntityCollection result = Retry.Do(() => service.RetrieveMultiple(qe));

Retry class

The Retry class demonstrates how to retry requests that fail with transient errors based on known OrganizationServiceFault error codes. The Retry class waits before retrying. If the fault specifies a retry delay, wait according to the delay specified by the server. Else use exponential backoff to calculate the delay based on the number of retry attempts made.

using System;
using System.ServiceModel;
using System.Threading;

public class Retry
{
    private const int RateLimitExceededErrorCode = -2147015902;
    private const int TimeLimitExceededErrorCode = -2147015903;
    private const int ConcurrencyLimitExceededErrorCode = -2147015898;

    public static TResult Do<TResult>(Func<TResult> func, int maxRetries = 3)
    {
        int retryCount = 0;

        while (true)
        {
            try
            {
                return func();
            }
            catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> ex) 
                when (IsTransientError(ex))
            {
                if (++retryCount >= maxRetries)
                {
                    throw;
                }

                if (ex.Detail.ErrorCode == RateLimitExceededErrorCode)
                {
                    // Use Retry-After delay when specified
                    delay = (TimeSpan)ex.Detail.ErrorDetails["Retry-After"];
                }
                else
                {
                    // else use exponential backoff delay
                    delay = TimeSpan.FromSeconds(Math.Pow(2, retryCount));
                }

                Thread.Sleep(delay);
            }
        }
    }

    private static bool IsTransientError(FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> ex)
    {
        // You can add more transient fault codes to retry here
        if (ex.Detail.ErrorCode == RateLimitExceededErrorCode ||
            ex.Detail.ErrorCode == TimeLimitExceededErrorCode ||
            ex.Detail.ErrorCode == ConcurrencyLimitExceededErrorCode)
        {
            return true;
        }

        return false;
    }
}

See also

Use the Dynamics 365 for Customer Engagement Organization apps service
Use the Dynamics 365 for Customer Engagement Web API
Execute batch operations using the Web API
Use ExecuteMultiple to improve performance for bulk data load