WhatsApp Operators DailyThe Blueticks DispatchTuesday, June 30, 2026
Productivity

How to Build a WhatsApp Bot in Python on Your Own Number (No Meta Verification, 2026)

A working WhatsApp bot in Python, on the number you already use, without Meta Business verification. Send, receive replies over a webhook, auto-reply to keywords, and schedule messages, with runnable code and the failure modes nobody mentions.

DRBy Daniel Roth · June 30, 2026 · 10 min read
How to Build a WhatsApp Bot in Python on Your Own Number (No Meta Verification, 2026)

You want a WhatsApp bot. Something that answers "PRICE" with your price list, fires a reminder at 9am, and pings you when a customer replies. You sit down to build it in Python and hit the same wall everyone hits: the official path wants you to register a business with Meta, verify it, provision a separate number, and get message templates approved before you can send a single line of text.

That's a lot of process for a bot that just needs to talk to people from the number you already use. This guide builds the bot the other way. A WhatsApp bot in Python, running on your existing number, over a plain REST API, with no Meta verification step. Every code sample below runs against the live API. I'll also be straight about what this approach can't do, because there are real trade-offs.

What a Python WhatsApp bot can actually do (and what it can't)

A Python WhatsApp bot can send messages, receive incoming messages through a webhook, auto-reply based on what the sender writes, and schedule messages for a future time. It runs on your own number over a REST API. What it can't promise is the unlimited, guaranteed-delivery, broadcast-at-scale behavior of a verified business platform.

Here's the honest split between the two halves.

What you get:

  • Outbound sends. Order confirmations, alerts, reminders, follow-ups. Fire on any event in your backend.
  • Inbound handling. A webhook tells your code when someone messages your number back, so the bot can react to replies, not just blast.
  • Keyword auto-replies. Match on the text a sender sends ("HOURS", "STOP", an order number) and respond.
  • Scheduling. Hand the API a timestamp and it holds the message until then. No cron job of your own.

What you don't get:

  • A spam cannon. This runs on a real WhatsApp number, and WhatsApp's terms prohibit bulk unsolicited messaging. Abuse it and the number gets flagged.
  • The official Meta Cloud API's compliance posture for regulated, high-volume sending.

If your use case is transactional and conversational (a support bot, a reminder bot, a notifier), this fits. If you need to push a million template messages a month under Meta's commerce policy, that's a different product. I compared both transports in detail in the send-and-schedule API walkthrough, which is the sibling to this piece.

Why build on your own number instead of the Meta Cloud API

Building on your own number skips Meta Business verification, template pre-approval, and the per-template fees that come with the Cloud API. The bot sends from the number your contacts already recognize, over WhatsApp Web's transport. The trade-off is real: an own-number bot carries flagging and Terms-of-Service risk that the official, verified platform does not.

The Meta WhatsApp Cloud API is the sanctioned route, and Meta's developer documentation is clear that it requires business verification and an approved message-template flow before you can message customers at scale. That's the right call for a bank or an airline. For a solo developer, a small SaaS, or anyone testing an idea this weekend, it's a multi-day gate.

Here's the practical comparison.

own number vs cloud desk

Own-number API (this guide)Meta Cloud API
Meta Business verificationNot requiredRequired
Number usedYour existing oneA separate business number
Message templatesNone to approvePre-approval required
Per-template / conversation feesNone in the pathYes, per Meta pricing
Setup timeMinutesDays (verification + templates)
Best forTransactional + conversational botsHigh-volume, compliance-heavy sending
RiskNumber flagging if you spamLower, it's the sanctioned channel

If you're still deciding which side of that table you belong on, the do-I-need-the-WhatsApp-Business-API breakdown walks through it by volume and risk tolerance. For most bots that answer questions and send reminders, the own-number path gets you to a working product the same afternoon.

Step 1: Get your API key and send your first message in ~10 lines of Python

Create an API key in your Blueticks account, install the Python SDK, and call the send method. About ten lines gets a real WhatsApp message out the door from your own number. The base URL is https://api.blueticks.co and authentication is a single bearer token you keep server-side.

Keys are environment-scoped. A live key starts bt_live_, a test key starts bt_test_, so you can wire a sandbox path in CI without risking production. The key authenticates as your workspace and your WhatsApp number, so it does not belong in a browser bundle or a mobile app.

Install the SDK and send:

# pip install blueticks
from blueticks import Blueticks

client = Blueticks(api_key="bt_live_...")  # keep this on the server

msg = client.scheduled_messages.create(
    to="+14155551234",   # E.164 number, or a WhatsApp chat id
    type="text",
    text="Hello from my Python bot.",
)

print(msg.id, msg.status)

That's the whole "send" surface. The endpoint underneath is POST /v1/scheduled-messages. If you'd rather not install anything, the same call as raw HTTP:

curl -X POST https://api.blueticks.co/v1/scheduled-messages \
  -H "Authorization: Bearer bt_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+14155551234",
    "type": "text",
    "text": "Hello from my Python bot."
  }'

