Two keys per project, different permissions:
| Key | Header | Can |
|---|---|---|
| API Key (public) | X-API-Key: pk_abc123 |
Write events, identify users |
| API Secret (private) | Authorization: Bearer sk_abc123 |
Read/query data |
The API Key is safe to embed in client-side JS. It can only write, never read.
Track a single event.
{
"event": "subscription-update",
"user_id": "user@email.com",
"properties": {
"plan": "premium",
"mrr": 99
},
"timestamp": "2026-03-15T10:30:00Z",
"idempotency_key": "uuid-optional"
}event— required, stringuser_id— optional, string (your app's user identifier)properties— optional, object (any key/value pairs)timestamp— optional, ISO 8601 (defaults to server time)idempotency_key— optional, string (for dedup)
Response: 202 Accepted
{ "status": "accepted" }Track multiple events in one request. Same fields as /track, wrapped in an array.
{
"events": [
{ "event": "page-view", "user_id": "user@email.com", "properties": { "page": "/quotes" } },
{ "event": "quote-created", "user_id": "user@email.com", "properties": { "value": 5000 } }
]
}Response: 202 Accepted
{ "status": "accepted", "received": 2 }Max 100 events per batch.
Set or update properties on a user profile.
{
"user_id": "user@email.com",
"properties": {
"name": "Jane Doe",
"plan": "premium",
"company": "Acme Corp",
"role": "admin"
}
}Properties are merged (not replaced). To remove a property, set it to null.
Response: 202 Accepted
{ "status": "accepted" }Link an anonymous ID to an identified user.
{
"anonymous_id": "anon_abc123",
"user_id": "user@email.com"
}This triggers a background job to re-link all events from the anonymous profile to the identified user.
Response: 202 Accepted
{ "status": "accepted" }Time series of event counts.
Params:
event— required, event namefrom— required, ISO 8601 dateto— required, ISO 8601 dategranularity— optional,day(default) ormonth
Response:
{
"event": "subscription-update",
"granularity": "day",
"data": [
{ "date": "2026-03-01", "count": 12 },
{ "date": "2026-03-02", "count": 8 },
{ "date": "2026-03-03", "count": 15 }
]
}Leaderboard of most frequent events.
Params:
from— required, ISO 8601 dateto— required, ISO 8601 datelimit— optional, default 10
Response:
{
"data": [
{ "event": "page-view", "count": 1240 },
{ "event": "quote-created", "count": 89 },
{ "event": "subscription-update", "count": 34 }
]
}Activity history for a specific user.
Params:
user_id— required, the user's external IDfrom— optional, ISO 8601 dateto— optional, ISO 8601 datelimit— optional, default 50
Response:
{
"user_id": "user@email.com",
"data": [
{ "event": "subscription-update", "properties": { "plan": "premium" }, "timestamp": "2026-03-15T10:30:00Z" },
{ "event": "quote-created", "properties": { "value": 5000 }, "timestamp": "2026-03-15T09:15:00Z" }
]
}All errors return a consistent format:
{ "error": "description of what went wrong" }| Status | Meaning |
|---|---|
| 400 | Bad request (missing required fields) |
| 401 | Invalid or missing API key/secret |
| 422 | Validation error (e.g. batch > 100 events) |
| 429 | Rate limited |