# Limits

Source: https://kirora.io/docs/api/limits

Look up every fixed limit of the API in one table, with what each returns when you exceed it.

Every fixed limit of the `/v1` API in one place: what it is, what you get when you go
over it, and where it is explained. Each operation's reference page states its own
limits too.

A value over a field limit is refused with 400 `VALIDATION_ERROR`, with the field in
`errors` and, for a size or count, its `maximum`. Nothing in the request is
applied. A body over its route's size is refused with 413 `PAYLOAD_TOO_LARGE` before
it is read. Plan limits, such as emails a month or contacts, are not listed here: they
depend on the workspace owner's plan and are refused with 403 `PLAN_LIMIT_REACHED`
(see [Errors](https://kirora.io/docs/api/reference/errors)).

## Request size

A body larger than its route takes is refused whole with 413 `PAYLOAD_TOO_LARGE`; on the
routes below with a limit of their own, the message states it. Every batch at its
documented item limits fits.

| Route | Largest body |
| --- | --- |
| `POST /v1/transactional/messages` | 15 MiB (10 MB of attachments, base64-encoded) |
| `POST /v1/transactional/messages/batch` | 10 MiB |
| `POST /v1/campaigns/{id}/trigger` | 10 MiB |
| `POST /v1/events/batch`, `POST /v1/contacts/batch`, `POST /v1/suppressions` | 4 MiB |
| Every other `/v1` route | 1 MiB |

Uploads (`multipart/form-data`) have their own file limits, stated on each route and in
the table below.

## Batches and bulk calls

| Limit | Value | Over it | Guide |
| --- | --- | --- | --- |
| Contacts in `identifyContactsBatch` | 100 | 400 `VALIDATION_ERROR` | [Batches](https://kirora.io/docs/api/batches) |
| Events in `trackEventsBatch` | 100 | 400 `VALIDATION_ERROR` | [Batches](https://kirora.io/docs/api/batches) |
| Messages in `sendTransactionalMessagesBatch` | 100 | 400 `VALIDATION_ERROR` | [Batches](https://kirora.io/docs/api/batches) |
| Recipients of `triggerCampaign` | 5,000 | 400 `VALIDATION_ERROR` | [Transactional messages](https://kirora.io/docs/api/transactional#marketing-to-a-list-you-choose) |
| Contacts in `addListContacts` or `deleteListContacts` | 500 | 400 `VALIDATION_ERROR` | [Rate limits](https://kirora.io/docs/api/reference/rate-limits#staying-under-the-limit) |
| Contacts in `deleteContacts` | 100 | 400 `VALIDATION_ERROR` | API reference |
| Addresses in `addSuppressions` | 10,000 | 400 `VALIDATION_ERROR` | API reference |
| Addresses in `deleteSuppressions` | 100 | 400 `VALIDATION_ERROR` | API reference |
| Calls in one Segment request | 500 calls and 500 KB; 32 KB a call | 413 over 500 KB, 400 over 500 calls; a call over 32 KB is listed in `refused` | [Sending data from Segment](https://kirora.io/docs/api/segment) |

## Events

| Limit | Value | Over it | Guide |
| --- | --- | --- | --- |
| Distinct event names per workspace | 1,000 (Kirora's own `contact.*` events not counted) | 409 `EVENT_NAME_LIMIT_REACHED` | [Events](https://kirora.io/docs/api/events#event-names) |
| Event name | 128 characters, no control characters | 400 `VALIDATION_ERROR` | [Events](https://kirora.io/docs/api/events#event-names) |
| Property name | 64 characters | 400 `VALIDATION_ERROR` | [Events](https://kirora.io/docs/api/events#property-names) |
| `properties` | 16 KB as JSON | 400 `VALIDATION_ERROR` | [Events](https://kirora.io/docs/api/events#property-names) |
| `products` in an event's properties | 250 lines | 400 `VALIDATION_ERROR` | [E-commerce events](https://kirora.io/docs/api/ecommerce-events) |
| `occurredAt` | At most 24 hours in the future | 400 `VALIDATION_ERROR` | [Events](https://kirora.io/docs/api/events) |
| Age of an event that starts a journey | 72 hours after `occurredAt` | Recorded, but starts no journey | [Events](https://kirora.io/docs/api/events) |

## Contacts

| Limit | Value | Over it | Guide |
| --- | --- | --- | --- |
| `externalId` | 255 characters | 400 `VALIDATION_ERROR` | [IDs and identity](https://kirora.io/docs/api/identifiers) |
| Custom attributes | 50 keys and 16 KB in all; a key up to 64 characters | 400 `VALIDATION_ERROR` | API reference |
| Topic choices in one identify | 50 | 400 `VALIDATION_ERROR` | API reference |
| `meta.count` on `listContacts` and `listSegmentContacts` | Counted up to 10,000 | `meta.countExact` is `false` | [Pagination](https://kirora.io/docs/api/reference/pagination#there-is-no-total-except-on-contacts-and-segment-contacts) |

## Sending

| Limit | Value | Over it | Guide |
| --- | --- | --- | --- |
| Attachments on a transactional email | 10 files, 10 MB in all once decoded | 400 `VALIDATION_ERROR` | [Transactional messages](https://kirora.io/docs/api/transactional#attachments) |
| Attachments in a transactional batch | None allowed | That entry is refused with `VALIDATION_ERROR` | [Transactional messages](https://kirora.io/docs/api/transactional#attachments) |
| `sendAt` on a transactional send | In the future, at most 90 days ahead | 400 `VALIDATION_ERROR` at `sendAt` | [Transactional messages](https://kirora.io/docs/api/transactional#sending-later) |
| A campaign's `schedule` | A fixed `sendAt` in the future; a local `localDateTime` still to come in one time zone | 400 `INVALID_SCHEDULE` | [Campaigns and journeys](https://kirora.io/docs/api/campaigns-and-journeys) |
| A campaign's `delivery.expiresAfter` | 1 to 720 hours; longer than `abTest.decideAfter` | 400 `VALIDATION_ERROR` | [Campaigns and journeys](https://kirora.io/docs/api/campaigns-and-journeys#4-slow-it-down-or-let-it-expire) |
| A campaign's `delivery.rateLimit.recipients` | 1 to 1,000,000 per minute, hour or day | 400 `VALIDATION_ERROR` | [Campaigns and journeys](https://kirora.io/docs/api/campaigns-and-journeys#4-slow-it-down-or-let-it-expire) |
| A campaign's `abTest.decideAfter` | 1 to 72 hours, or `null` to pick by hand | 400 `VALIDATION_ERROR` | [Campaigns and journeys](https://kirora.io/docs/api/campaigns-and-journeys) |
| Email subject | 998 characters | 400 `VALIDATION_ERROR` | API reference |
| Campaign audience sent at once | 5,000; a larger one, or an A/B test, is queued in the background | Not refused: the campaign reads `sending` until queued | [Campaigns and journeys](https://kirora.io/docs/api/campaigns-and-journeys) |

## Audiences, journeys and templates

| Limit | Value | Over it | Guide |
| --- | --- | --- | --- |
| Rules or groups in one segment group | 20 | 400 `VALIDATION_ERROR` | [Objects and rules](https://kirora.io/docs/api/models#segment-rules) |
| Group nesting | Two levels: the top group and groups inside it | 400 `VALIDATION_ERROR` | [Objects and rules](https://kirora.io/docs/api/models#segment-rules) |
| Segments referring to segments | 3 levels deep, 1,000 rules once written out in full | 400 `VALIDATION_ERROR` at `rules` | [Objects and rules](https://kirora.io/docs/api/models#segment-rules) |
| Values in an `in` rule | 100 | 400 `VALIDATION_ERROR` | [Objects and rules](https://kirora.io/docs/api/models#segment-rules) |
| Property conditions (`where`) on an event rule | 5 | 400 `VALIDATION_ERROR` | [Objects and rules](https://kirora.io/docs/api/models#segment-rules) |
| Journey steps | 50 | 400 `VALIDATION_ERROR` | [Objects and rules](https://kirora.io/docs/api/models#journey-definitions) |
| Journey exit conditions | 10 | 400 `VALIDATION_ERROR` | [Objects and rules](https://kirora.io/docs/api/models#journey-definitions) |
| Paths in a `split` step; in a `random_split` step | 5; 2 to 5 | 400 `VALIDATION_ERROR` | [Objects and rules](https://kirora.io/docs/api/models#journey-definitions) |
| Template blocks | 200 across the whole tree, five levels deep | 400 `VALIDATION_ERROR` | [Objects and rules](https://kirora.io/docs/api/models#template-documents) |
| Template `alias` | 64 characters | 400 `VALIDATION_ERROR` | [Objects and rules](https://kirora.io/docs/api/models#template-documents) |
| Image upload or copy (`uploadAsset`, `importAsset`) | One PNG, JPEG or GIF, 2 MB and 25 megapixels | 413 `PAYLOAD_TOO_LARGE` over 2 MB on upload; 400 `VALIDATION_ERROR` otherwise | API reference |

## Keys, sessions and webhooks

| Limit | Value | Over it | Guide |
| --- | --- | --- | --- |
| `allowedIps` on a key | 50 addresses or CIDR ranges | Refused when you save the key | [Authentication](https://kirora.io/docs/api/reference/authentication#restricting-a-key-to-your-servers) |
| `allowedOrigins` on a partner key | 20 origins | Refused when you save the key | [Choose an integration](https://kirora.io/docs/api/integrations) |
| Rotation overlap | Up to 168 hours (7 days) | Refused when you rotate | [Authentication](https://kirora.io/docs/api/reference/authentication#handling-keys) |
| Embed session | Valid 15 minutes | 401 `UNAUTHORIZED` once expired | [Choose an integration](https://kirora.io/docs/api/integrations) |
| `testSendTo` on an embed session | 5 addresses | 400 `VALIDATION_ERROR` | [Choose an integration](https://kirora.io/docs/api/integrations) |
| User token for the web SDK | Valid at most 24 hours | 401 from the SDK's endpoints | [Web SDK](https://kirora.io/docs/sdks/web) |
| Webhook URL | HTTPS, public host, 2,000 characters | 400 `VALIDATION_ERROR` | [Webhooks](https://kirora.io/docs/api/webhooks) |
| Webhook response time | 5 seconds | Counted as a failed delivery and retried | [Webhooks](https://kirora.io/docs/api/webhooks#delivery-and-retries) |

## Reading lists and reports

| Limit | Value | Over it | Guide |
| --- | --- | --- | --- |
| `limit` | Default 20, maximum 100 | 400 `VALIDATION_ERROR` | [Pagination](https://kirora.io/docs/api/reference/pagination) |
| `query` | 64 characters on tags; 100 on contacts and messages; 128 on the event catalogue and attributes; 200 on campaigns, templates, segments, a contact's segments, journeys, forms and assets; 255 on lists; 320 on the event log and suppressions | 400 `VALIDATION_ERROR` | [Pagination](https://kirora.io/docs/api/reference/pagination#the-parameters) |
| Report window (`from`, `to`) | Ends no later than the end of today, at most 366 days; without them, the last 30 days ending today | 400 `VALIDATION_ERROR` | [Pagination](https://kirora.io/docs/api/reference/pagination#date-ranges) |
| Campaign series | 72 hours of hourly points or 366 days of daily ones | Later events fall outside the series | [Pagination](https://kirora.io/docs/api/reference/pagination#date-ranges) |
| Campaign breakdown | The 50 largest rows; the rest summed in `other` | Not refused | [Campaigns and journeys](https://kirora.io/docs/api/campaigns-and-journeys#5-read-the-results) |
| `listCampaignReports`, `getFunnelReport` | A plan with advanced reports | 403 `PLAN_FEATURE_UNAVAILABLE` | [Errors](https://kirora.io/docs/api/reference/errors) |

## Retries and rates

| Limit | Value | Over it | Guide |
| --- | --- | --- | --- |
| `Idempotency-Key` | 8 to 255 characters; a key is kept 24 hours (a trigger's, as long as its campaign) | 400 `VALIDATION_ERROR` | [Idempotency](https://kirora.io/docs/api/reference/idempotency) |
| Requests per minute | Per key and per workspace, in three budgets | 429 `RATE_LIMITED` with `Retry-After` | [Rate limits](https://kirora.io/docs/api/reference/rate-limits) |

## Next steps

- [Rate limits](https://kirora.io/docs/api/reference/rate-limits) — the request budgets and how to back off
- [Batches and asynchronous results](https://kirora.io/docs/api/batches) — send up to 100 items in one call
- [Errors](https://kirora.io/docs/api/reference/errors) — every code these limits return