SETTO API

Populate

How the populate query parameter embeds relations, the exact allow-list per endpoint, the defaults each endpoint applies, and the rules for nested paths.

Relations are opt-in. By default each endpoint returns its own columns plus a small set of relations; anything else you ask for with populate, a comma-separated list of allow-listed paths. A relation key is absent from the JSON entirely unless it was populated — so 'divisions' in tournament is a reliable test for "did I ask for this?", and an unknown path is a 400 rather than a silently ignored parameter.

Syntax

GET /v1/external/tournaments/{idx}?populate=club,divisions,divisions.rounds
  • Comma-separated, no spaces.
  • Order does not matter.
  • Repeating a value is harmless.
  • A value outside the endpoint's allow-list is a 400 — see Errors.

populate replaces the default, it does not extend it

The moment you send a populate parameter, the endpoint's default list is gone. GET /v1/external/tournaments/{idx} returns club and divisions when you send nothing; ?populate=divisions returns divisions and no club. If you want both, say both.

Nested paths need their parents

Paths are dotted: draws.rounds.games.score.sets. You do not have to list the ancestors — the API expands them for you, so populate=draws.rounds.games.score.sets implies draws, draws.rounds, draws.rounds.games and draws.rounds.games.score. Listing them explicitly changes nothing.

The reverse is not true: populating a parent does not bring its children. populate=games gives you games whose homeTeam and score keys are missing; you need games.homeTeam and games.score for those.

Per-endpoint allow-lists

GET /v1/external/tournaments

List rows stay deliberately shallow — this is the one endpoint that paginates, so a fat row multiplies by 100.

AllowedDefault
club(none)

GET /v1/external/tournaments/{idx}

AllowedDefault
club, club.courts, divisions, divisions.rounds, sponsors, circuit, circuitCategory, rankedTiebreakRulesclub,divisions

GET /v1/external/tournaments/{idx}/divisions

AllowedDefault
rounds, teams, teams.playerProfiles, teams.leagueTeam, circuitCategoryrounds

GET /v1/external/tournaments/{idx}/teams

AllowedDefault
playerProfiles, captainProfile, leagueTeam, divisionplayerProfiles,division

GET /v1/external/rounds/{idx}

The richest endpoint, and the one whose default is worth knowing by heart.

Allowed:

division
pools
pools.teams
pools.standings
pools.standings.team
pools.standings.teamStats
games
games.homeTeam
games.awayTeam
games.winner
games.court
games.score
games.score.sets
draws
draws.rounds
draws.rounds.games
draws.rounds.games.homeTeam
draws.rounds.games.awayTeam
draws.rounds.games.winner
draws.rounds.games.court
draws.rounds.games.score
draws.rounds.games.score.sets

Default when you send no populate:

division
pools
pools.teams
games
games.homeTeam
games.awayTeam
games.winner
draws.rounds
draws.rounds.games
draws.rounds.games.homeTeam
draws.rounds.games.awayTeam
draws.rounds.games.winner
draws.rounds.games.score
draws.rounds.games.score.sets

The default gives bracket scores but not flat-list scores

Note the asymmetry: the default populates draws.rounds.games.score (+ sets) but not games.score. If you are reading results off the flat games array, ask for games.score,games.score.sets explicitly — and remember that doing so replaces the whole default list, so re-list everything else you need.

Populate keys are not always response keys

Three paths embed under a different name than the one you request:

You populateIt appears as
playerProfiles (or teams.playerProfiles)players: [{ idx, firstName, lastName, avatar }]
captainProfilecaptainProfileIdx: string | null
pools.standings.teamStatsstandings[].teamStats

Worked examples

A public draw sheet for one knockout round — brackets, winners and scores, no group tables:

curl -s -G https://api-production-ea80.up.railway.app/v1/external/rounds/2e7f5b10-8c94-4d63-a1b8-7f0e3d6c2a95 \
  -H "Authorization: Bearer $SETTO_API_TOKEN" \
  -d populate=draws.rounds.games.homeTeam,draws.rounds.games.awayTeam,draws.rounds.games.winner,draws.rounds.games.score.sets

A group-stage standings table:

curl -s -G https://api-production-ea80.up.railway.app/v1/external/rounds/9d3c6a84-7e15-4b02-8f6d-1c4a9e7b53f2 \
  -H "Authorization: Bearer $SETTO_API_TOKEN" \
  -d populate=pools.standings.team,pools.standings.teamStats

A full category listing with every registered pair and its players:

curl -s -G https://api-production-ea80.up.railway.app/v1/external/tournaments/3c9b7f52-6d41-4a8e-b1f0-2e7d5c9a4b18/divisions \
  -H "Authorization: Bearer $SETTO_API_TOKEN" \
  -d populate=teams,teams.playerProfiles

Cost

Each populated path is more joins and more rows. Three habits keep responses fast:

  1. Ask for the leaves you render, nothing else. pools.standings.teamStats without pools.standings.team is a legal, cheaper request if you already know the teams.
  2. Do not populate on the list endpoint unless you need the club. Fetch the ids from the list, then fetch the one tournament you are rendering.
  3. Cache. Round payloads change only when a score is entered. Combine a short TTL with conditional requests and one round page costs you a handful of requests an hour instead of one per visitor.

On this page