Forem

Fred Santos
Fred Santos

Posted on

How to Send WhatsApp Messages via API in Python Without Twilio

How to Send WhatsApp Messages via API in Python Without Twilio

Twilio's WhatsApp API works, but it's expensive ($0.05+ per conversation), requires a complex account setup, and the sandbox is frustrating to use during development. For developers in Brazil and Latin America especially, there are better options.

This tutorial shows how to send WhatsApp messages โ€” both template messages and free-form replies โ€” using IteraTools' WhatsApp endpoint, with complete curl and Python examples.

What You'll Need

No WhatsApp Business account registration. No Meta app approval. No webhook setup for basic sending.

What to Consider When Choosing a WhatsApp API

Tool Price Setup Time Template Required Limitations
IteraTools ~$0.005/msg (credits) Minutes No (for replies) Volume limits
Twilio $0.05/conversation + msg fee Hours/days Yes (for outbound) Expensive
360dialog $50/mo + per msg Days (BSP approval) Yes Complex setup
Meta Cloud API Free (infra costs) Days (app review) Yes Self-managed
Zapi / WPPConnect ~$30/mo Hours No TOS gray area

Sending a Message โ€” curl

Send a WhatsApp message via IteraTools:

curl -X POST https://api.iteratools.com/v1/whatsapp/send \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+5511999998888",
    "message": "Hello! Your order #1234 has been shipped and will arrive by Friday."
  }'
Enter fullscreen mode Exit fullscreen mode

Response:

{
  "message_id": "wamid.abc123xyz",
  "status": "sent",
  "to": "+5511999998888",
  "credits_used": 1
}
Enter fullscreen mode Exit fullscreen mode

Send a message with media:

curl -X POST https://api.iteratools.com/v1/whatsapp/send \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+5511999998888",
    "message": "Here is your invoice:",
    "media_url": "https://example.com/invoice_1234.pdf",
    "media_type": "document"
  }'
Enter fullscreen mode Exit fullscreen mode

Receiving Replies โ€” Webhook Setup

To receive incoming WhatsApp messages, set up a webhook endpoint in your IteraTools dashboard. Incoming messages will be POSTed to your URL:

{
  "from": "+5511999998888",
  "message": "Yes, please confirm my order.",
  "message_id": "wamid.inbound456",
  "timestamp": "2025-03-15T14:32:00Z"
}
Enter fullscreen mode Exit fullscreen mode

Reply to an incoming message:

curl -X POST https://api.iteratools.com/v1/whatsapp/reply \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message_id": "wamid.inbound456",
    "message": "Great! Your order has been confirmed. ๐ŸŽ‰"
  }'
Enter fullscreen mode Exit fullscreen mode

Complete Python Example

import requests
from flask import Flask, request, jsonify

API_KEY = "your_api_key_here"
BASE_URL = "https://api.iteratools.com/v1"

# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# Sending Messages
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

def send_whatsapp(to: str, message: str, media_url: str = None) -> dict:
    """Send a WhatsApp message."""
    payload = {
        "to": to,
        "message": message
    }
    if media_url:
        payload["media_url"] = media_url
        payload["media_type"] = "image"

    response = requests.post(
        f"{BASE_URL}/whatsapp/send",
        headers={"Authorization": f"Bearer {API_KEY}"},
        json=payload
    )
    response.raise_for_status()
    return response.json()

def reply_whatsapp(message_id: str, reply_text: str) -> dict:
    """Reply to an incoming WhatsApp message."""
    response = requests.post(
        f"{BASE_URL}/whatsapp/reply",
        headers={"Authorization": f"Bearer {API_KEY}"},
        json={
            "message_id": message_id,
            "message": reply_text
        }
    )
    response.raise_for_status()
    return response.json()

# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# Webhook Server (to receive messages)
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

app = Flask(__name__)

def handle_incoming_message(sender: str, text: str, message_id: str) -> str:
    """Simple keyword-based auto-reply bot."""
    text_lower = text.lower().strip()

    if any(word in text_lower for word in ["hi", "hello", "hey", "oi", "olรก"]):
        return "Hello! ๐Ÿ‘‹ How can I help you today?"

    elif "order" in text_lower or "pedido" in text_lower:
        return (
            "To check your order status, please reply with your order number. "
            "Example: ORDER 12345"
        )

    elif text_lower.startswith("order "):
        order_num = text_lower.replace("order ", "").strip()
        # In production, look up the order in your database
        return f"Order #{order_num}: Shipped โœ… Expected delivery: Friday, March 21"

    else:
        return (
            "I didn't understand that. Try:\n"
            "โ€ข *ORDER [number]* โ€” check order status\n"
            "โ€ข *HELP* โ€” see all commands"
        )

@app.route("/webhook/whatsapp", methods=["POST"])
def whatsapp_webhook():
    data = request.json

    sender = data.get("from")
    text = data.get("message", "")
    message_id = data.get("message_id")

    print(f"Incoming from {sender}: {text}")

    # Generate auto-reply
    reply_text = handle_incoming_message(sender, text, message_id)

    # Send reply
    result = reply_whatsapp(message_id, reply_text)
    print(f"Replied: {result['status']}")

    return jsonify({"status": "ok"})

# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# Main: Bulk Send Example
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

if __name__ == "__main__":
    # Example: Send order confirmations to a list of customers
    orders = [
        {"phone": "+5511999990001", "order": "1001", "item": "Wireless Headphones"},
        {"phone": "+5511999990002", "order": "1002", "item": "Phone Case"},
        {"phone": "+5521999990003", "order": "1003", "item": "USB-C Cable"},
    ]

    for order in orders:
        message = (
            f"Hi! Your order #{order['order']} ({order['item']}) "
            f"has been confirmed and will ship within 24 hours. "
            f"Thank you for your purchase! ๐Ÿ›๏ธ"
        )
        result = send_whatsapp(order["phone"], message)
        print(f"Sent to {order['phone']}: {result['status']}")

    # Start webhook server (run in production with gunicorn)
    # app.run(port=5000, debug=True)
Enter fullscreen mode Exit fullscreen mode

Sending WhatsApp Without Code (one-liner)

For quick testing:

# Send a test message right now
curl -X POST https://api.iteratools.com/v1/whatsapp/send \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to": "+YOUR_NUMBER", "message": "Test from IteraTools API โœ…"}'
Enter fullscreen mode Exit fullscreen mode

Conclusion

Sending WhatsApp messages via API doesn't have to mean navigating Twilio's pricing or Meta's Business Manager setup. IteraTools makes it straightforward: one API key, one endpoint, pay per message with no monthly fees.

For projects already using IteraTools for scraping, PDFs, or image processing, adding WhatsApp notifications is a 5-minute addition โ€” no new accounts needed.

โ†’ Get started at api.iteratools.com

Top comments (0)