Skip to content
Skip to main content

How to list iCloud contacts

Last updated:

iCloud exposes its address book through CardDAV instead of a public REST API. A native iCloud grant lets you use the Nylas Contacts API to read and write that CardDAV data through the same JSON schema you use for Google, Microsoft, Yahoo, and Exchange contacts.

Why use Nylas instead of iCloud CardDAV directly?

Section titled “Why use Nylas instead of iCloud CardDAV directly?”
  • One REST contract. Nylas handles CardDAV discovery, PROPFIND and REPORT requests, vCard parsing, conditional writes, and pagination.
  • Grant-bound resource validation. Nylas treats CardDAV href values as untrusted and validates them against the address books discovered for the grant.
  • Normalized groups and fields. Explicit group vCard records become Contact Groups, while address-book containers and combined views such as All Contacts stay out of the group list.
  • Cross-provider application code. The same Contacts endpoints and response schema work across supported providers.

You need:

  • A Nylas application with a valid API key.
  • An iCloud connector in the application.
  • A native iCloud grant authenticated with an app-specific password.

iCloud Contacts doesn’t use OAuth scopes. Ask the user to create an app-specific password by following Apple’s app-specific password instructions, then use it to authenticate the native iCloud grant. See the iCloud provider guide for connector and authentication details.

Call GET /v3/grants/{grant_id}/contacts. The default and supported writable source is address_book, so you can omit the source parameter or set source=address_book explicitly.

The native iCloud source rules are strict:

  • Omitting source selects CardDAV address_book contacts.
  • source=inbox is available only when Nylas enables the legacy mailbox-derived feature. It’s read-only.
  • source=domain and compound values such as source=address_book,inbox return an invalid request error.
  • Offset pagination is unsupported. Use the returned next_cursor as page_token.

Create, update, and delete iCloud contacts

Section titled “Create, update, and delete iCloud contacts”

The standard Contact create, update, and delete endpoints write the native iCloud address book through CardDAV. The API selects the grant’s writable address book. If it can’t find one, or discovers more than one possible writable address book, the write fails instead of guessing.

CardDAV updates use ETag values internally for conditional writes, but the ETag isn’t part of the public Contact ID. A conflicting provider edit can produce a conflict or partial-failure error. Fetch the current contact before retrying.

iCloud supports the email, phone_number, and group filters for CardDAV address-book contacts. Use one source per request.

Continue through the result set by passing next_cursor back as page_token:

Contact metadata filtering uses metadata_pair=key1:value. You can combine it with limit and page_token, but not with email, phone_number, group, recurse, or a non-default source. See Add metadata to a contact for create, update, clear, and cleanup behavior.

Native contact IDs start with carddav_v1_; Contact Group IDs start with carddav_group_v1_. Both encode only the canonical provider href and apply to one grant. Treat them as opaque values and persist the latest ID from every response.

An ordinary edit keeps the same contact ID while the href remains stable. If the user moves a contact in iCloud, the href and ID can change. Nylas can then observe a deletion under the old ID and an update under the new ID. The API doesn’t keep an old-to-new mapping, and href reuse means the ID is a current locator rather than a permanent tombstone.

GET /contacts/groups returns only explicit group vCard records. All Contacts and address-book collections aren’t mutable groups. Filter members with GET /contacts?group=<CONTACT_GROUP_ID>, and change membership through the Contact groups field. The API doesn’t create, rename, or delete Contact Group resources.

Native iCloud grants can emit contact.updated and contact.deleted for provider-originated CardDAV changes and Contacts API writes.

The first successful CardDAV scan establishes a baseline and doesn’t publish the user’s existing contacts. Later changes appear after the next configured polling cycle plus processing and retry time, so delivery isn’t real-time. Duplicate notifications are possible; make the handler idempotent and deduplicate by the CloudEvent id.

  • Profile-picture retrieval with profile_picture=true is unsupported for native CardDAV contacts.
  • Nylas validates address-book and group href values against resources discovered for the grant. Invalid or cross-grant IDs fail closed.
  • Provider authentication, permission, resource-size, rate-limit, and timeout errors are returned as provider errors.
  • Concurrent group membership changes can conflict. Fetch the latest Contact and Contact Groups before retrying.
  • Contact metadata is Nylas-owned and never written to the iCloud vCard. Provider and metadata writes are separate and not atomic.

After the native grant exists, nylas contacts list --source address_book returns its CardDAV contacts without a separate CardDAV client.

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.