The response gives you an id (poll it for status), a key (the WhatsApp wire id, filled in once the message dispatches), a status, and lifecycle timestamps. Grab a bt_test_ key from dev.blueticks.co and you can run this in two minutes. Beyond type: "text", the same call accepts type: "media" and type: "poll".

Step 2: Receive incoming messages with a webhook

A bot that only sends is a notifier, not a bot. To make it conversational, register a webhook. You tell the API a URL and a list of events, and it POSTs to your endpoint when those events happen, including when someone replies to your number. Every delivery is signed, so you verify it before trusting the payload.

Register the hook once, in Python:

hook = client.webhooks.create(
    url="https://your-app.example.com/webhooks/blueticks",
    events=["message.received", "message.delivered", "message.failed"],
)
print(hook.secret)   # returned once, store it to verify signatures

Now stand up a receiver. The request carries an X-Blueticks-Signature header of the form sha256=<hex>, an HMAC-SHA256 of the raw request body keyed with the secret you got at registration. Here's a minimal Flask endpoint that checks it:

import hmac, hashlib
from flask import Flask, request, abort

app = Flask(__name__)
WEBHOOK_SECRET = "whsec_..."   # the hook.secret from registration

def valid_signature(raw_body: bytes, header: str) -> bool:
    expected = "sha256=" + hmac.new(
        WEBHOOK_SECRET.encode(), raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, header or "")

@app.post("/webhooks/blueticks")
def receive():
    raw = request.get_data()                       # raw bytes, before parsing
    sig = request.headers.get("X-Blueticks-Signature")
    if not valid_signature(raw, sig):
        abort(401)
    event = request.get_json()
    print(event)                                   # read the shape off this
    return "", 200

One practical tip before you build on the payload: print that first event and read the field names off a real delivery. The exact inbound shape is in the webhook reference, and printing it beats guessing. Verify the signature against the raw body bytes, not a re-serialized version, or your HMAC won't match. The full event catalogue and signature details are in the sibling API guide.

Step 3: Make the bot auto-reply to keywords

Auto-replying is a small router: read the inbound text, match it against keywords, and call the send endpoint with a response. The whole bot brain is a dictionary lookup plus a default fallback. Because you already verify the signature in Step 2, the only new work is parsing the message and deciding what to say back.

webhook loop whiteboard

Extend the receiver from Step 2. I keep the routing table dead simple and defensive, using .get() so a missing field never crashes the handler:

REPLIES = {
    "hours": "We're open Mon-Fri, 9am to 6pm.",
    "price": "Plans start at $12/mo. Reply DEMO for a walkthrough.",
    "stop":  "You're unsubscribed. Reply START to opt back in.",
}

def handle_inbound(event, client):
    data   = event.get("data", {})          # confirm keys from a real payload
    sender = data.get("from")
    body   = (data.get("text") or "").strip().lower()
    if not sender:
        return

    reply = REPLIES.get(body, "Thanks! A human will get back to you shortly.")
    client.scheduled_messages.create(to=sender, type="text", text=reply)

Wire it into the Flask route, right after the signature check passes:

    event = request.get_json()
    if event.get("type") == "message.received":
        handle_inbound(event, client)
    return "", 200

That's a functioning keyword bot. The receive to reply loop is: WhatsApp message lands, the API POSTs your webhook, you match the keyword, you call send, the reply goes out from your number. A few gotchas worth building in from day one: lowercase and strip the inbound text before matching (people type " Price " with spaces and caps), always include a fallback so silence never happens, and return 200 fast, then do slow work in a background task so the webhook doesn't time out. Field names like from and text should be confirmed against the printed payload from Step 2 before you ship, since the reference is the source of truth, not this snippet.

Step 4: Schedule a message for later from Python

To schedule instead of send now, add one field to the same call: send_at, an ISO 8601 timestamp. The API holds the message and dispatches it at that time. No queue, no worker, no cron of your own. The timestamp must be at least 10 seconds in the future and at most 365 days out.

This is the part that's genuinely hard to find elsewhere, and it turns the bot from reactive to proactive. A standup nudge, a trial-ending warning, a "your appointment is tomorrow" reminder.

from datetime import datetime, timedelta, timezone
from blueticks import Blueticks

client = Blueticks(api_key="bt_live_...")

send_at = datetime.now(timezone.utc) + timedelta(hours=12)

client.scheduled_messages.create(
    to="+14155551234",
    type="text",
    text="Reminder: your appointment is tomorrow at 10:00am.",
    send_at=send_at.isoformat(),
)

scheduled reminder phone morning

The message comes back with status: "scheduled" until its time arrives. Because it lives as a real queued record, you can manage it after the fact: GET /v1/scheduled-messages/{id} reads its status, a PATCH to the same id edits the text or moves the send_at while it's still pending, and there's a cancel path to pull it before it fires. That cancel window is the useful trick. Schedule the "your payment is due tomorrow" reminder up front, then cancel it the moment your payment webhook lands. The customer who already paid never gets nagged. The scheduling endpoint and its constraints are documented at dev.blueticks.co.

Step 5: Keep the bot reliable with retries, idempotency, and honest failure modes

