Some QuickBooks Desktop users have unexpectedly large amounts of data. In addition, QuickBooks Desktop is slow and memory-intensive. Without proper limits or pagination, a request might inadvertently retrieve tens of thousands of rows, which can take many minutes to return. To avoid overwhelming QuickBooks Desktop, you may need to split your requests into smaller chunks. This process is called pagination.

Pagination via the Node.js client library

Cursor-based pagination is not yet supported in the Node.js client library. Instead, we recommend batching your requests by date range and concatenating the results. This will avoid overwhelming QuickBooks Desktop by requesting only a subset of the data at a time.

For example, the following fetches all invoices from the provided date range in yearly batches:

Fetch invoices in yearly batches
import Conductor, { QbdTypes } from "conductor-node";

async function getQbdInvoices(
  endUserId: string,
  startDate: string,
): Promise<QbdTypes.InvoiceRet[]> {
  const conductor = new Conductor("{{YOUR_SECRET_KEY}}");

  const startYear = new Date(startDate).getFullYear();
  const endYear = new Date().getFullYear();

  // Fetch invoices in yearly batches to avoid overwhelming QuickBooks Desktop
  // because some users have tens of thousands of invoices.
  const allInvoices: QbdTypes.InvoiceRet[] = [];
  for (let year = startYear; year <= endYear; year++) {
    const yearStartDate = year === startYear ? startDate : `${year}-01-01`;
    const yearEndDate = `${year}-12-31`;

    const yearInvoices = await conductor.qbd.invoice.query(endUserId, {
      TxnDateRangeFilter: {
        FromTxnDate: yearStartDate,
        ToTxnDate: yearEndDate,
      },
    });

    allInvoices.push(...yearInvoices);
  }
  return allInvoices;
}

Pagination via the REST API

If you are using the REST API directly, you can paginate requests by following the instructions below.

Initiating pagination

To paginate a QuickBooks Desktop query, include the MaxReturned parameter in your request body, specifying the maximum number of records you want to receive in a single response. If the total number of records exceeds MaxReturned, the response will include two additional fields:

  1. IteratorID: A unique identifier for the current query.
  2. IteratorRemainingCount: The number of records remaining to be fetched.

Here’s an example of a pagination request:

Start pagination request with MaxReturned
curl -X POST https://api.conductor.is/v1/end-users/{{END_USER_ID}}/request/quickbooks_desktop \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer {{YOUR_SECRET_KEY}}" \
  -d '{
    "InvoiceQueryRq": {
      "MaxReturned": 2
    }
  }'

And here’s an example of a pagination response:

Example pagination response with IteratorID and IteratorRemainingCount
{
  "InvoiceQueryRs": {
    "IteratorID": "fce6b40f-9700-435b-949c-f2587af31536",
    "IteratorRemainingCount": 0,
    "InvoiceRet": [
      {
        "TxnID": "4E4-1703631996",
        "TimeCreated": "2021-01-01T00:00:00Z",
        "CustomerRef": {
          "ListID": "80000020-1703631977",
          "FullName": "Alice"
        },
        "RefNumber": "INV-1",
        "IsPaid": true,
        "TotalAmount": 100
      },
      {
        "TxnID": "4E4-1703631997",
        "TimeCreated": "2021-01-02T00:00:00Z",
        "CustomerRef": {
          "ListID": "80000020-1703631978",
          "FullName": "Bob"
        },
        "RefNumber": "INV-2",
        "IsPaid": false,
        "TotalAmount": 200
      }
    ]
  }
}

Continuing pagination

If IteratorRemainingCount is greater than 0, send another request with the same request body as the previous request, but include the IteratorID parameter on the root level of the request body, set to the value from the previous response.

The parameter IteratorID goes on the root level of the request body, not inside the query object.

Continue pagination request with IteratorID
curl -X POST https://api.conductor.is/v1/end-users/{{END_USER_ID}}/request/quickbooks_desktop \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer {{YOUR_SECRET_KEY}}" \
  -d '{
    // ❗ `IteratorID` is at root level of the request body
    "IteratorID": "fce6b40f-9700-435b-949c-f2587af31536",
    "InvoiceQueryRq": {
      "MaxReturned": 1000
    }
  }'

Considerations

  • The iterator can only be used within the same QuickBooks Web Connector session, which we keep open for 10 seconds. If the iterator is not used within that time, you will receive an error. Therefore, you must implement the pagination logic programmatically in your backend to ensure that the iterator is used within the 3-second window.
    • The short expiry is due to how the QuickBooks Web Connector handles pagination. Upon the initial request, the QuickBooks Web Connector requests the entire result set from QuickBooks Desktop, stores it in local memory, and returns only the first batch. As a result, the first request will take longer because QBD must process and assemble the entire result set. Subsequent requests will return immediately.
  • If you attempt to use the iterator after the previous request returned IteratorRemainingCount of 0, you will receive an error.