Crossdeck Docs
Dashboard

Limits & quotas

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

EndpointSustained rateBurst capacityToken cost
POST /v1/events100 / s1,000Batch size — 1 event = 1 token
POST /v1/purchases10 / s1001 per request
POST /v1/entitlements100 / s1,0001 per request
POST /v1/heartbeat1 / s101 per request
POST /v1/identity/forget1 / s101 per request
POST /v1/alias10 / s1001 per request

One additional velocity check sits on top of the /v1/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.

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.

SurfaceLimitOn violation
/v1/events — batch size1–100 events per requestReject (400)
/v1/eventsproperties8 KB serialized JSON per eventReject (400)
/v1/eventsname1–128 chars, A–Z a–z 0–9 _ . - :Reject (400)
/v1/eventseventId1–64 chars, A–Z a–z 0–9 _ -Reject (400)
/v1/events — identity hintsAt least one of developerUserId (1–256 chars), anonymousId (1–128 chars), crossdeckCustomerId (cdcust_…) per event; multiple allowed, each present hint validatedReject (400)
/v1/eventstags32 keys per event; keys ≤ 64 chars (a–z 0–9 _ -); values ≤ 64 charsSoft-drop bad entries
/v1/eventscategoryTags16 entries per event; ≤ 32 chars each; lowercased and deduplicated on ingestSoft-drop bad entries
/v1/aliastraits32 top-level keys; keys ≤ 64 chars; values primitive only — strings over 1,024 chars truncated, nested objects and arrays droppedSoft-drop / truncate
/v1/migration — user import1,000 rows per requestReject (400)
/v1/releases/sourcemaps — files100 source maps per requestReject (400)
/v1/releases/sourcemaps — map size10 MB per .map filePer-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.

BoundWebNodeSwiftAndroidReact Native
Default batch size2020202020
Default flush interval2,000 ms2,000 ms2,000 ms2,000 ms2,000 ms
Queue cap (events)1,0001,0001,0001,0001,000
Queue durabilityDurable (localStorage), replayed on next bootIn-memory; drained on shutdown signals, 2,000 ms boundedDurable (disk), rehydrated on launchDurable (disk), rehydrated on launchDurable (persisted store), trimmed to 1,000 on restore
String value truncation1,024 chars1,024 chars1,024 chars1,024 chars1,024 chars
Properties byte cap (client)8 KB8 KB8 KB
Max nesting depth5532325

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.

CapWebNodeReact Native
Reports per fingerprint per minute555
Reports per session (all fingerprints)100100100

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:

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 for the full contract.