cURL (Windows)
curl "https://api.bulkneo.com/v1/instances/YOUR_INSTANCE_ID/qr-status" -H "apikey: YOUR_API_KEY"const options = {method: 'GET', headers: {apikey: '<api-key>'}};
fetch('https://api.bulkneo.com/v1/instances/{id}/qr-status', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.bulkneo.com/v1/instances/{id}/qr-status"
headers = {"apikey": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text){
"success": true,
"data": {
"instance_id": "3f9c1a2b-7d4e-4c81-9f0a-2b6d5e8c1a34",
"label": "Sales",
"status": "connecting",
"connected": false,
"phone_number": null,
"qrcode": {
"base64": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...",
"code": "2@Xj4kR...",
"pairingCode": null
},
"rotation_count": 2,
"rotation_limit": 6,
"exhausted": false
},
"request_id": "b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
}{
"success": false,
"error": {
"code": "invalid_request",
"message": "Field \"text\" is required and must be a non-empty string."
},
"request_id": "b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
}{
"success": false,
"error": {
"code": "invalid_api_key",
"message": "Invalid or revoked API key."
},
"request_id": "b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
}{
"success": false,
"error": {
"code": "trial_quota_exceeded",
"message": "Trial message quota reached (500 messages). Upgrade to a paid plan to keep sending.",
"details": {
"trial_message_limit": 500
}
},
"request_id": "b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
}{
"success": false,
"error": {
"code": "instance_not_found",
"message": "Instance not found. Check the instance_id and that it belongs to your account."
},
"request_id": "b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
}{
"success": false,
"error": {
"code": "number_already_connected",
"message": "This WhatsApp number is already connected on another of your instances. Disconnect it there first, or scan a different number."
},
"request_id": "b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
}{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded, retry in 12s.",
"details": {
"retry_after_seconds": 12,
"limit_per_minute": 20,
"scope": "messages"
}
},
"request_id": "b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
}{
"success": false,
"error": {
"code": "internal_error",
"message": "An unexpected error occurred."
},
"request_id": "b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
}{
"success": false,
"error": {
"code": "upstream_unavailable",
"message": "The messaging service is temporarily unavailable. Please retry shortly."
},
"request_id": "b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
}Instances
Poll while someone scans
GET /v1/instances/{id}/qr-status — current QR, rotation count, and whether the scan has landed.
GET
/
v1
/
instances
/
{id}
/
qr-status
cURL (Windows)
curl "https://api.bulkneo.com/v1/instances/YOUR_INSTANCE_ID/qr-status" -H "apikey: YOUR_API_KEY"const options = {method: 'GET', headers: {apikey: '<api-key>'}};
fetch('https://api.bulkneo.com/v1/instances/{id}/qr-status', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.bulkneo.com/v1/instances/{id}/qr-status"
headers = {"apikey": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text){
"success": true,
"data": {
"instance_id": "3f9c1a2b-7d4e-4c81-9f0a-2b6d5e8c1a34",
"label": "Sales",
"status": "connecting",
"connected": false,
"phone_number": null,
"qrcode": {
"base64": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...",
"code": "2@Xj4kR...",
"pairingCode": null
},
"rotation_count": 2,
"rotation_limit": 6,
"exhausted": false
},
"request_id": "b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
}{
"success": false,
"error": {
"code": "invalid_request",
"message": "Field \"text\" is required and must be a non-empty string."
},
"request_id": "b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
}{
"success": false,
"error": {
"code": "invalid_api_key",
"message": "Invalid or revoked API key."
},
"request_id": "b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
}{
"success": false,
"error": {
"code": "trial_quota_exceeded",
"message": "Trial message quota reached (500 messages). Upgrade to a paid plan to keep sending.",
"details": {
"trial_message_limit": 500
}
},
"request_id": "b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
}{
"success": false,
"error": {
"code": "instance_not_found",
"message": "Instance not found. Check the instance_id and that it belongs to your account."
},
"request_id": "b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
}{
"success": false,
"error": {
"code": "number_already_connected",
"message": "This WhatsApp number is already connected on another of your instances. Disconnect it there first, or scan a different number."
},
"request_id": "b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
}{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded, retry in 12s.",
"details": {
"retry_after_seconds": 12,
"limit_per_minute": 20,
"scope": "messages"
}
},
"request_id": "b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
}{
"success": false,
"error": {
"code": "internal_error",
"message": "An unexpected error occurred."
},
"request_id": "b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
}{
"success": false,
"error": {
"code": "upstream_unavailable",
"message": "The messaging service is temporarily unavailable. Please retry shortly."
},
"request_id": "b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
}The playground on this page is live. There is no test mode and no sandbox: pressing Send authenticates with the key you paste and really calls
https://api.bulkneo.com — a send delivers a real WhatsApp message to the number you type, and an instance call really changes your account. Use your own number while you are exploring, and paste a key only from a machine you trust.Your browser remembers what you type into the playground, so a key you paste here is still in the Authorization box on your next visit. Nobody else sees it — it never leaves your machine and it is not part of this site — but on a shared or borrowed computer, clear the site data when you are done.How to use it
1
Poll about every 2.5 seconds
The instance endpoints allow 120 requests a minute, which is comfortable for this.
2
Refresh the QR you are showing
Each response carries the current QR — the code rotates while nobody scans it, so keep repainting from the latest response.
3
Stop when connected is true
The number is paired and ready to send.
phone_number tells you which number was linked.4
Start over if exhausted is true
The pairing attempt has run out of QR codes and no further code can connect. Call
GET /v1/instances/{id}/qr to begin a new attempt.Fields worth knowing
integer | null
How many QR codes this pairing attempt has issued so far.
integer
How many it may issue before the attempt is exhausted.
boolean
true once the attempt is spent. No QR is returned in that case — showing the last one would be showing a code that can never pair.boolean
Present and
true when the live check could not be completed on this poll. Not an error — keep polling. It means “we could not confirm anything this tick”, so keep the QR on screen rather than showing a failure.string
Diagnostic only — which source reported the connection. Safe to ignore.
Example
A single poll:curl https://api.bulkneo.com/v1/instances/YOUR_INSTANCE_ID/qr-status \
-H "apikey: YOUR_API_KEY"
curl "https://api.bulkneo.com/v1/instances/YOUR_INSTANCE_ID/qr-status" -H "apikey: YOUR_API_KEY"
Node.js — poll until connected
async function waitForScan(instanceId, { timeoutMs = 120_000 } = {}) {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
const res = await fetch(
`https://api.bulkneo.com/v1/instances/${instanceId}/qr-status`,
{ headers: { apikey: process.env.BULKNEO_API_KEY } },
);
const { data } = await res.json();
if (data.connected) return data.phone_number;
if (data.exhausted) throw new Error("QR expired — start a new pairing attempt");
if (data.qrcode?.base64) repaintQr(data.qrcode.base64);
await new Promise((r) => setTimeout(r, 2500));
}
throw new Error("nobody scanned it in time");
}
If the same WhatsApp number is already connected on another of your instances, this returns
409 number_already_connected. The session already running keeps working — disconnect it there first, or scan a different number.Authorizations
Your API key, sent in an apikey request header. Never in the URL, never as a Bearer token.
Path Parameters
The instance_id of one of your numbers.

