Skip to main content

Bird Python SDK

The official Python SDK for the Bird API: email, SMS, WhatsApp, verification, and Realtime, over one typed client.

📚 Documentation: https://bird.com/docs/sdks/python

Status: in development. The PyPI distribution name is messagebird-sdk; the import package is bird.

Requires Python 3.10+.

Install

pip install messagebird-sdk      # or: uv add messagebird-sdk

This SDK is generated from Bird's public OpenAPI bundle inside Bird's internal monorepo, which is the single source of truth; this repository tracks tagged releases. Generation runs in the monorepo, so make generate won't work from a clone here — see CONTRIBUTING.md.

Quickstart

from bird import APIError, Bird

with Bird() as client:
    try:
        message = client.email.send(
            from_={"email": "onboarding@messagebird.dev", "name": "Bird"},
            to=["delivered@messagebird.dev"],
            subject="Hello from Bird",
            html="<p>My first Bird email.</p>",
        )
        print(message.id, message.status)
    except APIError as err:
        print("send failed:", err)

api_key and base_url fall back to the BIRD_API_KEY / BIRD_BASE_URL environment variables, so Bird() with no arguments works when they are set. Use the client as a context manager (with Bird(...) as client:) to close the underlying HTTP connection pool.

Email

# Send
message = client.email.send(from_="hi@acme.com", to=["c@x.com"], subject="Hi", text="hello")

# Fetch
message = client.email.get("em_01krd…")

# List — iterating the page auto-paginates across cursors
for message in client.email.list(status="delivered"):
    print(message.id, message.status)

Client-wide email defaults

Defaults fill any unset send field; a per-send value always wins.

client = Bird(
    api_key="bk_eu1_...",
    email_defaults={"from_": "noreply@acme.com", "reply_to": ["support@acme.com"]},
)
client.email.send(to=["c@x.com"], subject="Receipt", text="…")  # uses noreply@acme.com

WhatsApp

Templates are currently the only supported content type, so every send must include one; Bird selects the sender number from the template's category.

# Send
message = client.whatsapp.send(
    to="+31612345678",
    template="bird_otp",
    language="en",
    components=[{"type": "body", "parameters": [{"type": "text", "text": "123456"}]}],
)

# Fetch
message = client.whatsapp.get("wam_01krd…")

# List — iterating the page auto-paginates across cursors
for message in client.whatsapp.list(status=["delivered"]):
    print(message.id, message.status)

Realtime

Every Realtime call is scoped to one Realtime app and authenticated with that app's own key and secret — separate from your Bird API key — configured once on the client. Calling a Realtime method without them raises BirdError before any request is sent.

client = Bird(api_key="bk_eu1_...", realtime_key="rk_...", realtime_secret="rs_...")

# Publish one event to up to 100 channels
client.realtime.publish(
    "rap_01krd…",
    event="order-updated",
    channels=["orders", "orders-42"],
    data={"id": 42, "status": "shipped"},
    exclude_connection_id="81721.1907241",  # don't echo back to the client that acted
)

# Publish up to 10 events at once — each targets a single channel
client.realtime.publish_batch(
    "rap_01krd…",
    events=[
        {"event": "order-created", "channel": "orders", "data": {"id": 1}},
        {"event": "order-updated", "channel": "orders", "data": {"id": 2}},
    ],
)

# Live channel state — a snapshot, not a paginated collection
for channel in client.realtime.channels.list("rap_01krd…", prefix="presence-").data:
    print(channel.name)

channel = client.realtime.channels.get("rap_01krd…", "presence-lobby", include=["member_count"])
members = client.realtime.channels.members("rap_01krd…", "presence-lobby")

# Close every connection authenticated as this member
client.realtime.members.disconnect("rap_01krd…", "member:42")

Webhooks

from bird import Bird, WebhookVerificationError

client = Bird(api_key="bk_eu1_...", webhook_secret="whsec_...")

# In your web handler — pass the RAW request body (bytes) and the request headers
try:
    event = client.webhooks.unwrap(request.body, request.headers)
except WebhookVerificationError:
    return Response(status=400)

if event.root.type == "email.delivered":
    print("delivered:", event.root.data.message_id)

Endpoint management (registering/listing webhook endpoints) is not in this release; it returns once the delivery substrate stabilises.

Errors

Every failure raises a typed exception rooted at BirdError. APIError covers anything that goes wrong issuing a request — including transport failures — so a single except APIError is enough; APIStatusError carries the HTTP status_code.

from bird import APIStatusError, RateLimitError, ValidationError

try:
    client.email.send(
        from_={"email": "onboarding@messagebird.dev", "name": "Bird"},
        to=["delivered@messagebird.dev"],
        subject="Hello from Bird",
        text="My first Bird email.",
    )
except RateLimitError as err:
    print("rate limited; retry after", err.retry_after)
except ValidationError as err:
    print(err.status_code, err.details)
except APIStatusError as err:
    print(err.status_code, err.code, err.request_id)

