Skip to main content

Base URL

All requests are HTTPS and all bodies are JSON.
If you bought BulkNeo through a reseller, your API address may be different. Your portal shows the address that applies to your account — use that one. Everything else on this site is identical.

Every request needs

string
required
Your API key. See Authentication.
string
application/json on any request with a body.
uuid
required
Which of your connected numbers to send from. Copy it from your numbers screen in the portal, or list them with GET /v1/instances. It must belong to your account.
string
required
The recipient, with country code and no leading zero — for example 919876543210. Spaces, dashes, brackets and a leading + are accepted and stripped; 6 to 15 digits must remain.Not used by POST /v1/numbers/check, which takes a numbers array instead.
Unknown extra fields in a body are ignored rather than rejected, so adding your own bookkeeping field to a request will not break it — but nothing will be done with it either.

One path per message type

There is no single /messages endpoint with a type field. Each kind of message has its own path:
On each path every field is either always required or always optional — there is no “caption is allowed unless the type is audio” matrix to learn.

Every response

Success:
Failure:
request_id is on every response either way. Log it — it is what support needs to find your request.

What a send returns

uuid
The number the message was sent from.
string
The recipient, as we normalised it.
string
Echoes the message type you sent.
string
Always sent — meaning accepted for delivery.
string | null
The message’s own id. Keep it — it is what you quote or react to later.
string | null
The delivery state at the moment of sending, for example PENDING. A snapshot, not a delivery receipt — there is no way to ask later whether a message was read.
A 200 means WhatsApp accepted the message for delivery. It does not promise the recipient received it, read it, or — for buttons and lists — that it rendered the way you designed it.

Replying to a message

Any send except a reaction accepts two optional fields that turn it into a reply:
string
The data.message_id of the message being replied to.
boolean
default:"false"
true when the quoted message is one you sent from this number. Only valid together with quoted_message_id.
See Replying to a message.

Rate limits

Per minute, per API key. There are two separate budgets, and every response says which one it is describing in X-RateLimit-Scope: Every rate-limited response also carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (seconds until your allowance goes back up), and a 429 adds Retry-After. Details on the Errors page.
If you cache a limit, cache it against its scope. X-RateLimit-Limit: 120 from an instance call is not your send allowance.

Try it from this site

Every endpoint page below has a playground. Paste your API key and a real instance_id into it and it sends a real request to the live API.
The playground is not a sandbox. There is no test mode: a send from this page sends a real WhatsApp message to the number you type. Use your own number while you are exploring.