Skip to main content

Python SDK for the Unsent API - Send transactional emails with ease

Project description

unsent Python SDK

Prerequisites

Installation

pip

pip install unsent

poetry

poetry add unsent

Usage

Basic Setup

from unsent import unsent

client = unsent("us_12345")

Environment Variables

You can also set your API key using environment variables:

# Set UNSENT_API_KEY or UNSENT_API_KEY in your environment
# Then initialize without passing the key
client = unsent()

Sending Emails

Simple Email

data, error = client.emails.send({
    "to": "hello@acme.com",
    "from": "hello@company.com",
    "subject": "unsent email",
    "html": "<p>unsent is the best email service provider to send emails</p>",
    "text": "unsent is the best email service provider to send emails",
})

if error:
    print(f"Error: {error}")
else:
    print(f"Email sent! ID: {data['id']}")

Email with Attachments

data, error = client.emails.send({
    "to": "hello@acme.com",
    "from": "hello@company.com",
    "subject": "Email with attachment",
    "html": "<p>Please find the attachment below</p>",
    "attachments": [
        {
            "filename": "document.pdf",
            "content": "base64-encoded-content-here",
        }
    ],
})

Scheduled Email

from datetime import datetime, timedelta

# Schedule email for 1 hour from now
scheduled_time = datetime.now() + timedelta(hours=1)

data, error = client.emails.send({
    "to": "hello@acme.com",
    "from": "hello@company.com",
    "subject": "Scheduled email",
    "html": "<p>This email was scheduled</p>",
    "scheduledAt": scheduled_time,
})

Batch Emails

emails = [
    {
        "to": "user1@example.com",
        "from": "hello@company.com",
        "subject": "Hello User 1",
        "html": "<p>Welcome User 1</p>",
    },
    {
        "to": "user2@example.com",
        "from": "hello@company.com",
        "subject": "Hello User 2",
        "html": "<p>Welcome User 2</p>",
    },
]

data, error = client.emails.batch(emails)

if error:
    print(f"Error: {error}")
else:
    print(f"Sent {len(data['emails'])} emails")

Idempotent Retries

# Idempotent retries: same payload + same key returns the original response
payload = {
    "to": "hello@acme.com",
    "from": "hello@company.com",
    "subject": "Welcome!",
    "html": "<p>Welcome to our service</p>",
}

resp, _ = client.emails.send(
    payload=payload,
    options={"idempotency_key": "signup-123"},
)

# Works for batch requests as well
resp, _ = client.emails.batch(
    payload=[payload],
    options={"idempotency_key": "bulk-welcome-1"},
)

# If the same key is reused with a different payload, the API responds with HTTP 409.

Managing Emails

Get Email Details

data, error = client.emails.get("email_id")

if error:
    print(f"Error: {error}")
else:
    print(f"Email status: {data['status']}")

Update Email

data, error = client.emails.update("email_id", {
    "subject": "Updated subject",
    "html": "<p>Updated content</p>",
})

Cancel Scheduled Email

data, error = client.emails.cancel("email_id")

if error:
    print(f"Error: {error}")
else:
    print("Email cancelled successfully")

Managing Contacts

Create Contact

data, error = client.contacts.create("contact_book_id", {
    "email": "user@example.com",
    "firstName": "John",
    "lastName": "Doe",
    "metadata": {
        "company": "Acme Inc",
        "role": "Developer"
    }
})

Get Contact

data, error = client.contacts.get("contact_book_id", "contact_id")

Update Contact

data, error = client.contacts.update("contact_book_id", "contact_id", {
    "firstName": "Jane",
    "metadata": {
        "role": "Senior Developer"
    }
})

Upsert Contact

# Creates if doesn't exist, updates if exists
data, error = client.contacts.upsert("contact_book_id", "contact_id", {
    "email": "user@example.com",
    "firstName": "John",
    "lastName": "Doe",
})

Delete Contact

data, error = client.contacts.delete(
    book_id="contact_book_id",
    contact_id="contact_id"
)

Managing Campaigns

Create Campaign

from unsent import types

campaign_payload: types.CampaignCreate = {
    "name": "Welcome Series",
    "subject": "Welcome to our service!",
    "html": "<p>Thanks for joining us!</p>",
    "from": "welcome@example.com",
    "contactBookId": "cb_1234567890",
}

campaign_resp, error = client.campaigns.create(payload=campaign_payload)

if error:
    print(f"Error: {error}")
else:
    print(f"Campaign created! ID: {campaign_resp['id']}")

Schedule Campaign

from unsent import types

schedule_payload: types.CampaignSchedule = {
    "scheduledAt": "2024-12-01T10:00:00Z",
}

schedule_resp, error = client.campaigns.schedule(
    campaign_id=campaign_resp["id"],
    payload=schedule_payload
)

if error:
    print(f"Error: {error}")
else:
    print("Campaign scheduled successfully!")

Pause/Resume Campaigns

