---
title: "Search and export"
description: "Find messages, threads and inboxes with full-text search and filters, fetch messages in batches, and export an inbox as mbox or JSON lines."
canonical: "https://machinemail.dev/docs/guides/search-and-export"
---

# Search and export

> Find messages, threads and inboxes with full-text search and filters, fetch messages in batches, and export an inbox as mbox or JSON lines.

Source: https://machinemail.dev/docs/guides/search-and-export

## Search messages in an inbox

```bash title="Terminal"
curl "https://api.machinemail.dev/v0/inboxes/support-agent@machinemail.dev/messages/search?q=invoice%20march" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

How `q` matches:

- It searches the subject, the from, to and cc addresses, the plain-text body (or the HTML with tags stripped) and attachment filenames.
- Every word must match. `invoice march` finds messages that contain both.
- The last word is a prefix, so `invoi` finds `invoice`. Earlier words match whole words.
- Case does not matter. Punctuation other than `@ . + _ ' -` inside words is ignored.

Search also takes `before` and `after` (ISO 8601 timestamps), `limit` and `page_token`. It never returns mail with the hidden labels `SPAM`, `BLOCKED`, `TRASH`, `QUARANTINE` or `UNAUTHENTICATED`.

```json title="Response (trimmed)"
{
  "count": 1,
  "limit": 50,
  "messages": [
    {
      "message_id": "msg_01m4fybrc20000ypd31v108d71",
      "thread_id": "thd_01m4fybrc2000195s5ghmfqfnq",
      "labels": ["INBOX", "UNREAD"],
      "from": "ada@example.com",
      "subject": "Invoice",
      "preview": "Invoice attached.",
      "attachments": [{ "attachment_id": "att_01m4fybrc10000qyygf5yv5thv", "filename": "invoice.pdf", "size": 13, "content_type": "application/pdf" }],
      "direction": "inbound",
      "trust_level": "unknown"
    }
  ]
}
```

Results are message items, without bodies or trust reports. Fetch the full message by ID when you need them.

## Search threads

Pass `q` to the thread list, either across every inbox the key can see or within one inbox:

```bash title="Terminal"
curl "https://api.machinemail.dev/v0/threads?q=invoice" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"

curl "https://api.machinemail.dev/v0/inboxes/support-agent@machinemail.dev/threads?q=invoice" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

`/v0/threads/search?q=` and `/v0/inboxes/{inbox_id}/threads/search?q=` do the same thing. Thread search runs the message search above and returns the threads those messages belong to. It considers the newest 500 matching messages; when there were more, the response has `"truncated": true`. Narrow the query or add `after` and `before` in that case.

## Filter lists

Message and thread lists take filters that combine with `q` (threads) or work on their own:

| Parameter                 | Effect                                                                                                   |
| ------------------------- | -------------------------------------------------------------------------------------------------------- |
| `labels`                  | Only items with any of these labels. Repeat the parameter, or pass a comma-separated list or JSON array. |
| `before`, `after`         | ISO 8601 bounds on the message `timestamp`.                                                              |
| `ascending`               | `true` for oldest first.                                                                                 |
| `include_spam`            | Include `SPAM`. Needs `label_spam_read`.                                                                 |
| `include_blocked`         | Include `BLOCKED`. Needs `label_blocked_read`.                                                           |
| `include_trash`           | Include `TRASH`. Needs `label_trash_read`.                                                               |
| `include_unauthenticated` | Include `UNAUTHENTICATED`. Needs `label_unauthenticated_read`.                                           |
| `include_quarantine`      | Include `QUARANTINE`.                                                                                    |
| `limit`, `page_token`     | Pagination: 50 per page by default, at most 500.                                                         |

Asking for a hidden label in `labels` also includes it, so `labels=QUARANTINE` lists the quarantine. Without the matching permission the hidden label stays excluded. See [Threads and messages](https://machinemail.dev/docs/concepts/threads-and-messages).

```bash title="Quarantine review queue"
curl "https://api.machinemail.dev/v0/inboxes/support-agent@machinemail.dev/messages?labels=QUARANTINE&after=2026-10-01T00:00:00Z" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

