PyWa Logo

PyWa

The Python framework for building WhatsApp bots.
Fast. Typed. Production-ready. From prototype to production in minutes.

PyPI Version Downloads Python Versions Tests Docs License Code Quality


PyWa is a comprehensive, fully-typed Python framework for the WhatsApp Cloud API. It handles everything β€” sending messages, receiving webhooks, building interactive flows, managing templates, handling calls β€” so you can focus on building your bot, not wrestling with the API.

pip install -U "pywa[server]"

✨ Why PyWa?#

πŸ’¬ Rich Messaging Text, images, videos, documents, audio, stickers, locations, contacts, buttons, lists & more
πŸ“© Real-Time Webhooks Messages, button clicks, delivery receipts, read receipts, reactions & account updates
πŸ”” Listeners Chain conversations naturally β€” wait for replies, clicks, or reactions inline
πŸ“„ Templates Create, send, and manage WhatsApp message templates with full parameter support
♻️ Flows Build rich, multi-screen interactive WhatsApp Flows in pure Python
πŸ“ž Calls Handle incoming and outgoing WhatsApp call events
⚑ Async Support Full async/await API via pywa_async β€” same interface, zero friction
πŸ”Œ Server Integrations Built-in webhook server, or plug into your existing FastAPI / Flask app
πŸ›‘οΈ Type Safety Fully typed - full autocomplete and static analysis everywhere
πŸ”¬ Smart Filters Composable filters with logical operators for precise update routing
🧰 CLI Tools pywa dev for local development, pywa run for production

πŸš€ Quick Start#

1. Echo Bot β€” 5 lines of code#

# main.py
from pywa import WhatsApp, filters, types

wa = WhatsApp(
    phone_id="1234567890",
    token="EAA...",
    app_id=1234567890,
    app_secret="********",
    callback_url="https://your-domain.ngrok-free.app",
    verify_token="my-verify-token",
)


@wa.on_message(filters.text)
def echo(_: WhatsApp, msg: types.Message):
    msg.reply(f"You said: {msg.text}")

Start the webhook server:

pywa dev    # Local development
pywa run    # Production

2. Rich Messages β€” Buttons, Media & More#

from pywa import WhatsApp, types

wa = WhatsApp(phone_id="1234567890", token="EAA...")

# Text with interactive buttons
wa.send_message(
    to="9876543210",
    text="How can I help you today?",
    buttons=[
        types.Button(title="πŸ“‹ Menu", callback_data="menu"),
        types.Button(title="πŸ’¬ Support", callback_data="help"),
    ],
)

# Images, documents, audio β€” one-liners
wa.send_image(to="9876543210", image="https://example.com/photo.jpg", caption="Check this out!")
wa.send_document(to="9876543210", document="report.pdf")

3. Conversational Flows β€” Listeners#

Handlers define entry points. Listeners let you continue the conversation naturally:

@wa.on_message(filters.command("start"))
def start(_: WhatsApp, msg: types.Message):
    name = msg.reply("What's your name?").wait_for_reply(filters=filters.text).text
    msg.reply(f"Nice to meet you, {name}!")

4. Plug Into Your Own Server#

from fastapi import FastAPI
from pywa import WhatsApp, filters, types

app = FastAPI()
wa = WhatsApp(..., server=app, webhook_endpoint="/webhook")


@wa.on_message(filters.text)
def echo(_: WhatsApp, msg: types.Message):
    msg.reply(msg.text)


@app.get("/")
def health():
    return {"status": "ok"}

5. Full Async Support#

Same API. Just swap the import and add await:

from pywa_async import WhatsApp, filters, types

wa = WhatsApp(...)


@wa.on_message(filters.text)
async def hello(_: WhatsApp, msg: types.Message):
    await msg.react("πŸ‘‹")
    await msg.reply("Hello from async PyWa!")

6. Templates#

Create and send WhatsApp message templates with full parameter support:

from pywa import WhatsApp
from pywa.types.templates import *

wa = WhatsApp(..., waba_id=123456)

wa.create_template(
    template=Template(
        name="order_update",
        category=TemplateCategory.MARKETING,
        language=TemplateLanguage.ENGLISH_US,
        parameter_format=ParamFormat.NAMED,
        components=[
            ht := HeaderText("Your order #{{order_id}} has shipped!", order_id="12345"),
            bt := BodyText("Track it with code {{code}}.", code="ABC123"),
            FooterText(text="Powered by PyWa"),
            Buttons(buttons=[
                url := URLButton(text="Track Order", url="https://example.com/track/{{1}}", example="12345"),
                QuickReplyButton(text="Unsubscribe"),
            ]),
        ],
    ),
)

# Send it
wa.send_template(
    to="9876543210",
    name="order_update",
    language=TemplateLanguage.ENGLISH_US,
    params=[
        ht.params(order_id="67890"),
        bt.params(code="XYZ789"),
        url.params(url_variable="67890", index=0),
    ],
)

7. Interactive Flows#

Build WhatsApp Flows β€” multi-screen interactive experiences β€” entirely in Python:

from pywa import WhatsApp, types
from pywa.types.flows import *

wa = WhatsApp(..., waba_id=123456)

my_flow = FlowJSON(
    screens=[
        Screen(
            id="SIGNUP",
            title="Join Our Newsletter",
            layout=Layout(children=[
                TextHeading(text="Subscribe for updates"),
                name := TextInput(name="name", label="Name", input_type=InputType.TEXT),
                email := TextInput(name="email", label="Email", input_type=InputType.EMAIL, required=True),
                Footer(
                    label="Subscribe",
                    on_click_action=CompleteAction(
                        payload={"name": name.ref, "email": email.ref}
                    ),
                ),
            ]),
        )
    ]
)

