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,
)

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

Download files

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

Source Distribution

warmbly-0.2.0.tar.gz (105.8 kB view details)

Uploaded Source

Built Distribution

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

warmbly-0.2.0-py3-none-any.whl (131.8 kB view details)

Uploaded Python 3

File details

Details for the file warmbly-0.2.0.tar.gz.

File metadata

  • Download URL: warmbly-0.2.0.tar.gz
  • Upload date:
  • Size: 105.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for warmbly-0.2.0.tar.gz
Algorithm Hash digest
SHA256 44441cea1028d1880316836476b0e5177a7e21b1bbb6a1fd3e8670c5dc31864d
MD5 07b3d2e422f0924f2a52e904c68a186e
BLAKE2b-256 e2add66fe7fbe7be9e4ad00967c52ba32a40444690b421d0692f19d7bf1bfe24

See more details on using hashes here.

Provenance

The following attestation bundles were made for warmbly-0.2.0.tar.gz:

Publisher: release.yml on warmbly/warmbly-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file warmbly-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: warmbly-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 131.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for warmbly-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a5071f6cc6c17be79b1653702057049ae452f40916f49adb7594eb7910c9d3d0
MD5 3ff10dc2f0e6650a2b6f4c1e24d69700
BLAKE2b-256 3cb54e7dbe06e160b4c02544c0452d5ec945f52a306606e9a29c789058661814

See more details on using hashes here.

Provenance

The following attestation bundles were made for warmbly-0.2.0-py3-none-any.whl:

Publisher: release.yml on warmbly/warmbly-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 files

Supported by

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