PyWa Logo

πŸš€ Build WhatsApp Bots in Python β€’ Fast. Effortless. Powerful.

πŸ€– Hey there! I am using PyWa.


πŸ’« PyWa is an all-in-one Python framework for the WhatsApp Cloud API.

Send rich media messages, use interactive buttons, listen to real-time events, build and send flows, design and send template messages, and enjoy blazing-fast async support with full integration for FastAPI, Flask, and more. Fully typed, documented, and production-ready β€” build powerful bots in minutes.

πŸ“„ Quick Documentation Index#

Get Started β€’ Client β€’ Handlers β€’ Listeners β€’ Updates β€’ Filters β€’ Templates β€’ Flows β€’ Calls


⚑ Why PyWa?#

  • πŸš€ Fast & Simple – Focus on building, not boilerplate.

  • πŸ’¬ Rich Messaging – Text, images, files, audio, locations, contacts, buttons & more.

  • πŸ“© Real-Time Updates – Messages, callbacks, delivery/read receipts, account updates, and more.

  • πŸ”” Listeners – Wait for user replies, clicks, or reactions with ease.

  • πŸ“„ Templates – Create and send powerful WhatsApp templates.

  • ♻️ Flows – Build interactive WhatsApp flows effortlessly.

  • πŸ“ž Calls Support – Receive and handle call events.

  • πŸ”„ Webhook-Ready – Built-in server for development and production, or attach to your own FastAPI/Flask app.

  • πŸ”¬ Filters – Advanced filtering for incoming updates.

  • βœ… Production-Ready – Typed, documented, and fully tested.


πŸ‘¨β€πŸ’» How to Use#

1. Handle incoming messages#

Start with a WhatsApp client and register handlers for the updates you want to receive. During development, run the webhook server with pywa dev. For production, use pywa run.

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

callback_url = utils.start_ngrok_tunnel(
    auth_token="NGROK_AUTH_TOKEN",
    domain="your-domain.ngrok-free.app",
)

wa = WhatsApp(
    phone_id="1234567890",
    token="EAA...",
    app_id="1234567890",
    app_secret="********",
    callback_url=callback_url,
    verify_token="my-verify-token",
)


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

Run it:

pywa dev

Use this when deploying:

pywa run

See the Handlers guide for decorators, filters, callback URL registration, custom servers, and handler flow.

2. Send messages, media, templates, flows, and manage resources#

Use the same client to send messages, media, templates, flows, and to manage WhatsApp Business resources.

from pywa import WhatsApp, types

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

wa.send_message(
    to="9876543210",
    text="Hello from PyWa!",
    buttons=[
        types.Button(title="Menu", callback_data="menu"),
        types.Button(title="Help", callback_data="help"),
    ],
)

wa.send_image(
    to="9876543210",
    image="https://example.com/image.jpg",
    caption="Check out this image!",
)

See the Client guide for the full API.

3. Listen for the next user response#

Handlers are great for entry points. When you need to continue a conversation, use listeners.

from pywa import WhatsApp, filters, types

wa = WhatsApp(...)


@wa.on_message(filters.command("start"))
def start(client: 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}!")

See the Listeners guide for timeouts, filters, callbacks, and waiting for clicks or replies.

4. Use your own server when needed#

The CLI is the easiest way to run pywa, but you can also attach pywa to an existing FastAPI or Flask app.

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

app = FastAPI()

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


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

Run your server normally

5. Async usage#

PyWa also supports async usage with the same API. Replace imports from pywa with pywa_async and use await.

from pywa_async import WhatsApp, filters, types, utils

callback_url = utils.start_ngrok_tunnel(auth_token="NGROK_AUTH_TOKEN")

wa = WhatsApp(..., callback_url=callback_url)


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

6. Create and send template messages#

See Templates for more details and examples.

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

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

