# Limits & quotas

> Source: https://cross-deck.com/docs/limits-and-quotas/ · Crossdeck developer docs (generated from the published page).

Reference
5 min read
·
Every operational number, one page

Every limit Crossdeck enforces, in one place: per-endpoint API rate limits, request and payload caps, the bounds each SDK applies before an event ever leaves the device, and the error-capture caps that stop a crash loop from flooding your project. Defaults are generous enough that most projects never see a 429. If you need more, per-project overrides exist — [contact support](https://cross-deck.com/docs/index.html#support) with the endpoint and the rate you need.

## API rate limits

Rate limits are token buckets, scoped **per project, per environment, per endpoint**. The bucket refills linearly at the sustained rate and holds at most the burst capacity. Each request spends tokens equal to its cost; a request that the bucket can't cover is throttled. Burst capacity accrues while you're quiet — a project idle overnight can spend its full 1,000-event burst in a single batch, as long as the sustained average stays under the rate.

Endpoint Sustained rate Burst capacity Token cost

POST /v1/events 100 / s 1,000 Batch size — 1 event = 1 token
POST /v1/purchases 10 / s 100 1 per request
POST /v1/entitlements 100 / s 1,000 1 per request
POST /v1/heartbeat 1 / s 10 1 per request
POST /v1/identity/forget 1 / s 10 1 per request
POST /v1/identity/alias 10 / s 100 1 per request

One additional velocity check sits on top of the `/v1/identity/alias` bucket: **more than 30 `identify()` calls for the same `userId` within 60 seconds** returns 429 with `retryAfterMs: 60000`. It defends against an identify loop hammering one user record; the per-endpoint bucket above still applies independently.

Limits fail open and can be raised.

If the rate-limit store is unreachable, requests are *allowed*, never dropped — Crossdeck's infrastructure misbehaving is not your problem. Defaults are per-endpoint; per-project overrides are stored server-side and raised through [support](https://cross-deck.com/docs/index.html#support).

## Request & payload caps

Independent of rate, each request body is bounded. Two enforcement modes: **reject** fails the whole request with 400 and the index of the first bad event; **soft-drop** strips the offending field (or entry) and ingests the rest, reporting what was dropped.

Surface Limit On violation

/v1/events — batch size 1–100 events per request Reject (400)
/v1/events — properties 8 KB serialized JSON per event Reject (400)
/v1/events — name 1–128 chars, A–Z a–z 0–9 _ . - : Reject (400)
/v1/events — eventId 1–64 chars, A–Z a–z 0–9 _ - Reject (400)
/v1/events — identity hints At least one of developerUserId (1–256 chars), anonymousId (1–128 chars), crossdeckCustomerId ( cdcust_… ) per event; multiple allowed, each present hint validated Reject (400)
/v1/events — tags 32 keys per event; keys ≤ 64 chars ( a–z 0–9 _ - ); values ≤ 64 chars Soft-drop bad entries
/v1/events — categoryTags 16 entries per event; ≤ 32 chars each; lowercased and deduplicated on ingest Soft-drop bad entries
/v1/identity/alias — traits 32 top-level keys; keys ≤ 64 chars; values primitive only — strings over 1,024 chars truncated, nested objects and arrays dropped Soft-drop / truncate
/v1/migration/users — user import 1,000 rows per request Reject (400)
/v1/releases/sourcemaps — files 100 source maps per request Reject (400)
/v1/releases/sourcemaps — map size 10 MB per .map file Per-file error; siblings still upload

## SDK client-side bounds

Each SDK batches, validates, and queues events before they reach the API. These are the shipped defaults — batch size and flush interval are configurable at `init`.

Bound Web Node Swift Android React Native

Default batch size 20 20 20 20 20
Default flush interval 1,500 ms 1,500 ms 2,000 ms 2,000 ms 1,500 ms
Queue cap (events) 1,000 1,000 1,000 1,000 1,000
Queue durability Durable ( localStorage ), replayed on next boot In-memory; drained on shutdown signals, 2,000 ms bounded Durable (disk), rehydrated on launch Durable (disk), rehydrated on launch Durable (persisted store), trimmed to 1,000 on restore
String value truncation 1,024 chars 1,024 chars 1,024 chars 1,024 chars 1,024 chars
Properties byte cap (client) 8 KB 8 KB — — 8 KB
Max nesting depth 5 5 32 32 5

Queue overflow evicts **oldest-first**: when the buffer exceeds 1,000 events, the oldest events are dropped and the newest 1,000 kept. Values past the depth cap are coerced to `"[depth-exceeded]"`; long strings are truncated, not rejected. Swift and Android skip the client-side 8 KB serialization check — the server's 8 KB per-event cap still applies to every SDK.

Node's queue is in-memory.

The server SDK keeps its buffer in process memory — there is no durable store to replay after a crash. On `SIGTERM` / `SIGINT` / `beforeExit` it drains the queue once, bounded at 2,000 ms by default. For clean shutdowns, `await server.flush()` before exiting.

## Error-capture caps

Automatic error capture is rate-limited on the client so a render loop or retry storm can't flood your project with identical reports.

Cap Web Node React Native

Reports per fingerprint per minute 5 5 5
Reports per session (all fingerprints) 100 100 100

Both caps are configurable per-fingerprint sampling aside (`maxPerFingerprintPerMinute`, `maxPerSession` in the error-capture config). Excess reports are dropped client-side and never sent. Swift and Android do not enforce these client caps; the server-side per-fingerprint sampling below is the backstop for every SDK.

## What happens at each limit

### 429 — fully throttled

When a request can't land at all, the response is **HTTP 429** with two headers:

- `Retry-After` — whole seconds to wait before retrying, computed from the bucket's deficit, minimum 1. SDKs honour it.

- `X-Crossdeck-Sample-Rate` — a 0.01–1.00 hint of what fraction of same-fingerprint events the client should locally sample for the next minute. Current SDKs ignore it; it ships for forward compatibility.

### Throttled batches still keep issues visible

A throttled `/v1/events` batch is not all-or-nothing. **1 error event per fingerprint per minute** is still ingested so new and ongoing issues stay visible on the dashboard; the rest are dropped and counted. Non-error events in a throttled batch are dropped. If anything landed, the response is **202** with a `throttled` block — `{ dropped, sampleRate, retryAfterMs }` — plus the headers above. Dropped events are gone; retrying them would spend the same empty bucket, so the SDK's durable queue does not re-send them.

### Reject vs soft-drop

Payload violations split two ways, per the table above. Hard caps (batch size, 8 KB properties, name / id / identity shapes) **reject the whole batch with 400** and the index of the first failing event — fix that event and retry. Decoration fields (`tags`, `categoryTags`, identify `traits`) **soft-drop**: the bad entries are stripped, the event ingests, and the response reports what was dropped.

### Fail-open

If the rate-limit store itself fails, the request is **allowed** — never throttled, never dropped. The failure is logged and alerted on Crossdeck's side. An infrastructure problem on our end never costs you events.

### 426 — PARK, don't drop

If an SDK's wire format is ever retired, the API answers **HTTP 426** naming the minimum SDK version. Conformant SDKs **PARK**: events are held in the durable on-device queue and backfill automatically once the app ships an upgraded SDK — nothing is lost. See [SDK event durability](https://cross-deck.com/docs/sdk-event-durability/index.html) for the full contract.

## Related

- [SDK event durability](https://cross-deck.com/docs/sdk-event-durability/index.html) — the durable queue, replay-on-boot, and the PARK-on-426 contract in depth.

- [Track events](https://cross-deck.com/docs/track-events/index.html) — event shape, properties, and batching from the SDK side.

- [Capture an error](https://cross-deck.com/docs/capture-an-error/index.html) — automatic and manual error capture, fingerprinting, and sampling.

- [API keys & authentication](https://cross-deck.com/docs/api-keys/index.html) — key types and which endpoints each key may call.
