Skip to main content
AvailableVerified against 76c1f85Markdown

Read KPIs

The KPI API returns metrics about the players linked to you, computed by PlayerPal from your activity, your facts and your Links. Every endpoint is a read-only GET under /v1/kpis. Your organisation is taken from your credential: there is no party parameter, and you can never query another partner's numbers. Metric definitions are in Metrics.

You will
  • Discover the KPIs your party type can read
  • Read one KPI as a series and break it down
  • Compare yourself with the network and anonymous peers
  • Handle suppressed cells and errors
You need
  • A key ID and secret, or an OAuth client, with the kpis.read scope
  • A request signer, as in the Quickstart
  • Some delivered activity or facts
Time
About 20 minutes
For
Operators and game providers

Endpoint map​

The catalogue and own-tier KPIs serve operators and game providers. The overview also serves affiliates.

GroupEndpointUse it when
Own KPIsGET /v1/kpis/catalogueYou need the KPI IDs your party type can read
GET /v1/kpis/ownCharting one metric by day, week or month
GET /v1/kpis/breakdownRanking markets, games or counterparts for one metric
GET /v1/kpis/overviewShowing every overview KPI with availability and trend in one call
ComparisonGET /v1/kpis/compareComparing your players with the rest of the PlayerPal network
GET /v1/kpis/compare/summaryEvery comparison KPI for a period in one call
GET /v1/kpis/benchmarksComparing your totals with anonymous peers of your type
GET /v1/kpis/cohorts/retentionMonthly first-play cohorts and a pooled network curve
GET /v1/kpis/funnels/linkSeeing how Links turn into play
InsightsGET /v1/kpis/intelligence/insights, /insights/{id}Ranked, explained changes and opportunities
GET /v1/kpis/intelligence/ask, /suggestionsMapping a short question to a fixed analysis
ExploreGET /v1/kpis/explore/*Building detailed analytics views with filters

Every KPI call is recorded in PlayerPal's audit log with your organisation and client. Each endpoint's parameters are in the API reference.

1List your catalogue

GET/v1/kpis/catalogueKPI catalogue for your party typekpis.readReference →

Start here. The catalogue lists the own-tier KPIs your party type can read, with each KPI's unit, the grains it supports, whether it takes a market filter (markets) and whether the account floor applies (account_floor).

KPIUnitWho
own.rounds, own.active_accountscountOperator, provider
own.bet_eur, own.win_eur, own.ggrEUROperator, provider
own.linked_players, own.new_links, own.unlinkscountOperator, provider
own.link_intents, own.link_conversioncount, ratioOperator
own.points_issuedpointsOperator, provider
own.ingest_acceptanceratioOperator, provider
own.batch_latency_p95secondsProvider
GET /v1/kpis/catalogue
playerpal.sh
HEADERS=(); while IFS= read -r h; do HEADERS+=("$h"); done < <(sign GET /v1/kpis/catalogue "" "")
curl -sS "$API/v1/kpis/catalogue" -H "${HEADERS[0]}" -H "${HEADERS[1]}" -H "${HEADERS[2]}"
# 200 {"kpis": [{id, label, unit, ...}]}
Response200
{
"kpis": [
{ "id": "own.rounds", "label": "Rounds", "unit": "count",
"markets": true, "grains": ["day", "week", "month"],
"account_floor": true },
{ "id": "own.new_links", "label": "New links", "unit": "count",
"markets": false, "grains": ["day", "week", "month"],
"account_floor": false }
]
}

2Read one KPI

GET/v1/kpis/ownOne of your own KPIs over a periodkpis.readReference →

own returns one KPI as a series, with the total, the previous period and the change between them.

  • from and to are YYYY-MM-DD, UTC and inclusive, and the range must be under 400 days.
  • grain is day (default), week (ISO weeks from Monday) or month.
  • market is ISO 3166-1 alpha-2, for example MT, and works only for KPIs with markets: true.

Every period in the range is returned. A period with no activity is 0, or null for ratios and latencies. previous is the period of equal length just before from, and previous_series aligns it with series for overlays. delta_pct is null when either total is missing or the previous total is 0.

Query parameters
  • kpistring
    KPI id from the catalogue.
  • fromstring
    Start date (YYYY-MM-DD).
  • tostring
    End date (YYYY-MM-DD).
  • grainstring
    Time grain.
  • marketstring
    Filter: market.
Checkpoint

You get 200 with one series entry for every week in the range. A week backed by too few accounts shows "suppressed": true.

GET /v1/kpis/own
playerpal.sh
QUERY='from=2026-09-01&grain=week&kpi=own.rounds&to=2026-09-30' # already sorted and encoded
HEADERS=(); while IFS= read -r h; do HEADERS+=("$h"); done < <(sign GET /v1/kpis/own "$QUERY" "")
curl -sS "$API/v1/kpis/own?$QUERY" -H "${HEADERS[0]}" -H "${HEADERS[1]}" -H "${HEADERS[2]}"
Response200
{
"kpi": "own.rounds", "label": "Rounds", "unit": "count",
"from": "2026-09-01", "to": "2026-09-30", "grain": "week",
"market": null,
"series": [
{ "period": "2026-08-31", "value": 1240 },
{ "period": "2026-09-07", "value": null, "suppressed": true },
{ "period": "2026-09-14", "value": 1310 },
{ "period": "2026-09-21", "value": 1290 },
{ "period": "2026-09-28", "value": 1280 }
],
"total": { "value": 5160 },
"previous": { "from": "2026-08-02", "to": "2026-08-31", "value": 4800 },
"previous_series": [
{ "value": 610 }, { "value": 1150 }, { "value": 1180 },
{ "value": 1220 }, { "value": 1240 }, { "value": 1200 }
],
"delta_pct": 0.075,
"generated_at": "2026-10-13T12:00:00.000Z"
}

3Break it down

GET/v1/kpis/breakdownOne KPI by market, game or partnerkpis.readReference →

breakdown takes the same parameters as own, except grain, plus dimension: market, game or partner. A partner is one of your own integration counterparts: a studio if you are an operator, an operator if you are a studio. Breakdowns support activity KPIs only; others return 400 breakdown_not_supported.

The response holds the top 10 rows by value. For additive KPIs, each row has its share of the visible total and the remaining rows are folded into one Other (n) row. Rows under the account floor are never listed; they are counted in suppressed_rows.

Query parameters
  • kpistring
    KPI id from the catalogue.
  • dimensionstring
    Breakdown dimension.
    One of: marketgamepartner
  • fromstring
    Start date (YYYY-MM-DD).
  • tostring
    End date (YYYY-MM-DD).
  • marketstring
    Filter: market.
GET /v1/kpis/breakdown
playerpal.sh
QUERY='dimension=market&from=2026-09-01&kpi=own.rounds&to=2026-09-30' # already sorted and encoded
HEADERS=(); while IFS= read -r h; do HEADERS+=("$h"); done < <(sign GET /v1/kpis/breakdown "$QUERY" "")
curl -sS "$API/v1/kpis/breakdown?$QUERY" -H "${HEADERS[0]}" -H "${HEADERS[1]}" -H "${HEADERS[2]}"
Response200
{
"kpi": "own.rounds", "label": "Rounds", "unit": "count",
"dimension": "market",
"from": "2026-09-01", "to": "2026-09-30", "market": null,
"rows": [
{ "key": "MT", "label": "MT", "value": 3100, "share": 0.6055 },
{ "key": "DE", "label": "DE", "value": 1400, "share": 0.2734 },
{ "key": "FI", "label": "FI", "value": 620, "share": 0.1211 }
],
"suppressed_rows": 2
}

4Compare with the network

GET/v1/kpis/compareYour players vs the rest of the PlayerPal networkkpis.readReference →

compare takes a cmp.* KPI and a closed period: grain=month with period=2026-09, or grain=week with period=2026-W38. An open or future period returns 400 period_not_closed.

  • you is your players' median for per-player KPIs, or your ratio for party KPIs.
  • index is you ÷ network median × 100, and band is the network quartile your value falls in.
  • Network size and party counts are given only as bands, never exact numbers.
  • With a market, a market too thin to publish falls back to the global figure with fallback: "global".

The network side is published only when the rest of the network spans at least 5 parties of your type and 50 accounts, and no single party holds more than 50% of the measure (or two more than 75%).

GET/v1/kpis/benchmarkskpis.readReference →

benchmarks compares your totals with anonymous peers of your type, using the same period, grain and market. It takes own.rounds, own.bet_eur, own.ggr or own.active_accounts (global or per market), or own.linked_players, own.ingest_acceptance or own.link_conversion (global only). A segment is published only with at least 6 parties in it, you plus 5 others, and no dominant peer.

GET /v1/kpis/compare?grain=month&kpi=cmp.bet_per_player&period=2026-09200
{
"kpi": "cmp.bet_per_player", "period": "2026-09", "grain": "month",
"from": "2026-09-01", "to": "2026-09-30",
"market": null, "fallback": null,
"you": 45,
"network": { "baseline": "rest_of_network",
"p25": 20, "p50": 36, "p75": 52, "mean": 38.4,
"size_band": "100-999", "parties_band": "5-9" },
"index": 125, "band": "p50-p75",
"suppressed": false, "reason": null,
"computed_at": "2026-10-01T06:00:00.000Z"
}
GET /v1/kpis/benchmarks?grain=month&kpi=own.bet_eur&period=2026-09200
{
"kpi": "own.bet_eur", "period": "2026-09", "grain": "month",
"from": "2026-09-01", "to": "2026-09-30",
"market": null, "fallback": null,
"peers": { "party_type": "OPERATOR", "count_band": "5-9",
"p25": 1200, "p50": 1800, "p75": 2600, "mean": 1950 },
"band": "p0-p25", "suppressed": false, "reason": null,
"computed_at": "2026-10-01T06:00:00.000Z"
}

5Handle suppression and errors

PlayerPal withholds any cell that describes too few people, and the rule is the same for every caller. See Privacy thresholds.

  • Own KPIs and breakdowns: for KPIs with account_floor: true, a cell backed by fewer than 50 distinct Accounts comes back as "value": null, "suppressed": true. Show it as withheld, never as zero.
  • Comparisons and benchmarks: a withheld value has suppressed: true and a reason.
reasonMeaning
peer_floorThe rest of the network spans fewer than 5 parties of your type
account_floorThe rest of the network has fewer than 50 accounts
dominanceOne party has more than 50% of the measure, or two have more than 75%
y_floorYour own population is under 50 accounts; the network is still shown
elsewhere_floorYour players' activity elsewhere spans too few parties, or one dominates it
not_computedNo data for that period yet
StatusCodes
400kpi_unknown, kpi_not_available_for_party, date_range_invalid, date_range_too_long, grain_invalid, market_invalid, market_not_supported, dimension_invalid, breakdown_not_supported, month_range_invalid, month_range_too_long, period_not_closed, filter_invalid
403partner_scope_missing: the credential lacks kpis.read
404Codes ending in _not_found, such as insight_not_found
502, 503kpi_backend_error, kpi_backend_not_configured. Retry later
A comparison withheld200
{
"kpi": "cmp.bet_per_player", "period": "2026-09", "grain": "month",
"from": "2026-09-01", "to": "2026-09-30",
"market": null, "fallback": null,
"you": 45, "network": null,
"index": null, "band": null,
"suppressed": true, "reason": "peer_floor",
"computed_at": "2026-10-01T06:00:00.000Z"
}
Range too long400
{
"statusCode": 400,
"message": "date_range_too_long",
"error": "Bad Request"
}

Overview, cohorts, insights and Explore​

Overview. GET /v1/kpis/overview?from=…&to=… returns every overview KPI for your audience, each with value, trend, source, lastUpdated and a dataAvailability that is never faked as zero: available, requires_feed (with a requirement naming the missing feed), not_applicable (for example commercial KPIs for studios) or suppressed. It also returns chart panels, each with its own availability. Operators see their own players, studios see players of their games, and affiliates see only their verified-attributed players.

Cohorts and funnels. /cohorts/retention takes from and to as YYYY-MM, defaults to the last 12 closed months and allows at most 24. /funnels/link takes YYYY-MM-DD dates; the whole funnel is suppressed when its first step is under the account floor.

Insights. /intelligence/insights returns ranked insights and an executive summary from fixed rules and statistical tests. No language model computes or writes them. /intelligence/ask takes q (up to 300 characters are used) and routes it to one fixed analysis; it never runs free-form queries. See Insights.

Explore. /v1/kpis/explore/options, performance, summary, dimension (with dim=game, market or partner), players, retention, segments, funnel, game, comparison and benchmark-history back detailed analytics views. Each takes from and to, plus optional game, market and partner filters. Call explore/options first: it lists only filter values with at least 50 players. A malformed filter, or a partner that is not one of your counterparts, returns 400 filter_invalid.