Home
Official SDKs

Python SDK

Sync and async tropmail clients with pagination and retries.
pip install tropmail

Requires Python 3.10+. The client reads TROPMAIL_API_KEY unless you pass the key.

Source: github.com/tropmail/tropmail-python

Quick start

from tropmail import TropMail

with TropMail() as client:
    boxes = client.mailboxes.list()
    mailbox = boxes[0]
    print(f"{mailbox.email}: {mailbox.opened_count} opened")

    for email in client.emails.iterate(mailbox_id=mailbox.id, status="Open"):
        print(email.timestamp, email.from_.address, email.subject)

Pass the key explicitly if you prefer:

client = TropMail("tm_live_your32charalphanumericapikeyhere")

Async

import asyncio
from tropmail import AsyncTropMail

async def main() -> None:
    async with AsyncTropMail() as client:
        boxes = await client.mailboxes.list()
        mailbox_id = boxes[0].id
        async for email in client.emails.iterate(mailbox_id=mailbox_id, status="Favorite"):
            print(email.subject)

asyncio.run(main())

AsyncTropMail is the same surface as TropMail with await.

Reading an email

get() returns the HTML view by default. Pass the email object you already have instead of a bare id and the SDK forwards its timestamp for a faster lookup.

page = client.emails.list(mailbox_id=mailbox.id, limit=10)
email = page.emails[0]

detail = client.emails.get(mailbox.id, email, view="text")
print(detail.content)

for attachment in detail.attachments:
    print(attachment.filename, attachment.size, attachment.scan_status)

Markdown

The markdown view is generated on demand. get_markdown() retries 504 for you:

detail = client.emails.get_markdown(mailbox.id, email)
print(detail.content)

Actions

client.emails.favorite(mailbox.id, email)
client.emails.close(mailbox.id, email)
client.emails.block(mailbox.id, email)
client.emails.clear_action(mailbox.id, email)

Those are shorthands for update():

client.emails.update(mailbox.id, email, email_state="Open", action_status="Favorite")
page = client.emails.search("invoice", mailbox_id=mailbox.id, limit=25)
for email in client.emails.search_iterate("invoice", mailbox_id=mailbox.id):
    print(email.subject)

Search responses report total: 0. Iterators page until they see a short page.

Attachments

import time

info = client.attachments.get(mailbox.id, attachment_id)
print(info.filename, info.scan_status)

client.attachments.scan(mailbox.id, attachment_id)
while True:
    info = client.attachments.get(mailbox.id, attachment_id)
    if info.scan_status != "Processing":
        break
    time.sleep(2)
print(info.scan_status, info.scan_result)

path = client.attachments.download_to(mailbox.id, attachment_id, "/tmp/invoice.pdf")

A file whose size is 0 is a real empty attachment. download_to writes an empty file.

Downloads use your API key on the same host as the rest of the API.

Errors

from tropmail import (
    AuthenticationError, InvalidAPIKeyError, NotFoundError, RateLimitError,
    TropMailError,
)

try:
    client.emails.get(mailbox.id, "does-not-exist")
except NotFoundError as exc:
    print(exc.status, exc.request_id)
except RateLimitError as exc:
    print("retry after", exc.retry_after)
except TropMailError as exc:
    print("request failed:", exc)

Every error carries status, message, and request_id.

A missing or malformed key raises InvalidAPIKeyError before any HTTP call. HTTP 400 from the API is ValidationError.

ExceptionHTTP
InvalidAPIKeyErrorlocal (no request)
ValidationError400
AuthenticationError401
TierError403
NotFoundError404
RateLimitError429
MarkdownTimeoutError504
ServerError5xx
ConnectionErrortransport failure

Rate limits

The client reads X-RateLimit-* headers and stays within your account limit.

print(client.rate_limit)

Disable pacing with TropMail(throttle=False) if you manage concurrency yourself.

Configuration

client = TropMail(
    api_key=None,                                  # else TROPMAIL_API_KEY
    base_url="https://api.tropmail.com/api/v1",
    timeout=120.0,
    max_retries=3,
    throttle=True,
)

Retries with backoff on 429, 502, 503, 504, and transport errors. Reads retry. Mutations do not.

Examples

FileWhat it shows
quickstart.pymailbox summary, listing, reading one message
async_inbox.pyAsyncTropMail, concurrent reads
save_attachments.pyscan and download to disk