QuickBooks Desktop API Performance — Batching, Queuing, and Multiple Company Files
QuickBooks Desktop has a rule that shapes every integration built on it: one open company file at a time. LedgerCrossing is the single door through which all your integrations reach QuickBooks, so it runs one queue into one QuickBooks session — and schedules that session intelligently.
This page explains what that means for your integration's performance, and the handful of practices that keep everything fast.
The two costs
| Operation | Typical cost |
|---|---|
| A request against the company file that's already open | 0.2–5 seconds |
| Switching company files (QuickBooks closes one file, opens another) | 15–20 seconds |
That second number is QuickBooks itself, not LedgerCrossing — and it's why everything below boils down to one idea: don't pay the switch more often than you must.
What LedgerCrossing does for you automatically
- File affinity — requests for the currently open file are served first.
- Switch grace — before paying a file switch, LedgerCrossing holds the open file about a second for your next request. Sequential integrations (send a request, read the response, send the next) get their whole burst served in one run without any client-side changes.
- Fairness — no file is held longer than 60 seconds while another file's requests wait. Two integrations working different files take turns in 60-second windows instead of starving each other.
- Back-pressure — a request that can't be served within 60 seconds is
rejected with HTTP 429 and a
Retry-Afterheader rather than hanging.
What your integration should do
One company file? Nothing. Plain sequential request-response is the fully supported normal case.
Many operations at once? Either put multiple request elements in one qbXML envelope (one HTTP call, one combined response), or send independent requests concurrently — both keep the file open across the whole set.
More than one company file? It works out of the box — callers take turns in fair windows. But if you control the schedules, run different-file jobs at different times and nobody ever waits.
Reading the signals
Every response tells you what happened in the queue:
Queue-Wait-Ms— how long the request waited its turn.File-Contention: true— present only when the wait was caused by another integration working a different company file. If you see this regularly, two of your systems are sharing QuickBooks time — consider scheduling them apart.- 429 +
Retry-After— the queue is saturated; wait the stated seconds and retry.
AI agents connected through LedgerCrossing's MCP server get the same information as a plain-language note in their tool results, so an agent can decide on its own to batch, pause, or come back later.
On the server, every cross-file collision is logged as a warning naming both
files and the wait time — one findstr "File contention" over the logs shows
you exactly when it happened and who was involved.
Ordering, and where the queue detail lives
Within one company file, requests are strictly FIFO. Across files, affinity
reorders them: a newer same-file request is served before an older other-file
request, until the fairness caps force a switch. Nothing starves — the
worst-case cross-file wait is bounded at about one hold window plus one switch —
but it does mean queue depth is not an ETA. To reason about your request,
read its Queue-Wait-Ms and File-Contention headers, not the global depth.
/health is liveness-only for remote callers. Since v1.6.0, a caller on
another machine gets {"success":true,"status":"ok"} and nothing more. The
detail — version, quickbooks_status, open_company_file, processing_file,
and queue_depth — moved to GET /api/v1/diagnostics, which requires your API
key. A client still reading those fields from /health gets missing fields
rather than an error, so this is worth checking in code you wrote before 1.6.0.
The split exists because the detailed payload names the company file's UNC path,
and that does not belong in an unauthenticated response.
Related: bounded queries
LedgerCrossing rejects unbounded queries before they reach QuickBooks (a query
for "all transactions" can hold the company file for minutes). Every query needs
one of: a <MaxReturned> cap, a key lookup (TxnID, ListID, RefNumber, FullName, …),
or an iterator that you page to completion. The rejection message names all
three fixes — agents self-correct from it.
Where this comes from
One queue, one QuickBooks session, one door for every integration — that's the design that makes the scheduling above possible, and it's what LedgerCrossing replaces the QuickBooks Web Connector with. Agents reach the same queue through the MCP server.
QuickBooks and QuickBooks Desktop are registered trademarks of Intuit Inc. LedgerCrossing is an independent product for QuickBooks Desktop and is not affiliated with or endorsed by Intuit.