Skip to content
Skip to main content

How to list Exchange contacts

Last updated:

Microsoft announced EWS retirement in 2022, yet plenty of enterprises still run Exchange in their own data centers, and that on-premises address book has no REST API of its own. Pulling contacts out of it natively means hand-building SOAP envelopes, parsing XML responses, and dealing with autodiscovery before you read a single name.

The Nylas Contacts API reads an on-premises Exchange address book over Exchange Web Services (EWS) and returns the same contact schema you get from Google or Outlook. One GET request, no SOAP, and your contact code doesn’t branch on whether the mailbox lives in the cloud or in a closet down the hall.

EWS is a SOAP-based XML protocol with no native JSON or REST surface. Reading the address book directly means building XML request envelopes, parsing XML responses, decoding SOAP faults, and solving autodiscovery to locate the server first. The 4 hurdles below are where on-premises integrations stall:

  • SOAP and XML. Every FindItem call ships a hand-built XML envelope, and every response needs an XML parser. There’s no JSON path.
  • Autodiscovery. EWS finds its endpoint from the user’s email address, and the lookup is frequently misconfigured in enterprise DNS.
  • Authentication. EWS uses basic or NTLM credentials over TLS, plus 16-character app passwords when two-factor is on, rather than an OAuth redirect.
  • No unified schema. EWS contact fields look nothing like Graph or Google fields, so a direct integration can’t share parsing code with any cloud provider.

The API folds all 4 into one REST call returning one contact object. If you have deep EWS experience and only target on-premises Exchange, integrating directly stays an option.

To list Exchange contacts you need a connected on-premises mailbox and an EWS connector scoped for contacts. Setup takes 2 pieces: a grant for the Exchange account and the ews.contacts scope on the connector your application uses.

  • A Nylas application with a valid API key
  • A grant for an on-premises Exchange account
  • An EWS connector created with the ews.contacts scope

For the full connect-and-grant walkthrough, including autodiscovery and impersonation, see How to connect Exchange (EWS) accounts.

EWS defines a separate Nylas scope per data type, and the connector’s scopes field accepts a single value. For contacts you set it to ews.contacts; mail uses ews.messages and calendar uses ews.calendars. Create a dedicated connector for each data type your app touches, because one connector can’t carry mail and contacts at once. The Exchange on-premises provider guide lists every scope and the dashboard steps to add them.

Send a GET request to /v3/grants/{grant_id}/contacts with the grant ID for the on-premises mailbox. The endpoint returns the address book as a data array of contact objects plus a request_id, and a single page returns up to 30 contacts by default. Each object carries the unified schema, so one parser reads Exchange, Outlook, and Gmail contacts without provider branches.

The request below lists contacts for one grant. The response is the standard Nylas contact object, with emails, phone_numbers, job_title, and company_name populated from the Exchange account.

For create, update, and delete operations plus the complete field reference, see Manage contacts with the Contacts API.

Exchange contacts behave differently from cloud providers: the list endpoint accepts only source=address_book, with source=inbox and contact groups unsupported. The Nylas CLI reflects that: nylas contacts list returns an Exchange grant’s saved contacts, and the source filter has just one valid value.

The Nylas CLI reads contacts from your terminal with the same source model as the Contacts API. After nylas init and nylas auth login, contacts list returns 50 contacts by default and pages through everything automatically once --limit goes over 200:

# List 50 contacts (default)
nylas contacts list
# Only the saved address book, skipping auto-collected senders
nylas contacts list --source address_book --limit 100

Every contact carries a source: address_book (saved by the user), inbox (auto-collected from sent mail), or domain (the organization directory). Filter deliberately, because a busy account can hold thousands of inbox contacts the user never saved. To find a specific person rather than list everyone, contacts search matches on company, email, or phone:

# Find contacts at a company who have an email on file
nylas contacts search --company "Acme" --has-email

See the contacts list and contacts search command reference for every flag.

So on Exchange, nylas contacts list --source address_book is the meaningful query; --source inbox returns nothing, and contact groups aren’t available either. Listing returns 50 contacts by default. EWS contact fields don’t match Graph or Google, so treat the normalized output as the canonical shape rather than mapping field by field.

Narrow the result set with query parameters instead of paging the whole address book client-side. On EWS the list endpoint accepts 3 working filters, email, phone_number, and source, but the parameters behave differently than they do on cloud providers: source=inbox isn’t supported, and the group parameter doesn’t work at all. That leaves source=address_book as the only valid source value for an Exchange grant.

This curl request returns saved contacts whose email contains jane. The phone_number filter also works on EWS, so you can match a saved phone string the same way.

Don’t reach for compound sources like source=address_book,inbox on Exchange. The API supports comma-separated sources for IMAP and iCloud only, so on EWS you pass a single source value.

Exchange on-premises contacts behave differently from the cloud providers in 5 areas worth knowing before you build: the connection model, the on-prem versus Exchange Online split, the unsupported source and group filters, network reachability, and admin-set throttling. The notes below cover each so an enterprise quirk doesn’t surprise you.

Nylas reads on-premises Exchange contacts through Exchange Web Services, the SOAP protocol Exchange has shipped since 2007. The API runs autodiscovery to find the server’s EWS endpoint from the user’s email address, authenticates with the stored credentials, then issues EWS contact operations and normalizes the result into the unified schema. You never touch a SOAP envelope or an EWS field name. Reading contacts requires the ews.contacts scope on the connector, the same per-data-type scope model the connect guide describes for mail and calendar.

These are two different worlds, and contacts route through different code in each. A mailbox hosted by Microsoft in the cloud, including Exchange Online, Microsoft 365, and Outlook.com, uses the microsoft connector built on Microsoft Graph. A mailbox on a server the organization runs itself uses the ews connector. Every mailbox lives in exactly one of these, so if your users are on Exchange Online, follow How to list Microsoft contacts instead. To pull contacts from Gmail or Google Workspace, see How to list Google contacts.

The source parameter and the missing inbox value

Section titled “The source parameter and the missing inbox value”

EWS supports the source parameter, but it accepts only address_book for an Exchange grant. The inbox value, which surfaces people a user has emailed but never saved, isn’t available on EWS, so you can’t suggest unsaved recipients from message history the way you can on Google or Microsoft. If you call the endpoint without a source, it defaults to address_book and returns the saved contacts, which is the behavior you want on Exchange anyway.

Unlike Outlook contact folders and Google labels, the group query parameter isn’t supported for EWS at all. You can’t filter an Exchange address book to a single Contact Group through the API, and recurse is a Microsoft-only parameter that has no effect here either. If your app organizes contacts by group, do the grouping client-side after listing, or store group membership in your own data layer rather than relying on a server-side group filter.

The Exchange server must accept connections from outside the corporate network with a valid public TLS certificate, over port 443 by default. A mailbox locked behind a VPN, an internal-only firewall rule, or a self-signed certificate can’t be reached, so a contact list call fails before it ever queries the address book. If the server sits behind a firewall, allowlist Nylas IP addresses, available on contract plans with static IP routing. Microsoft’s autodiscover reference documents the endpoint lookup that has to succeed first.

Exchange rate limits come from a throttling policy the server administrator configures, not from Nylas, so the ceilings vary per deployment. When the server throttles a request, the API surfaces a Retry-After header telling you how many seconds to wait, often 30 seconds or more. A single tight sync loop across many mailboxes can trip a strict policy in seconds. Sync contact changes with webhooks instead of polling so you stay well under whatever limit the admin set.