Skip to content

Repository files navigation

evolution-whatsapp

A modern Python client for Evolution API v2 — send and receive WhatsApp messages from Python.

PyPIPythonDocsCILicense: MITDOI

evolution-whatsapp is the Python twin of the R package evolution (source). It keeps the same mental model — a preconfigured client, snake_casesend_* helpers, jid() — and adds a modern Python stack:

  • httpx with sync and async clients (EvoClient / AsyncEvoClient)
  • pydantic v2 models for webhook events + parse_webhook()
  • structlog structured, timed logging via verbose=True
  • local-file → base64 auto-encoding, automatic retries, typed errors
  • optional FastAPI webhook router and pandasas_dataframe() for pipelines

This package is an independent wrapper for the Evolution API and is not affiliated with WhatsApp or Meta.

Installation

pip install evolution-whatsapp
# with extras:
pip install "evolution-whatsapp[fastapi]"# webhook_router()
pip install "evolution-whatsapp[pandas]"# as_dataframe()

The distribution is evolution-whatsapp; the import name is evolution_api (the bare evolution import is already taken on PyPI — see DECISIONS.md).

Quick start

fromevolution_apiimportEvoClient, jidclient=EvoClient(
base_url="https://YOUR-HOST",
api_key="...", # or set the EVO_APIKEY env varinstance="yourInstance",
timeout=30, # or the EVOLUTION_TIMEOUT env var (default 60)
)
# Send a simple message (verbose logs timing + a response preview)client.send_text("5581999990000", "Hello from Python!", verbose=True)
jid("+55 81 99999-0000") # -> "5581999990000@s.whatsapp.net"

Async

importasynciofromevolution_apiimportAsyncEvoClientasyncdefmain():
asyncwithAsyncEvoClient(base_url="https://YOUR-HOST", api_key="...", instance="inst") asclient:
awaitclient.send_text("5581999990000", "Hello from async Python!")
asyncio.run(main())

Functions overview

MethodDescriptionKey arguments
EvoClient() / AsyncEvoClient()Preconfigured clientbase_url, api_key, instance, timeout
send_text()Plain text messagenumber, text, delay, verbose
send_status()Status / story (text or media)type, content, caption
send_media()Image / video / document (URL, base64, or file)number, mediatype, mimetype, media, file_name
send_whatsapp_audio()Voice note (PTT)number, audio
send_sticker()Sticker (URL, base64, or file)number, sticker
send_location()Location pinnumber, latitude, longitude, name
send_contact()One or more contacts (auto wuid)number, contact
send_reaction()Emoji reactionkey, reaction
send_buttons()Interactive buttons ⚠️number, buttons
send_poll()Pollnumber, name, values
send_list()Interactive list ⚠️number, sections, button_text
check_is_whatsapp() / check_numbers()Check if numbers are on WhatsAppnumbers
connection_state()Channel connection / health check
jid()Build a WhatsApp JID from a phone numbernumber

⚠️send_buttons() / send_list(): interactive buttons and lists are not supported on the Baileys (WhatsApp Web) connector and may be discontinued — they work only on the Cloud API connector. Both emit a warning and suggest send_poll().

💡 Local files:send_media(), send_sticker() and send_whatsapp_audio() accept local paths (including ~/...), auto-encoded to base64.

Examples

# Media from a URLclient.send_media("5581999990000", "image", "image/png",
media="https://www.r-project.org/logo/Rlogo.png",
file_name="Rlogo.png", caption="R Logo")
# Media from a local file (auto base64)client.send_media("5581999990000", "document", "application/pdf",
media="~/report.pdf", file_name="report.pdf")
# Pollclient.send_poll("5581999990000", "Favourite language?",
["R", "Python", "Julia"], selectable_count=1)
# Contact (wuid auto-generated)client.send_contact("5581999990000", {
"fullName": "Jane Doe", "phoneNumber": "+5581999990000",
"organization": "Company Ltd.", "email": "jane@example.com",
})
# Check numbersclient.check_is_whatsapp(["5581999990000", "5511988887777"])

Receiving webhooks

fromevolution_api.webhooksimportparse_webhookevent=parse_webhook(request_json)
ifevent.event_type=="MESSAGES_UPSERT":
print(event.data.key.remote_jid, event.data.message)

FastAPI router (extra fastapi):

fromfastapiimportFastAPIfromevolution_api.webhooksimportwebhook_routerasyncdefon_event(event):
ifevent.event_type=="MESSAGES_UPSERT":
...
app=FastAPI()
app.include_router(webhook_router(on_event))

Drain to a DataFrame (extra pandas):

fromevolution_api.webhooksimportas_dataframedf=as_dataframe([parse_webhook(p) forpinpayloads])

Configuration

SettingDefaultDescription
EVO_APIKEY (env)API key if api_key is not passed
EVO_INSTANCE (env)Instance if instance is not passed
EVOLUTION_TIMEOUT (env) / timeout=60HTTP timeout in seconds
verbose=Trueper-callStructured logging with timing + response preview

Relationship to the R package

This is a faithful port of StrategicProjects/evolution (CRAN). See the Parity with the R package page in the docs and DECISIONS.md for where Python idioms intentionally differ.

License

MIT © 2026 André Leite, Hugo Vasconcelos & Diogo Bezerra. See LICENSE.

About

A modern Python client for the Evolution API v2 (WhatsApp) — the twin of the R 'evolution' package.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages