SETTO API

Paginación

Cómo funcionan limit y offset en GET /v1/external/tournaments, qué contiene el sobre meta, y por qué los demás endpoints de listado devuelven todo de una vez.

Exactamente un endpoint pagina: GET /v1/external/tournaments. Toma limit y offset, devuelve sus filas bajo data y reporta la ventana bajo meta. Los otros dos endpoints de listado — categorías y equipos — devuelven un arreglo JSON pelón con todas las filas de ese torneo, porque ambos están acotados por cuántas categorías e inscripciones cabe en un solo torneo.

El sobre

GET /v1/external/tournaments?limit=2&offset=0
{
  "data": [
    {
      "idx": "3c9b7f52-6d41-4a8e-b1f0-2e7d5c9a4b18",
      "name": "Torneo Apertura 2026"
    },
    { "idx": "a41e08d7-33b2-4c96-8f5a-6b0d2e1c7934", "name": "Liga Otoño 2026" }
  ],
  "meta": { "limit": 2, "offset": 0, "total": 37 }
}
CampoSignificado
dataLas filas de esta ventana
meta.limitEl tamaño de página que se aplicó realmente
meta.offsetEl offset que se aplicó realmente
meta.totalTotal de filas que cumplen los filtros, ignorando limit y offset

Parámetros

ParámetroTipoPor defectoRango
limitentero251100
offsetentero00 o mayor

Los valores fuera de rango son un 400, no un ajuste automático: limit=0, limit=101 y offset=-1 fallan la validación. Los no enteros también fallan.

Orden

Los torneos regresan ordenados por startDate descendente — la temporada más reciente primero. El orden es estable entre páginas para un conjunto de datos fijo.

Recorrer todas las páginas

Detente cuando hayas juntado meta.total filas, o cuando una página regrese corta.

async function listAllTournaments(token, params = {}) {
  const BASE = 'https://api.setto.io/v1';
  const limit = 100;
  const all = [];
  let offset = 0;
  let total = Infinity;

  while (offset < total) {
    const query = new URLSearchParams({
      ...params,
      limit: String(limit),
      offset: String(offset),
    });
    const res = await fetch(`${BASE}/external/tournaments?${query}`, {
      headers: { Authorization: `Bearer ${token}` },
    });
    if (!res.ok) throw new Error(`SETTO API ${res.status}`);

    const { data, meta } = await res.json();
    all.push(...data);
    total = meta.total;
    offset += limit;

    if (data.length === 0) break; // defensivo: nunca dar vueltas para siempre
  }

  return all;
}
def list_all_tournaments(token, **params):
    BASE = "https://api.setto.io/v1"
    headers = {"Authorization": f"Bearer {token}"}
    limit, offset, total, out = 100, 0, None, []

    while total is None or offset < total:
        res = requests.get(
            f"{BASE}/external/tournaments",
            headers=headers,
            params={**params, "limit": limit, "offset": offset},
            timeout=10,
        )
        res.raise_for_status()
        body = res.json()
        if not body["data"]:
            break
        out.extend(body["data"])
        total = body["meta"]["total"]
        offset += limit

    return out

Filtra antes de paginar

status y sport se aplican antes de la ventana, así que meta.total refleja el conjunto ya filtrado. Acotar con status=IN_PROGRESS casi siempre sale más barato que paginar por un archivo histórico.

Deriva del offset

La paginación por offset lee una lista en movimiento. Si se crea un torneo, o se edita su startDate, mientras estás entre páginas, una fila puede cruzar el límite de página y devolverse dos veces o saltarse.

Para una exportación larga:

  • pagina con limit=100 para cruzar menos fronteras;
  • elimina duplicados por idx conforme acumulas;
  • o fija la ventana con un filtro que no cambie bajo tus pies, como status=COMPLETED.

No trates offset como un cursor que puedas persistir entre ejecuciones — vuelve a correr el recorrido desde offset=0 cada vez.

Los otros listados

GET /v1/external/tournaments/{idx}/divisions y GET /v1/external/tournaments/{idx}/teams no toman limit ni offset y devuelven un arreglo JSON directo — sin sobre data/meta:

GET /v1/external/tournaments/{idx}/divisions
[
  {
    "idx": "7b2f4d16-9c83-4e50-a7d1-3f6c8b204e59",
    "name": "Cuarta Fuerza Varonil",
    "number": 1
  },
  {
    "idx": "c58a1e93-0b74-42df-96e8-5a3d7f1b28c0",
    "name": "Segunda Fuerza Femenil",
    "number": 2
  }
]

Las categorías vienen ordenadas por number ascendente. Los equipos vienen ordenados por seed ascendente con los no sembrados al final, y luego por name.

Si un torneo muy grande vuelve incómodos esos payloads, acota la llamada de equipos con el filtro division?division={divisionIdx} — en lugar de pedir un tamaño de página que no existe.

En esta página