> ## Documentation Index
> Fetch the complete documentation index at: https://docs.keebai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# GET /v1/messages/bulk/:broadcastId

> Returns the status and counters of a bulk send: total, sent, failed, and pending.

Returns the status of a broadcast created by [`POST /v1/messages/bulk`](/dev/endpoints/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

```bash theme={null}
curl https://api.keebai.com/v1/messages/bulk/65f4b5c6d7e8f9a0b1c2d3e4 \
  -H "Authorization: Bearer kbai_pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```

## Response

### 200 OK

```json theme={null}
{
  "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`.

```json theme={null}
{
  "error": {
    "code": "INVALID_BROADCAST_ID",
    "message": "Bad Request"
  }
}
```

### 404 Not Found

The broadcast does not exist or belongs to another tenant.

```json theme={null}
{
  "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:

```python theme={null}
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")
```

<Tip>
  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.
</Tip>


## OpenAPI

````yaml GET /v1/messages/bulk/{broadcastId}
openapi: 3.0.0
info:
  title: Keebai Public API
  description: Superficie pública REST autenticada con Personal Access Tokens (PAT).
  version: 1.0.0
  contact: {}
servers:
  - url: https://api.keebai.com
    description: Production
security: []
tags: []
paths:
  /v1/messages/bulk/{broadcastId}:
    get:
      tags:
        - messages
      summary: Status of a previously scheduled bulk broadcast
      operationId: PublicMessagesController_getBulkStatus
      parameters:
        - name: broadcastId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BulkStatusResponse'
      security:
        - PAT: []
components:
  schemas:
    BulkStatusResponse:
      type: object
      properties:
        broadcast_id:
          type: string
        status:
          type: string
        total_recipients:
          type: number
        sent:
          type: number
        failed:
          type: number
        pending:
          type: number
        started_at:
          type: string
          nullable: true
        completed_at:
          type: string
          nullable: true
      required:
        - broadcast_id
        - status
        - total_recipients
        - sent
        - failed
        - pending
  securitySchemes:
    PAT:
      scheme: bearer
      bearerFormat: kbai_pk_<hex>
      type: http
      description: >-
        Personal Access Token con prefijo `kbai_pk_`. Generar desde el portal
        con permiso `developer.manage_tokens`.

````