Skip to main content

warmbly-py

The official Python SDK for the Warmbly API: REST resources, OAuth2, and a realtime gateway, with first-class sync and async support.

PyPI version Python versions Downloads CI License Ruff Docs


Installation

pip install warmbly

Interactive OAuth2 flows and secure token storage are an optional extra:

pip install "warmbly[oauth]"

Requires Python 3.10+.

Quickstart

import os
from warmbly import Warmbly

client = Warmbly(api_key=os.environ["WARMBLY_API_KEY"])

campaign = client.campaigns.create(name="Q3 outreach")
print(campaign.id)

for key in client.api_keys.list():
    print(key.name, key.status)

The client reads WARMBLY_API_KEY from the environment automatically, so you can also just write client = Warmbly().

Async

Every method has an await-able twin on AsyncWarmbly: swap the class, add await, and iterate with async for:

import asyncio
import os
from warmbly import AsyncWarmbly


async def main() -> None:
    client = AsyncWarmbly(api_key=os.environ["WARMBLY_API_KEY"])
    campaign = await client.campaigns.create(name="Q3 outreach")
    print(campaign.id)

    async for key in client.api_keys.list():
        print(key.name)

    await client.close()


asyncio.run(main())

Authentication

The SDK supports all three Warmbly auth modes; each is sent as a bearer token.

Mode How
API key Warmbly(api_key="wmbly_...") or WARMBLY_API_KEY env var
OAuth2 access token Warmbly(api_key="wmat_...") (any bearer token works)
OAuth2 flow from warmbly.oauth import OAuth2Client: see the OAuth guide

OAuth2 in three lines

from warmbly.oauth import OAuth2Client

oauth = OAuth2Client(client_id="wmcid_...", client_secret="wmcs_...",
                     redirect_uri="https://app.example.com/callback")

# 1. Send the user to authorize (PKCE handled for you):
url, state, verifier = oauth.authorization_url(scopes=["read_campaigns", "send_campaigns"])

# 2. Exchange the code returned to your redirect URI:
token = oauth.exchange_code(code, state=state, expected_state=state, code_verifier=verifier)

# 3. Use the access token:
client = Warmbly(api_key=token.access_token)

You can also register and manage OAuth2 applications programmatically via client.oauth_applications.create(...).

Realtime gateway

Subscribe to live events over a single resilient WebSocket connection (heartbeats, automatic reconnect, and session resume are handled for you):

import asyncio
from warmbly import AsyncGatewayClient


async def main() -> None:
    gateway = AsyncGatewayClient(token="wmbly_...")  # needs the realtime_subscribe scope

    @gateway.on_event("CAMPAIGN_STARTED")
    async def handle(payload: dict) -> None:
        print("campaign started:", payload["campaign_id"])

    await gateway.connect()
    await gateway.subscribe("org:00000000-0000-0000-0000-000000000000")
    await gateway.run_forever()


asyncio.run(main())

Pagination

List endpoints return an iterator that transparently fetches every page:

for campaign in client.campaigns.list():  # walks all pages
    print(campaign.name)

page = client.api_keys.list()             # or work a page at a time
print(page.data, page.has_more, page.next_cursor)

Error handling

Every error inherits from warmbly.WarmblyError. HTTP failures map to a status-specific subclass carrying .status_code, .request_id, and the parsed .body.

Status Exception
400 BadRequestError
401 AuthenticationError
403 PermissionDeniedError
404 NotFoundError
409 ConflictError
422 UnprocessableEntityError
429 RateLimitError (.retry_after)
5xx InternalServerError
network / timeout APIConnectionError / APITimeoutError
OAuth token endpoint OAuthError (.error, .error_description)
from warmbly import Warmbly, RateLimitError, NotFoundError

client = Warmbly()
try:
    client.campaigns.retrieve("missing")
except NotFoundError:
    ...
except RateLimitError as err:
    print(f"retry after {err.retry_after}s (request {err.request_id})")

Configuration

client = Warmbly(
    api_key="wmbly_...",
    base_url="https://api.warmbly.com/v1",  # or WARMBLY_BASE_URL
    timeout=30.0,                            # seconds, or an httpx.Timeout
    max_retries=2,                           # 408/409/429/5xx with backoff + jitter
)

Idempotency keys are added automatically to write requests so retries are safe; pass idempotency_key=... to a method to supply your own.

Webhooks

Verify inbound webhook signatures before trusting a payload. The X-Warmbly-Signature header is t=<unix>,v1=<hex>, where the digest covers "{t}.{raw_body}"; the helper checks it in constant time and rejects a stale timestamp as a replay.

from warmbly import verify_webhook_signature

event = verify_webhook_signature(
    payload=request.body,  # raw bytes, never re-serialized
    signature=request.headers["X-Warmbly-Signature"],
    secret=endpoint_secret,
)

Resources

Each API group hangs off the client as an attribute:

Sending campaigns, emails, templates, generation, outreach, deliverability, warmup_routing, suppressions
Audience contacts, segments, forms, folders, tags, categories
Inbox & CRM unibox, crm, meetings, automations, lead_sync
Insight analytics, advisor, audit_logs, tasks
Platform me, api_keys, oauth_applications, teams, webhooks, integrations, plans, timezones
AI ai_skills, ai_tools

client.ai_tools is the REST tool surface for agents that do not speak MCP: list the tools the credential may use, in OpenAI function-calling shape if you want, and call them by name.

tools = [t.to_dict() for t in client.ai_tools.list(format="openai")]
result = client.ai_tools.call("list_campaigns", arguments={"status": "active"})

The clients cover what an API key or OAuth token can reach. Routes that need a browser session — organization governance, billing, mailbox onboarding, the OAuth consent flow — are deliberately absent, because a long-lived credential cannot call them.

Documentation

Full documentation, guides, and the API reference live at warmbly-py.readthedocs.io.

Contributing

Contributions are welcome! Please read CONTRIBUTING.md and our Code of Conduct to get started.

License

MIT © Warmbly

Metadata

Release files for warmbly 0.3.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 warmbly 0.3.0
File Size Uploaded Provenance Actions
warmbly-0.3.0.tar.gz 130.6 kB With provenance Details for warmbly-0.3.0.tar.gz

Built distribution (wheel)

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

Total release size: 288.8 kB

Release files / warmbly-0.3.0.tar.gz

Download URL warmbly-0.3.0.tar.gz
Size 130.6 kB
Tags Source
SHA-256 checksum
How to use checksums
850c868084967427f27801621406da0c936ea4b5ad29a75a68de3603b9f30784
BLAKE2b-256 checksum
How to use checksums
3b3a1eab19c7d4d5f59db9bbfaac1971190cd0f9a7946c9e9f2f4659f90a6be1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.

Transparency log

Release files / warmbly-0.3.0-py3-none-any.whl

Download URL warmbly-0.3.0-py3-none-any.whl
Size 158.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c11b292b01596868b7fa7bdbdeb2d87ee3802e10ffcee7211294220947cb28be
BLAKE2b-256 checksum
How to use checksums
a3f3e79b17622f753aadb66b1ee87fd403bbedf6eed4c5847387b078f272ffcc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.

Transparency log

Security for warmbly 0.3.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for warmbly-0.3.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files Full provenance

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