> **Note:** For inbound mail, `timestamp` comes from the sender's `Date` header. Use `before` and `after` for reporting, not as a cursor for new mail. For new mail, use the `UNREAD` label or
> [events](https://machinemail.dev/docs/guides/realtime-events).

## Search inboxes

```bash title="Terminal"
curl "https://api.machinemail.dev/v0/inboxes/search?q=support" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

This matches `q` as a substring of the address or the display name. It scans the newest 500 inboxes in the key's scope and sets `"truncated": true` when there were more. The response has `count` and `inboxes` but no pagination.

## Fetch many messages at once

`batch-get` returns full messages, with bodies and trust reports, for up to 100 IDs:

```bash title="Terminal"
curl https://api.machinemail.dev/v0/inboxes/support-agent@machinemail.dev/messages/batch-get \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message_ids": ["msg_01m4fybrc20000ypd31v108d71", "msg_01m4fy8han0000y3ge3m73vw3w"]}'
```

IDs that do not exist in the inbox are skipped, and results keep the order you asked for. `batch-update` takes the same `message_ids` with `add_labels` and `remove_labels`, which is the quickest way to mark search results read:

```bash title="Terminal"
curl https://api.machinemail.dev/v0/inboxes/support-agent@machinemail.dev/messages/batch-update \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message_ids": ["msg_01m4fybrc20000ypd31v108d71"], "remove_labels": ["UNREAD"]}'
```

## Export an inbox

`GET /v0/inboxes/{inbox_id}/export` streams every message in the inbox, oldest first, as a file download. It includes all labels, hidden ones too.

| `format`         | Content type           | Each entry                                                                                             |
| ---------------- | ---------------------- | ------------------------------------------------------------------------------------------------------ |
| `mbox` (default) | `application/mbox`     | The original raw MIME, in mboxrd format. Opens in Thunderbird, Apple Mail and most mail tools.         |
| `jsonl`          | `application/x-ndjson` | One full message per line, in the same JSON as `GET .../messages/{message_id}`, trust report included. |

**curl**

```bash title="Terminal"
curl -o support-agent.mbox "https://api.machinemail.dev/v0/inboxes/support-agent@machinemail.dev/export?format=mbox" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"

curl -o support-agent.jsonl "https://api.machinemail.dev/v0/inboxes/support-agent@machinemail.dev/export?format=jsonl" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

**Python**

```python title="export.py"
import json, os, requests

url = "https://api.machinemail.dev/v0/inboxes/support-agent@machinemail.dev/export"
with requests.get(url, params={"format": "jsonl"}, headers={"Authorization": f"Bearer {os.environ['MACHINEMAIL_API_KEY']}"}, stream=True) as res:
    res.raise_for_status()
    for line in res.iter_lines():
        if line:
            msg = json.loads(line)
            print(msg["timestamp"], msg["direction"], msg.get("subject"))
```

Export needs `message_read`. Attachments are inside the mbox MIME; the JSON lines export has attachment metadata only, so download files separately if you need them. See [Attachments](https://machinemail.dev/docs/guides/attachments).

Export an ephemeral inbox before it expires. Once the reaper deletes it, its mail is gone.

## Raw MIME for one message

```bash title="Terminal"
curl -o message.eml https://api.machinemail.dev/v0/inboxes/support-agent@machinemail.dev/messages/msg_01m4fybrc20000ypd31v108d71/raw \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

The response is the original message as received (or as sent) with `Content-Type: message/rfc822`. Raw MIME is untrusted input; do not pass it to a model.

## Related

- [Threads and messages](https://machinemail.dev/docs/concepts/threads-and-messages)
- [Pagination](https://machinemail.dev/docs/reference/pagination)
- [Attachments](https://machinemail.dev/docs/guides/attachments)
