Apple doesn’t ship a REST contacts API. iCloud exposes its address book over CardDAV, the same WebDAV-based protocol it uses for calendars, and it gates access behind an app-specific password rather than OAuth. So instead of an events.insert-style endpoint, you’re writing CardDAV PROPFIND and REPORT requests against contacts.icloud.com and parsing vCard payloads by hand.
This recipe reads an iCloud address book through one call to the Nylas Contacts API. You get the same contact schema returned for Gmail, Outlook, and Exchange accounts, so a single parser handles every provider and you never touch CardDAV or vCard directly.
Why use Nylas instead of iCloud CardDAV directly?
Section titled “Why use Nylas instead of iCloud CardDAV directly?”CardDAV is a sync protocol, not a query API, so reading contacts the native way means more plumbing than a contact picker should need. One Nylas request replaces a CardDAV handshake plus vCard parsing across all 6 supported providers. These 3 differences from a typical REST integration are where the work piles up:
- No OAuth, no scopes. iCloud authenticates with an app-specific password the user generates at appleid.apple.com. There’s no consent screen and no scope list, so you store and rotate that credential yourself unless a grant holds it for you.
- vCard parsing. CardDAV
REPORTrequests return vCard 3.0 text, not JSON. You parseFN,EMAIL,TEL, andORGlines and reconcile them into objects. The API does this and hands back JSON fields likeemails,phone_numbers, andcompany_name. - One schema across providers. The same
GET /v3/grants/{grant_id}/contactsrequest reads an iCloud, Gmail, Outlook, or Exchange address book, so your picker code stays provider-agnostic.
Before you begin
Section titled “Before you begin”To list iCloud contacts you need a connected iCloud account, and that connection depends on 2 things Apple requires: an iCloud connector in your project and an app-specific password from the user. iCloud doesn’t use OAuth scopes, so there’s no permission list to request.
- A Nylas application with a valid API key
- An iCloud connector added to that application
- A grant for an iCloud account authorized with an app-specific password
Create an iCloud app password
Section titled “Create an iCloud app password”Apple requires an app-specific password to connect any third-party app, and it must be generated by the user because Apple offers no API for it. Direct the user to appleid.apple.com, have them sign in, and walk them through Apple’s app-specific password steps. They enter that 16-character password during auth instead of their main Apple ID password. The full connector and Bring Your Own Authentication flow lives in the iCloud provider guide.
List iCloud contacts
Section titled “List iCloud contacts”Send a GET request to /v3/grants/{grant_id}/contacts with the grant ID for the connected iCloud account. The response is a data array of contact objects plus a request_id, and a single page returns 30 contacts by default and up to 200 with the limit parameter. Each object carries emails, phone_numbers, job_title, company_name, and groups populated from the iCloud address book.
The samples below list contacts for one grant and print the unified response.
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", "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 complete field list, see Manage contacts with the Contacts API.
List iCloud contacts from the terminal
Section titled “List iCloud contacts from the terminal”iCloud exposes contacts over CardDAV, and each user must create a 16-character app-specific password before any client connects. The Nylas CLI reads them once that grant exists: nylas contacts list returns the account’s contacts straight from your terminal, with no CardDAV client to build or sync logic to maintain.
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 sendersnylas contacts list --source address_book --limit 100Every 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 filenylas contacts search --company "Acme" --has-emailSee the contacts list and contacts search command reference for every flag.
Like other IMAP-style providers, an iCloud contact holds at most one email and one phone number, so don’t expect the multi-value fields a Google contact carries. Listing returns 50 contacts by default. Filter to the saved set with nylas contacts list --source address_book, which skips auto-collected senders.
Filter iCloud contacts
Section titled “Filter iCloud contacts”Three query parameters narrow an iCloud contact list: email, phone_number, and source. iCloud supports the phone_number filter, which Microsoft doesn’t, so you can match a contact by digits as well as by address. The source parameter picks between saved contacts (address_book) and the auto-generated entries Nylas derives from message participants (inbox). The recurse parameter is Microsoft-only, so it has no effect on an iCloud grant.
This curl request returns contacts whose email contains jane from the user’s saved iCloud address book. Swap email for phone_number to match a number instead.
curl --request GET \ --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/contacts?source=address_book&email=jane' \ --header 'Authorization: Bearer <NYLAS_API_KEY>'const contacts = await nylas.contacts.list({ identifier: grantId, queryParams: { source: "address_book", email: "jane", },});contacts = nylas.contacts.list( grant_id, query_params={ "source": "address_book", "email": "jane", })iCloud is one of only 2 providers that accept a compound source filter, so you can pass source=address_book,inbox to merge saved and auto-generated contacts in a single request.
Things to know about iCloud contacts
Section titled “Things to know about iCloud contacts”iCloud contacts arrive as vCard 3.0 over CardDAV, and that single fact shapes the 4 details below: the protocol underneath, the app-password auth model, compound source filtering, and the 90-day cache that governs sync timing. Read these before you wire the address book into a picker or a sync job.
CardDAV is the protocol underneath
Section titled “CardDAV is the protocol underneath”Nylas reads iCloud contacts over CardDAV, the address-book extension to WebDAV defined in RFC 6352, rather than a JSON REST API. CardDAV speaks PROPFIND and REPORT requests and returns vCard 3.0 payloads, which the API normalizes into the unified contact object. Two effects fall out of this. First, iCloud contacts have no concept of system labels like Gmail’s starred, so the groups array reflects only CardDAV address-book collections. Second, contact IDs are stable CardDAV resource URLs, so they survive across syncs and are safe to store as foreign keys.
Auth uses an app-specific password, not OAuth scopes
Section titled “Auth uses an app-specific password, not OAuth scopes”Every other major contacts provider uses OAuth, but iCloud uses an app-specific password the user generates manually. There’s no scope list and no admin consent step, which removes a verification hurdle that Google’s sensitive contacts scopes and Microsoft’s Contacts.Read admin consent both impose. The tradeoff is credential management: a user must regenerate the 16-character password if they reset their Apple ID password or revoke the app, and the grant moves to an error state until they re-authenticate. Surface a reconnect prompt so a stale password doesn’t silently stop contact reads.
Compound source filters are an iCloud advantage
Section titled “Compound source filters are an iCloud advantage”The source parameter accepts a comma-separated list on iCloud and IMAP only, the 2 providers where Nylas reads contacts from both the CardDAV address book and message participants. Pass source=address_book,inbox to return saved contacts and auto-generated ones in a single call, which Google and Microsoft can’t do in one request. This is useful for recipient autocomplete, where you want both the user’s saved contacts and the people they’ve recently emailed ranked together. Filtering on source=address_book alone returns only the entries the user explicitly saved in the Contacts app.
Sync timing and the 90-day cache
Section titled “Sync timing and the 90-day cache”Nylas caches iCloud data for 90 days and reads new contact changes from CardDAV on its sync schedule rather than in real time, so a contact added on an iPhone may take a short interval to surface through the list call. iCloud also doesn’t emit auto-generated “other contacts” the way Gmail builds a separate collection, so the inbox source is derived from message participants instead of an Apple-managed list. Apple enforces account-level limits documented in its iCloud system limits, so if you sync many accounts on a schedule, stagger the jobs rather than firing them at once.
Paginate through results
Section titled “Paginate through results”The list endpoint returns 30 contacts per page by default, up to a maximum of 200, and uses cursor pagination through the page_token query parameter. Each response includes a next_cursor value when more contacts exist; pass it as page_token on the next request to fetch the following page. Loop until a response omits next_cursor, which signals the last page.
curl --request GET \ --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/contacts?limit=50&page_token=<NEXT_CURSOR>' \ --header 'Authorization: Bearer <NYLAS_API_KEY>'What’s next
Section titled “What’s next”- How to list Google contacts for the People API equivalent and the
sourcefield - How to list Microsoft contacts for Graph contact folders and admin consent
- How to search contacts to match by email, phone, group, or source
- iCloud provider guide for the connector, app passwords, and Bring Your Own Authentication flow
- Manage contacts with the Contacts API for create, update, delete, and the full schema