# Create a template
wa.create_template(
    template=Template(
        name="buy_new_iphone_x",
        category=TemplateCategory.MARKETING,
        language=TemplateLanguage.ENGLISH_US,
        parameter_format=ParamFormat.NAMED,
        components=[
            ht := HeaderText("The New iPhone {{iphone_num}} is here!", iphone_num=15),
            bt := BodyText("Buy now and use the code {{code}} to get {{per}}% off!", code="WA_IPHONE_15", per=15),
            FooterText(text="Powered by PyWa"),
            Buttons(
                buttons=[
                    url := URLButton(text="Buy Now", url="https://example.com/shop/{{1}}", example="iphone15"),
                    PhoneNumberButton(text="Call Us", phone_number="1234567890"),
                    qrb1 := QuickReplyButton(text="Unsubscribe from marketing messages"),
                    qrb2 := QuickReplyButton(text="Unsubscribe from all messages"),
                ]
            ),

        ]
    ),
)

# Send the template message
wa.send_template(
    to="9876543210",
    name="buy_new_iphone_x",
    language=TemplateLanguage.ENGLISH_US,
    params=[
        ht.params(iphone_num=30),
        bt.params(code="WA_IPHONE_30", per=30),
        url.params(url_variable="iphone30", index=0),
        qrb1.params(callback_data="unsubscribe_from_marketing_messages", index=1),
        qrb2.params(callback_data="unsubscribe_from_all_messages", index=2),
    ]
)

7. Create and send flows#

See Flows for much more details and examples.

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

# Create a WhatsApp client
wa = WhatsApp(..., waba_id=123456)

# Build a flow
my_flow_json = FlowJSON(
    screens=[
        Screen(
            id="NEWSLETTER",
            title="PyWa Newsletter",
            layout=Layout(
                children=[
                    TextHeading(text="Subscribe to our newsletter"),
                    name := TextInput(
                        name="name",
                        label="Name",
                        input_type=InputType.TEXT,
                        required=False,
                    ),
                    email := TextInput(
                        name="email",
                        label="Email",
                        input_type=InputType.EMAIL,
                        required=True,
                    ),
                    Footer(
                        label="Subscribe",
                        on_click_action=CompleteAction(
                            payload={  # Payload to send to the server
                                "name": name.ref,
                                "email": email.ref,
                            }
                        )
                    )
                ]
            )
        )
    ]
)

# Create the flow
wa.create_flow(
    name="subscribe_to_newsletter",
    categories=[FlowCategory.SIGN_UP, FlowCategory.OTHER],
    flow_json=my_flow_json,
    publish=True
)

# Send the flow to a user
wa.send_text(
    to="9876543210",
    text="Hello from PyWa!",
    buttons=types.FlowButton(
        title="Subscribe to our newsletter!",
        flow_name="subscribe_to_newsletter",
    )
)


# Handle the flow response
@wa.on_flow_completion
def handle_flow_response(_: WhatsApp, flow: types.FlowCompletion):
    flow.reply(
        text=f"Thank you for subscribing to our newsletter, {flow.response['name']}! "
             f"We will send you updates to {flow.response['email']}.",
        buttons=[types.Button(title="Unsubscribe", callback_data="unsubscribe")]
    )

πŸŽ› Installation#

  • Install using pip3:

pip3 install -U pywa
  • Install the built-in webhook server for pywa dev and pywa run:

pip3 install -U "pywa[server]"
  • Install ngrok if you want to use utils.start_ngrok_tunnel() for local development:

pip3 install -U ngrok
  • Install from source (the bleeding edge):

pip3 install -U git+https://github.com/david-lev/pywa.git
  • If you use Flows and want pywa’s default encryption helpers:

pip3 install -U "pywa[cryptography]"

πŸ’Ύ Requirements#

  • Python 3.10 or higher - https://www.python.org

πŸ“– Setup and Usage#

See the Documentation for detailed instructions

βš–οΈ License#

This project is licensed under the MIT License - see the LICENSE file for details

πŸ”± Contributing#

Contributions are welcome! Please see the Contributing Guide for more information.

πŸ—£ Community#

Join the Telegram Group to discuss, ask questions, and share your projects built with PyWa!

πŸ“ Changelog#


NOTE: pywa follows the semver versioning standard.

4.0.0b7 (2026-04-30) Latest Beta#

  • [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

  • [templates] enhance CreativeFeaturesSpec with new attributes for improved media handling and text optimization

  • [templates] remove TemplateSubCategory class and update sub_category type to string

  • [message] fix reply to message in EditedMessage and DeletedMessage

See the full changelog here.

πŸ”— Contents#