7.3 Service Protection API Limits & Retry Policies

Key Takeaways

  • Service protection limits are evaluated together over a rolling 5-minute window: 6,000 requests, 20 minutes of combined execution time, and 52 concurrent requests, per user/Application User per web server.
  • Crossing any one of the three thresholds returns HTTP 429, which includes a Retry-After header specifying how many seconds to wait.
  • Well-behaved clients honor Retry-After, apply exponential backoff with jitter on subsequent retries, and cap total retry attempts rather than looping without delay.
  • Favoring $batch/ExecuteMultiple/CreateMultiple over per-record loops and selecting only needed columns both reduce exposure to these limits proactively.
  • High-volume integrations should be architected around bulk operations and staggered scheduling from the start; retry policy is a safety net, not a substitute for right-sizing throughput.
Last updated: July 2026

Both the Web API and the Organization service run behind service protection API limits designed to keep any single user or application from monopolizing a Dataverse environment's shared compute capacity. PL-400 tests whether a developer can recognize when these limits are in play and write client code that behaves well when it hits them, rather than hammering the platform with retries.

What the Service Protection Limits Measure

Dataverse enforces three related limits, evaluated together over a rolling five-minute window, per user (or Application User) per web server node:

LimitThreshold (per 5-minute window)
Number of requests6,000 requests
Combined execution time20 minutes (1,200 seconds) of total execution time across requests
Number of concurrent requests52 concurrent requests

Crossing any one of the three trips the limit — a caller doesn't need to exceed all three simultaneously. These thresholds apply to both the Web API and Organization service equally, since both ultimately execute against the same platform.

What Happens When a Limit Is Hit

When a client crosses a service protection threshold, Dataverse returns an HTTP 429 (Too Many Requests) response. Critically, that response includes a Retry-After header, expressed in seconds, telling the caller precisely how long to wait before the platform will accept another request from that identity. This is not a suggestion — it is the mechanism Dataverse uses to signal "back off now, resume at this time."

Writing a Well-Behaved Retry Policy

A PL-400-ready client implementation should:

  1. Catch 429 responses explicitly rather than treating them as generic failures to surface to the user.
  2. Read and honor the Retry-After header value — wait that many seconds (or milliseconds, depending on SDK) before resubmitting.
  3. Apply exponential backoff with jitter for any additional retries beyond the first, in case the same identity trips the limit again immediately after the wait — this avoids a thundering-herd pattern where many callers retry at the exact same instant.
  4. Cap the total number of retry attempts so a systemic outage doesn't cause an infinite retry loop that consumes its own resources.
  5. Never retry in a tight loop with no delay — this is the single worst pattern, since it guarantees the client re-trips the same limit and can extend an outage rather than recovering from it.

The .NET SDK's ServiceClient and the Web API SDK client libraries have built-in retry logic that already implements this pattern; developers writing raw HTTP calls (from an Azure Function, for example) are responsible for implementing it themselves.

Design Choices That Reduce the Chance of Hitting Limits

Beyond reactive retry handling, several proactive design decisions reduce how often a well-built integration bumps into service protection limits in the first place:

  • Favor batch and bulk messages ($batch, ExecuteMultipleRequest, CreateMultipleRequest) over loops of single-record calls — fewer total requests directly reduces exposure to the request-count limit.
  • Select only needed columns — smaller payloads generally execute faster, reducing exposure to the combined execution-time limit.
  • Throttle concurrency deliberately in integration code (e.g., a bounded SemaphoreSlim around parallel calls) rather than firing dozens of requests simultaneously and hoping the platform absorbs it.
  • Spread scheduled or bulk-import jobs across time rather than launching all of them at the top of every hour, which concentrates load and increases the odds of tripping the concurrent-request limit for a shared Application User.

Why This Matters for Architecture Decisions

Recognizing service protection limits is also a design-time signal, not just a runtime error-handling concern: an integration that will legitimately need sustained high-volume throughput (large data migrations, high-frequency near-real-time sync) should be architected around bulk operations, change tracking, and possibly staggered/queued processing from the outset — retry logic is a safety net for occasional spikes, not a substitute for right-sizing the integration pattern to the expected volume.

A Minimal Retry Pattern

Custom HTTP clients calling the Web API directly (for example, from an Azure Function) commonly implement retry logic along these lines:

async Task<HttpResponseMessage> SendWithRetryAsync(HttpRequestMessage request, int maxAttempts = 5)
{
    for (int attempt = 1; attempt <= maxAttempts; attempt++)
    {
        var response = await httpClient.SendAsync(request);
        if (response.StatusCode != (HttpStatusCode)429) return response;

        var delay = response.Headers.RetryAfter?.Delta
                    ?? TimeSpan.FromSeconds(Math.Pow(2, attempt));
        await Task.Delay(delay);
    }
    throw new Exception("Exceeded retry attempts against Dataverse service protection limits.");
}

The key line is reading response.Headers.RetryAfter first and only falling back to a computed exponential delay if the header is absent — the platform's own guidance always takes priority over a client-guessed backoff interval.

Distinguishing Service Protection Limits from Storage or Licensing Limits

PL-400 scenario questions sometimes pair a 429-style throttling error with unrelated symptoms — a storage capacity warning, or a licensing-based limitation on API requests for a given user's license tier — to test whether a candidate can tell them apart. Service protection limits are about the rate and volume of calls in a short window and are resolved by waiting and retrying; storage limits are about how much data is stored and are resolved by managing or purchasing capacity; license-tier request allocations are a separate governance mechanism for overall API consumption across a tenant. Only the first of these three is addressed by the retry-with-backoff pattern covered in this section — the other two require an administrative or licensing response, not a code change.

Test Your Knowledge

A Web API call returns HTTP 429 because of Dataverse's service protection limits. What should a well-behaved client do next?

A
B
C
D
Test Your Knowledge

Which three factors together define Dataverse's service protection API limits within a rolling 5-minute window?

A
B
C
D