# Parse an email body with an API

Source: https://developer.nylas.com/docs/cookbook/email/parse-email-body/

Raw email is one of the worst data formats you'll ever have to read. A single message can carry a nested MIME tree, base64-encoded parts, quoted-printable bodies, mismatched character sets, and both an HTML and a plain-text copy of the same content. Parsing that by hand with Python's `email` module or a tangle of IMAP `FETCH` commands works until it doesn't, and the day it breaks is usually the day a customer sends mail from a client you never tested.

This guide shows how to retrieve a message through an API and get back a clean, already-parsed body plus the fields you actually care about: sender, recipients, subject, date, and attachments. No MIME tree walking, no charset guessing, no IMAP socket handling. The examples are Python-forward, with a curl version for each call.

## How do I parse an email body in Python using an API?

Send `GET /v3/grants/{grant_id}/messages/{message_id}` and read the `body`, `subject`, `from`, and `to` fields off the JSON response. The API parses the MIME structure for you and returns the body as a single string: HTML when the message has it, plain text otherwise. You skip Python's `email.message_from_bytes` and the multipart walking it forces.

The call below fetches one message by ID and prints the parsed pieces. The `body` field holds the decoded content, `snippet` gives you the first 100 characters with HTML stripped (handy for previews), and `from` is a list of name/email pairs because a message can technically have more than one sender. One request returns all of it.

```python [parseBody-Python SDK]
from nylas import Client

nylas = Client(api_key="<NYLAS_API_KEY>")

message = nylas.messages.find(
    identifier="<NYLAS_GRANT_ID>",
    message_id="<MESSAGE_ID>",
).data

print("Subject:", message.subject)
print("From:", message.from_[0]["email"])
print("Preview:", message.snippet)
print("Body:", message.body)
```

```bash
curl --request GET \
  --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages/<MESSAGE_ID>' \
  --header 'Authorization: Bearer <NYLAS_API_KEY>'
```

## How do I read incoming email using an IMAP API?

Connect an IMAP mailbox once as a grant, then call `GET /v3/grants/{grant_id}/messages` to read incoming mail through the same parsed JSON shape Gmail and Outlook return. You never open an IMAP socket, send `FETCH` commands, or decode MIME parts yourself. The unified layer speaks IMAP for you and hands back the body already extracted.

The list call below pulls the most recent messages, newest first, and works identically across Google, Microsoft, Yahoo, iCloud, and generic IMAP accounts. Set `limit` up to 200 per page; the default is 50. Reading each message's `body` here is the same field you'd read from the single-message call, so one parser handles every provider. That cross-provider consistency is the biggest reason to go through an API instead of a raw IMAP client.

```python [parseList-Python SDK]
from nylas import Client

nylas = Client(api_key="<NYLAS_API_KEY>")

messages = nylas.messages.list(
    identifier="<NYLAS_GRANT_ID>",
    query_params={"limit": 50},
).data

for message in messages:
    print(message.subject, "->", message.snippet)
```

```bash
curl --request GET \
  --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages?limit=50' \
  --header 'Authorization: Bearer <NYLAS_API_KEY>'
```

## How can I extract structured data from incoming email?

Fetch the message, then strip the noise before you read it. The clean messages call, `PUT /v3/grants/{grant_id}/messages/clean`, removes signatures, quoted reply history, and tracking pixels, leaving the meaningful body. Feed that tidy text to a parser or a language model, and your field extraction gets noticeably more accurate because there's no boilerplate to confuse it.

The request below cleans messages in one call, since `message_id` is an array. Set `html_as_markdown` to `true` when the output is headed for an LLM, because markdown keeps headings and lists that plain text drops (this beta flag requires `images_as_markdown` to be `true`). Add `remove_conclusion_phrases` to strip closing lines such as "Best" and "Regards", and `ignore_tables` to drop `<table>` markup while keeping the row text. For order confirmations, receipts, and lead forms, cleaning first removes the parts that have nothing to do with the data you want, which is why field extraction lands more reliably on the cleaned body than on the raw one.

```python [parseClean-Python SDK]


response = requests.put(
    "https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages/clean",
    headers={"Authorization": "Bearer <NYLAS_API_KEY>"},
    json={
        "message_id": ["<MESSAGE_ID>"],
        "ignore_links": True,
        "ignore_images": True,
        "remove_conclusion_phrases": True,
    },
)

cleaned = response.json()["data"][0]["conversation"]
print(cleaned)
```

```bash
curl --request PUT \
  --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages/clean' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer <NYLAS_API_KEY>' \
  --data '{
    "message_id": ["<MESSAGE_ID>"],
    "ignore_links": true,
    "ignore_images": true,
    "remove_conclusion_phrases": true
  }'
```

## API parsing vs writing your own MIME parser

Python's standard library can parse email, and for a single trusted source it's fine. The trouble starts at scale: you own the IMAP connection pool, the charset fallbacks, the multipart edge cases, and the per-provider auth. The table compares the two approaches on the work that actually fills your sprint, with the unified API column first.

| Task | Unified API | Your own MIME parser |
| --- | --- | --- |
| **Get the body** | Read the `body` field | Walk the MIME tree, pick HTML vs text |
| **Decode parts** | Done for you | Handle base64, quoted-printable, charsets |
| **Strip quotes/signatures** | `messages/clean` call | Write and maintain regex heuristics |
| **Connect a mailbox** | One grant, any provider | Per-provider IMAP/OAuth wiring |
| **Get attachments** | `attachments` array on the message | Parse and decode each MIME part |

The honest tradeoff: if you only ever read one internal IMAP mailbox and want zero external dependencies, Python's built-in [`email` package](https://docs.python.org/3/library/email.html) is a perfectly good answer, and it ships with the language. The API earns its place once you support multiple providers, need clean body text, or don't want to babysit MIME parsing in production. Below roughly 100 messages a day from a single trusted sender, rolling your own is reasonable.

## What fields can I extract from a parsed message?

A parsed message returns everything you'd otherwise reconstruct from MIME headers and parts. Beyond `body` and `snippet`, you get `from`, `to`, `cc`, and `bcc` as name/email pairs, `subject`, `date` as a Unix timestamp, `thread_id` for grouping replies, and an `attachments` array with each file's `content_type`, `filename`, and `size`. The `unread` and `starred` booleans round out the common metadata.

When you need the raw envelope, the `fields` query parameter accepts `include_headers` to return the full header array, or `raw_mime` to get the Base64url-encoded original message and decode it yourself. To trim the response and cut latency, pass `select` with a comma-separated list, for example `select=id,subject,from,date`, so you transfer only the 4 fields you read. That's a real bandwidth win when you list hundreds of messages and only render a subject line and sender, and it pairs well with `limit` to keep each page small.

```bash
curl --request GET \
  --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages?select=id,subject,from,date&limit=50' \
  --header 'Authorization: Bearer <NYLAS_API_KEY>'
```


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


## What's next

- [Clean message HTML and quoted text](/docs/cookbook/email/clean-messages/) to strip signatures and replies before parsing
- [Extract structured data from email](/docs/cookbook/ai/extract-data-from-email/) to turn a cleaned body into JSON with a language model
- [List Google email messages](/docs/cookbook/email/messages/list-messages-google/) for filtering and pagination on Gmail accounts
- [Getting started with Nylas](/docs/v3/getting-started/) to create a project, connector, and your first grant