async-social-bridge
Async messaging adapters for Meta platforms (WhatsApp, Messenger, Instagram).
Send and parse messages with a clean, typed Python API. Zero bloat — onlyhttpxandpydanticas dependencies.
Features
- Async First — Built on
httpxfor 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, andtextfrom messy webhook payloads - Multi-Tenant Ready — Dynamic access tokens per request; ideal for SaaS platforms managing multiple client accounts
- Extensible — Subclass
BaseAdapterandBaseParserto 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)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| async_social_bridge-0.1.1.tar.gz | 14.5 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| async_social_bridge-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 25.9 kB
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.
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 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.