A modern, strictly-typed Python wrapper for the Meta Messenger Platform (Graph API).
Most Python libraries for Facebook Messenger are either abandoned, lack type hints, or were written back in the Python 2/3 transition era. pymsgr was built to give developers an intuitive, production-grade experience using modern Python (3.10+), Pydantic v2, and full typing support.
Note & Credit: This project is heavily inspired by pywa (the awesome WhatsApp Cloud API library). If you've used
pywabefore, you'll feel right at home with the handler syntax, contextual replies, and overall developer experience.
Features
- Strictly Typed & Autocomplete-Ready: Built from the ground up with Pydantic v2. Your IDE knows every incoming event field and outbound payload parameter.
- Contextual Reply Helpers: Reply directly using
msg.reply_text(),msg.reply_buttons(), ormsg.typing_on()without juggling PSIDs and page IDs manually. - Built-in Webhook Handlers: Zero-boilerplate webhook setup with first-class support for both FastAPI and Flask.
- Webhook Security Out of the Box: Automatic HMAC-SHA256 signature verification (
X-Hub-Signature-256) and verification challenge (hub.challenge) handshakes. - Multi-Tenant / SaaS Friendly: Pass a single token, a mapping dictionary, or a dynamic callable/database lookup function for multi-page bots.
- Rich Templates: Clean models for Buttons, Generic Templates (Carousels), Quick Replies, and sender actions.
Installation
Install with pip:
pip install pymsgr
Or install with webhook integrations:
# For FastAPI & Uvicorn
pip install "pymsgr[fastapi]"
# For Flask
pip install "pymsgr[flask]"
# Everything included
pip install "pymsgr[all]"
Quick Start (FastAPI)
Here is a simple bot that verifies your webhook and echoes back greetings:
from fastapi import FastAPI
import uvicorn
from pymsgr import Messenger
from pymsgr.server import FastAPIIntegration
from pymsgr.types import Message, QuickReply
app = FastAPI()
bot = Messenger(
page_access_token="YOUR_PAGE_ACCESS_TOKEN",
verify_token="YOUR_CUSTOM_VERIFY_TOKEN",
app_secret="YOUR_META_APP_SECRET",
)
# Automatically registers GET and POST routes on /webhook
FastAPIIntegration(bot, app, path="/webhook")
@bot.on_message()
def handle_message(msg: Message):
# Contextual shortcuts — no need to pass sender_id or page_id
if msg.text and msg.text.lower() == "hello":
msg.reply_text(
text="Hey there! How can I help you today?",
quick_replies=[
QuickReply(title="Pricing", payload="ACTION_PRICING"),
QuickReply(title="Contact Support", payload="ACTION_SUPPORT"),
],
)
@bot.on_postback()
def handle_postback(msg: Message):
if msg.postback_payload == "GET_STARTED":
msg.reply_text("Welcome aboard! Let's get started.")
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
Using with Flask
Prefer Flask? The setup is just as simple:
from flask import Flask
from pymsgr import Messenger
from pymsgr.server import FlaskIntegration
from pymsgr.types import Message
app = Flask(__name__)
bot = Messenger(
page_access_token="YOUR_PAGE_ACCESS_TOKEN",
verify_token="YOUR_CUSTOM_VERIFY_TOKEN",
app_secret="YOUR_META_APP_SECRET",
)
FlaskIntegration(bot, app, path="/webhook")
@bot.on_message()
def handle_message(msg: Message):
msg.reply_text(f"You said: {msg.text}")
if __name__ == "__main__":
app.run(port=8000)
Multi-Page & SaaS Architecture
If you're building a multi-tenant bot where different Facebook Pages use the same backend, you don't need multiple client instances. pymsgr allows dynamic token resolution:
1. Dictionary Mapping
bot = Messenger(
page_access_token={
"PAGE_ID_1": "TOKEN_FOR_PAGE_1",
"PAGE_ID_2": "TOKEN_FOR_PAGE_2",
},
verify_token="shared_verify_token",
app_secret="shared_app_secret",
)
2. Dynamic Database Lookup (Callable)
def get_page_token(page_id: str) -> str:
# Fetch token from your database or cache
page = db.query(Page).filter_by(id=page_id).first()
return page.access_token
bot = Messenger(
page_access_token=get_page_token,
verify_token="shared_verify_token",
app_secret="shared_app_secret",
)
Interactive Message Templates
Button Template
from pymsgr.types import PostbackButton, UrlButton
bot.send_buttons(
psid=user_id,
page_id=page_id,
text="What would you like to check out?",
buttons=[
UrlButton(title="Visit Website", url="https://example.com"),
PostbackButton(title="View Order", payload="VIEW_ORDER_123"),
],
)
Generic Template (Carousel)
from pymsgr.types import GenericElement, PostbackButton, UrlButton
elements = [
GenericElement(
title="Product 1",
subtitle="$29.99 - In Stock",
image_url="https://example.com/item1.jpg",
buttons=[PostbackButton(title="Buy Now", payload="BUY_1")],
),
GenericElement(
title="Product 2",
subtitle="$49.99 - Limited Supply",
image_url="https://example.com/item2.jpg",
buttons=[PostbackButton(title="Buy Now", payload="BUY_2")],
),
]
bot.send_generic_template(psid=user_id, page_id=page_id, elements=elements)
Event Handlers
You can register listeners for various incoming events:
@bot.on_message()
def on_msg(msg: Message): ...
@bot.on_postback()
def on_postback(msg: Message): ...
@bot.on_read()
def on_read(msg: Message): ...
@bot.on_delivery()
def on_delivery(msg: Message): ...
@bot.on_reaction()
def on_reaction(msg: Message): ...
Contributing
Contributions are always welcome! Feel free to open an issue if you discover a bug or want to request a feature, or submit a pull request directly.
- Fork the repository
- Create your branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
Distributed under the MIT License. See LICENSE for more information.
Metadata
Release files for pymsgr 0.1.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 | |
|---|---|---|---|
| pymsgr-0.1.0.tar.gz | 27.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pymsgr-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 52.2 kB
Release files / pymsgr-0.1.0.tar.gz
| Download URL | pymsgr-0.1.0.tar.gz |
|---|---|
| Size | 27.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d3cc06c019029e6660c03a15af90abe8300f8ee9094fc12980c4de7b9bf85996
|
|
BLAKE2b-256 checksum How to use checksums |
44ef97660ffc006cbeca474c5d78e9deae48d508c452cfad55a76fc48ae1338c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Release files / pymsgr-0.1.0-py3-none-any.whl
| Download URL | pymsgr-0.1.0-py3-none-any.whl |
|---|---|
| Size | 24.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bd29a0d19ee8134d050815b82f16dd57c58e5347eafa67c3bdc9bf924efa696b
|
|
BLAKE2b-256 checksum How to use checksums |
d117befa09d0bed9186d9f25b2877555c710e102e628d10d8fdeb0bbb57a9ca7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|