You want a contact picker that pulls from a user’s Google or Outlook address book. Doing it natively means two integrations: Google’s People API with its own OAuth scopes and people.connections.list paging, and Microsoft Graph with GET /me/contacts and a different permission model. The field names don’t match, and neither covers the other.
The Nylas Contacts API returns supported provider address books through one endpoint with one schema. The same request also covers hosted IMAP, EWS, and native iCloud or Yahoo CardDAV contacts, so your picker code doesn’t branch on the provider.
Why use the Nylas Contacts API instead of native contact APIs?
Section titled “Why use the Nylas Contacts API instead of native contact APIs?”Native contact APIs are provider-specific: Google exposes contacts through the People API, Microsoft through Graph, and iCloud and Yahoo through CardDAV. Each has its own auth, field names, paging, and change model. Nylas normalizes them into one Contact object.
The table contrasts the native setup with the unified call.
| Property | Google People API | Microsoft Graph | CardDAV | Nylas Contacts API |
|---|---|---|---|---|
| Endpoint | people.connections.list | GET /me/contacts | PROPFIND and REPORT | GET /v3/grants/{id}/contacts |
| Schema | Google field names | Graph field names | vCard | One unified Contact object |
| Providers | Google only | Microsoft only | iCloud or Yahoo | Google, Microsoft, EWS, hosted IMAP, iCloud, and Yahoo |
| Address sources | Connections | Default folder | Address books | Provider-dependent address_book, domain, and inbox |
How do I fetch contacts from a user’s account?
Section titled “How do I fetch contacts from a user’s account?”Send a GET request to /v3/grants/{grant_id}/contacts. Nylas returns the user’s address book as an array of Contact objects with one schema. For native iCloud and Yahoo grants, the default address_book source reads CardDAV data. A generic hosted IMAP grant keeps its existing Contact behavior and doesn’t inherit native CardDAV support.
The request below lists contacts for a grant. The response includes a request_id and a data array of contacts.
curl --compressed --request GET \ --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/contacts' \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json'{ "request_id": "1", "data": [ { "birthday": "1960-12-31", "company_name": "Nylas", "emails": [ { "type": "work", }, { "type": "home", } ], "given_name": "Leyah", "grant_id": "<NYLAS_GRANT_ID>", "groups": [{ "id": "starred" }, { "id": "friends" }], "id": "<CONTACT_ID>", "im_addresses": [ { "type": "jabber", "im_address": "jabber_at_leyah" }, { "type": "msn", "im_address": "leyah.miller" } ], "job_title": "Software Engineer", "manager_name": "Nyla", "middle_name": "Allison", "metadata": { "key1": "customer-123", "crm_record": "crm-456" }, "nickname": "Allie", "notes": "Loves ramen", "object": "contact", "office_location": "123 Main Street", "phone_numbers": [ { "type": "work", "number": "+1-555-555-5555" }, { "type": "home", "number": "+1-555-555-5556" } ], "physical_addresses": [ { "type": "work", "street_address": "123 Main Street", "postal_code": "94107", "state": "CA", "country": "US", "city": "San Francisco" }, { "type": "home", "street_address": "123 Main Street", "postal_code": "94107", "state": "CA", "country": "US", "city": "San Francisco" } ], "picture_url": "https://example.com/picture.jpg", "source": "address_book", "surname": "Miller", "web_pages": [ { "type": "work", "url": "<WEBPAGE_URL>" }, { "type": "home", "url": "<WEBPAGE_URL>" } ] } ], "next_cursor": "2"}import Nylas from "nylas";
const nylas = new Nylas({ apiKey: "<NYLAS_API_KEY>", apiUri: "<NYLAS_API_URI>",});
async function fetchContacts() { try { const identifier = "<NYLAS_GRANT_ID>"; const contacts = await nylas.contacts.list({ identifier, queryParams: {}, });
console.log("Recent Contacts:", contacts); } catch (error) { console.error("Error fetching drafts:", error); }}
fetchContacts();from nylas import Client
nylas = Client( "<NYLAS_API_KEY>", "<NYLAS_API_URI>")
grant_id = "<NYLAS_GRANT_ID>"
contacts = nylas.contacts.list( grant_id,)
print(contacts)For create, update, and delete operations plus the full field reference, see Manage contacts with the Contacts API.
How do I autocomplete email recipients?
Section titled “How do I autocomplete email recipients?”Set the source query parameter to inbox to surface everyone the user has emailed, not only the people saved in their address book. Nylas recognizes 3 contact sources: address_book (saved contacts), domain (the organization directory), and inbox (participants from messages). The inbox source is what powers a recipient picker that suggests real correspondents.
The request below combines the inbox source with the email filter to narrow suggestions as the user types.
curl --request GET \ --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/contacts?source=inbox&email=jane' \ --header 'Authorization: Bearer <NYLAS_API_KEY>'The inbox source needs an extra scope: contacts.other.readonly on Google and People.Read on Microsoft. Exchange (EWS) doesn’t support it. Native iCloud and Yahoo inbox contacts are separately gated and read-only, and those grants reject domain and compound source filters. The scopes reference lists the provider requirements.
List and sync contacts from the terminal
Section titled “List and sync contacts from the terminal”The Nylas CLI reads an address book directly: nylas contacts list returns the saved contacts. There’s no initial sync to wait on, which nylas contacts sync explains, since v3 passes contact requests straight through to the provider instead of maintaining a local copy you’d have to sync.
# List the account's contactsnylas contacts list
# Read how contact access works in v3 (no local sync model)nylas contacts syncBecause requests pass through to the provider, contacts stay current with no initial-sync delay. Listing returns 50 contacts by default and paginates automatically over 200. See the contacts list and contacts sync command reference.
How do I keep contacts in sync?
Section titled “How do I keep contacts in sync?”Subscribe a webhook to the contact.updated and contact.deleted triggers on providers that support them. There’s no contact.created trigger; a new contact arrives as contact.updated.
Google has no push API for contacts, so Nylas polls Google accounts every five minutes. Native iCloud is also polled: its first successful scan establishes a silent baseline, and later changes can emit contact.updated or contact.deleted. Duplicates are possible, and the iCloud cursor can advance before publication, so its feed is best effort rather than exactly-once or lossless. Native Yahoo emits no Contact notifications, including for API writes. Use periodic paginated reconciliation for iCloud and Yahoo. See Get real-time updates with webhooks for subscription setup.
Things to know about contacts across providers
Section titled “Things to know about contacts across providers”Contact behavior differs enough between providers that a few limits are worth knowing before you build. Microsoft Graph caps a contact at 3 email addresses and leaves the email type as null by default, so don’t rely on work or home labels for Outlook contacts. Exchange (EWS) goes further: it can’t add contacts to groups and drops the suffix field entirely.
Group filtering works on supported providers except EWS. Native iCloud and Yahoo return explicit provider-backed group vCard records, not address-book containers. CardDAV IDs derive from the provider href and can change after a provider-side move, so persist the latest ID. For the complete per-provider list, see Contacts limitations.
What’s next
Section titled “What’s next”- Manage contacts with the Contacts API for create, update, delete, and the full schema
- Contacts API scopes for the exact Google and Microsoft scope strings
- Get real-time updates with webhooks to keep contacts in sync
- Connect user accounts with OAuth to authorize contact access