Skip to main content

Listmonk Email API Client for Python

Project description

Listmonk Email API Client for Python

Client for the open source, self-hosted Listmonk email platform based on httpx2 and pydantic.

listmonk is intended for integrating your Listmonk instance into your web app. The Listmonk API is extensive but this only covers the subset that most developers will need for common SaaS actions such as subscribe, unsubscribe, and segment users (into separate lists).

So while it doesn't currently cover every endpoint (for example campaign analytics and bulk subscriber deletion are not yet implemented) it will likely work for you. That said, PRs are welcome.

🔀 Async is currently planned but not yet implemented. With the httpx2-base, it should be trivial if needed.

Documentation

📚 Full documentation lives at mkennedy.codes/docs/listmonk, including the complete API Reference for every function, model, and exception in the library.

Core Features

  • Add a subscriber to your subscribed users.
  • 🙎 Get subscriber details by email, ID, UUID, and more.
  • 📝 Modify subscriber details (including custom attribute collection).
  • 🔍 Search your users based on app and custom attributes.
  • 🏥 Check the health and connectivity of your instance.
  • 👥 Retrieve your segmentation lists, list details, and subscribers.
  • 🙅 Unsubscribe and block users who don't want to be contacted further.
  • 💥 Completely delete a subscriber from your instance.
  • 📧 Send transactional email with template data (e.g. password reset emails).
  • 📨 Manage campaign (bulk) emails from the API.
  • 📎 Upload media and attach files to campaigns.
  • 🎨 Edit and create templates to control the overall look and feel of campaigns.
  • 📝 Create, edit and delete lists.
  • ✅ Fully type annotated and ships py.typed, so mypy/pyright type checking works out of the box.

Installation

Just pip install listmonk

Usage

import pathlib
import listmonk
from listmonk.models import MailingList, Subscriber
from typing import Optional

listmonk.set_url_base('https://yourlistmonkurl.com')

listmonk.login('sammy_z', '1234')
valid: bool = listmonk.verify_login()

# Is it alive and working?
up: bool = listmonk.is_healthy()

# Create, update, and delete lists
new_list: MailingList = listmonk.create_list(list_name="my_new_list")
listmonk.update_list(list_id=new_list.id, description='Updated description')
listmonk.delete_list(new_list.id)

# Read data about your lists
lists: list[MailingList] = listmonk.lists()
the_list: MailingList = listmonk.list_by_id(list_id=7)

# Various ways to access existing subscribers
subscribers: list[Subscriber] = listmonk.subscribers(list_id=9)

subscriber: Optional[Subscriber] = listmonk.subscriber_by_email('testuser@some.domain')
subscriber: Optional[Subscriber] = listmonk.subscriber_by_id(2001)
subscriber: Optional[Subscriber] = listmonk.subscriber_by_uuid('f6668cf0-1c...')

# Create a new subscriber
new_subscriber = listmonk.create_subscriber(
    'testuser@some.domain', 'Jane Doe',
    {1, 7, 9}, pre_confirm=True, attribs={...})

# Change the email, custom rating, and add to lists 4 & 6, remove from 5.
subscriber.email = 'newemail@some.domain'
subscriber.attribs['rating'] = 7
subscriber = listmonk.update_subscriber(subscriber, {4, 6}, {5})

# Bulk-add existing subscribers to lists in one call
listmonk.add_subscribers_to_lists([2001, 2002], [4, 6])

# Confirm single-opt-ins via the API (e.g. for when you manage that on your platform)
listmonk.confirm_optin(subscriber.uuid, the_list.uuid)

# Disable then re-enable a subscriber
subscriber = listmonk.disable_subscriber(subscriber)
subscriber = listmonk.enable_subscriber(subscriber)

# Block (unsubscribe) them
listmonk.block_subscriber(subscriber)

# Fully delete them from your system
listmonk.delete_subscriber(subscriber.email)

# Send an individual, transactional email (e.g. password reset)
to_email = 'testuser@some.domain'
from_email = 'app@your.domain'
template_id = 3  # *TX* template ID from listmonk
template_data = {'full_name': 'Test User', 'reset_code': 'abc123'}

status: bool = listmonk.send_transactional_email(
    to_email, template_id, from_email=from_email,
    template_data=template_data, content_type='html')

# Optional plaintext fallback for multipart HTML emails.
status = listmonk.send_transactional_email(
    to_email,
    template_id,
    from_email=from_email,
    template_data=template_data,
    content_type='html',
    altbody='Plaintext order summary for mail clients that prefer text.'
)

# You can also add one or multiple attachments with transactional mails
attachments = [
    pathlib.Path("/path/to/your/file1.pdf"),
    pathlib.Path("/path/to/your/file2.png")
]

status: bool = listmonk.send_transactional_email(
    to_email,
    template_id,
    from_email=from_email,
    template_data=template_data,
    attachments=attachments,
    content_type='html',
    altbody='Plaintext fallback body'
)

# Access existing campaigns
from listmonk.models import Campaign
from datetime import datetime, timedelta

campaigns: list[Campaign] = listmonk.campaigns()
campaign: Optional[Campaign] = listmonk.campaign_by_id(15)

