REST API

Send a WhatsApp message with one API call

Scan a QR code, copy your token, send.

One POST request with a chat id and a message. No business verification, no template review, no per-message fee — your own number, connected in minutes.

curl --request POST \
  --url https://waapi.app/api/v1/instances/123/client/action/send-message \
  --header 'authorization: Bearer your-api-token' \
  --header 'content-type: application/json' \
  --data '{
    "chatId": "[email protected]",
    "message": "Hi Anna, your table for two is booked for Friday at 7 pm."
}'

How it works

From sign-up to first message in three steps

  1. 1

    Connect your number

    Create an instance and scan its QR code with your phone, or enter a pairing code. The number you already use is the sender.

  2. 2

    Copy your API token

    Create a token in your account and note the instance id shown in your dashboard.

  3. 3

    Send a POST request

    Call the send-message action with a chat id and the text. The response tells you whether the message went out.

The code

The same request in four languages

Replace 123 with your instance id and the token with your own. The chat id is the recipient’s number in international format without the plus sign, followed by @c.us.

The outer status says your request reached the instance. data.status says whether the message was actually sent — check that one.

Full API reference

curl --request POST \
  --url https://waapi.app/api/v1/instances/123/client/action/send-message \
  --header 'authorization: Bearer your-api-token' \
  --header 'content-type: application/json' \
  --data '{
    "chatId": "[email protected]",
    "message": "Hi Anna, your table for two is booked for Friday at 7 pm."
}'
<?php

$instanceId = 123;
$apiToken = 'your-api-token';

$ch = curl_init("https://waapi.app/api/v1/instances/{$instanceId}/client/action/send-message");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'authorization: Bearer ' . $apiToken,
        'content-type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'chatId' => '[email protected]',
        'message' => 'Hi Anna, your table for two is booked for Friday at 7 pm.',
    ]),
]);

echo curl_exec($ch);
import requests

instance_id = 123
api_token = "your-api-token"

response = requests.post(
    f"https://waapi.app/api/v1/instances/{instance_id}/client/action/send-message",
    headers={"authorization": f"Bearer {api_token}"},
    json={
        "chatId": "[email protected]",
        "message": "Hi Anna, your table for two is booked for Friday at 7 pm.",
    },
)

print(response.json())
const instanceId = 123;
const apiToken = 'your-api-token';

fetch(`https://waapi.app/api/v1/instances/${instanceId}/client/action/send-message`, {
  method: 'POST',
  headers: {
    authorization: `Bearer ${apiToken}`,
    'content-type': 'application/json',
  },
  body: JSON.stringify({
    "chatId": "[email protected]",
    "message": "Hi Anna, your table for two is booked for Friday at 7 pm.",
  }),
})
  .then((res) => res.json())
  .then(console.log);

Response

{
    "status": "success",
    "data": {
        "status": "success",
        "instanceId": "123",
        "data": {
            "_data": {
                "id": {
                    "fromMe": true,
                    "remote": "[email protected]",
                    "id": "3EB0123456789ABCDEF",
                    "_serialized": "[email protected]_3EB0123456789ABCDEF"
                },
                "body": "Hi Anna, your table for two is booked for Friday at 7 pm.",
                "type": "chat",
                "t": 1759395600
            }
        }
    }
}

Authentication

Get your API key (API token)

Your API key is a token you create yourself under API Tokens in your account. Give it a name, choose what it may do — create, read, update, delete — and copy it once. Every request sends it as Authorization: Bearer <token>.

There is no Meta developer account and no app review in between. One token covers all your instances, and you can use it from as many applications as you like — or create one per application and revoke it on its own.

Beyond text

What you can send

Every card is a documented action of the same API — same token, same URL pattern.

Text

Plain messages with line breaks and emoji, replies to an earlier message, mentions in groups and link previews.

send-message

Images, video, audio, files

JPG, PNG, MP4, MP3, PDF, Office files and more — from a public URL or as base64. Audio can go out as a voice note.

send-media

Groups and channels

The same call works for a group (an id ending in @g.us) or a channel you manage (@newsletter).

send-message

Polls and events

Ask a question with several answers, read the votes back, or invite people to a dated event.

create-poll

Reactions, edits, forwards

React to a message, edit or delete one you sent, forward it to another chat.

react-to-message

Contacts and locations

Share a contact card or a pin on the map.

send-vcard

Webhooks

Receive replies by webhook

Set a webhook URL on your instance and every incoming message arrives there as a JSON POST — together with delivery updates, reactions and group events. Each request is signed with your webhook secret, and a failed delivery is retried with exponential backoff.

Reply by calling the same send-message action with the chat id from the webhook.

Limits

Limits and sending safely

There is no cap on how many messages you send. What gets numbers restricted is contacting many people who never wrote to you — so send protection is on by default for every new number.

1–2 s
pause between two messages, per number
20
new conversations per hour, per number
60
new conversations per day, per number

What counts as a new conversation: a person who has never written to your number and whom you have not messaged in the last 7 days. Replies to people who wrote to you first and messages to groups and channels never count, and a follow-up within those 7 days doesn’t count twice.

Need more? Spread new contacts across several numbers, or switch send protection off for a number after confirming the warning — at your own risk.

How to avoid restrictions

Your first instance is free
One instance, free for your first month — with code FREEINSTANCE, entered at checkout.
Flat rate

Flat rate, simple

$10 / month, per connected number

Unlimited messages. Media, groups, channels, SDKs and MCP included. Cancel anytime.

  • No per-message fees
  • No per-user fees
  • No setup fee
  • Cancel anytime

Questions developers ask first

Approval, keys, costs and replies — the short answers.

No. There is no business verification, no app review and no message template to get approved. You link your number by QR code and start sending.

In your account under API Tokens. Create a token, choose its permissions and copy it. The instance id is shown on the instance in your dashboard.

No. You pay a flat monthly price per connected number, whether you send ten messages or ten thousand.

Yes. The send-media action takes a public URL or base64 content — images, video, audio, voice notes and documents such as PDF or Office files.

Set a webhook URL on your instance. Incoming messages are posted to it as JSON, signed with your webhook secret.

Your own. Messages go out from the number you linked to the instance, so recipients see your name and profile picture as usual.

Your first message is one request away

3-day free trial · No credit card · Cancel anytime.