Transient failures (timeouts, 429, 5xx) retry automatically with jittered backoff that honors Retry-After; a mutation reuses one idempotency key across attempts, so a retried write never double-applies.

Raw response

Reach the status, headers, and request_id alongside the parsed model:

raw = client.email.with_raw_response.send(from_="hi@acme.com", to=["c@x.com"], subject="Hi", text="…")
print(raw.status_code, raw.request_id)
message = raw.parse()

Async

AsyncBird mirrors Bird method-for-method — await each call and async for over a list:

import asyncio
from bird import AsyncBird

async def main() -> None:
    async with AsyncBird(api_key="bk_eu1_...") as client:
        await client.email.send(from_="hi@acme.com", to=["c@x.com"], subject="Hi", text="hello")
        async for message in client.email.list(status="delivered"):
            print(message.id)

asyncio.run(main())

Configuration

Option Description
api_key API key; falls back to BIRD_API_KEY.
region / base_url Region (or explicit base URL); falls back to the key prefix / BIRD_BASE_URL.
timeout, max_retries Request timeout and retry budget; overridable per call via options.
webhook_secret Signing secret for webhooks.unwrap.
realtime_key / realtime_secret Realtime app credentials, sent as X-Realtime-Key / X-Realtime-Secret on every client.realtime call.
email_defaults Client-wide send defaults.
http_client Inject your own httpx.Client / AsyncClient.

client.with_options(...) derives a new client (reusing the connection pool); every method also takes a trailing options for per-call timeout / max_retries / idempotency_key / extra_headers.

Escape hatch

Any endpoint outside the typed surface is reachable through the verb methods, with the same auth, retries, and idempotency:

from bird import EmailMessage

message = client.get("/v1/email/messages/em_01krd...", cast_to=EmailMessage)
client.post("/v1/some/new/endpoint", body={"key": "value"})

Design

The wire models are generated from the OpenAPI spec into bird._generated; this package is the hand-written idiomatic layer on top.

Release files for messagebird-sdk 0.28.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 messagebird-sdk 0.28.0
File Size Uploaded
messagebird_sdk-0.28.0.tar.gz 111.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for messagebird-sdk 0.28.0
File Interpreter ABI Platform
messagebird_sdk-0.28.0-py3-none-any.whl Python 3 none any Details

Total release size: 246.7 kB

Release files / messagebird_sdk-0.28.0.tar.gz

Download URL messagebird_sdk-0.28.0.tar.gz
Size 111.8 kB
Tags Source
SHA-256 checksum
How to use checksums
b3c6afaa4e03bdf509de29b29301d5e145aa6defd432134566668eb5100f26ca
BLAKE2b-256 checksum
How to use checksums
594a9cbb35dd466ef5655c65057ae720390217976ebc9c2cde24cf63e133ad70
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 Aug 13, 2026.

Transparency log

Release files / messagebird_sdk-0.28.0-py3-none-any.whl

Download URL messagebird_sdk-0.28.0-py3-none-any.whl
Size 134.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
acb39f191af4d27c503ee3ea8599472e4cd6cde881a4b7ece2ef7cdef4b81e2d
BLAKE2b-256 checksum
How to use checksums
f97ee7a36de0adc470ee1fb1a46c36591c4b2c38010dd60ed15761ebd540b88a
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 Aug 13, 2026.

Transparency log

Release history Release notifications | RSS feed

0.71.0

2 release files

0.70.0

2 release files

0.69.0

2 release files

0.68.0

2 release files

0.67.0

2 release files

0.66.0

2 release files

0.65.0

2 release files

0.64.1

2 release files

0.64.0

2 release files

0.63.0

2 release files

0.62.0

2 release files

0.61.0

2 release files

0.60.0

2 release files

0.59.0

2 release files

0.45.0

2 release files

0.44.0

2 release files

0.43.0

2 release files

0.42.0

2 release files

0.41.1

2 release files

0.41.0

2 release files

0.40.0

2 release files

0.39.0

2 release files

0.38.2

2 release files

0.38.1

2 release files

0.38.0

2 release files

0.37.2

2 release files

0.37.1

2 release files

0.37.0

2 release files

0.36.0

2 release files

0.35.0

2 release files

0.34.0

2 release files

0.33.1

2 release files

0.33.0

2 release files

0.32.0

2 release files

0.31.0

2 release files

0.30.0

2 release files

0.29.0

2 release files

This release

0.28.0 This release

2 release files

0.27.0

2 release files

0.26.0

2 release files

0.25.1

2 release files

0.25.0

2 release files

0.24.0

2 release files

0.23.0

2 release files

0.16.0

2 release files

0.15.0

2 release files

0.14.0

2 release files

0.13.0

2 release files

0.12.2

2 release files

0.12.1

2 release files

0.12.0

2 release files

0.11.0

2 release files

0.10.0

2 release files

0.9.2

2 release files

0.9.1

2 release files

0.9.0

2 release files

0.8.4

2 release files

0.8.3

2 release files

0.8.2

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.5

2 release files

0.7.4

2 release files

0.7.3

2 release files

0.7.2

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

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