# Upload media and create a campaign with attachments
media = listmonk.upload_media(pathlib.Path("/path/to/report.pdf"))
# Or upload from bytes: media = listmonk.upload_media(pdf_bytes, filename="report.pdf")

listmonk.create_campaign(name='This is my Great Campaign!',
                         subject="You won't believe this!",
                         body='<p>Some Insane HTML!</p>',  # Optional
                         alt_body='Some Insane TXT!',  # Optional
                         send_at=datetime.now() + timedelta(hours=1),  # Optional
                         template_id=5,  # Optional; defaults to None (server uses its default template)
                         list_ids={1, 2},  # Optional Defaults to 1
                         tags=['good', 'better', 'best'],  # Optional
                         media_ids=[media.id],  # Optional, attach uploaded media
                         )

# Update A Campaign
campaign_to_update: Optional[Campaign] = listmonk.campaign_by_id(15)
campaign_to_update.name = "More Elegant Name"
campaign_to_update.subject = "Even More Clickbait!!"
campaign_to_update.body = "<p>There's a lot more we need to say so we're updating this programmatically!"
campaign_to_update.altbody = "There's a lot more we need to say so we're updating this programmatically!"
campaign_to_update.lists = [3, 4]

# Existing attachments are kept on update; pass media_ids=[...] to change them
# (or media_ids=[] to remove them all).
listmonk.update_campaign(campaign_to_update)

# Delete a Campaign (by its numeric ID)
listmonk.delete_campaign(15)

# Preview Campaign
preview = listmonk.campaign_preview_by_id(15)
print(preview.preview)

# Access existing Templates
from listmonk.models import Template
templates: list[Template] = listmonk.templates()
template: Template = listmonk.template_by_id(2)

# Create a new Template for Campaigns
new_template = listmonk.create_template(
    name='NEW TEMPLATE',
    body='<p>Some Insane HTML! {{ template "content" . }} </p>',
    type='campaign',
)

# Update A Template
new_template.name = "Bob's Great Template"
listmonk.update_template(new_template)

# Mark a template as the default for its type
listmonk.set_default_template(new_template.id)

# Delete a Template
listmonk.delete_template(3)

# Preview Template
preview = listmonk.template_preview_by_id(3)
print(preview.preview)

# Create a new template for Transactional Emails
# (the {{ template "content" . }} placeholder is required in every template body)
new_tx_template = listmonk.create_template(
    name='NEW TX TEMPLATE',
    subject='Your Transactional Email Subject',
    body='<p>Hi {{ .Subscriber.FirstName }}! {{ template "content" . }}</p>',
    type='tx',
)

F.A.Q.

I got httpx2.HTTPStatusError: Client error '403 Forbidden'

If you encounter an error like this in your console:

httpx2.HTTPStatusError: Client error '403 Forbidden' for url 'https://yoursite.local/api/subscribers?page=1&per_page=100&query=subscribers.email='john@example.com''

It means the authenticated user doesn’t have sufficient permissions to run SQL queries on subscriber data.

Solution: Check the role assigned to your user. It must include the subscribers:sql_query permission to allow executing SQL queries on subscriber data. You can review and update user roles in your system’s admin panel. [Reference]

I got an SSL certificate verification error against my self-hosted Listmonk

httpx2 validates TLS certificates against the bundled certifi CA list by default. If you self-host Listmonk behind a custom or corporate Certificate Authority you may see an error like:

ssl.SSLCertVerificationError: certificate verify failed: unable to get local issuer certificate

Solution: Point the client at your CA bundle via the standard environment variables before using the library:

export SSL_CERT_FILE=/path/to/your-ca-bundle.pem   # or SSL_CERT_DIR=/path/to/ca-dir

Want to contribute?

PRs are welcome. But please open an issue first to see if the proposed feature fits with the direction of this library.

Enjoy.

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

listmonk-0.4.2.tar.gz (88.7 kB view details)

Uploaded Source

Built Distribution

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

listmonk-0.4.2-py3-none-any.whl (25.6 kB view details)

Uploaded Python 3

File details

Details for the file listmonk-0.4.2.tar.gz.

File metadata

  • Download URL: listmonk-0.4.2.tar.gz
  • Upload date:
  • Size: 88.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for listmonk-0.4.2.tar.gz
Algorithm Hash digest
SHA256 3a2222064c21b8abb7c7e7c9e610b5620d7daa9b883ae4751cc4d887f1a595bf
MD5 72fa6e0437c9ab179e7e68f9f2d18a63
BLAKE2b-256 ab0f9a4c35e4f53306a8f26ce5dae77f39e83301e8f5ff64c5bba337af4bd9d9

See more details on using hashes here.

File details

Details for the file listmonk-0.4.2-py3-none-any.whl.

File metadata

  • Download URL: listmonk-0.4.2-py3-none-any.whl
  • Upload date:
  • Size: 25.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for listmonk-0.4.2-py3-none-any.whl
Algorithm Hash digest
SHA256 5d9144c8a9f59137bd3a81a16c81f807a92b5aff4db5cd1219690523f0a6b266
MD5 e3698e7e8b72ebff9477a13216022954
BLAKE2b-256 88bbdde6b6dd1d6bc6d858903fd73cd40754ea16d5d6ae955d1252daf754a7e3

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