GET /users supports six filter parameters that cover most real-world scenarios. This page walks through the practical patterns: when to use which filter, how to combine them, and how to pair them with pagination and throttling for production-grade reads.
If you haven’t yet, skim the Users concept page first — this workflow page builds on the field shapes and endpoint structure documented there.
When to use this workflow
The six filter parameters
All parameters are optional. Combining multiple parameters narrows the result set (AND logic — a user must match all filters).
Scenario 1: Recently active volunteers
Goal: find users whose record has been updated in the last 30 days.JavaScript
What “updated” captures
Theupdated_at timestamp changes when the User record is modified — including:
- Profile updates (name, email, phone, address, etc.)
- Membership status changes
- Form completions (likely — confirm against live behavior)
- Profile field value updates
updated_at does NOT necessarily change when the user participates in a Project Date. Participation creates a Participation record, not a direct User modification. To detect “users who participated recently,” query Project Dates and follow back to users, not users?updated_after.
Use this filter for change detection
This is the primary filter for incremental sync patterns:JavaScript
updated_at actually seen — not to new Date() — so an interrupted sync resumes correctly. See Pagination: Advancing the checkpoint correctly.
Scenario 2: Users created in a time window
Goal: users created within a specific calendar month.JavaScript
The before vs after semantics
The exact inclusion of the boundary (≥ vs > on
after, ≤ vs < on before) isn’t strictly specified. Using exclusive next-month-start for created_before (e.g., April 1 for “March users”) prevents accidentally including April records.
Combining with name or email filters
Filter parameters AND together — adding more narrows the result:JavaScript
Scenario 3: Search users by name
Goal: find users matching a name fragment, typically for an interactive search box.JavaScript
name_like matches first OR last name
name_like=wayne matches:
first_name: "Wayne", last_name: "Smith"✓first_name: "Bruce", last_name: "Wayne"✓first_name: "Wayland", last_name: "Smith"✓ (substring match)
Handling ambiguous results
For interactive UIs, present multiple matches for user selection:JavaScript
Scenario 4: Filter by email domain
Goal: find users with emails from a specific domain (often for organizational segmentation).JavaScript
Why the secondary filter
email_like=@wayne.example is a substring match — it would also match @wayne.example.com if such an address exists. The client-side endsWith filter ensures the match is actually at the end of the email.
For most domains this isn’t an issue, but for short or common domain prefixes (e.g., @vol.com could match @volunteer-orgs.com), the secondary filter prevents false positives.
Scenario 5: Full dataset read
Goal: pull every user — typically for a one-time backfill or daily reconciliation.JavaScript
Page size considerations
Volunteer’s page size is platform-controlled (default 15) — not partner-controllable viaper_page. A customer with 10,000 users requires ~667 page requests for a full read.
For large customers:
See Rate Limits for the throttling pattern and Sync Architecture Patterns for the broader backfill design.
Scenario 6: Filters that don’t exist
A few useful filters that the API does not expose:
For any of these, the pattern is:
JavaScript
Putting it together
A reference function that handles the common cases robustly:JavaScript
list({...}) for cases the named methods don’t cover.
Performance and rate-limit considerations
A few practical notes for production-scale workloads:
See API Performance Tips for the broader patterns.
Common bugs to avoid
A few patterns that look right but produce subtle issues:Treating meta.total as a constant
meta.total is the count at the time of the request — if users are being added during your iteration, the count can change. For “did I get them all?” checks, use links.next === null as the truth, not “I read meta.total records.”
Ignoring null in prev/next
Use simple null-checks (if (page.links.next)), not string comparison. See Pagination: the "null" vs null warning.
Date format inconsistency
All datetime filter parameters expect ISO 8601 strings. Sending a Unix timestamp, aDate.toString() value, or a non-UTC ISO string can produce silent failures (returning everything because the date didn’t parse) or wrong results.
JavaScript
Forgetting URL encoding
URLSearchParams handles encoding for you. Manual concatenation breaks on spaces, ampersands, and other special characters:
JavaScript
Where to go next
Create or Update a User
Once you’ve found (or haven’t found) the user, the upsert workflow.
Find a User by Email
The focused workflow for email-based lookups.
Pagination
The full pagination pattern these workflows depend on.
Rate Limits
The throttling pattern for bulk reads.