# List and sync contacts from Google and Outlook

Source: https://developer.nylas.com/docs/cookbook/contacts/list-and-sync-contacts/

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 both through one endpoint with one schema. The same request reads a Gmail address book and an Outlook one, so your picker code stops caring which provider a user signed in with.

## Why use the Nylas Contacts API instead of native contact APIs?

Native contact APIs are provider-specific: Google exposes contacts through the People API and Microsoft through Graph, each with its own auth, field names, and paging. A multi-provider app maintains both and reconciles the differences by hand. Nylas normalizes them into one contact object, so a single request and a single parser cover all 3 providers: Google, Microsoft, and Exchange.

The table contrasts the native setup with the unified call.

| Property        | Google People API            | Microsoft Graph                | **Nylas Contacts API**           |
| --------------- | ---------------------------- | ------------------------------ | -------------------------------- |
| Endpoint        | `people.connections.list`    | `GET /me/contacts`             | **`GET /v3/grants/{id}/contacts`** |
| Schema          | Google field names           | Graph field names              | **One unified contact object**   |
| Providers       | Google only                  | Microsoft only                 | **Google, Microsoft, EWS**       |
| Address sources | Connections                  | Default folder                 | **Address book, domain, inbox**  |

## 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 a single schema, whether the account is Gmail, Outlook, or Exchange. Each object carries the full contact (`emails`, `phone_numbers`, `job_title`, `company_name`, and `groups`), so one parser handles all 3 providers.

The request below lists contacts for a grant. The response includes a `request_id` and a `data` array of contacts.

```bash
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'

```

```json
{
  "request_id": "1",
  "data": [
    {
      "birthday": "1960-12-31",
      "company_name": "Nylas",
      "emails": [
        {
          "type": "work",
          "email": "leyah.miller@example.com"
        },
        {
          "type": "home",
          "email": "leyah@example.com"
        }
      ],
      "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",
      "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"
}


```

```js [listContacts-Node.js]

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();


```

```python
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](/docs/v3/email/contacts/).

## 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.

```bash
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. It works on those 2 providers only; Exchange (EWS) doesn't support the `inbox` source, so build EWS recipient pickers on the `email` filter instead, which works the same across all 3 providers. The [scopes reference](/docs/dev-guide/scopes/#contacts-api-scopes) lists the exact strings.

## How do I keep contacts in sync?

Subscribe a webhook to the `contact.updated` and `contact.deleted` triggers, the 2 contact events Nylas emits. Your endpoint receives a notification whenever a contact changes or is removed, so you mirror the change instead of re-listing the whole address book. There's no `contact.created` trigger; a brand-new contact arrives as a `contact.updated` notification.

One caveat worth planning for: Google has no push API for contacts, so Nylas polls Google accounts every 5 minutes and the webhook fires after that interval rather than instantly. Microsoft delivers changes faster. See [Get real-time updates with webhooks](/docs/cookbook/use-cases/build/realtime-webhooks/) for the subscription setup.

## 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 through the `group` query parameter works on Google and Microsoft but isn't supported on EWS. Google's 5-minute contact polling also means newly added Google contacts can take a few minutes to appear. For the complete per-provider list, see the [Contacts limitations](/docs/v3/email/contacts/#contacts-limitations) and Google's [People API documentation](https://developers.google.com/people).

## What's next

- [Manage contacts with the Contacts API](/docs/v3/email/contacts/) for create, update, delete, and the full schema
- [Contacts API scopes](/docs/dev-guide/scopes/#contacts-api-scopes) for the exact Google and Microsoft scope strings
- [Get real-time updates with webhooks](/docs/cookbook/use-cases/build/realtime-webhooks/) to keep contacts in sync
- [Connect user accounts with OAuth](/docs/cookbook/use-cases/build/connect-user-accounts-oauth/) to authorize contact access