Skip to content
Skip to main content

Build recipient autocomplete

Last updated:

Type two letters into Gmail’s “To:” field and it suggests the right person before you finish their name. Users now expect that everywhere, and a blank compose box with no suggestions feels broken. Building it per provider is the painful part: Google’s People API and Microsoft Graph each have their own auth, paging, and field names, and neither one covers the other’s contacts.

The Nylas Contacts API returns both address books through one endpoint with one schema. You pull contacts once, cache them, and filter on the client as the user types. That’s the whole pattern, and it works the same whether the signed-in account is Gmail, Outlook, or Exchange.

How do I build recipient autocomplete from contacts?

Section titled “How do I build recipient autocomplete from contacts?”

Recipient autocomplete is a 3-step pattern: fetch the user’s contacts once, cache them in memory, then filter that cache by prefix on every keystroke. You hit the API a handful of times at load, not once per character, so suggestions render in under a millisecond instead of waiting on a network round trip for each letter typed.

The steps below map directly to the sections in this recipe:

  1. Fetch all contacts from the Contacts API and page through the results.
  2. Cache them in a flat list of { name, email } entries keyed for fast prefix matching.
  3. Filter that cache as the user types, debouncing input so you rank and render only the top few matches.

The same flow covers Google, Microsoft, and Exchange because the API returns one unified contact object. You write the picker once instead of branching on provider.

Send a GET request to /v3/grants/{grant_id}/contacts to read the user’s address book. The response is a data array of contact objects, each carrying an emails array plus name fields like given_name and surname. The default page size is 30 contacts and the maximum is 200, so you typically page through everyone once at load and cache the result.

The request below lists contacts for a grant. Raise limit to 200 to cut the number of round trips, then follow the next_cursor to fetch the next page.

Pass the next_cursor value as the page_token query parameter to read the next page. Keep looping until a response comes back without a next_cursor, which means you’ve reached the end of the address book.

How do I cache contacts for fast autocomplete?

Section titled “How do I cache contacts for fast autocomplete?”

Cache contacts client-side because per-keystroke API calls are too slow and too expensive. A network round trip adds 50 to 300 milliseconds per request, and a fast typist fires 5 or more keystrokes per second, so live calls both feel laggy and burn through rate limits. Filtering an in-memory list instead returns matches in well under a millisecond.

Flatten the API response into one entry per email address, then filter that array by prefix. Debounce the input by about 150 milliseconds so you match on a pause, not on every letter. The snippet below builds the index once and exposes a search function the input handler calls.

Refresh the cache on a timer, every 5 minutes is a reasonable default, so contacts the user adds mid-session still surface. For accounts that send webhooks, the contact.updated trigger lets you patch the cache on change instead of rebuilding it on a fixed interval.

How do I include people the user writes to but hasn’t saved?

Section titled “How do I include people the user writes to but hasn’t saved?”

Set the source query parameter to inbox to surface everyone the user has corresponded with, beyond saved contacts. The API recognizes 3 sources: address_book (contacts the user saved), domain (the organization directory), and inbox (auto-generated contacts pulled from sent and received mail). The inbox source is what fills a picker with real correspondents who were never added by hand.

The request below merges the address_book and inbox sources so your index covers both saved people and frequent correspondents. Compound source filters like this work on IMAP and iCloud. For other providers, query each source on its own and merge the results yourself.

Coverage of these auto-generated contacts varies by provider. The inbox source needs the contacts.other.readonly scope on Google and People.Read on Microsoft, and Exchange (EWS) doesn’t support it at all. For EWS, build the picker on saved contacts plus recipients you pull from the Messages API instead. The scopes reference lists the exact strings.

A good recipient picker is more than a prefix filter. A few details separate a usable index from a frustrating one, and most of them come down to how you shape the cached data before it ever reaches the dropdown.

Deduplicate by email, not by name. One person often appears more than once, as a saved address_book entry and again as an auto-generated inbox contact. Lowercase every address and key your index by email address so the same person shows up once. Two contacts can share a display name, so never deduplicate on name alone.

Rank by how often the user writes to someone. Alphabetical order buries the people the user writes to most. Pull recent sent messages from the Messages API, count how often each address appears as a recipient, and sort matches by that frequency. The 5 to 10 most-contacted addresses cover the majority of real sends, so weighting by send count makes the first suggestion correct far more often.

Don’t expose the whole address book. Return suggestions only after the user types at least 2 characters, and cap the dropdown at 5 to 8 entries. Dumping every contact into the page leaks the user’s full network into client-side state and slows rendering. Filter server-side if your address books run to thousands of entries.

Watch the provider quirks. Google organizes contacts into groups and polls for changes every 5 minutes, so a contact added seconds ago may not appear yet. Microsoft Graph caps a single contact at 3 email addresses and leaves the email type as null, so don’t rely on work or home labels to pick a primary address. Build your ranking on send frequency instead of provider labels.