There’s no free-text search endpoint for contacts. You can’t pass a q or search term and get fuzzy matches back. The Contacts API filters by exact field values: an email address, a phone number, a group ID, or a source. Knowing that upfront saves you from hunting for a parameter that doesn’t exist, so this page shows what the 7 list filters do and how to match names in your own code.
Search contacts by email or phone number
Section titled “Search contacts by email or phone number”Pass the email or phone_number query parameter on GET /v3/grants/{grant_id}/contacts to find a contact by one of their addresses. The email filter matches across all 6 providers; phone_number works on Google, IMAP, iCloud, Yahoo, and EWS but not Microsoft. Both match the stored value, so they’re best when you already know the exact address.
The request below filters the address book to contacts holding the given email address. The response is the same data array you get from an unfiltered list, narrowed to matches.
curl --compressed --request GET \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>'Both SDKs accept the filters through query_params. The Node.js and Python calls below send a phone_number lookup, which returns every contact storing that number across its phone_numbers array.
const contacts = await nylas.contacts.list({ identifier: "<NYLAS_GRANT_ID>", queryParams: { phoneNumber: "+1-555-555-5555" },});console.log(contacts.data);contacts = nylas.contacts.list( "<NYLAS_GRANT_ID>", query_params={"phone_number": "+1-555-555-5555"},)print(contacts.data)Filter contacts by group or source
Section titled “Filter contacts by group or source”Narrow results by where a contact lives using group and source. The group parameter takes a Contact Group ID and works on Google and Microsoft, not EWS. The source parameter accepts address_book (saved contacts, the default), domain (the organization directory), or inbox (people Nylas generates from messages). Combining the two scopes a query to one segment of the address book.
The request below returns saved contacts from a single group. Use source=address_book to skip the generated inbox entries so you only see people the user saved.
curl --compressed --request GET \ --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/contacts?group=friends&source=address_book' \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>'The SDK call mirrors that filter. For IMAP and iCloud you can pass a comma-separated source such as address_book,inbox to merge both segments in one request; the other 4 providers accept a single source value only.
contacts = nylas.contacts.list( "<NYLAS_GRANT_ID>", query_params={"group": "friends", "source": "address_book"},)print(contacts.data)Find a contact by name
Section titled “Find a contact by name”The API has no name-search parameter, so to find someone by name you list contacts and filter the given_name and surname fields in your own code. This is the honest tradeoff: name matching happens client-side, on whatever page of results you fetched. The default page size is 30 contacts and the maximum is 200, so a name search only sees the page you loaded unless you paginate through the rest.
The snippet below fetches one page and checks whether each name field contains the search term, case-insensitive. It returns matches from those 30 contacts, which is fine for small address books but misses anyone on a later page.
const page = await nylas.contacts.list({ identifier: "<NYLAS_GRANT_ID>", queryParams: { limit: 50 },});
const term = "miller";const matches = page.data.filter((c) => { const name = `${c.givenName ?? ""} ${c.surname ?? ""}`.toLowerCase(); return name.includes(term);});console.log(matches);The Python version does the same local filter. For an address book larger than 200 contacts, loop through every page first (see the pagination pattern below), then run the match over the combined set so no one gets skipped.
page = nylas.contacts.list( "<NYLAS_GRANT_ID>", query_params={"limit": 50},)
term = "miller"matches = [ c for c in page.data if term in f"{c.given_name or ''} {c.surname or ''}".lower()]print(matches)Things to know about searching contacts
Section titled “Things to know about searching contacts”Filtering is exact, not fuzzy. The email, phone_number, and group parameters match stored values, so partial typing won’t surface partial matches the way a search box would. Real fuzzy search means listing contacts and ranking them yourself, which is why an autocomplete picker needs a local cache rather than a live call per keystroke. The 7 available filters are email, phone_number, source, group, recurse, limit, and page_token.
A few provider behaviors shape what’s searchable. On Microsoft, the recurse=true flag pulls contacts from a group’s subgroups, but the recursion goes only 1 level deep, so deeper nesting still needs separate calls. Google polls contacts every 5 minutes, so a contact added seconds ago may not appear in a filter result yet. EWS doesn’t support group filtering or the inbox source at all, which limits how you segment Exchange address books.
For large address books, pagination isn’t optional: the maximum page is 200 contacts, and a 5,000-contact directory takes 25 requests to read in full. If you’re building autocomplete, cache the full contact list locally and filter in memory rather than calling the API on every keystroke. The list and sync contacts guide covers the webhook-driven cache that keeps that local copy current. For the exact filter semantics per provider, see Microsoft’s list contacts reference and Google’s People API documentation.
Paginate through results
Section titled “Paginate through results”When a filter or name search spans more than one page, follow the cursor. Each list response includes a next_cursor value; pass it back as the page_token query parameter to fetch the next 200 contacts. Repeat until next_cursor is absent. This is the only way to search a full address book, since every filter still respects the page limit.
The request below fetches the second page using a cursor from a prior response. Loop this until no cursor comes back, accumulating contacts so your client-side name or field match runs over the complete set.
curl --compressed --request GET \ --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/contacts?limit=200&page_token=<NEXT_CURSOR>' \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>'What’s next
Section titled “What’s next”- List and sync contacts for recipient autocomplete and webhook-driven caching
- How to list Google contacts for Google-specific scopes and quotas
- Filter contacts with contact groups for group IDs and membership
- Manage contacts with the Contacts API for the full field schema
- Contacts API reference for every endpoint and parameter