Reliability on an own-number bot comes down to four habits: send idempotently so retries don't double-message, back off on errors instead of hammering, pace your volume so WhatsApp doesn't flag the number, and listen for message.failed so you know when a send didn't land. The transport runs on a live WhatsApp Web session, which is the source of both its upside and its failure modes.

Start with idempotency. The send endpoint accepts an Idempotency-Key header. Pass a stable key per logical message and a network retry won't send twice:

import requests, uuid

idem_key = str(uuid.uuid4())   # reuse this exact key on any retry

resp = requests.post(
    "https://api.blueticks.co/v1/scheduled-messages",
    headers={
        "Authorization": "Bearer bt_live_...",
        "Idempotency-Key": idem_key,
        "Content-Type": "application/json",
    },
    json={"to": "+14155551234", "type": "text", "text": "Payment received."},
    timeout=15,
)

Now the failure modes nobody warns you about, because operators have been burned by all of these:

  • The session can drop. This runs on a WhatsApp Web session tied to your number. WhatsApp's multi-device mode links up to four devices to your number, but the bot's session is bound to its own linked slot. If you log out of WhatsApp on your phone, remove that linked device, or hit the four-device limit, the session ends and queued sends stop.
  • Your number can get banned. WhatsApp watches for spammy patterns. A number that suddenly fires hundreds of unsolicited messages gets rate-limited or banned. This risk is inherent to the own-number category, not to any one provider. Ramp volume slowly, message people who expect to hear from you, and keep content relevant.
  • "No verification" is not "no rules." There are no per-template fees because there are no Meta templates in the path. That's a cost advantage, not a license to spam.
  • Sends can silently queue if the host is offline. On the basic plan the machine running the session has to be on at send time. For 24/7 delivery you need an always-on gateway, covered below.

As one developer who shipped a reminder bot on this stack put it to me: "The send code took an afternoon. Getting the pacing right so my number stayed healthy took the next two weeks." That's the honest ratio. The send-and-schedule guide has the full webhook event list (message.queued, message.sending, message.delivered, message.read, message.failed) you can subscribe to for exactly this monitoring.

When a script isn't enough: scheduling UI, campaigns, and dashboards

A Python script is perfect for event-driven sends and a keyword bot. It's the wrong tool when a non-developer needs to schedule a one-off, when you want a one-to-many campaign with per-recipient variables, or when you need a dashboard to see what delivered. For those, the same Blueticks account that issued your API key has a UI and an always-on gateway layered on top.

Three things the product gives you that a raw script doesn't:

  1. A scheduling and campaign UI. Build an audience (a named contact list with custom variables), then send one templated message to everyone with {first_name} style tokens. Useful when marketing wants to fire a blast without touching your code.
  2. An offline gateway. The always-on mode sends even when your computer is closed, so a 9am reminder fires whether or not your laptop is awake.
  3. A delivery dashboard. See queued, sent, delivered, read, and failed at a glance instead of grepping logs.

One plan note that matters for bots: the Free plan can send, but it appends a "Powered by blueticks.co" footer to messages, and the paid Pro plan removes that branding and unlocks the always-on offline mode. Match the plan to whether branding and 24/7 delivery matter for your integration.

If you're ready to build, grab a test key, fire the curl call from Step 1, and you'll have a message out in about two minutes. The full reference, SDK installs, and an interactive sandbox are at dev.blueticks.co.

FAQ

Do I need to verify a business with Meta to build a WhatsApp bot in Python? No, not on the own-number path described here. The bot sends from your existing WhatsApp number over a REST API, with no Meta Business verification and no template approval. The trade-off is the flagging and Terms-of-Service risk that comes with an own-number transport, which the official Meta Cloud API doesn't carry.

What can a Python WhatsApp bot actually do? It can send messages, receive incoming messages through a signed webhook, auto-reply based on keywords, and schedule messages for a future time, all from your own number. What it can't do is guarantee unlimited high-volume broadcasting, which is what the verified Meta Cloud API is built for.

How do I receive replies, not just send? Register a webhook with an inbound message event. When someone messages your number, the API POSTs the event to your URL. Verify the X-Blueticks-Signature HMAC against the raw body, then route the text to your reply logic. The full receiver is in Step 2 above.

Can I really schedule a WhatsApp message from Python for a future date? Yes. Add a send_at ISO 8601 timestamp to the send call. It must be at least 10 seconds in the future and within 365 days. The message is held server-side with status: "scheduled", and you can edit or cancel it before it fires.

Will my number get banned if I run a bot on it? It can, if you send spammy or unsolicited volume. WhatsApp's terms prohibit bulk messaging that mimics spam. Pace your sends, message people who expect to hear from you, and ramp volume gradually. No provider can guarantee zero risk on an own-number transport.

Which languages have official SDKs? Python and Node both install as the blueticks package, and everything is reachable as plain REST, so any HTTP client in any language works.

Email

The Dispatch, every week.

One sharp WhatsApp growth tactic in your inbox each week. Joined by 238,000+ founders, marketers and support leads.

Free forever. No spam, unsubscribe in one click.