Skip to main content

ToSend Python SDK

Official Python SDK for the ToSend email API.

PyPI version Python Versions

Installation

pip install tosend

Examples

See the examples folder for complete working examples:

export TOSEND_API_KEY=tsend_your_api_key
python examples/send_email.py

Quick Start

from tosend import ToSend

client = ToSend("tsend_your_api_key")

response = client.send(
    from_address={"email": "hello@yourdomain.com", "name": "Your App"},
    to=[{"email": "user@example.com"}],
    subject="Welcome!",
    html="<h1>Hello!</h1><p>Thanks for signing up.</p>",
)

print(response.message_id)

Usage

Send an Email

response = client.send(
    from_address={"email": "hello@yourdomain.com", "name": "Your App"},
    to=[
        {"email": "user@example.com"},
        {"email": "another@example.com", "name": "John Doe"},
    ],
    subject="Hello!",
    html="<h1>Welcome</h1>",
    text="Welcome",  # optional
)

With CC, BCC, and Reply-To

response = client.send(
    from_address={"email": "hello@yourdomain.com"},
    to=[{"email": "user@example.com"}],
    cc=[{"email": "cc@example.com"}],
    bcc=[{"email": "bcc@example.com"}],
    reply_to={"email": "support@yourdomain.com", "name": "Support"},
    subject="Hello",
    html="<p>Hello World</p>",
)

With Attachments

import base64

with open("invoice.pdf", "rb") as f:
    content = base64.b64encode(f.read()).decode()

response = client.send(
    from_address={"email": "hello@yourdomain.com"},
    to=[{"email": "user@example.com"}],
    subject="Your Invoice",
    html="<p>Please find your invoice attached.</p>",
    attachments=[
        {
            "type": "application/pdf",
            "name": "invoice.pdf",
            "content": content,
        }
    ],
)

Batch Sending

Send multiple emails in a single request:

response = client.batch([
    {
        "from": {"email": "hello@yourdomain.com"},
        "to": [{"email": "user1@example.com"}],
        "subject": "Hello User 1",
        "html": "<p>Welcome!</p>",
    },
    {
        "from": {"email": "hello@yourdomain.com"},
        "to": [{"email": "user2@example.com"}],
        "subject": "Hello User 2",
        "html": "<p>Welcome!</p>",
    },
])

for result in response.results:
    if result.status == "success":
        print(f"Sent: {result.message_id}")
    else:
        print(f"Failed: {result.message}")

Get Account Info

info = client.get_account_info()

print(info.account.status)            # "active"
print(info.account.credit_balance)    # None means the account does not consume credits
print(info.account.limit_per_second)
print(info.account.bounce_rate)       # a fraction between 0 and 1

for domain in info.domains:
    print(f"{domain.domain_name}: {domain.verification_status}")

Admin API

ToSendAdmin is a separate client for the Admin API. It takes an admin key (tsend_admin_...) and refuses a sending key at construction. Each call needs the matching scope on the key; a missing one raises ToSendError with status 403 and error.code == "insufficient_scope".

import os
from tosend import ToSendAdmin

admin = ToSendAdmin(os.environ["TOSEND_ADMIN_KEY"])

# Act on one of your client teams (sends X-Account-Id)
client = admin.for_account(4203)

domain = client.create_domain("client.example.com")
detail = client.get_domain(domain["id"])
print(detail["dns_records"]["dkim"])  # show these records to your client

# Needs api_keys:write and emails:send
key = client.create_sending_key("Client: Acme Corp")
print(key["api_key"])  # returned once, store it now

# Needs emails:send
client.send(
    from_address={"email": "hello@client.example.com"},
    to=[{"email": "user@example.com"}],
    subject="Hi",
    html="<p>Hello</p>",
)

Also available: get_info, list_domains, delete_domain, list_emails, get_email, list_suppressions, delete_suppression, list_webhooks, create_webhook, update_webhook, delete_webhook, list_sending_keys, delete_sending_key, list_teams, create_team, update_team and batch. List methods take page and per_page and return a dict with data, total, page and per_page; single-resource methods return the resource as a dict.

Configuration

With Options

client = ToSend(
    api_key="tsend_your_api_key",
    base_url="https://api.tosend.com",  # optional
    timeout=60,  # optional, in seconds
)

Error Handling

from tosend import ToSend, ToSendError

client = ToSend("tsend_your_api_key")

try:
    response = client.send(
        from_address={"email": "hello@yourdomain.com"},
        to=[{"email": "user@example.com"}],
        subject="Hello",
        html="<p>Hello</p>",
    )
except ToSendError as e:
    print(f"Error: {e.message}")
    print(f"Status: {e.status_code}")
    print(f"Type: {e.error_type}")
    print(f"Details: {e.errors}")

    if e.is_validation_error:
        # Handle validation error (422)
        pass

    if e.is_authentication_error:
        # Handle auth error (401/403)
        pass

    if e.is_rate_limit_error:
        # Handle rate limit (429)
        pass

Type Hints

The SDK includes full type hints. You can also use the provided dataclasses:

from tosend import ToSend, Address, SendEmailRequest

client = ToSend("tsend_your_api_key")

# Using Address dataclass
from_addr = Address(email="hello@yourdomain.com", name="Your App")
to_addr = Address(email="user@example.com")

response = client.send(
    from_address=from_addr,
    to=[to_addr],
    subject="Hello!",
    html="<h1>Welcome!</h1>",
)

Requirements

  • Python 3.8 or higher
  • No external dependencies (uses only standard library)

License

MIT

Release files for tosend 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tosend 1.1.0
File Size Uploaded
tosend-1.1.0.tar.gz 15.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tosend 1.1.0
File Interpreter ABI Platform
tosend-1.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 28.6 kB

Release files / tosend-1.1.0.tar.gz

Download URL tosend-1.1.0.tar.gz
Size 15.3 kB
Tags Source
SHA-256 checksum
How to use checksums
4a18baac97b6d38864f033011d0b42c6437a612e74f40af02ac0ef4e8f4a2e78
BLAKE2b-256 checksum
How to use checksums
b709685d1484f70c34bc77167d0497631fd1c10c9e2623d9f78477b1a1679232
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Release files / tosend-1.1.0-py3-none-any.whl

Download URL tosend-1.1.0-py3-none-any.whl
Size 13.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f08a4cd2c3981609421bfb978070cc47827a1d5b27013c20bfeee949a2e4d4c6
BLAKE2b-256 checksum
How to use checksums
ff0e04ef80f7d964e4a4dd42999b88e828634971da09ec161542ed172de644a4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Release history Release notifications | RSS feed

This release

1.1.0 This release

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page