Source: https://cli.nylas.com/guides/export-contacts-to-csv-cli

# Export Contacts to CSV from the CLI

Sales asks for the contact list 'as a spreadsheet,' and you reach for a Python script. You don't need one. The CLI returns contacts as JSON; jq's @csv operator turns them into properly quoted rows that Excel and Google Sheets open without complaint. This guide builds a correct CSV export — a header row, RFC 4180 quoting, and null-safe fallbacks for the ragged fields contacts always have, like a missing surname, a blank company, or a person with three email addresses.

Written by [Pouya Sanooei](https://cli.nylas.com/authors/pouya-sanooei) Software Engineer

Reviewed by [Qasim Muhammad](https://cli.nylas.com/authors/qasim-muhammad)

Updated June 9, 2026

> **TL;DR:** Pull contacts with `nylas contacts list --json`, then pipe to `jq -r` with the `@csv` operator. `@csv` quotes per RFC 4180, so commas in a company name don't shift your columns, and `// empty` keeps a missing surname from collapsing a row. The trick that makes addresses books survive the trip is at the bottom: flattening a contact with three emails.

Command references used in this guide: [`nylas contacts list`](https://cli.nylas.com/docs/commands/contacts-search), [`nylas contacts search`](https://cli.nylas.com/docs/commands/contacts-search), and [`nylas contacts show`](https://cli.nylas.com/docs/commands/contacts-show).

## How do I export contacts to CSV?

You export contacts to CSV by piping the CLI's JSON through jq's `@csv` operator, which formats an array of values as one correctly quoted CSV row. The `nylas contacts list --json` command returns every contact as a JSON object; jq maps each one to a row of the fields you want. The `-r` flag prints raw output, so the file holds literal CSV rather than JSON-escaped strings.

Use `@csv` instead of joining values with commas yourself because CSV quoting is harder than it looks. A company named “Acme, Inc.” contains a comma; a job title with a quote in it breaks a naive join. The `@csv` operator implements the quoting rules from [RFC 4180](https://datatracker.ietf.org/doc/html/rfc4180), the 2005 spec that Excel and Google Sheets both follow, so those cases just work. One operator replaces a pile of escaping code.

```bash
# Build a CSV with a header row, one contact per line
{
  echo 'given_name,surname,email,company'
  nylas contacts list --json --limit 500 \
    | jq -r '.[] | [
        (.given_name // ""),
        (.surname // ""),
        (.emails[0].email // ""),
        (.company_name // "")
      ] | @csv'
} > contacts.csv
```

## Why does my CSV need null-safe fallbacks?

Contact data is ragged: one record has a surname and no company, the next has a company and no email. Without a fallback, jq emits `null` for a missing field, and a row with a missing trailing value can come out one column short. The `// empty` and `// ""` operators in jq supply a default, so every one of your 500 rows carries the same column count.

The distinction matters: `// ""` substitutes an empty string for a null field, keeping the cell present but blank, while `// empty` drops the value entirely from a stream. For a fixed-width CSV row you almost always want `// ""` on each cell. Reserve `// empty` for skipping whole contacts, such as records with no email at all. The jq manual documents both as alternative operators.

```bash
# Skip contacts with no email, blank-fill the rest
nylas contacts list --json --limit 500 \
  | jq -r '.[]
      | select(.emails[0].email // empty)
      | [
          (.given_name // ""),
          (.surname // ""),
          (.emails[0].email // ""),
          (.company_name // ""),
          (.job_title // "")
        ] | @csv' > contacts.csv
```

## How do I handle contacts with multiple emails?

A single contact often carries two or three addresses — a work email, a personal one, an old alias. The Nylas v3 contact object stores them as an `emails` array of objects, each with an `email` and a `type`. You have two choices: collapse the array into one cell, or explode one contact into several rows. Pick the shape your downstream tool expects.

To collapse, map the array to its email values and join them with a semicolon — not a comma, which would break the CSV column. To explode, iterate the array so each address becomes its own row, repeating the name fields. A mailing-list importer usually wants exploded rows; a CRM dedupe usually wants the collapsed form. The first example below joins; the second emits one row per email for a contact, so a person with 3 addresses produces 3 rows.

```bash
# Collapse: all emails in one semicolon-joined cell
nylas contacts list --json --limit 500 \
  | jq -r '.[] | [
      (.given_name // ""),
      (.surname // ""),
      ((.emails // []) | map(.email) | join(";")),
      (.company_name // "")
    ] | @csv' > contacts-joined.csv

# Explode: one row per email address
nylas contacts list --json --limit 500 \
  | jq -r '.[]
      | . as $c
      | (.emails // [])[]
      | [ ($c.given_name // ""), ($c.surname // ""), .email, .type ]
      | @csv' > contacts-exploded.csv
```

## How do I export only a subset of contacts?

The `nylas contacts search` command filters server-side before the JSON ever reaches jq, which is faster than pulling every contact and discarding most. It accepts `--company`, `--email`, `--phone`, and `--source`, plus `--has-email` to drop records with no address. Company name is a partial match, so `--company Acme` catches “Acme Inc” too.

Source filtering is the most useful for a clean export. The `--source` flag takes `address_book`, `inbox`, or `domain`. Pulling only `address_book` excludes the auto-collected addresses Gmail and Outlook harvest from your inbox, which are usually noise in a contact export. Provider IDs are native: Google polls roughly every 5 minutes, while Microsoft syncs in real time through Graph.

```bash
# Only saved address-book contacts at one company, with an email
{
  echo 'given_name,surname,email,company'
  nylas contacts search --company "Acme" --source address_book --has-email --json \
    | jq -r '.[] | [
        (.given_name // ""),
        (.surname // ""),
        (.emails[0].email // ""),
        (.company_name // "")
      ] | @csv'
} > acme-contacts.csv
```

## How do I make the contacts CSV open cleanly in Excel?

Two details decide whether the contacts CSV opens cleanly in Excel: character encoding and how the app reads the first row. Write the file as UTF-8 so accented names survive; if Excel still shows garbled characters, prepend a UTF-8 byte-order mark so it detects the encoding. Excel treats the first line as a header automatically, which is why echoing a header row before the jq output matters.

Add the BOM with a 3-byte prefix before the header, or set it in jq. Google Sheets needs no BOM — it reads UTF-8 by default — so keep one canonical export and only add the prefix for Excel users. Quote the whole pipeline once, save it as a shell script, and anyone on the team can regenerate the export with a single command in under 2 seconds for a few hundred contacts.

```bash
# UTF-8 BOM so Excel reads accented names correctly
{
  printf '\xEF\xBB\xBF'
  echo 'given_name,surname,email,company'
  nylas contacts list --json --limit 500 \
    | jq -r '.[] | [
        (.given_name // ""),
        (.surname // ""),
        (.emails[0].email // ""),
        (.company_name // "")
      ] | @csv'
} > contacts-excel.csv
```

## Next steps

- [Manage contacts from the terminal](https://cli.nylas.com/guides/manage-contacts-from-terminal) — list, search, create, and update before you export
- [Sync contacts across Gmail and Outlook](https://cli.nylas.com/guides/sync-contacts-gmail-outlook) — keep the source address books current
- [Dedupe contacts from the CLI](https://cli.nylas.com/guides/dedupe-contacts-cli) — clean duplicates before the export
- [Export email to CSV](https://cli.nylas.com/guides/email-to-csv-export) — the same jq @csv pattern for messages
- [Load email into Snowflake](https://cli.nylas.com/guides/email-to-snowflake) — push structured data into a warehouse instead of a flat file
- [Full command reference](https://cli.nylas.com/docs/commands) — every flag and subcommand documented
- jq's [official manual](https://jqlang.github.io/jq/manual/) documents the `@csv` and `//` operators
- The [Nylas v3 Contacts API](https://developer.nylas.com/docs/api/v3/ecc/#tag/contacts) documents every field in the contact object
- Provider field mapping: [Google People API](https://developers.google.com/people/api/rest/v1/people.connections/list) and [Microsoft Graph contact resource](https://learn.microsoft.com/en-us/graph/api/resources/contact)
