# How to list Google contacts

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

Google retired its standalone Contacts API in January 2022 and folded everything into the People API, so any code written against the old `google.com/m8/feeds` endpoints stopped working. The People API that replaced it splits a user's address book across "connections" (saved contacts) and "other contacts" (auto-created from Gmail), each behind a different OAuth scope and verification tier. That split, plus Google's app verification review, is where most teams get stuck.

This recipe shows how to pull Gmail and Google Workspace contacts through one call to the Nylas Contacts API, then filter and sync them. You get a single contact schema and one set of credentials instead of two People API resources and a GCP verification process.

## Why use Nylas instead of the Google People API directly?

The People API asks for more setup than a contact picker usually warrants:

- **Two resources, two scopes.** Saved contacts come from `people.connections.list` with the `contacts` scope; auto-created suggestions come from `otherContacts.list` with the separate `contacts.other.readonly` scope. The API merges them for you and exposes both through the `source` field.
- **App verification.** Both contact scopes are classified as sensitive, so a production Google app needs OAuth verification before users outside your test list can connect. The API handles token storage and refresh once a grant exists.
- **Per-project quotas.** The People API enforces a default 90 read requests per minute per user. Polling many users from your own GCP project can exhaust that fast, and the API smooths it with managed retries.
- **One schema across providers.** The same `GET /v3/grants/{grant_id}/contacts` request reads a Gmail address book, an Outlook one, or Exchange, so your picker code doesn't branch on provider.

## Before you begin

You'll need a working project and an authorized account before any contacts come back. A grant represents one connected Google account, and the contacts you list always belong to that single grant. Set these up first:

- A [Nylas application](/docs/v3/getting-started/) with a valid API key
- A [grant](/docs/v3/auth/) for a Gmail or Google Workspace account, authorized with a contacts scope
- A GCP project with the [Google OAuth scopes](/docs/provider-guides/google/) your integration needs


> **Info:** 
> **New to Nylas?** Start with the [quickstart guide](/docs/v3/getting-started/) to set up your app and connect a test account before continuing here.


## List Google contacts

Send a `GET` request to `/v3/grants/{grant_id}/contacts` with the grant ID for the connected Google account. The response is a `data` array of contact objects plus a `request_id`, and by default a single page returns up to 50 contacts. Every object carries the same fields (`emails`, `phone_numbers`, `job_title`, `company_name`, and `groups`) whether the account is Gmail or Google Workspace.

The samples below list contacts for one grant and print the unified response.

```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 SDK]

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 [listContacts-Python SDK]

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 complete field list, see [Manage contacts with the Contacts API](/docs/v3/email/contacts/).

## Filter Google contacts

Four query parameters narrow a Google contact list: `email`, `phone_number`, `group`, and `source`. The `source` parameter is the most useful one for Google, since it picks between saved contacts (`address_book`) and the auto-created suggestions Gmail builds from your sent mail (`inbox`). A fifth parameter, `recurse`, exists but is Microsoft-only, so it has no effect on Google grants. Combine these with `limit` to keep pages small.

The `email` filter matches any contact whose email address contains the value you pass, which makes it the right choice for an autocomplete box.

```bash
curl --request GET \
  --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/contacts?source=inbox&email=jane&limit=10' \
  --header 'Authorization: Bearer <NYLAS_API_KEY>'
```

```js [filterGoogleContacts-Node.js SDK]
const contacts = await nylas.contacts.list({
  identifier: grantId,
  queryParams: {
    source: "inbox",
    email: "jane",
    limit: 10,
  },
});
```

```python [filterGoogleContacts-Python SDK]
contacts = nylas.contacts.list(
    grant_id,
    query_params={
        "source": "inbox",
        "email": "jane",
        "limit": 10,
    },
)
```

To list only the contacts inside one Google label, pass that label's ID to the `group` parameter. Google contact groups map directly to the labels you create in Google Contacts, so `group` reads the same membership Gmail shows in its sidebar.

## Things to know about Google contacts

Google's contact model has a few quirks that shape how a list call behaves. The four below cover where contacts come from, how groups and photos work, which scope you need, and the quotas that throttle reads.

### The source field maps to People API resources

Google exposes contacts through two People API resources, and the `source` field tells you which one a contact came from. `address_book` contacts are the saved entries from `people.connections.list`, the ones a user typed into Google Contacts. `inbox` contacts are the "other contacts" Gmail auto-creates from people you've emailed, returned by `otherContacts.list`. A heavy email user can have thousands of these auto-created entries against a few hundred saved ones, so filter on `source=address_book` when you only want real, saved contacts.

### Contact groups are Google labels

The `group` query parameter filters by Google's contact labels, which the People API calls contact groups. Each label has a stable resource ID, and the `groups` array on every contact object lists the labels that contact belongs to. System groups like `myContacts` and `starred` are always present, while user-created labels get their own IDs. Use the [List contact groups endpoint](/docs/reference/api/contacts/list-contact-groups/) to fetch the IDs before filtering, because passing a display name instead of an ID returns an empty list.

### Profile photos need a separate request

The list response doesn't embed photo bytes; instead each contact carries a `picture_url` you fetch separately when you need the image. Google stores high-resolution profile photos, often 512 by 512 pixels, so loading them inline for a long list is wasteful. Request the photo lazily as a contact scrolls into view, and cache it, rather than pulling every image up front.

### Scopes and rate limits

Reading saved contacts needs the `contacts` or `contacts.readonly` scope, and the `inbox` source additionally needs `contacts.other.readonly`. Both are sensitive scopes, so a production app must pass Google's OAuth verification. On quotas, the People API defaults to 90 read requests per minute per user, and your GCP project carries an overall daily ceiling on top of that. The API retries throttled requests, but if you sync many accounts on a schedule, stagger the jobs rather than firing them at once. See Google's [People API documentation](https://developers.google.com/people) for the current quota figures and scope definitions.

## What's next

- [List and sync contacts from Google and Outlook](/docs/cookbook/contacts/list-and-sync-contacts/) for autocomplete and webhook sync
- [Filter contacts by group](/docs/cookbook/contacts/contact-groups/) to read membership of a Google label
- [Contacts API reference](/docs/reference/api/contacts/) for every endpoint, parameter, and field
- [Connect user accounts with OAuth](/docs/v3/auth/) to authorize contact access for a grant
- [Google provider guide](/docs/provider-guides/google/) for OAuth scopes, verification, and Workspace setup