Skip to content

WhatsApp API with Python: a FastAPI webhook that replies

To use the WhatsApp API from Python, expose one HTTP route for the webhook and call a REST API to reply. No SDK is required: the signature is an HMAC-SHA256 you verify in a few lines, and every call is JSON over HTTPS.

Updated November 10, 2026Read as Markdown

The app

Python

# main.py — pip install fastapi uvicorn httpximport hashlib, hmac, json, os, timeimport httpxfrom fastapi import FastAPI, Request, Responseapp = FastAPI()API = "https://nodium.io/api/v1"def verify_nodium(raw_body: bytes, header: str, secret: str) -> bool: parts = header.split(",") t = next((p[2:] for p in parts if p.startswith("t=")), None) signatures = [p[3:] for p in parts if p.startswith("v1=")] if not t or abs(time.time() - int(t)) > 300: return False expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest() return any(hmac.compare_digest(sig, expected) for sig in signatures)@app.post("/webhooks/nodium")async def nodium_webhook(request: Request): raw_body = await request.body() # the raw bytes, before any JSON parsing header = request.headers.get("x-nodium-signature", "") if not verify_nodium(raw_body, header, os.environ["NODIUM_WEBHOOK_SECRET"]): return Response(status_code=400) event = json.loads(raw_body) if event["event"] == "message.received" and event["data"]["type"] == "text": async with httpx.AsyncClient() as client: await client.post( f"{API}/messages", headers={"Authorization": f"Bearer {os.environ['NODIUM_KEY']}"}, json={ "channelId": event["data"]["channel_id"], "to": event["data"]["from"], "type": "text", "text": f"You wrote: {event['data']['text']}", "idempotencyKey": f"reply-{event['id']}", }, ) return Response(status_code=200)

Run it with uvicorn main:app, with NODIUM_KEY and NODIUM_WEBHOOK_SECRET in the environment.

Tell Nodium where to call you

Shell

curl https://nodium.io/api/v1/webhooks \ -H "Authorization: Bearer $NODIUM_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-app.example.com/webhooks/nodium", "events": ["message.received"] }'# → data.secret (whsec_…): shown once

Next

  • Write first to a customer with an approved template: free text only goes out within the 24-hour window.
  • Answer 200 quickly and do slow work afterwards: an event that is not acknowledged is delivered again, with the same id.
  • Connect your customers' numbers with one key for all of them.

Frequently asked questions

Is there a Python SDK?

Not yet. The API is plain REST, described in OpenAPI 3.1, and the reference carries a Python example for every route.

Does it work with Django or Flask?

Yes. Only two things matter: verify the signature on the raw request body, before any JSON parsing, and answer 200 quickly.

Try it with your own number.

Build is free: 3,000 messages a month, no card.

Talk to the Nodium team

Tell us what you are connecting. We get back to you within one business day, with a price that fits your WhatsApp numbers and your volume.