+44 (0)1782 976577 letstalk@eazydynamics.com
Table of Contents

    API Throughput for Business Central


    Moving large volumes of data between systems shouldn’t mean timeouts, duplicates, or late month-ends. In this blog, we share the four habits we use in real projectspagination, throttling, idempotency, and backoff – to keep integrations fast, predictable, and safe. We’ll show how they fit together in Business Central (BC), where teams go wrong, and what to implement first.

    Who this is for

    Owners, finance managers, and ops leads in SMEs connecting eCommerce, WMS, and web apps to Dynamics 365 Business Central. If you’re planning an integration or want your existing one to stop stalling at peak times, start here.

    The problem in one minute

    APIs are shared roads with speed limits. Go too fast and you’ll hit rate limits (429 errors). Go too big and you’ll run out of memory or time.

    When networks wobble, retries can create duplicate documents. The fix isn’t one trick—it’s a small set of practices that work together:

    • Pagination – fetch/process in small, ordered batches.

    • Throttling – shape your request rate to stay under limits.

    • Idempotency – make retries safe (no duplicates).

    • Backoff – slow down smartly when the API is busy.

    How the four habits fit together (the flow)

    1. Queue the work. Build a list of items to sync (e.g., orders modified since T-1).
    2. Paginate. Pull a page (stable order), process, checkpoint, repeat.
    3. Throttle. Respect BC’s rate caps and your own concurrency.
    4. Retry with idempotency. If a call fails, retry safely using unique keys.
    5. Backoff with jitter. If you see “try later”, wait, then try again with an increased wait.
    6. Checkpoint & monitor. Record last success; track throughput, error % and p95 latency.

    If you find this article useful, click and subscribe to our newsletter - Business Central Uplugged - helping you use what you've already paid for!

    1) Pagination — keep the chunks small and stable

    What’s changed / what’s current in BC

    • Server-driven paging: When a result exceeds the server page size, BC returns an @odata.nextLink. Follow that link as-is until it disappears; treat the token as opaque.

    • Max page size: BC Online uses a maximum of 20,000 entities per page (can’t be changed online; on-prem can be configured). You can request smaller pages with Prefer: odata.maxpagesize=N (N ≤ 20,000) to reduce memory/latency and avoid timeouts.

    What good looks like

    • Choose a stable sort (for example, lastModifiedDateTime then id).

    • Follow @odata.nextLink when present; otherwise, if you deliberately want smaller chunks, add Prefer: odata.maxpagesize=….

    • Checkpoint after each page so you can resume mid-run without re-processing.

    Business Central angle (extras that help):
    If you’re doing heavy reads, add Data-Access-Intent: ReadOnly on GETs to hit read replicas and reduce load on primaries.

    2) Throttling — stay fast without tripping limits

    BC Online limits to design around (current at April–September 2025):

    • Per environment (OData): speed cap 600/min in Production, 300/min in Sandbox; max concurrent 5; max connections 100; max queue 95; max batch 100 ops; operation timeout 8 minutes; max page 20,000.

    • Per user (newer model): 6,000 OData requests per 5-minute sliding window plus the same concurrency/queue caps applied per user (spreading load across service users scales throughput, subject to platform capacity).

    What good looks like

    • Detect 429s and pace requests (requests/sec) so you stay under the caps.

    • Cap concurrency per endpoint; put workers behind a token/leaky bucket or simple queue so bursts don’t overwhelm the API.

    • If you need to push higher throughput, distribute calls across multiple application users while staying within licence and security policy.

    3) Idempotency — retries without duplicates

    Why it matters: Networks fail. If a “Create” times out, you’ll retry. Without idempotency, you risk double-creating documents.

    BC-specific truths

    • Don’t rely on External Document No. It’s useful for search, but not unique by default, so it won’t prevent duplicates. Use a dedicated external ID field (with a unique index) or a small mapping table (External ID ↔ systemId), and enforce uniqueness in AL or middleware.

    • Prefer upserts (create-or-update) by first searching on your external key; treat replays as update/no-op rather than insert.

    • For updates, use optimistic concurrency: send the @odata.etag back in If-Match so you don’t overwrite someone else’s change. PATCH requires If-Match in BC.

    4) Backoff — be polite when the API is busy

    What’s current in BC error handling

    • 502 / 503: BC includes a Retry-After header—honour it.

    • 429: treat as “slow down”. Retry-After may not be present, so fall back to capped exponential backoff with jitter.

    • Timeouts: OData requests time out after ~8 minutes; split work accordingly.

    What good looks like

    • Classify errors: retryable (timeouts, 429, 502, 503) vs non-retryable (validation/business rule failures).

    • Implement exponential backoff with jitter, plus sensible caps (max attempts, max backoff), and a dead-letter queue for items that need human fixes.

    What to implement first (if you only have a day)

    1. Add pagination with explicit ordering and checkpoints; follow @odata.nextLink and/or send Prefer: odata.maxpagesize= for smaller chunks.
    2. Wrap calls with retry + backoff (use Retry-After when present on 502/503).
    3. Introduce a dedicated external ID and enforce uniqueness; don’t rely on External Document No.
    4. Throttle to a steady rate and keep within env/user caps; then tune page size and concurrency by measurement.

    Monitoring that actually helps

    • Throughput: items/minute and pages/minute.

    • Quality: error rate, retries per success.

    • Latency: p95/p99 per endpoint.

    • Limits: 429 counts, average Retry-After (where present).

    • Recovery: dead-letter volume and age.

    Common mistakes (and easy fixes)

    • “We’ll just retry everything.”
      Don’t. Classify errors; never retry validation failures.

    • “Bigger pages are faster.”
      Often slower overall. Start modest, measure, then tune.

    • “Duplicates are a data problem.”
      They’re a design problem. Add idempotency keys.

    • “429s are harmless.”
      They’re a signal to back off and/or lower concurrency.

    Throughput boosters you can use safely

    • Webhooks for standard/custom APIs to reduce polling. Subscriptions require a handshake (return validationToken), expire after ~3 days, and must be renewed; BC Online allows up to 200 subscriptions.

    • Batching ($batch) to cut round trips—max 100 operations per batch. Use with care alongside idempotency. Microsoft Learn

    Quick reference: the four habits at a glance

    Habit Primary goal Implement with… Anti-pattern to avoid
    Pagination Reliability & memory safety Server-driven paging (@odata.nextLink), Prefer: odata.maxpagesize, checkpoints Huge, ad-hoc batches
    Throttling Stay under rate limits Rate caps, per-user windows, queues Spiky bursts that trigger 429s
    Idempotency Safe retries, no duplicates Dedicated external ID + upsert, ETag/If-Match Blind “create” on every retry
    Backoff Graceful recovery Exponential backoff + jitter, honour Retry-After Immediate, repeated hammering

     

    Implementation checklist

    • Follow @odata.nextLink and/or set Prefer: odata.maxpagesize; checkpoint after each page.

    • Add a throttle (req/s) and cap concurrency per endpoint; design to env/user caps.

    • Persist a dedicated external ID (or mapping) and enforce uniqueness in AL/middleware.
    • Implement retry policy: retryable vs non-retryable; exponential backoff with jitter; use Retry-After where present; max attempts.
    • Instrument metrics and alerts for throughput, errors, 429s, timeouts.
    • Document the runbook: how to re-run from checkpoint; how to drain the dead-letter queue.

    FAQs

    What page size should we start with?
    Start modest and measure total job time, not just per-page speed. Follow
    @odata.nextLink when it appears, and use
    Prefer: odata.maxpagesize=... for smaller chunks if pages are slow.
    What’s the difference between throttling and backoff?
    Throttling sets your steady request rate so you stay under BC limits.
    Backoff is a temporary slow-down when the API signals “too busy” — you pause
    longer between retries, then resume normal speed.
    How do idempotency keys prevent duplicates?
    Each write carries a unique, stable external ID. If a retry sends the same key again, the
    target treats it as the same logical operation — update or no-op, not a new record.
    Don’t rely on External Document No.; it isn’t unique by default.
    Which errors should we retry?
    Retry transient issues (timeouts, 429, 502, 503) with exponential backoff;
    honour any Retry-After header. Don’t retry validation or business-rule errors —
    surface them for a human fix.
    Does Business Central have hard numbers for API limits?

    Yes. Design to the documented caps and monitor them:

    • Production speed cap: ~600 requests/min per environment
    • Sandbox speed cap: ~300 requests/min per environment
    • Max concurrent OData operations: 5
    • Queue size: ~95; Max batch operations: 100
    • OData operation timeout: ~8 minutes
    • Per-user window: ~6,000 requests per 5-minute sliding window

    How we can help

    We’ve implemented these patterns across multiple Business Central integrations for SMEs. If you want a second pair of eyes or a quicker route to a stable month-end, book a 30-minute Integration Review. We’ll assess your current flow and give you a practical action plan.

    Enter your details below or call us on +44 (0) 1782 976577