# Pause a campaign
pause_resp, error = client.campaigns.pause(campaign_id="campaign_123")

if error:
    print(f"Error: {error}")
else:
    print("Campaign paused successfully!")

# Resume a campaign
resume_resp, error = client.campaigns.resume(campaign_id="campaign_123")

if error:
    print(f"Error: {error}")
else:
    print("Campaign resumed successfully!")

Get Campaign Details

data, error = client.campaigns.get("campaign_id")

if error:
    print(f"Error: {error}")
else:
    print(f"Campaign status: {data['status']}")
    print(f"Recipients: {data['total']}")
    print(f"Sent: {data['sent']}")

Managing Domains

List Domains

data, error = client.domains.list()

if error:
    print(f"Error: {error}")
else:
    for domain in data:
        print(f"Domain: {domain['domain']}, Status: {domain['status']}")

Create Domain

data, error = client.domains.create({
    "domain": "example.com"
})

Verify Domain

data, error = client.domains.verify(domain_id=123)

if error:
    print(f"Error: {error}")
else:
    print(f"Verification status: {data['status']}")

Get Domain

data, error = client.domains.get(domain_id=123)

Error Handling

By default, the SDK raises exceptions on HTTP errors:

from unsent import unsent, unsentHTTPError

client = unsent("us_12345")

try:
    data, error = client.emails.send({
        "to": "invalid-email",
        "from": "hello@company.com",
        "subject": "Test",
        "html": "<p>Test</p>",
    })
except unsentHTTPError as e:
    print(f"HTTP {e.status_code}: {e.error['message']}")

To disable automatic error raising:

client = unsent("us_12345", raise_on_error=False)

data, error = client.emails.send({
    "to": "hello@acme.com",
    "from": "hello@company.com",
    "subject": "Test",
    "html": "<p>Test</p>",
})

if error:
    print(f"Error: {error['message']}")
else:
    print("Success!")

Custom Session

For advanced use cases, you can provide your own requests.Session:

import requests
from unsent import unsent

session = requests.Session()
session.verify = False  # Not recommended for production!

client = unsent("us_12345", session=session)

API Reference

Client Methods

  • unsent(key, url, raise_on_error=True, session=None) - Initialize the client

Email Methods

  • client.emails.send(payload) - Send an email (alias for create)
  • client.emails.create(payload) - Create and send an email
  • client.emails.batch(emails) - Send multiple emails in batch
  • client.emails.get(email_id) - Get email details
  • client.emails.update(email_id, payload) - Update a scheduled email
  • client.emails.cancel(email_id) - Cancel a scheduled email

Contact Methods

  • client.contacts.create(book_id, payload) - Create a contact
  • client.contacts.get(book_id, contact_id) - Get contact details
  • client.contacts.update(book_id, contact_id, payload) - Update a contact
  • client.contacts.upsert(book_id, contact_id, payload) - Upsert a contact
  • client.contacts.delete(book_id, contact_id) - Delete a contact

Campaign Methods

  • client.campaigns.create(payload) - Create a campaign
  • client.campaigns.get(campaign_id) - Get campaign details
  • client.campaigns.schedule(campaign_id, payload) - Schedule a campaign
  • client.campaigns.pause(campaign_id) - Pause a campaign
  • client.campaigns.resume(campaign_id) - Resume a campaign

Domain Methods

  • client.domains.list() - List all domains
  • client.domains.create(payload) - Create a domain
  • client.domains.verify(domain_id) - Verify a domain
  • client.domains.get(domain_id) - Get domain details

Requirements

  • Python 3.8+
  • requests >= 2.32.0
  • typing_extensions >= 4.7

License

MIT

Support

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

unsent-1.0.1.tar.gz (10.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

unsent-1.0.1-py3-none-any.whl (11.4 kB view details)

Uploaded Python 3

File details

Details for the file unsent-1.0.1.tar.gz.

File metadata

  • Download URL: unsent-1.0.1.tar.gz
  • Upload date:
  • Size: 10.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.2.1 CPython/3.13.8 Darwin/25.0.0

File hashes

Hashes for unsent-1.0.1.tar.gz
Algorithm Hash digest
SHA256 2b9602d51e334cdf3de0045738ace6bfedb150d3cf3a1c86314fd15dfbd086db
MD5 52d9146daf2311450b73dd55c206343a
BLAKE2b-256 2ca259c0e8a20e8b4bc96df681299633b4d4e4e6cbbf3f358042fcfc3f5a87fd

See more details on using hashes here.

File details

Details for the file unsent-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: unsent-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 11.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.2.1 CPython/3.13.8 Darwin/25.0.0

File hashes

Hashes for unsent-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 f72b43c6fce64f9f04d84cbf4ae1545a84f105f352d05ce3b283eb4c2a8c2dfb
MD5 90d001bd991007d7b57ce238902f1251
BLAKE2b-256 6456779cee41277b6df89766ce64e71af44380469319a945d873d3a26bbf5666

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page