Run a query
Send aGET request to /services/data/v{version}/query with the SOQL statement in the q parameter. Replace {version} with a served API version. URL-encode the statement through your client’s query-parameter API.
For example, query the account from the record guide:
totalSize, done, and records in the JSON response. A successful query with no matches returns an empty result. That result may also reflect the actor’s record visibility.
Use ORDER BY when a test depends on row order. Add a unique tie-breaker such as Id when your primary sort field can have duplicate values. A query without an explicit order is unsuitable for an assertion about which row comes first.
Follow every result page
After processingrecords, check done. If it is false, request the returned nextRecordsUrl through the same authenticated client and process that page. Repeat until done is true.
Keep the actor and app connection unchanged while following a locator. Do not rebuild the query with increasing OFFSET, append the original q to locator requests, or infer completion from a short page.
The replica binds each locator to the user who ran the query. Locators expire two days after the query opens. An invalid, expired, or other user’s locator returns INVALID_QUERY_LOCATOR. Restart the query when your application can safely do so.
A page holds up to 2,000 records. To change that, send Sforce-Query-Options: batchSize=N with N from 200 to 2,000.
For assertions over changing data, complete one query’s pages before making further writes. The locator does not add records inserted after it opens. Ordinary query pages omit records deleted between pages, while totalSize stays unchanged. A locator is not an immutable copy of every record value.
Query relationships
Use describe’s relationship names. A lookup field name and the corresponding relationship name are different request values. To read a contact’s parent account, use a parent path:Aggregate records
For a count, useSELECT COUNT() FROM Account. Read the count from totalSize. COUNT() does not produce a normal record list.
For grouped results, select the group field and an aggregate:
EXCEEDED_ID_LIMIT. Add LIMIT to keep it on one page.
Inspect deleted records
Send the statement to/services/data/v{version}/queryAll to include deleted and archived records. For a deleted test account, select Id, Name, and IsDeleted with a filter on its returned ID.
Follow nextRecordsUrl as returned even when it uses /query/. The locator retains the original queryAll behavior. Ordinary /query omits deleted records.
Diagnose query failures
InspecterrorCode and message before retrying. Confirm the object, field, and relationship names against describe for the same actor and API version.
The replica supports selected SOQL filters, parent paths, child queries, grouping, aggregates, date literals, and semi-joins. It returns MALFORMED_QUERY with the message <feature> is not supported for FOR UPDATE, UPDATE TRACKING, UPDATE VIEWSTAT, WITH SYSTEM_MODE, WITH DATA CATEGORY, other WITH filters, DISTANCE(), FORMULA(), and a FROM list with relationship aliases. WITH SECURITY_ENFORCED also fails. WITH USER_MODE, FOR VIEW, and FOR REFERENCE are accepted and change nothing.
Do not use the explain parameter to validate Salesforce query plans. The replica does not implement that behavior. See the known differences for other query and permission gaps.