evolution-whatsapp
A modern Python client for Evolution API v2 — send and receive WhatsApp messages from Python.
evolution-whatsapp is the Python twin of the R package
evolution
(source). It keeps the same
mental model — a preconfigured client, snake_case send_* helpers, jid() —
and adds a modern Python stack:
httpxwith sync and async clients (EvoClient/AsyncEvoClient)pydanticv2 models for webhook events +parse_webhook()structlogstructured, timed logging viaverbose=True- local-file → base64 auto-encoding, automatic retries, typed errors
- optional FastAPI webhook router and pandas
as_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 isevolution_api(the bareevolutionimport is already taken on PyPI — seeDECISIONS.md).
Quick start
from evolution_api import EvoClient, jid
client = EvoClient(
base_url="https://YOUR-HOST",
api_key="...", # or set the EVO_APIKEY env var
instance="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
import asyncio
from evolution_api import AsyncEvoClient
async def main():
async with AsyncEvoClient(base_url="https://YOUR-HOST", api_key="...", instance="inst") as client:
await client.send_text("5581999990000", "Hello from async Python!")
asyncio.run(main())
Functions overview
| Method | Description | Key arguments |
|---|---|---|
EvoClient() / AsyncEvoClient() |
Preconfigured client | base_url, api_key, instance, timeout |
send_text() |
Plain text message | number, 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 pin | number, latitude, longitude, name |
send_contact() |
One or more contacts (auto wuid) |
number, contact |
send_reaction() |
Emoji reaction | key, reaction |
send_buttons() |
Interactive buttons ⚠️ | number, buttons |
send_poll() |
Poll | number, name, values |
send_list() |
Interactive list ⚠️ | number, sections, button_text |
check_is_whatsapp() / check_numbers() |
Check if numbers are on WhatsApp | numbers |
connection_state() |
Channel connection / health check | — |
jid() |
Build a WhatsApp JID from a phone number | number |
⚠️
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 suggestsend_poll().💡 Local files:
send_media(),send_sticker()andsend_whatsapp_audio()accept local paths (including~/...), auto-encoded to base64.
Examples
# Media from a URL
client.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")
# Poll
client.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 numbers
client.check_is_whatsapp(["5581999990000", "5511988887777"])
Receiving webhooks
from evolution_api.webhooks import parse_webhook
event = parse_webhook(request_json)
if event.event_type == "MESSAGES_UPSERT":
print(event.data.key.remote_jid, event.data.message)
FastAPI router (extra fastapi):
from fastapi import FastAPI
from evolution_api.webhooks import webhook_router
async def on_event(event):
if event.event_type == "MESSAGES_UPSERT":
...
app = FastAPI()
app.include_router(webhook_router(on_event))
Drain to a DataFrame (extra pandas):
from evolution_api.webhooks import as_dataframe
df = as_dataframe([parse_webhook(p) for p in payloads])
Configuration
| Setting | Default | Description |
|---|---|---|
EVO_APIKEY (env) |
— | API key if api_key is not passed |
EVO_INSTANCE (env) |
— | Instance if instance is not passed |
EVOLUTION_TIMEOUT (env) / timeout= |
60 |
HTTP timeout in seconds |
verbose=True |
per-call | Structured 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.
Metadata
Release files for evolution-whatsapp 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 | |
|---|---|---|---|
| evolution_whatsapp-0.1.1.tar.gz | 180.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| evolution_whatsapp-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 206.6 kB
Release files / evolution_whatsapp-0.1.1.tar.gz
| Download URL | evolution_whatsapp-0.1.1.tar.gz |
|---|---|
| Size | 180.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f0935fda3e87e68c37413670ae154aeec4469e9cf772c166da6aaecb7acd6d99
|
|
BLAKE2b-256 checksum How to use checksums |
9f2bcfc34de4cdbe4533388d0f35503a444f3901d419a9fa949f7e0729d9d9f0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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.
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 Jul 14, 2026.
Transparency logRelease files / evolution_whatsapp-0.1.1-py3-none-any.whl
| Download URL | evolution_whatsapp-0.1.1-py3-none-any.whl |
|---|---|
| Size | 25.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3a6dfbf3f060096348ee32b6ded9fa61a37f1bddbbbc0b7e01792caaf1e3d0eb
|
|
BLAKE2b-256 checksum How to use checksums |
e15789e31854a517e4fea9794c20b849a128af0756edb792125ded51d032880c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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.
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 Jul 14, 2026.
Transparency log