NewAds Insights — see what each Facebook ad costs and the WhatsApp leads it brings. Free on every plan. See Ads Insights →
Lead Pattern

Developers · Python

Send a WhatsApp message with Python

Working Python code for the Lead Pattern WhatsApp API — send an approved template to start a conversation, reply with text inside the 24-hour window, and handle errors and rate limits.

Last updated: 6 November 2026

✨ Ask AI about this page:ChatGPT logoChatGPTClaude logoClaudePerplexity logoPerplexity

Quick answer

Create an API key in your Lead Pattern dashboard, then POST JSON with the requests library to https://public.leadpattern.com/v2/whatsapp/message/template (approved template) or https://public.leadpattern.com/whatsapp/message/text (reply within 24 hours), with the key in the Authorization header — no "Bearer" prefix. A 201 response with type "success" means WhatsApp accepted the message.

API reference

Step by step

Send a WhatsApp message with Python

1

Get an API key

In your Lead Pattern project dashboard, open API Keys and create a key. A key can call every public API endpoint your plan includes — keep it secret and on your server, never in browser or app code.

2

Set the Authorization header

Send the key as-is in the Authorization header on every request — no "Bearer" prefix — plus Content-Type: application/json. The base URL is https://public.leadpattern.com.

3

Send a template message

To start a conversation, or to message someone whose 24-hour window has closed, POST an approved template to /v2/whatsapp/message/template with named placeholders (or /whatsapp/message/template with positional ones).

4

Send a text message inside the 24-hour window

Once the customer has written to you in the last 24 hours, POST to /whatsapp/message/text with "to" and content.text. Outside the window this returns 403 — send a template instead.

5

Handle the response and errors

A successful send returns 201 with type "success" and a WhatsApp messageId. Errors come back with an HTTP status (400, 401, 402, 403, 404, 409, 422, 429, 500). On 429, wait for the Retry-After header before retrying. The Python code below does all of this.

1–2 · API key, Authorization header and response helperpython
# pip install requests
import os
import time
import requests

BASE_URL = "https://public.leadpattern.com"
API_KEY = os.environ["LEADPATTERN_API_KEY"]  # YOUR_API_KEY — keep it on the server

HEADERS = {
    "Authorization": API_KEY,  # the raw key, NO "Bearer " prefix
    "Content-Type": "application/json",
}



class LeadPatternError(Exception):
    def __init__(self, status, body, retry_after=None):
        super().__init__(f"HTTP {status}: {body}")
        self.status, self.body, self.retry_after = status, body, retry_after


def handle(r):
    """Return data on 201, otherwise raise with the status, body and Retry-After."""
    if r.status_code == 201:
        return r.json()["data"]          # {"messageId": ...} or {"messages": [...]}
    try:
        body = r.json()                  # e.g. {"type": "error", "code": 403, "message": "..."}
    except ValueError:
        body = r.text
    raise LeadPatternError(r.status_code, body, r.headers.get("Retry-After"))


# Optional: check connectivity (public endpoint, no key needed)
print(requests.get(f"{BASE_URL}/health-check", timeout=10).status_code)
3 · Send an approved template (v2, named placeholders)python
def send_template(to, template_name, language, placeholders):
    """Send an approved template. placeholders = [{"name": "..."}, {"order_id": "..."}]"""
    payload = {
        "messages": [
            {
                "to": to,  # E.164 with country code, e.g. "+91XXXXXXXXXX"
                "content": {
                    "templateName": template_name,
                    "language": language,
                    "templateData": {"body": {"placeholders": placeholders}},
                },
            }
        ]
    }
    r = requests.post(f"{BASE_URL}/v2/whatsapp/message/template",
                      json=payload, headers=HEADERS, timeout=30)
    return handle(r)


result = send_template("+91XXXXXXXXXX", "order_confirmation", "en",
                       [{"name": "Customer"}, {"order_id": "ORD-1234"}])
print(result["messages"][0]["messageId"])
Success response (201) — templatejson
{
  "type": "success",
  "data": {
    "messages": [
      {
        "status": "SENT",
        "recipient": "91XXXXXXXXXX",
        "messageId": "wamid.abc123"
      }
    ]
  }
}
4 · Send a text message inside the 24-hour windowpython
def send_text(to, text, preview_url=False):
    """Free-form text — only works within 24 hours of the customer's last message."""
    payload = {"to": to, "content": {"text": text, "previewUrl": preview_url}}
    r = requests.post(f"{BASE_URL}/whatsapp/message/text",
                      json=payload, headers=HEADERS, timeout=30)
    return handle(r)


