Skip to main content

async-social-bridge

Async messaging adapters for Meta platforms (WhatsApp, Messenger, Instagram).
Send and parse messages with a clean, typed Python API. Zero bloat — only httpx and pydantic as dependencies.

Features

  • Async First — Built on httpx for high-performance, non-blocking I/O
  • Fully Typed — Pydantic models with full IDE autocomplete and runtime validation
  • Unified API — One adapter and one parser for all Meta platforms. No platform-specific spaghetti code
  • Zero Boilerplate — Automatically extracts platform, sender_id, and text from messy webhook payloads
  • Multi-Tenant Ready — Dynamic access tokens per request; ideal for SaaS platforms managing multiple client accounts
  • Extensible — Subclass BaseAdapter and BaseParser to add any platform (Telegram, Slack, Discord, etc.)

Installation

pip install async-social-bridge

Quick Start: A Complete Example

Here is how async-social-bridge fits into a real FastAPI webhook endpoint — from raw payload to AI reply.

from fastapi import FastAPI, Request
from socialbridge.meta import MetaParser, MetaAdapter
from socialbridge.models import OutgoingMessage

app = FastAPI()
parser = MetaParser()

@app.post("/webhook")
async def webhook(request: Request):
    # Step 1: Get the raw JSON from Meta
    payload = await request.json()

    # Step 2: Parse the messy nested payload into clean Python objects
    messages = parser.parse(payload)

    for msg in messages:
        # msg.platform     -> "whatsapp", "messenger", or "instagram"
        # msg.sender_id    -> the user who messaged you
        # msg.receiver_id  -> your WhatsApp Phone ID or Facebook Page ID
        # msg.text         -> the plain text content

        # Step 3: Look up your tenant's token using the receiver_id
        # access_token = await db.get_token(msg.receiver_id)
        access_token = "YOUR_ACCESS_TOKEN"

        # Step 4: Build the reply
        reply = OutgoingMessage(
            platform=msg.platform,
            receiver_id=msg.receiver_id,
            to=msg.sender_id,
            text=f"You said: {msg.text}"
        )

        # Step 5: Send the reply — one API for all platforms
        async with MetaAdapter(access_token=access_token) as meta:
            result = await meta.send_text_message(reply)
            if result.success:
                print(f"Sent! ID: {result.metadata['message_id']}")

    return {"status": "ok"}

Data Models Reference

IncomingMessage

Returned by MetaParser.parse(). You never construct this yourself — the library creates it from the raw webhook payload.

Attribute Type Description
platform str "whatsapp", "messenger", or "instagram"
sender_id str The user who sent the message. Pass to OutgoingMessage.to to reply.
receiver_id str Your WhatsApp Phone Number ID or Facebook Page ID. Use this to fetch the right token from your database.
text str The plain text content of the message. Empty string if non-text.
timestamp datetime UTC timestamp of when the message was received by the library.
metadata dict Platform extras (e.g., WhatsApp includes sender_name here).
raw dict | None The original, unmodified webhook JSON for advanced use cases.

OutgoingMessage

Passed to MetaAdapter.send_text_message(). You create this yourself with your reply data.

Attribute Type Description
platform str Target platform: "whatsapp", "messenger", or "instagram"
receiver_id str Your WhatsApp Phone Number ID or Facebook Page ID (the sender identity).
to str The recipient's user ID. Use IncomingMessage.sender_id to reply.
text str The text you want to send.

SendResult

Returned by MetaAdapter.send_text_message().

Attribute Type Description
success bool True if the Meta API returned 200 OK.
error str | None Human-readable error message if the send failed.
status_code int | None The raw HTTP status code from Meta.
metadata dict Contains message_id returned by Meta (e.g., wamid.xxx...).
raw_response dict | None The full raw JSON response from Meta for debugging.

Error Handling

The library raises specific exceptions so you can handle failures precisely without catching generic Exception:

from socialbridge.exceptions import ParseError, SendError

# ParseError: raised when the webhook payload is not a recognized Meta format
try:
    messages = parser.parse(payload)
except ParseError as e:
    print(f"Unrecognized payload: {e}")
    return {"status": "ignored"}

# SendError: raised when Meta's API returns a non-200 response
try:
    result = await meta.send_text_message(reply)
except SendError as e:
    print(f"Failed to send. HTTP {e.status_code}: {e}")

Extending: Adding a New Platform

Create a new platform adapter by subclassing BaseAdapter and BaseParser:

from socialbridge.base import BaseAdapter, BaseParser
from socialbridge.models import IncomingMessage, OutgoingMessage, SendResult
from socialbridge.exceptions import SendError


class TelegramParser(BaseParser):
    def parse(self, payload: dict) -> list[IncomingMessage]:
        messages = []
        for update in payload.get("updates", []):
            msg = update.get("message", {})
            messages.append(IncomingMessage(
                platform="telegram",
                sender_id=str(msg["from"]["id"]),
                receiver_id=str(msg["chat"]["id"]),
                text=msg.get("text", ""),
                raw=update,
            ))
        return messages


class TelegramAdapter(BaseAdapter):
    def __init__(self, bot_token: str):
        super().__init__(
            access_token=bot_token,
            base_url="https://api.telegram.org",
        )

    async def send_text_message(self, message: OutgoingMessage) -> SendResult:
        resp = await self._client.post(
            f"/bot{self.access_token}/sendMessage",
            json={"chat_id": message.to, "text": message.text},
        )
        if resp.status_code != 200:
            raise SendError(
                message=f"Telegram API error",
                status_code=resp.status_code,
                response_body=resp.text,
            )
        data = resp.json()
        return SendResult(
            success=True,
            metadata={"message_id": str(data["result"]["message_id"])},
            status_code=resp.status_code,
            raw_response=data,
        )

Supported Platforms

  • WhatsApp Business Cloud
  • Facebook Messenger
  • Instagram Direct

License

MIT

Metadata

Release files for async-social-bridge 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for async-social-bridge 0.1.1
File Size Uploaded Provenance
async_social_bridge-0.1.1.tar.gz 14.5 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for async-social-bridge 0.1.1
File Interpreter ABI Platform
async_social_bridge-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 25.9 kB

Release files / async_social_bridge-0.1.1.tar.gz

Download URL async_social_bridge-0.1.1.tar.gz
Size 14.5 kB
Tags Source
SHA-256 checksum
How to use checksums
bf08348fbf2f27601c65cbd756ccb9453b6913928b565024797abe9e80c2f1af
BLAKE2b-256 checksum
How to use checksums
88f9ec0ef52475178859bf029bab53b7445c31f646cf121e77fccdc5020c17b9
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 4, 2026.

Transparency log

Release files / async_social_bridge-0.1.1-py3-none-any.whl

Download URL async_social_bridge-0.1.1-py3-none-any.whl
Size 11.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6444a05ea916e6201bba6fb645de17769d20f789501fff89cf8c2b2acb47d51c
BLAKE2b-256 checksum
How to use checksums
08d6be6112083b26bc96c27b62a66d0aeb70c1ba9bbc8253830fe8f3e54d4451
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 4, 2026.

Transparency log

Security for async-social-bridge 0.1.1

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

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for async_social_bridge-0.1.1.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 Aug 4, 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.

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