POST /api/Contact/Query is the primary endpoint for retrieving Contact records by criteria other than ID. This workflow walks through three of the most common scenarios for partner integrations: incremental sync by modification date, tag-based or custom-field-based queries, and bulk export of large result sets.
If you have not read Pagination and Filtering, start there — this page builds on the structured-filter pattern introduced there.
Scenario
Your integration needs to retrieve Contacts that match some criteria. The most common cases for partners:- Incremental sync. Find Contacts modified since your last sync run to keep your local copy current.
- Targeted retrieval. Find Contacts matching a specific tag, custom field value, or attribute for a specific workflow (e.g., everyone tagged “Major Donor” for a major-gift campaign).
- Bulk export. Retrieve every Contact for an initial data load or reporting export.
Prerequisites
- A valid CRM+ API token — see Authentication.
- Understanding of the filter structure (
groups[].conditions[]) — see Pagination and Filtering. - The list of valid filter parameters and operators for the customer’s organization, retrieved from
GET /api/Contact/QueryOptions.
Step 1: discover valid filter parameters
Before constructing filters, retrieve the parameters and operators valid for the current organization.Contact Type, Last Modified Date, and similar parameters exist in most organizations but the exact set — including custom-field-derived parameters — varies.
Pattern 1: incremental sync by modification date
The most common use of Contact Query in a partner integration: find Contacts modified since the last sync run.JavaScript
- Sort by modification date ascending. Processing oldest-to-newest means that if the sync is interrupted partway through, you can resume from the highest
modifiedDateTimeUtcyou’ve fully processed. - Track the highest modification date observed. Use this as the floor for the next sync run, not the wall-clock time when you started the run. This avoids a race condition where records modified during the sync would be missed.
- Use
take=1000. Large page sizes minimize round trips and rate-limit budget consumption. See Rate Limits.
Picking the sync interval
Run the incremental sync on an interval driven by your customer’s tolerance for data staleness:Pattern 2: targeted retrieval by tag or custom field
Find Contacts matching a specific tag, custom field value, or attribute. Useful for partner workflows that target specific donor segments.By tag
By custom field value
parameter value for a custom field depends on the field’s name configured in the organization. Discover via GET /api/Contact/QueryOptions — see Pagination and Filtering.
By contact type and state
Pattern 3: bulk export
For an initial backfill or a full-export reporting use case, retrieve every Contact:JavaScript
- Empty
groups[]returns every Contact. includeArchived: trueincludes archived records — important for completeness in an initial load.- Sort by
idis the most stable sort field for resumability. If you sort bymodifiedDateTimeUtcon a full export and records get modified mid-export, you can re-process the same record or miss others; sorting byid(an immutable field) is safer for full exports.
Resumable exports
For very large exports (hundreds of thousands of records) that may take hours, persist the last successfully-processedid between batches. If the export is interrupted, resume from that ID rather than restarting:
JavaScript
skip-based loop with an ID-cursor-based loop also avoids the skip overhead — at high skip values, server-side query performance can degrade. ID-cursor pagination keeps each query bounded to a fresh range.
Confirm the exact parameter name (
Contact Id vs. Id vs. ContactId) by calling GET /api/Contact/QueryOptions. The value used above is illustrative; the live API’s enum is the authoritative source.Working with the response
POST /api/Contact/Query returns an abbreviated Contact representation in the list array — id, name, contactType, contactName, address, email, phone, and contactViewUrl. For most sync workflows this is enough; the email and phone in the result are sufficient to surface in your platform’s UI and to identify the donor for downstream processing.
If you need the full Contact record (with ContactIndividuals, all addresses, custom fields, contactReferences, etc.), there are two options:
Including archived contacts
By default,POST /api/Contact/Query excludes archived Contacts. Include them by setting includeArchived: true in the body:
modifiedDateTimeUtc, but the record is excluded from default queries. Without includeArchived: true, your sync sees the archived Contact as “missing” rather than detecting the archive event.
See Statuses and Lifecycle States for the broader treatment.
Performance considerations
take=1000for bulk operations. Each request returns up to 1,000 Contacts and consumes one rate-limit slot. At maximum throughput, you can retrieve 1.5 million Contacts per hour — well above the size of any typical nonprofit’s donor database.take=25ortake=50for interactive UIs. Smaller pages load quickly and avoid fetching records the user never sees.- Sort on indexed fields. Default sort orders are indexed. Custom
sortByvalues may be slow on large result sets. - Filter aggressively. A query that returns 500 Contacts after filtering is much cheaper than a query that returns 50,000 you discard client-side. Push filters into the request body wherever possible.
Error handling
Bulk queries are the most rate-limit-sensitive class of request. A misconfigured loop that paginates without checking termination conditions can burn the full hourly budget in minutes. Always assert that the loop is making progress (the
skip value increases or the result set is finite).
Where to go next
Query Donations by Date Range
The Gift equivalent of this workflow — same pattern, different resource.
Sync External Donations into Virtuous
Use this query pattern as a reconciliation backstop in a full sync architecture.
Pagination and Filtering
The underlying mechanics of
skip/take and the filter structure.Webhooks Overview
The preferred primary signal for change detection — use this query pattern as a backstop.