Status of a previously scheduled bulk broadcast
curl --request GET \
--url https://api.keebai.com/v1/messages/bulk/{broadcastId} \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.keebai.com/v1/messages/bulk/{broadcastId}"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.keebai.com/v1/messages/bulk/{broadcastId}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.keebai.com/v1/messages/bulk/{broadcastId}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.keebai.com/v1/messages/bulk/{broadcastId}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.keebai.com/v1/messages/bulk/{broadcastId}")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.keebai.com/v1/messages/bulk/{broadcastId}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"broadcast_id": "<string>",
"status": "<string>",
"total_recipients": 123,
"sent": 123,
"failed": 123,
"pending": 123,
"started_at": "<string>",
"completed_at": "<string>"
}GET /v1/messages/bulk/:broadcastId
Returns the status and counters of a bulk send: total, sent, failed, and pending.
GET
/
v1
/
messages
/
bulk
/
{broadcastId}
Status of a previously scheduled bulk broadcast
curl --request GET \
--url https://api.keebai.com/v1/messages/bulk/{broadcastId} \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.keebai.com/v1/messages/bulk/{broadcastId}"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.keebai.com/v1/messages/bulk/{broadcastId}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.keebai.com/v1/messages/bulk/{broadcastId}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.keebai.com/v1/messages/bulk/{broadcastId}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.keebai.com/v1/messages/bulk/{broadcastId}")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.keebai.com/v1/messages/bulk/{broadcastId}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"broadcast_id": "<string>",
"status": "<string>",
"total_recipients": 123,
"sent": 123,
"failed": 123,
"pending": 123,
"started_at": "<string>",
"completed_at": "<string>"
}Returns the status of a broadcast created by
POST /v1/messages/bulk.
Endpoint
GET https://api.keebai.com/v1/messages/bulk/:broadcastId
Required scope
messages:bulk
Headers
| Header | Required | Value |
|---|---|---|
Authorization | Yes | Bearer kbai_pk_<token> |
Path params
| Param | Type | Description |
|---|---|---|
broadcastId | string | ObjectId of the broadcast returned when the send was created. |
Example request
curl https://api.keebai.com/v1/messages/bulk/65f4b5c6d7e8f9a0b1c2d3e4 \
-H "Authorization: Bearer kbai_pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Response
200 OK
{
"broadcast_id": "65f4b5c6d7e8f9a0b1c2d3e4",
"status": "IN_PROGRESS",
"total_recipients": 1500,
"sent": 980,
"failed": 12,
"pending": 508,
"started_at": "2026-04-25T13:45:02.123Z",
"completed_at": null
}
| Field | Type | Description |
|---|---|---|
broadcast_id | string | Same id you requested. |
status | string | PENDING, IN_PROGRESS, COMPLETED, FAILED, CANCELLED. |
total_recipients | number | Total recipients at broadcast creation. |
sent | number | Messages Meta confirmed as sent. |
failed | number | Messages that failed (rejected by Meta or dispatch error). |
pending | number | Messages still in queue. |
started_at | string | null | ISO 8601 timestamp when processing started. null if not started yet. |
completed_at | string | null | ISO 8601 timestamp when processing finished. null if still in progress. |
400 Bad Request
broadcastId is not a valid ObjectId.
{
"error": {
"code": "INVALID_BROADCAST_ID",
"message": "Bad Request"
}
}
404 Not Found
The broadcast does not exist or belongs to another tenant.{
"error": {
"code": "BROADCAST_NOT_FOUND",
"message": "Not Found"
}
}
401 / 403
Standard auth and scope errors.Polling pattern
To wait for a broadcast to finish from your integration:import os, time, requests
def wait_broadcast(broadcast_id: str, timeout: int = 600) -> dict:
start = time.monotonic()
headers = {"Authorization": f"Bearer {os.environ['KEEBAI_API_TOKEN']}"}
while time.monotonic() - start < timeout:
resp = requests.get(
f"https://api.keebai.com/v1/messages/bulk/{broadcast_id}",
headers=headers, timeout=10,
)
resp.raise_for_status()
data = resp.json()
if data["status"] in ("COMPLETED", "FAILED", "CANCELLED"):
return data
time.sleep(5)
raise TimeoutError(f"Broadcast {broadcast_id} did not finish in {timeout}s")
Every poll costs one request against your 1,000/day quota, so polling every 5s for an hour would spend 720 of it. Poll every 30s for large campaigns, and stop as soon as the status is terminal.
Authorizations
Personal Access Token con prefijo kbai_pk_. Generar desde el portal con permiso developer.manage_tokens.
Path Parameters
Was this page helpful?
⌘I