wa.create_flow(name="newsletter_signup", categories=[FlowCategory.SIGN_UP], flow_json=my_flow, publish=True)


@wa.on_flow_completion
def on_signup(_: WhatsApp, flow: types.FlowCompletion):
    flow.reply(text=f"Welcome, {flow.response['name']}! You're subscribed at {flow.response['email']}.")

8. Account & Resource Management#

Beyond messaging, PyWa gives you full control over your WhatsApp Business resources:

from pywa import WhatsApp

wa = WhatsApp(phone_id="1234567890", token="EAA...", waba_id=123456)

# Business profile
profile = wa.get_business_profile()
wa.update_business_profile(about="Powered by PyWa", description="We build bots!", profile_picture="profile.jpg")

# Media management
media = wa.upload_media(media="photo.jpg")
media.stream(), media.download(), media.reupload(), media.delete()

# QR codes
qr = wa.create_qr_code(prefilled_message="Hi! I saw your QR code")
qr.qr_image_url, qr.code, qr.update(prefilled_message="Hello"), qr.delete()

# Usernames
wa.get_reserved_usernames()
wa.set_username(username="mybusiness")
wa.get_current_username()

# Groups
wa.create_group(subject="VIP Customers")
wa.get_groups()
wa.get_group_join_requests()

# Commerce
wa.get_commerce_settings()
wa.update_commerce_settings(is_catalog_visible=True, is_cart_enabled=
True)
# User management
wa.block_users(users=["9876543210"])
blocked = wa.get_blocked_users()

See the Client guide for the full resource management API β€” templates, flows, media, QR codes, commerce, groups, calls, and more.

9. Partners & Tech Providers#

Building a platform on top of WhatsApp? PyWa supports multi-WABA management, phone number provisioning, and callback routing for Solution Partners and Tech Providers:

from pywa import WhatsApp, types, filters

wa = WhatsApp(phone_id="1234567890", token="EAA...", waba_id=123456)


@wa.on_message(filters.sent_to(phone_number_id=...))
def handle_message_for_specific_phone_number(wa: WhatsApp, msg: types.Message): ...


@wa.on_account_update(filters.account_restriction)
def handle_account_restriction(wa: WhatsApp, update: types.AccountUpdate): ...


@wa.on_message(filters.account_deleted)
def handle_account_deletion(wa: WhatsApp, update: types.AccountUpdate): ...


# Get all WABAs you manage
shared_wabas = wa.get_shared_business_accounts()
owned_wabas = wa.get_owned_business_accounts()

# Provision phone numbers on a WABA
phone = wa.create_phone_number(country_calling_code="1", phone_number="5551234567", verified_name="John Doe")
wa.request_verification_code(phone_id=phone.id, code_method="SMS")
wa.verify_phone_number(code="123456", phone_id=phone.id)
wa.register_phone_number(phone_id=phone.id)

# Route webhooks per-WABA or per-phone
wa.override_waba_callback_url(callback_url="https://your-platform.com/waba/123")
wa.override_phone_callback_url(callback_url="https://your-platform.com/phone/456")

# Migrate templates & flows between WABAs
wa.migrate_templates(source_waba_id=111111, destination_waba_id=222222)
wa.migrate_flows(source_waba_id=111111, destination_waba_id=222222, source_flow_names=["flow_1"])

πŸ“¦ Installation#

# Core (sending messages, no webhook server)
pip install -U pywa

# With built-in webhook server (recommended)
pip install -U "pywa[server]"

# For Flow encryption support
pip install -U "pywa[cryptography]"

# Everything
pip install -U "pywa[server,cryptography]"

Requirements: Python 3.10+


πŸ“š Documentation#

Full documentation is available at pywa.readthedocs.io.

Topic

Description

Getting Started

Setup, first bot, and key concepts

Client

Sending messages, media, and managing resources

Handlers

Decorators, filters, and webhook routing

Listeners

Conversational flows and inline waiting

Updates

Message, callback, status, and system updates

Filters

Composable, reusable update filtering

Templates

Create, send, and manage message templates

Flows

Build interactive multi-screen flows

Calls

Handle voice call events


🀝 Contributing#

Contributions are welcome! Check out the Contributing Guide to get started.

πŸ’¬ Community#

Questions? Ideas? Join the conversation:

βš–οΈ License#

PyWa is open-source software licensed under the MIT License.

πŸ“ Changelog#


NOTE: pywa follows the semver versioning standard.

4.0.0 (2026-06-09) Latest#

WORK IN PROGRESS

4.0.0b7 (2026-04-30)#

  • [helpers] fix incoming updates in fastapi and starlette

  • [helpers] hide webhook endpoints from OpenAPI schema

  • [ci] streamline publish workflow for PyPI

4.0.0b6 (2026-04-25)#

  • [client] add run method to start the webhook server and listen for updates

  • [server] refactor webhook handling to include validation and improve endpoint management

  • [user] make profile temporary optional for testing

  • [utils] add start_ngrok_tunnel function to facilitate local webhook testing

  • [templates] enhance duplicate method to include target_waba_id parameter for template creation

  • [templates] add validation for URL variables in URLButton to enforce constraints on example and variable usage

4.0.0b5 (2026-04-17)#

  • [client] add methods for creating and verifying phone numbers on WhatsApp Business Account

  • [templates] enhance Template and TemplateDetails with new methods for component retrieval and validation

  • [templates] add param_names property for introspection of named parameters

  • [templates] enhance error handling for positional and named examples in HeaderText and BodyText

See the full changelog here.

πŸ”— Contents#