print(send_text("+91XXXXXXXXXX", "Hello! How can I help you?")["messageId"])
Success response (201) — textjson
{
  "type": "success",
  "data": {
    "messageId": "wamid.abc123",
    "status": "sent",
    "recipient": "91XXXXXXXXXX"
  }
}
5 · Handle errors and rate limitspython
def with_retry(fn, *args, attempts=3):
    """Retry only on 429 (rate limit), waiting for Retry-After."""
    for i in range(attempts):
        try:
            return fn(*args)
        except LeadPatternError as e:
            if e.status != 429 or i == attempts - 1:
                raise
            time.sleep(int(e.retry_after or 60))  # wait for Retry-After (docs: retry after 60 s)


try:
    with_retry(send_text, "+91XXXXXXXXXX", "Your order has shipped.")
except LeadPatternError as e:
    if e.status == 403:
        # 24-hour window closed (or IP / plan restriction) -> send a template instead
        with_retry(send_template, "+91XXXXXXXXXX", "order_confirmation", "en",
                   [{"name": "Customer"}, {"order_id": "ORD-1234"}])
    else:
        raise
Error response example (403, window closed)json
{
  "type": "error",
  "code": 403,
  "message": "Cannot send a free-form message. The 24-hour customer service window has expired. You may only send template messages after 24 hours of the last user message."
}

HTTP status codes

What each response means

StatusMeaning (from the API docs)
201Created — the message was accepted by WhatsApp. The response has type "success" and data with the messageId.
400Malformed payload, invalid JSON or a missing required field (e.g. "to" is required). Also returned when the project has not finished WhatsApp onboarding.
401Missing, revoked, inactive or malformed API key.
402Workspace subscription inactive or blocked, or plan quota exceeded.
403IP not in the key's whitelist, plan does not include the feature, project suspended — or, for a free-form message, the 24-hour window has closed.
404The entity, template or recipient does not exist.
409Conflict — the resource already exists or is in the wrong state.
422Validation failed, e.g. the phone number is not valid or the WhatsApp account is unlinked.
429Rate limit exceeded. Wait for the Retry-After header, then retry.
500Unexpected server error.

Default limits per API key: 60 requests a minute and 10 requests in flight at once, plus 300 requests a minute per IP address. The effective limit is the lower of the key's limit and your plan's maximum.

Template v1 or v2?

  • v2 — /v2/whatsapp/message/template (recommended by the docs): body placeholders are named, an array of objects like {"name": "..."}. Supports carousel templates (content.templateData.cards) and an optional scheduledAt (a future ISO date-time) per message to schedule instead of sending now.
  • v1 — /whatsapp/message/template: body placeholders are positional — a flat array of values matched in order to the template's variables. Carousel is not supported on v1.
  • Both take a messages array, so one request can send to several recipients. AUTHENTICATION (OTP) templates use their own endpoint — see the API docs.
  • For a DOCUMENT header, pass templateData.header with type, mediaUrl and filename (e.g. "invoice.pdf") so WhatsApp shows a readable file name.

When you need a template

The WhatsApp 24-hour window, which Meta calls the customer service window, starts when a customer messages your business and resets with every new message from them. While it is open you can send normal replies without approval. Once 24 hours pass with no customer message, you can only send pre-approved templates. The 24-hour window explained →

Questions developers ask

Is there an official Lead Pattern Python SDK?+

The API docs don't list one. The API is plain HTTPS + JSON, so the requests library (or any HTTP client) is all you need.

How do I send a WhatsApp message with Python?+

Create an API key in your Lead Pattern dashboard, then POST JSON to https://public.leadpattern.com with the key in the Authorization header (no Bearer prefix). Use /v2/whatsapp/message/template for an approved template, or /whatsapp/message/text for a reply inside the 24-hour window.

Why does my text message return 403?+

Most often the customer's 24-hour window has closed — free-form messages are only allowed within 24 hours of the customer's last message. Send an approved template instead. A 403 can also mean the key's IP whitelist blocks your server, or your plan does not include the feature.

What are the API rate limits?+

By default 60 requests a minute and 10 concurrent requests per API key, plus 300 requests a minute per IP address. Going over returns 429 with a Retry-After header. Starter includes 1,000 API calls a month; Growth includes 1,00,000 a month with unlimited API keys.

Which phone number format does the API expect?+

E.164 with the country code, for example +91XXXXXXXXXX for India.

Lead Pattern

Get your API key — free 14-day trial.

Set up the official WhatsApp Business API, create a key and send your first message today.

14-day free trial · No credit card · Free WhatsApp API setup

Ask AI about this page

Get a quick summary of this page from your favourite AI assistant — based only on leadpattern.com. Be one of the first to try it.