# Notify batch

`POST` `https://api.pushwoosh.com/messaging/v2/notify/batch`

Sends several messages in one call. Every item is an independent [`Notify`](/developer/api-reference/messaging-api-v2/notify/): it is validated exactly like a standalone `Notify` request, and it gets its own result.

<Aside type="caution" title="Read the results, not the status code">
The call itself returns HTTP 200 as long as the batch envelope is valid — even if every single item failed. A failing item never fails the whole request. Always check `results[]` and the `succeeded` / `failed` counters; do not treat a 200 response as confirmation that messages were sent.
</Aside>

<Aside type="tip" title="This is not a faster way to send">
`NotifyBatch` does not send messages faster than calling `Notify` directly. If your integration can already issue requests concurrently, running `Notify` in parallel threads/workers gives the same throughput — HTTP/2 handles that by design. Use `NotifyBatch` for convenience when your integration is not built for concurrency, not for speed.
</Aside>

## Request structure

```json title="Shape"
{
  "items": [
    {
      "item_id": "caller-defined-id",
      "request": { ... }
    }
  ]
}
```

| Field | Type | Description |
|---|---|---|
| `items` | array of `NotifyBatchItem` | Required, 1 to 500 entries (server-configured limit). Items are processed concurrently; results come back in request order regardless. |

### NotifyBatchItem

| Field | Type | Description |
|---|---|---|
| `item_id` | string | Optional caller-supplied correlation id, echoed back on the matching result. Not used by Pushwoosh otherwise. |
| `request` | [`NotifyRequest`](/developer/api-reference/messaging-api-v2/notify/#request-structure) | Same body as a single `Notify` call — `segment` or `transactional`, `transaction_id` included. Required. |

<Aside type="note" title="schedule is still required per item">
Each item's `request` needs its own `schedule`, exactly like a standalone `Notify` call. Omitting it does not fall back to "send now" — the item fails with an `EmptySchedule` error.
</Aside>

<Aside type="tip" title="Retrying a batch is safe">
Set `transaction_id` on each item's `request`. A repeat call with the same `transaction_id` returns the same `message_code` for that item within the 5-minute deduplication window described on the [`Notify`](/developer/api-reference/messaging-api-v2/notify/#request-structure) page — so retrying the whole batch after a network error does not duplicate the messages that already went out.
</Aside>

### Example

```bash
curl -X POST https://api.pushwoosh.com/messaging/v2/notify/batch \
  -H "Authorization: Token YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {
        "item_id": "order-1001",
        "request": {
          "transactional": {
            "application": "XXXXX-XXXXX",
            "platforms": ["IOS", "ANDROID"],
            "users": { "list": ["user-123"] },
            "payload": {
              "content": {
                "localized_content": {
                  "en": { "ios": { "body": "Your order has shipped." } }
                }
              }
            },
            "schedule": { "at": "2026-05-01T12:00:00Z" },
            "message_type": "MESSAGE_TYPE_TRANSACTIONAL",
            "transaction_id": "order-1001-shipped"
          }
        }
      },
      {
        "item_id": "order-1002",
        "request": {
          "transactional": {
            "application": "XXXXX-XXXXX",
            "platforms": ["IOS", "ANDROID"],
            "users": { "list": ["user-456"] },
            "payload": {
              "content": {
                "localized_content": {
                  "en": { "ios": { "body": "Your order has shipped." } }
                }
              }
            },
            "schedule": { "at": "2026-05-01T12:00:00Z" },
            "message_type": "MESSAGE_TYPE_TRANSACTIONAL",
            "transaction_id": "order-1002-shipped"
          }
        }
      }
    ]
  }'
```

## Response

```json
{
  "results": [
    {
      "item_id": "order-1001",
      "index": 0,
      "result": {
        "message_code": "XXXXX-XXXXX-XXXXX",
        "unknown_identifiers": []
      }
    },
    {
      "item_id": "order-1002",
      "index": 1,
      "error": {
        "code": 3,
        "message": "invalid argument",
        "reason": "EmptySchedule",
        "domain": "api.pushwoosh.com"
      }
    }
  ],
  "succeeded": 1,
  "failed": 1
}
```

| Field | Type | Description |
|---|---|---|
| `results` | array of `NotifyBatchResult` | One entry per requested item, in request order. |
| `succeeded` | integer | Count of items that returned a result. |
| `failed` | integer | Count of items that returned an error. |

### NotifyBatchResult

| Field | Type | Description |
|---|---|---|
| `item_id` | string | Echo of the request item's `item_id`. |
| `index` | integer | Zero-based position of the item in the request. Use this to correlate results when `item_id` was left empty. |
| `result` | object | Set when the item succeeded — same shape as a single [`Notify` response](/developer/api-reference/messaging-api-v2/notify/#response): `message_code` and `unknown_identifiers`. |
| `error` | `NotifyBatchError` | Set when the item failed. |

### NotifyBatchError

| Field | Type | Description |
|---|---|---|
| `code` | integer | gRPC status code (`google.rpc.Code`), e.g. `3` for `INVALID_ARGUMENT`. |
| `message` | string | Developer-facing error message, in English. |
| `reason` | string | Short status name — the same value a single `Notify` call would return, e.g. `EmptySchedule`. |
| `domain` | string | Error domain, `api.pushwoosh.com`. |

## Errors

Errors use the standard gRPC-Gateway error envelope: `{ "code": ..., "message": ..., "details": [...] }`. These are envelope-level errors that fail the whole request — an individual item failing produces a per-item `error` in `results[]` instead (see [NotifyBatchError](#notifybatcherror) above), not one of these.

| HTTP status | Condition |
|---|---|
| `400` | `items` is empty. |
| `400` | `items` has more than the server-configured limit (500 by default). |
| `400` | One of the items is missing `request`. |

**Example**

Sending 501 items when the limit is 500 returns HTTP `400`:

```json
{
  "code": 3,
  "message": "items must not exceed 500 entries, got 501",
  "details": []
}
```

## Related

<CardGrid>
  <LinkCard title="Notify" href="/developer/api-reference/messaging-api-v2/notify/" />
  <LinkCard title="Cancel" href="/developer/api-reference/messaging-api-v2/cancel/" />
  <LinkCard title="Payload reference" href="/developer/api-reference/messaging-api-v2/payload-reference/" />
  <LinkCard title="Messaging API v2 overview" href="/developer/api-reference/messaging-api-v2/" />
  <LinkCard title="Migration from v1" href="/developer/api-reference/messaging-api-v2/migration-from-v1/" />
</CardGrid>