API Docs
How to connect an instance, send WhatsApp messages and check their status through the API.
https://www.instamessage.in/api/v1
Getting started
Sign up free, create an instance, and scan its QR code with WhatsApp on your phone until its status shows Connected.
Open the connected instance's page and generate an API credential. You'll be
shown an instance_id and an access_token — the token is shown
once, so copy it somewhere safe immediately.
Call the API below with that instance_id and access_token.
Authentication
Send your access token as a Bearer token in the Authorization header:
Authorization: Bearer YOUR_ACCESS_TOKEN
- Tokens are tied to one instance. The
instance_idin the request body must match the token. - A revoked or unknown token gets a
401. - Never share your access token or commit it to a public repository.
Protect your WhatsApp number
WhatsApp can restrict or ban a number that looks like it's spamming — and we can't undo that.
- Only message people who gave you their number and expect to hear from you.
- Send messages people actually want: order updates, OTPs, appointment reminders, replies.
- Keep sending volumes steady — build up gradually on a new number.
- Personalise messages (name, order number) instead of sending one identical text to everyone.
- Stop immediately when someone asks you to, and make it easy to ask.
- Test with a number you can afford to lose.
- Send bulk promotional or marketing blasts to bought or scraped lists.
- Send hundreds of messages in a sudden burst from a fresh number.
- Message people who never contacted you or opted in.
- Send the exact same message and link to large numbers of people.
- Keep messaging numbers that don't reply or have blocked you.
You are responsible for everything sent from your number — see section 4 of our Terms. This service does not offer, and will not build, anything designed to get around WhatsApp's limits.
Endpoints
/api/v1/messages/sendSend a text, image, video, audio, voice note or document
30 / min
GET
/api/v1/messages/{message_id}Check a sent message's status
60 / min
POST
/api/v1/numbers/checkCheck if numbers are on WhatsApp (up to 20 per request)
10 / min
Rate limits are per access token and counted separately for each endpoint.
/api/v1/messages/send
Send a message (text or media)
Request body
| Field | Type | Description |
|---|---|---|
instance_id |
string (UUID) | Your instance's ID. Must match the token used. |
to |
string |
Recipient's phone number, digits only with country code (e.g. 919876543210), 7-15 digits.
Country code first, then the number: no
+, no spaces, no dashes, no leading 0.
For example India +91 98765 43210 → 919876543210, USA +1 (249) 989-3170 → 12499893170.
|
type optional |
string |
text (default), image, video, audio,
voice or document. Leave it out to send plain text.
|
message |
string |
The text, up to 4096 characters. Required for text.
For image, video and document it's an optional caption;
it's ignored for audio and voice.
|
media_url media only |
string (URL) |
A public http(s) link to the file. We download it, check it, and send it.
Every type except text needs either media_url or media (not both).
|
media media only |
file |
Upload the file directly instead of giving a link — send the request as
multipart/form-data (see the example below). Max upload on this server:
2M.
|
file_name optional |
string | For document: the name the recipient sees, e.g. Invoice-42.pdf. Defaults to the name in the URL. |
Media rules
type | Accepted files | Max size |
|---|---|---|
image | JPG, PNG, WebP | 5 MB |
video | MP4, 3GP | 16 MB |
audio | MP3, OGG, M4A, AAC, AMR — sent as a music/audio file | 16 MB |
voice | OGG (Opus) only — shows as a playable voice note | 16 MB |
document | Any file (PDF, Word, Excel, ZIP, …) | 100 MB |
The file type is checked from the file's actual content, not its name. The link must be publicly reachable (no login) and respond within 30 seconds.
Responses
| Status | Body | Meaning |
|---|---|---|
| 200 | {"success": true, "message_id": "...", "sent_via": "device", "fallback_status": null} |
Message sent from your linked number. |
| 200 | {"success": true, "message_id": "wamid...", "sent_via": "cloud_api", "fallback_status": "sent"} |
Your linked number couldn't send it, so it was sent through your Cloud API fallback. |
| 401 | — | Missing, unknown, or revoked access token. |
| 422 | {"success": false, "error": "instance_id does not match this token"} |
The body's instance_id doesn't belong to this token. |
| 422 | {"success": false, "error": "Instance is not connected"} |
The instance isn't currently connected to WhatsApp (and no Cloud API fallback is set up for this message). |
| 422 | {"success": false, "error": "You've reached your plan's monthly message limit. Upgrade to send more."} |
Your plan's monthly message quota is used up. |
| 422 | {"success": false, "error": "The file at media_url is too large (max 5 MB for this type)."} |
media_url couldn't be used: not reachable, not public, too large, empty, or the wrong
file type for type. The error says which.
|
| 502 | {"success": false, "error": "Could not send message", "fallback_status": null} |
The message could not be delivered (e.g. invalid number). fallback_status is
"failed" if the Cloud API fallback was tried and failed too, otherwise null.
|
Optional: Cloud API fallback
On your instance page you can add your own WhatsApp Business Cloud API phone number ID and
access token. Then, if a text message can't be sent from your linked number (including while the
instance is disconnected), it is retried through the Cloud API. Fallback messages come from your Cloud API
business number, and Meta bills them to your Meta account. Meta only accepts free-form text to people who messaged
that business number in the last 24 hours; other fallback sends end up as "fallback_status": "failed".
Rate limit: 30 requests per minute per access token. Code samples in 6 languages are below.
Example: send a PDF with a caption
curl -X POST https://www.instamessage.in/api/v1/messages/send \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"instance_id": "YOUR_INSTANCE_ID",
"to": "919876543210",
"type": "document",
"media_url": "https://example.com/files/invoice-42.pdf",
"file_name": "Invoice-42.pdf",
"message": "Here is your invoice"
}'
Example: upload a photo from your computer
curl -X POST https://www.instamessage.in/api/v1/messages/send \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Accept: application/json" \
-F "instance_id=YOUR_INSTANCE_ID" \
-F "to=919876543210" \
-F "type=image" \
-F "message=Here is the photo" \
-F "media=@/path/to/photo.jpg"
/api/v1/messages/{message_id}
Check message status
Request
No body. Put the message_id returned by POST /api/v1/messages/send in the URL
and use the same access token. A token can only look up messages sent from its own instance.
Responses
| Status | Body | Meaning |
|---|---|---|
| 200 | {"success": true, "message": {...}} |
Message found (full example below). |
| 401 | — | Missing, unknown, or revoked access token. |
| 404 | {"success": false, "error": "Message not found"} |
No message with this ID was sent from this token's instance. |
Example request
curl https://www.instamessage.in/api/v1/messages/MESSAGE_ID \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
status values
- pendingbeing sent right now.
- senthanded over to WhatsApp successfully (✓).
- deliveredreached the recipient's phone (✓✓).
delivered_atis set. - readthe recipient opened it (blue ✓✓).
read_atis set. - failedcould not be sent from your linked number. If
fallback_statusis"sent", it went out through your Cloud API fallback instead (sent_viais"cloud_api").
read only appears if the recipient has read receipts turned on in WhatsApp.
Want updates pushed to you instead of polling? Set a webhook on your instance — you'll get a
message.status event each time a message is delivered or read.
Example response
{
"success": true,
"message": {
"message_id": "3EB0A1B2C3D4E5F6",
"instance_id": "YOUR_INSTANCE_ID",
"direction": "outgoing",
"type": "text",
"to": "919876543210",
"status": "read",
"sent_via": "device",
"fallback_status": null,
"delivered_at": "2026-09-24T10:15:05+00:00",
"read_at": "2026-09-24T10:17:41+00:00",
"created_at": "2026-09-24T10:15:03+00:00",
"updated_at": "2026-09-24T10:15:04+00:00"
}
}
Rate limit: 60 requests per minute per access token.
/api/v1/numbers/check
Check if numbers are on WhatsApp
Check before you send, so you don't waste a message on a number without WhatsApp. Nothing is sent, and it doesn't count toward your monthly message limit. The instance must be connected.
Request body (JSON)
| Field | Type | Description |
|---|---|---|
instance_id |
string (UUID) | Your instance's ID. Must match the token used. |
numbers |
array of strings |
1 to 20 phone numbers, same format as to when sending:
country code first, digits only (e.g. 919876543210).
|
Responses
| Status | Body | Meaning |
|---|---|---|
| 200 | {"success": true, "results": [...]} |
One result per number (example below). |
| 401 | — | Missing, unknown, or revoked access token. |
| 422 | {"success": false, "error": "..."} |
Invalid numbers, more than 20, wrong instance_id, or instance not connected. |
| 429 | — | Too many requests; wait a minute. |
| 502 | {"success": false, "error": "Could not check numbers right now"} |
WhatsApp couldn't be asked right now; try again shortly. |
Example request
curl -X POST https://www.instamessage.in/api/v1/numbers/check \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"instance_id": "YOUR_INSTANCE_ID",
"numbers": ["919876543210", "12499793168"]
}'
whatsapp_number is the number as WhatsApp knows it. It's usually the same as
number, but WhatsApp adjusts some countries' numbers. It's null when the
number isn't on WhatsApp.
Example response
{
"success": true,
"results": [
{ "number": "919876543210", "on_whatsapp": true, "whatsapp_number": "919876543210" },
{ "number": "12499793168", "on_whatsapp": false, "whatsapp_number": null }
]
}
Rate limit: 10 requests per minute per access token (up to 20 numbers each). It's meant for checking real recipients, not for scanning lists of numbers.
Code samples: send a text message
curl -X POST https://www.instamessage.in/api/v1/messages/send \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"instance_id": "YOUR_INSTANCE_ID",
"to": "919876543210",
"message": "Hello from InstaMessage!"
}'
const response = await fetch("https://www.instamessage.in/api/v1/messages/send", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_ACCESS_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({
instance_id: "YOUR_INSTANCE_ID",
to: "919876543210",
message: "Hello from InstaMessage!",
}),
});
const data = await response.json();
console.log(data);
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => 'https://www.instamessage.in/api/v1/messages/send',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode([
'instance_id' => 'YOUR_INSTANCE_ID',
'to' => '919876543210',
'message' => 'Hello from InstaMessage!',
]),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Accept: application/json',
'Authorization: Bearer YOUR_ACCESS_TOKEN',
],
]);
$response = curl_exec($curl);
if ($response === false) {
// Couldn't reach the API at all (DNS, timeout, SSL, ...).
echo 'Connection error: ' . curl_error($curl);
} else {
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$data = json_decode($response, true);
if ($status === 200 && ! empty($data['success'])) {
echo 'Sent! Message ID: ' . $data['message_id'];
} else {
echo "Failed (HTTP $status): " . ($data['error'] ?? $response);
}
}
Uses PHP's built-in curl extension (enabled on almost every host) — no Composer package needed.
import requests
response = requests.post(
"https://www.instamessage.in/api/v1/messages/send",
headers={"Authorization": "Bearer YOUR_ACCESS_TOKEN"},
json={
"instance_id": "YOUR_INSTANCE_ID",
"to": "919876543210",
"message": "Hello from InstaMessage!",
},
)
data = response.json()
print(data)
Needs the requests package (pip install requests).
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "YOUR_ACCESS_TOKEN");
var payload = new
{
instance_id = "YOUR_INSTANCE_ID",
to = "919876543210",
message = "Hello from InstaMessage!"
};
var content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://www.instamessage.in/api/v1/messages/send", content);
var data = await response.Content.ReadAsStringAsync();
Console.WriteLine(data);
Uses only the .NET base class library (System.Text.Json) — no extra NuGet package needed.
HttpClient client = HttpClient.newHttpClient();
String body = "{"
+ "\"instance_id\":\"YOUR_INSTANCE_ID\","
+ "\"to\":\"919876543210\","
+ "\"message\":\"Hello from InstaMessage!\""
+ "}";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://www.instamessage.in/api/v1/messages/send"))
.header("Authorization", "Bearer YOUR_ACCESS_TOKEN")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
Uses java.net.http.HttpClient, built into the JDK since Java 11 — no extra dependency.