What's the difference between batch and real-time sales tax calculation?
Both patterns produce a tax amount on a transaction. They differ in when the calculation runs, who is waiting on the result, and which infrastructure layer carries the load. Six dimensions matter when an engineering team is splitting the calculation surface at a $20-80M DTC brand.
| Dimension | Real-time | Batch |
|---|---|---|
| When it runs | Inline with the customer-facing transaction. The checkout request blocks on the tax response. | On a fixed cadence (hourly, end-of-day, end-of-month), pulling a window of transactions and producing tax results in bulk. |
| Who is waiting | The customer at the cart or checkout page. | No one. The job runs against persisted transactions on the back office's schedule. |
| Latency budget | 150-300ms at p95 for the tax slice of a 1.5-3 second checkout target [1]. | Minutes to hours, bounded by job duration and downstream filing deadlines, not customer wall-clock. |
| Infrastructure | Tax API on the checkout request path, with cached fallback and timeout handling.Tax API on the checkout request path, with cached fallback and timeout handling. | Bulk calculation endpoint, idempotency cache, reconciliation pass, writeback to the order or invoice record. |
| Failure handling | Fail-open with cached rate, fail-closed at the order layer, or post-order recalculation. | Retry with exponential backoff, dead-letter on terminal failure, manual reprocessing from the job log. |
| Instrumentation | Request rate, p95 latency, error rate at the API tier. | Job duration, records-processed-per-second, reconciliation variance count at the batch tier. |
The two architectures are not mutually exclusive. At $30M the calculation layer is typically real-time on the DTC checkout and batch on a handful of back-office flows: settlement reconciliation, B2B invoice runs, end-of-day reconciliation. At $80M most brands run both paths simultaneously, with the batch path producing the ground-truth tax record that feeds reconciliation and filing. The engineering question is not "real-time or batch" but "which transactions belong on which path, and how do the two reconcile."
Which flows belong on real-time, and which belong on batch
The batch-versus-real-time decision splits cleanly by who is waiting. A customer at checkout needs the tax now, so the DTC checkout path is real-time. Nobody is waiting on a B2B invoice billed on net-30 terms, nobody is waiting on a marketplace settlement file that arrives daily from Amazon or Walmart, nobody is waiting on the end-of-day ground-truth recalculation that feeds the next month's filing. Those flows are batch. Brands that ran everything real-time paid for calculation capacity they did not need; brands that batched the checkout broke the customer experience. The split is not about cost or sophistication. It is about whether someone is staring at a screen waiting for the number.
Three flows require real-time calculation.
- DTC checkout display. The customer sees the exact tax line on the cart and checkout summary before completing the order. The calculation must resolve within the 150-300ms p95 budget that the broader 1.5-3 second checkout target allows for the tax slice[1]. Anything slower drags conversion at the moment of purchase.
- Payment authorization. The final amount sent to the payment processor at authorization must include tax. Authorizing $98.50 and then collecting $103.50 because tax was added post-auth is a payment-authorization mismatch, which most processors treat as a chargeback risk. The authorized amount has to be locked at the moment authorization fires.
- Order persistence at checkout. The tax amount displayed and authorized has to land on the persisted order before the order moves to fulfillment. Downstream systems (inventory reservation, fulfillment routing, customer notifications) read from the order record, and a missing or pending tax field there introduces ambiguity into the order lifecycle.
Four flows belong on the batch path.
- B2B invoicing on net terms. A B2B order invoiced net-30 does not need the tax line resolved at order entry. The invoice is the customer-facing artifact, and the tax line on the invoice can be calculated against the rates in effect at invoice time, not at order time. A nightly or weekly batch run against the day's pending invoices is the standard cadence.
- Marketplace settlement reconciliation. Settlement files from Amazon Selling Partner[3], Walmart Marketplace[4], and TikTok Shop arrive on the marketplace's schedule, typically daily, sometimes weekly. The brand's job is to tie each settlement line back to an order, capture the tax the marketplace already collected, and write the tax record into the brand's ground-truth log. No customer is waiting on this; it has to run before the close, not before the order.
- End-of-day ground-truth recalculation. The real-time checkout produces a customer-facing total under p95 budget pressure, which means cached rates and fail-open fallbacks come into play. The batch path runs the same orders through full taxability resolution and current rate tables, produces the ground-truth tax record, and writes any variance into the reconciliation log. The customer-displayed total never changes asynchronously; the ground-truth record does.
- Historical backfill. When a brand adds a new state to its filing footprint, registers retroactively, or onboards a new tax engine, the existing transaction history needs to be recalculated against the rate tables and taxability rules in effect at the original transaction date. This is a one-time or rare bulk job, run from a static order export, with no customer-facing latency requirement.
Platforms like TaxCloud expose a calculation API usable in both modes from a single integration: as the real-time endpoint the checkout extension calls on Shopify or Shopify Plus, and as the bulk endpoint the batch job iterates against for the back-office flows. The same idempotency, jurisdiction logic, and rate source apply across both paths, with no duplicate integration surface for the engineering team to maintain.
How real-time and batch interact without double-recording the same transaction
The double-recording hazard is the failure that shows up at month-end. The same logical transaction gets a real-time calculation at checkout and then a batch calculation in the nightly job, and the tax engine has two records for one order. At low volume the duplication shows up as a $20 variance on the reconciliation pass that nobody investigates. At $50M-plus volume the duplication becomes a structural problem: the calculation log overstates jurisdictional sales by the duplicate count, the SST filing pulled from that log overstates taxable receipts, and the controller's monthly close runs three days longer than it should because the reconciliation pass is chasing duplicates that should never have been written.
The fix is a single idempotency key, derived from inputs both paths can see, checked by both paths before write. The shape:
- Key inputs are stable across paths. The key hashes over the brand's internal order ID, the line items at calculation time (product or variant ID, quantity, unit price, taxability code), and the ship-to address. The real-time call hashes over those inputs at checkout; the batch call hashes over the same inputs from the persisted order record. If neither order nor line items have changed between the two events, both paths produce the same key. The semantics follow IETF RFC 9110 §9.2.2 on idempotent operations.[2]
- The batch path checks for an existing real-time record before writing. Before the batch worker writes a calculation result for an order, it looks up the idempotency key. If a real-time record already exists for that key, the batch worker either skips the write entirely (treating the real-time record as authoritative for the customer-displayed total) or writes a variance record that flags the difference for the reconciliation pass to resolve.
- The real-time path emits its key into the same idempotency cache the batch worker reads. This is the load-bearing detail. If the real-time and batch paths use separate idempotency caches, the check is theatre; both paths will still write. The cache has to be a single source of truth, typically a Redis or DynamoDB store keyed by the idempotency hash and indexed by order ID.
- The reconciliation pass catches anything the idempotency check missed. Cache evictions, partial failures, retries that hit a different cache region: any of these can produce a duplicate write that slipped the check. The reconciliation pass on the calculation log surfaces duplicate keys, paired calculation records on the same order ID, and any record where the real-time and batch results materially disagree.
The batch cadence a $30-70M DTC brand actually runs
The cadence brands actually run at $30-70M is end-of-day batch for reconciliation and exception handling, not real-time everywhere. The real-time path handles the customer-facing calculation at checkout. The batch path handles the ground-truth record and the corrections. Hourly batch shows up later, around $100M, when settlement file volume or B2B invoice cadence justifies the additional infrastructure. End-of-month batch is uncommon at the mid-market band; it surfaces in older NetSuite or QuickBooks Online setups where the brand has not yet pulled the calculation step out of the close cycle.
The cadence-by-revenue-band shape:
| Revenue band | Typical batch cadence | What drives the cadence |
|---|---|---|
| Under $20M | End-of-day for marketplace settlements only; everything else stays on the real-time path or inside Shopify Tax. | Volume does not justify a separate back-office calculation pipeline. The brand has not yet outgrown the checkout-native tax surface. |
| $20-30M | End-of-day batch on settlement reconciliation, B2B invoice runs (if any), and the ground-truth recalculation. | First inflection. The brand registers in more states, adds B2B or wholesale, and the close starts to take longer than the controller can absorb. |
| $30-70M | End-of-day batch as the steady state, with the ground-truth recalculation feeding the next-day reconciliation pass and the next-month filing. | The dual-path pattern is now standard. Real-time for checkout, batch for everything else, and the reconciliation pass closes the loop. |
| $70-100M | End-of-day batch with hourly settlement processing for high-frequency marketplace flows. | Marketplace settlement files start arriving on a sub-daily cadence, especially as TikTok Shop and Walmart channels grow. B2B invoice runs may shift to twice-daily. |
| Over $100M | Hourly or sub-hourly batch for settlement, B2B invoicing, and variance detection; end-of-day batch reserved for the close-aligned reconciliation pass. | Volume and channel mix make the latency from order to ground-truth record material. The brand cannot afford to discover a multi-thousand-dollar reconciliation variance the morning of the close. |
The cadence decision is not arbitrary. It is set by the slowest tolerable lag between the customer-facing transaction and the ground-truth record. End-of-day batch is the standard at $30-70M because the close is the natural cadence for the controller's exception-handling work. Pulling the cadence sub-daily before the brand has the operational surface to act on sub-daily exceptions just creates more reconciliation noise without faster decisions.
A well-designed calculation API runs in both real-time and bulk modes against the same idempotency cache and jurisdiction logic, which is what the dual-path pattern at this band requires. A brand running end-of-day batch iterates the bulk endpoint over the day's persisted orders, writes the calculation results to the order record alongside the existing real-time results, and pulls the variance count from the reporting API into the monthly close.
How the batch path handles in-flight corrections
In-flight corrections are the failure mode the batch path has to handle explicitly, not by default. A refund posted at 11:47pm on Tuesday lands in the same batch window as the original order from 8:14am Tuesday morning. An exchange posted at 11:47pm Tuesday for an order from Monday lands across windows. The rule that resolves the design choice is one sentence: net within the window where possible, carry to the next window with an explicit adjustment record where not.
Three correction patterns and how each is handled:
- Refund inside the same batch window. The original order and the refund both land in the day's batch run. The batch worker nets them within the window: the calculation log shows the original tax amount, the refund's tax reversal, and the net result. The reconciliation pass sees a paired record (one positive, one negative, same order ID), confirms the net to zero or to the partial-refund amount, and writes a single net-result entry to the close-aligned reconciliation log.
- Refund crossing batch windows. The original order ran through Monday's batch. The refund posts Tuesday at 11:47pm. The Tuesday batch picks up the refund as a standalone adjustment, looks up the original order by ID, writes a tax-reversal record linked to the original, and surfaces the cross-window adjustment in the reconciliation log. The original order's calculation record stays untouched; the adjustment is the artifact that closes the variance.
- Exchange or partial return crossing batch windows. Same shape as the cross-window refund, with the additional complexity that an exchange may produce both a tax reversal (on the returned item) and a new tax calculation (on the replacement item). The batch worker writes both as linked adjustments against the original order, the reconciliation pass surfaces the pair, and the controller decides whether the exchange is treated as a single net transaction or as a refund-plus-new-order pair for filing purposes.
The bigger the customer's downside risk on a correction, the bigger the upfront caveat. Multi-state corrections that cross filing periods (a December refund posted in January for a Q4 order) are the highest-stakes case. The refund's tax reversal hits a filing period that may already be closed, which forces a prior-period adjustment on the next filing or an amended return. The batch path has to flag these explicitly so the close-side workflow catches them before the filing goes out. How do you build sales tax audit-readiness into your monthly close? covers the close-side cadence that depends on a complete, on-time correction log.
The dual-path operating model at multi-state scale
The dual-path operating model is the steady state for a $30-70M DTC brand once the calculation surface has split. The real-time path handles the customer-facing calculation on Shopify, Shopify Plus, BigCommerce, or the custom checkout. The batch path handles the back-office flows: marketplace settlement reconciliation, B2B invoicing, end-of-day ground-truth recalculation, in-flight correction handling, historical backfill when needed. The reconciliation pass connects the two, surfaces variance between the real-time and batch results on a per-order basis, and produces the ground-truth calculation log that the monthly close and the next state filing draw from.
The operational properties the dual-path buys are:
- Lower checkout latency. The real-time call is bounded by the 150-300ms p95 budget. The batch path absorbs the heavier work (full taxability resolution, multi-jurisdiction rate-table reads, audit-log persistence) off the customer-facing wall clock.
- Decoupled engine availability. A tax-engine outage on the batch path produces a delayed reconciliation, not a checkout outage. The real-time path uses cached fallback when the API breaches its budget. The batch path catches up when the engine recovers.
- A ground-truth record built on its own path. The batch-produced calculation log is the document filing and audit are built on. Producing it as a separate path, rather than as a side effect of the real-time call, surfaces calculation lag and rate-source variance as observable metrics rather than reconciliation anomalies discovered at month-end.
At a multi-state, multi-channel steady state, the question is no longer whether to split real-time and batch. It is what the operating model looks like once you do. Platforms like TaxCloud handle this through a single calculation API usable in both real-time and bulk modes, native Shopify, Shopify Plus, and BigCommerce integration on the real-time path, idempotency support shared across both paths, and a reporting API that exposes the per-order calculation log for the batch reconciliation that closes the loop.