warmbly-py
The official Python SDK for the Warmbly API: REST resources, OAuth2, and a realtime gateway, with first-class sync and async support.
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)
| 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)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| 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 logRelease 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 logSecurity 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.
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 logA 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.