Find mail
Search words and phrases, combine message filters, and find matching conversations.
Read as Markdown ↗Use the ordinary received, sent or conversation listing to find mail. Search and filters work together in one request; no separate search endpoint is needed.
curl --get 'https://cherami.to/v1/inboxes/INBOX_ID/messages' \
-H "Authorization: Bearer $CHERAMI_API_KEY" \
--data-urlencode 'query="project invoice"' \
--data-urlencode 'from=alice@example.com' \
--data-urlencode 'labels_all=needs-attention' \
--data-urlencode 'order=relevance'With MCP, use list_messages, list_sent or list_threads with the same named filters. count_messages accepts the same filters as the received listing, without ordering or pagination.
With the TypeScript SDK, the same filters remain ordinary listing parameters:
for await (const message of client.iterate("listMessages", {
inbox_id: inboxId,
query: '"project invoice"',
from: "alice@example.com",
labels_all: ["needs-attention"],
order: "relevance",
}, { maxPages: 5 })) {
console.log(message.id);
}Use client.pages when you need the last page's next_cursor to resume a bounded scan. countMessages takes the received filters without pagination or order.
With the Python SDK, dictionary keys preserve HTTP filter names, including from:
for message in client.iterate("list_messages", {
"inbox_id": inbox_id,
"query": '"project invoice"',
"from": "alice@example.com",
"labels_all": ["needs-attention"],
"order": "relevance",
}, max_pages=5):
print(message["id"])With AsyncCherami, use async for. Python's pages and count_messages work like their TypeScript counterparts above.
Search words and phrases
query searches decoded subjects and message bodies. When a message has no usable plain-text alternative, search uses text extracted from HTML. Attachment contents, attachment filenames and transport headers are not searched. Quoted history remains searchable.
All query terms must match the same message, but may occur in different parts of its subject or body. Double quotes require consecutive words in one subject or body. For example, invoice "repair workshop" requires both invoice and the phrase repair workshop.
Matching is case-insensitive and ignores Latin diacritics. It matches whole tokens, not arbitrary substrings or word stems. Punctuation separates tokens; OR, NOT and wildcards have no special meaning. For a literal subject substring, use subject instead.
Search matches words and phrases, not semantic similarity. If you need semantic search, contact hello@cherami.to and tell us what you're trying to find.
Combine filters
All supplied conditions combine with AND. Omit a condition to leave it unrestricted.
| Parameter | Meaning |
|---|---|
query | Words and double-quoted phrases in subject/body. Up to 512 characters and 16 terms or phrases. |
from | Exact bare email address in From, case-insensitive. This is not the SMTP envelope sender or proof of identity. |
recipient | Exact bare email address in To/Cc/Bcc where present, case-insensitive. Bcc is commonly absent from received headers. |
subject | Literal case-insensitive subject substring, up to 998 characters. % and _ are literal characters. |
after | Inclusive service receipt/submission timestamp. |
before | Exclusive service receipt/submission timestamp. |
labels_all | Require every listed label. |
labels_any | Require at least one listed label. |
labels_none | Exclude messages carrying any listed label. |
Use YYYY-MM-DDTHH:mm:ss followed by Z or a numeric offset such as +02:00. Optional fractional seconds have one to three digits: 2026-10-01T00:00:00.123Z. Date-only values and times without a timezone are not accepted. URL-encode offsets in HTTP queries so + is not interpreted as a space.
Bounds use received time for incoming mail and submission time for outgoing mail, never the sender's Date header. after must precede before when both are supplied. To select a local calendar day, supply its start and the next day's start as after and before, each with the offset applicable at that boundary; daylight-saving transitions can make the interval shorter or longer than 24 hours.
In HTTP, repeat label parameters for multiple labels; in MCP and SDKs, supply arrays. Label matching is case-sensitive. See label filtering and the listing parameter reference for validation limits.
Text, sender and recipient filters match prepared received mail only. Pending, processing and failed messages remain available without those filters, including with date/label filters. Sent searches include accepted, rejected and unknown attempts; a match is not delivery confirmation.
Choose an order
order | Result order |
|---|---|
newest | Newest first; the default. |
oldest | Oldest first. |
relevance | Strongest keyword matches first; requires query. Subject matches receive extra weight. |
Relevance ties use newest first. For conversations, newest/oldest refers to the whole conversation's latest activity, not just its matching messages.
Follow next_cursor with the same filters and order. Results can change while you paginate; restart without a cursor for a fresh view.
Find a conversation
list_threads and GET /v1/inboxes/{inbox_id}/threads return a conversation when one member satisfies every condition. A sender match on one message and a keyword match on another do not qualify it. Label exclusions apply to that matching member, not every message in the conversation.
Filtered conversations include matching_message_ids, containing up to 100 matching IDs newest first, and matching_message_count, the total number of matching members. The ordinary conversation counts, subject and activity still describe the whole conversation. Read it with get_thread or the thread detail endpoint. Thread detail remains unfiltered.
Received-message reference · Sent-message reference · Conversation reference