QbXML for Querying for Customers, with iterators
Contents |
Iterator Overview
You might find that when querying for large result sets, you get HTTP time-out errors, Web Connector time-out errors, web server time-out errors, or web server data-overflow errors. This is because the resulting XML from some queries can get so large (imagine sending 15,000 customer XML records over the network, it's usually a several megabyte XML document) that the SOAP server/web server/Web Connector rejects the response or refuses to wait for that much data to transfer.
You can solve this problem using iterators. Iterators ask for a query to be split into multiple result sets, and you then retrieve each part of the entire result set individually, so the data transfer for each part is much smaller and faster.
Iterators are documented here, and further documented on page 115 of the QBSDK_ProGuide.pdf in the SDK documentation.
Below is an example of using iterators to query for a customers, fetching five (5) customers at a time. This query fetches customers with the following criteria:
- Modified after January 29th, 1984
- OwnerID is 0 (this just makes sure we get back DataExt values (custom fields) defined in the GUI)
Your initial request will look as below. Notice that we declare the iterator=“Start” attribute to start our iterator:
Starting the Iterator
<?xml version="1.0" encoding="utf-8"?> <?qbxml version="5.0"?> <QBXML> <QBXMLMsgsRq onError="continueOnError"> <CustomerQueryRq requestID="1" iterator="Start"> <MaxReturned>5</MaxReturned> <FromModifiedDate>1984-01-29T22:03:19</FromModifiedDate> <OwnerID>0</OwnerID> </CustomerQueryRq> </QBXMLMsgsRq> </QBXML>
QuickBooks will send you back a response containing the first five (5) customers that looks like below. Notice that QuickBooks has sent us back an iteratorID=”…” attribute and an iteratorRemainingCount=”…” attribute, indicating the ID used to identify the iterator, and the number of items left in the iterator.
Initial Iterator Starting Response
<?xml version="1.0" ?> <QBXML> <QBXMLMsgsRs> <CustomerQueryRs requestID="1" statusCode="0" statusSeverity="Info" statusMessage="Status OK" iteratorRemainingCount="18" iteratorID="{eb05f701-e727-472f-8ade-6753c4f67a46}"> <CustomerRet> <ListID>110000-1232697602</ListID> <TimeCreated>2009-01-23T03:00:02-05:00</TimeCreated> <TimeModified>2009-01-23T03:00:02-05:00</TimeModified> <EditSequence>1232697602</EditSequence> <Name>10th Customer</Name> <FullName>10th Customer</FullName> <IsActive>true</IsActive> <Sublevel>0</Sublevel> <Balance>0.00</Balance> <TotalBalance>0.00</TotalBalance> <SalesTaxCodeRef> <ListID>10000-1232327562</ListID> <FullName>Tax</FullName> </SalesTaxCodeRef> <ItemSalesTaxRef> <ListID>10000-1232327661</ListID> <FullName>Out of State</FullName> </ItemSalesTaxRef> <JobStatus>None</JobStatus> </CustomerRet> ... 4 more customer records will go here ... </CustomerQueryRs> </QBXMLMsgsRs> </QBXML>
You'll then check the iteratorRemainingCount attribute and, if it's greater than 0, send your next request using the iteratorID attribute. Remember, every subsequent request request using this iterator must use the same search criteria and that search criteria must be sent with every request.
So, in this case, every request for the next part of the iterator will resend the <MaxReturned>, <FromModifiedDate>, and <OwnerID> elements. Notice that we send the returned iteratorID=”…” attribute from the previous response, and declare the iterator=“Continue” attribute indicating we want to continue fetching from an existing iterator:
Continuing the Iterator
<?xml version="1.0" encoding="utf-8"?> <?qbxml version="5.0"?> <QBXML> <QBXMLMsgsRq onError="continueOnError"> <CustomerQueryRq requestID="2" iterator="Continue" iteratorID="{eb05f701-e727-472f-8ade-6753c4f67a46}"> <MaxReturned>5</MaxReturned> <FromModifiedDate>1984-01-29T22:03:19</FromModifiedDate> <OwnerID>0</OwnerID> </CustomerQueryRq> </QBXMLMsgsRq> </QBXML>
This process will repeat until there are no more items left in the iterator. On each response, you'll check the iteratorRemainingCount attribute, and if it's greater than 0, you'll issue another request to get the next part of the iterator.
Eventually, you'll notice that the iteratorRemainingCount attribute will dwindle to zero (0) at which point you'll stop issuing requests, because the iterator has no more records to return. If you do issue another request, you'll get back an error as the iterator has, at this point, expired.
When the Iterator has Finished
<?xml version=“1.0” ?> <QBXML> <QBXMLMsgsRs> <CustomerQueryRs requestID="5" statusCode="0" statusSeverity="Info" statusMessage="Status OK" iteratorRemainingCount="0" iteratorID="{eb05f701-e727-472f-8ade-6753c4f67a46}"> <CustomerRet> <ListID>1B0000-1232697643</ListID> <TimeCreated>2009-01-23T03:00:43-05:00</TimeCreated> <TimeModified>2009-01-23T03:00:43-05:00</TimeModified> <EditSequence>1232697643</EditSequence> <Name>Pat Daniels</Name> <FullName>Pat Daniels</FullName> <IsActive>true</IsActive> <Sublevel>0</Sublevel> <Balance>0.00</Balance> <TotalBalance>0.00</TotalBalance> <SalesTaxCodeRef> <ListID>10000-1232327562</ListID> <FullName>Tax</FullName> </SalesTaxCodeRef> <ItemSalesTaxRef> <ListID>10000-1232327661</ListID> <FullName>Out of State</FullName> </ItemSalesTaxRef> <JobStatus>None</JobStatus> </CustomerRet> ... 4 more customer records will go here ... </CustomerQueryRs> </QBXMLMsgsRs> </QBXML>