Tutorial

How to Send WhatsApp Messages from PHP (No Composer Required)

Send WhatsApp messages from PHP using plain cURL — no dependency required. Session setup, QR auth, sending text/images/files, and production error handling.

August 3, 2026By Retention Stack

Meta Description: Send WhatsApp messages from PHP with plain cURL — no Composer dependency required. Complete guide: session setup, QR auth, sending text/images/files, and production error handling.


Introduction

Most "WhatsApp API PHP" tutorials start with composer require and a wrapper library. You don't need one. The WhatsApp Messaging API is plain REST over HTTPS, so PHP's built-in cURL functions are enough to send your first message.

In this guide, you'll send a real WhatsApp message from PHP in under 5 minutes, then extend that into a small helper class you can drop into any codebase — vanilla PHP, WordPress, or Laravel.

What you'll need:

  • PHP 7.4+ with the curl extension (enabled by default on almost every host)
  • A RapidAPI account (free)
  • 5 minutes

Let's send a message.


Step 1: Get Your API Key (30 seconds)

  1. 1.Go to RapidAPI Hub
  2. 2.Search for "Whatsapp messaging bot"
  3. 3.Click Subscribe (Free plan available, no card required)
  4. 4.Open API Settings and copy your X-RapidAPI-Key

That key authenticates every request below.


Step 2: Create a WhatsApp Session

Before sending a message, pair a session — this is the WhatsApp account the API sends on behalf of.

php
<?php
// session.php
$apiKey  = getenv('RAPIDAPI_KEY');
$apiHost = 'whatsapp-messaging-bot.p.rapidapi.com';
$baseUrl = "https://$apiHost";
$sessionName = 'my-session';

function callApi(string $method, string $path, ?array $body = null): array {
    global $apiKey, $apiHost, $baseUrl;

    $ch = curl_init("$baseUrl$path");
    curl_setopt_array($ch, [
        CURLOPT_CUSTOMREQUEST  => $method,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => [
            "x-rapidapi-key: $apiKey",
            "x-rapidapi-host: $apiHost",
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS => $body ? json_encode($body) : null,
    ]);

    $response = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    return ['status' => $status, 'body' => json_decode($response, true)];
}

// Create the session
$result = callApi('POST', '/v1/sessions', ['name' => $sessionName]);
echo "Session created: " . json_encode($result['body']) . "\n";

// Get the QR code to authenticate
$qr = callApi('GET', "/v1/sessions/$sessionName/auth/qr");
echo "Scan this QR with WhatsApp → Linked Devices:\n";
echo $qr['body']['qr'] ?? 'QR not ready yet, retry in a second';

Run it:

bash
RAPIDAPI_KEY=your_key_here php session.php

Open WhatsApp on your phone → Settings → Linked Devices → Link a Device, and scan the printed QR code.


Step 3: Send Your First Message

Once the session shows as authenticated, sending is a single request:

php
<?php
// send.php
require 'session.php'; // reuses callApi() and $sessionName above

function sendWhatsAppMessage(string $chatId, string $text): array {
    global $sessionName;
    return callApi('POST', '/v1/sendText', [
        'chatId'  => $chatId,   // just the number: "14155552671"
        'text'    => $text,
        'session' => $sessionName,
    ]);
}

$result = sendWhatsAppMessage('14155552671', 'Hello from PHP!');

if ($result['status'] === 200 || $result['status'] === 201) {
    echo "Message sent.\n";
} else {
    echo "Send failed: " . json_encode($result['body']) . "\n";
}
bash
php send.php

Check your phone. That's a real WhatsApp message sent from PHP with zero dependencies.


Step 4: Sending Images, Files, and Voice Notes

Same callApi() helper, different endpoint:

php
function sendImage(string $chatId, string $imageUrl, string $caption = ''): array {
    global $sessionName;
    return callApi('POST', '/v1/sendImage', [
        'chatId'  => $chatId,
        'image'   => $imageUrl,
        'caption' => $caption,
        'session' => $sessionName,
    ]);
}

function sendFile(string $chatId, string $fileUrl, string $filename): array {
    global $sessionName;
    return callApi('POST', '/v1/sendFile', [
        'chatId'   => $chatId,
        'file'     => $fileUrl,
        'filename' => $filename,
        'session'  => $sessionName,
    ]);
}

Both take a public URL — no need to upload the file yourself first. Full parameter reference for every message type (including voice, video, location, and contact cards) is in the interactive API reference.


Production Best Practices

1. Retry with backoff

A single failed request shouldn't fail the whole job. Wrap sends in a retry loop:

php
function sendWithRetry(string $chatId, string $text, int $maxRetries = 3): array {
    for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
        $result = sendWhatsAppMessage($chatId, $text);
        if ($result['status'] < 400) {
            return $result;
        }
        sleep($attempt * 2); // 2s, 4s, 6s backoff
    }
    return $result;
}

2. Don't recreate sessions per request

A session is a persistent, authenticated connection — create it once, store the session name (in your .env, a config table, wherever your app already keeps settings), and reuse it across every subsequent request. Recreating it per request just adds latency for nothing.

3. Confirm delivery with a webhook, not the HTTP response

A 200 from /v1/sendText means "accepted," not "delivered" — a number that isn't on WhatsApp can still return success. Register a webhook and read the message.ack event for real delivery/read status:

php
callApi('POST', "/v1/sessions/$sessionName/webhooks", [
    'webhookUrl' => 'https://yourapp.com/webhooks/whatsapp',
    'events'     => ['message.ack'],
]);

Troubleshooting

IssueFix
401 UnauthorizedYour RAPIDAPI_KEY env var isn't set, or the key's been rotated in the RapidAPI dashboard.
cURL error 60 (SSL)Older PHP builds ship an outdated CA bundle — update curl.cainfo in php.ini or your host's cURL package.
QR code expires before you scan itQR codes are short-lived. Fetch it right before you're ready to scan, not minutes ahead.
Message accepted but never arrivesCheck message.ack via webhook — a 200 only means the API accepted the send, not that WhatsApp delivered it.

Next Steps

External references:


Try Free on RapidAPI → Get your API key in 30 seconds → Send your first PHP message in 5 minutes.

Ready to Get Started?

Try the WhatsApp API free on RapidAPI with no credit card required.

Try Free on RapidAPI