Rate limits and throughput
Two limits protect the service, and one limit comes from QuickBooks itself. In practice the QuickBooks limit is the one you notice.
Project rate limit
Section titled “Project rate limit”Each project can make 1,000 requests per 10 seconds, across all its keys and end users. Above that, the API returns 429 RATE_LIMITED with details.scope: "project" and a Retry-After header in seconds. Nothing was queued, so the request is safe to retry after the wait.
Every authenticated response carries RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset (seconds) and RateLimit-Policy: 1000;w=10. The limit is enforced across our edge network, so Remaining and Reset are estimates from the server that answered. Treat a 429 with Retry-After as authoritative.
Per-IP limits
Section titled “Per-IP limits”Two limits protect against abuse before a key is even checked: 2,000 requests per 10 seconds from one IP address, and 20 failed authentications per minute from one IP address, after which that address is refused for 60 seconds. Both return 429 RATE_LIMITED with details.scope: "ip". A correctly configured server never meets them.
Per-connection queue limits
Section titled “Per-connection queue limits”Each connection (one company file) accepts:
- at most 200 pending requests (queued, waiting or sent), and
- at most 50 sync callers waiting at the same time.
Beyond either limit, the API returns 429 CONNECTION_QUEUE_FULL with details.reason (pending_requests or sync_waiters). Nothing was queued. This usually means a loop is sending requests faster than QuickBooks can answer them. Async requests do not count toward the sync waiter limit, so batches belong in async mode.
QuickBooks throughput
Section titled “QuickBooks throughput”QuickBooks Desktop processes one request at a time for each company file, on your customer’s computer. A simple read in a warm session takes well under a second; a large report can take minutes. So:
- Parallelize across end users, not within one. Requests for different company files run independently. Sending ten parallel requests for one file only lengthens its queue.
- Ask for more per request. Use
limitup to 150 on cursor lists, and filters such asupdatedAfter, instead of retrieving records one by one. - Use async mode for batches. Queue the writes with
Prefer: respond-asyncand let webhooks tell you as each one completes. The queue keeps order, and your workers do not sit on open HTTP calls. - Keep the session warm. A request sent within about 2 seconds of the previous one finishing reuses the open QuickBooks session and skips the Web Connector check-in.
Handling 429
Section titled “Handling 429”The SDKs retry 429 responses automatically, waiting for Retry-After, up to their retry limit (two retries by default). For raw HTTP:
- Read
Retry-Afterand wait that many seconds. - Retry the same request. For writes, reuse the same
Idempotency-Key. - If you keep hitting
CONNECTION_QUEUE_FULLfor one end user, reduce that end user’s concurrency rather than retrying harder.
Other size limits
Section titled “Other size limits”| Limit | Value | Error |
|---|---|---|
| Request body | 20 MiB | 413 PAYLOAD_TOO_LARGE |
| Values in one array query parameter | 100, unless the operation says otherwise | 400 INVALID_PARAMETER |
| Page size on cursor lists | 1 to 150 | 400 INVALID_PARAMETER |
Sync wait (Daapi-Timeout-Seconds) |
1 to 300 seconds | 400 INVALID_PARAMETER |
Async queue lifetime (Daapi-Queue-Ttl-Seconds) |
10 to 86400 seconds | 400 INVALID_PARAMETER |
| QuickBooks response | Very large unpaged responses | 422 QBD_RESPONSE_TOO_LARGE |