Reading events back#
All read endpoints require events:read and are POST.
Your private key holds events:read already. Your public key does not, by default: it
can be granted from the key card on your dashboard, but everything on this page is then
readable by anyone who extracts the key from your game build. Prefer calling these from your
server.
| Endpoint | Answers |
|---|---|
/events/names |
Which events exist, with labels and volume. |
/events/trends |
Up to 8 events on one timeline, with previous-period comparison. |
/events/counts |
Totals for a set of events over a window — /events/trends without the per-day buckets. |
/events/user |
Per-event count and last-seen for one player. |
/events/users |
Top or most-recent players for one event. |
/events/leaderboard |
Top N by count, plus a given player's own rank. |
/events/list |
Raw event search, paginated. |
/events/funnel |
Step-by-step conversion, drop-off, and time between steps. |
Full request and response schemas for each are in the API reference, which can fire them live.
/events/counts#
Takes names (max 8) plus the usual period arguments and returns totals only:
{
"range": { "…the standard range object…" },
"totals": [
{ "name": "killed_boss", "label": "Boss defeated", "name_id": 12,
"event_count": 18420, "previous_event_count": 15330, "delta_pct": 20.16,
"unique_actors": 4820 }
],
"truncated": false
}
/events/user#
The one most integrations want.
{ "partner_user_id": "A1B2C3", "page": 1 }
{
"user": { "partner_user_id": "A1B2C3", "gamearly_user_id": "8a8c…", "linked": true,
"first_seen_at": "…", "last_seen_at": "…" },
"events": [
{ "name": { "id": 12, "name": "killed_boss", "label": "Boss defeated" },
"total_count": 47, "occurrence_count": 47, "trusted_count": 0,
"first_at": "…", "last_at": "…", "last_value": null, "value_sum": 0.0,
"duration_ms_total": 0 }
],
"events_info": { "total": 1, "pages": 1, "page": 1 }
}
Rate limits#
Ingest has its own, far higher limits than the rest of this API, tuned per project:
| Limit | Default |
|---|---|
| Requests per minute | 600 |
| Events per minute | 60,000 |
| Events per minute, per player | 600 |
| Events per batch | 200 |
| Distinct event names | 500 |
count per event |
1,000 |
All of these can be raised for your project — ask us. The read endpoints stay under the standard API limit.
Over a limit, the affected events come back in throttled, not as an error, and the
response carries retry_after in seconds. A 429 with a Retry-After header is possible from
the edge; back off and resend.
Anti-cheat, honestly#
What we do: cap count and value, cap events per player per minute, let you lock the
event catalog so unknown names are refused, separate trusted from untrusted events, and clamp
quest progress to the task target so volume cannot buy more than one completion. Suspicious
volume is flagged for a human, never auto-blocked — a false positive would strip a paying
player's progress.
What we do not do, and why: per-install request signing, device attestation and offline replay protection all rely on material that ships inside your game, so a determined client defeats them while the honest ones pay the complexity. If you need an event to be authoritative, send it from your server with your private key, which marks it trusted.
Recommended integration#
- Emit telemetry from the game client with your public key (
gak_pub_…). Batch, retry onthrottled, reuse ids. - Send anything that grants a reward from your server with your private key
(
gak_prv_…), then turn on "require trusted events for quests". - Call
/events/identifywhen an anonymous player signs in. - Let account linking handle the rest — past events are attributed automatically.