Developers

PongIQ ships a free, read-only public API and broadcast-ready overlay pages. No API key needed for now — please be nice: cache on your side where you can, poll no faster than the data changes, and expect rate limits to arrive later. Only published tournaments are exposed; drafts, emails and account data are never served.

API v1

Base URL is this site’s origin. Every response uses the envelope { "data": …, "meta": { "generatedAt": … } }, is cached for ~10 s (s-maxage=10, stale-while-revalidate=30) and 404s as JSON for unknown ids. The /v1 prefix is versioned — breaking changes will ship as /v2, never silently.

EndpointReturns
GET /api/v1/eventsPublished events list
GET /api/v1/events/{slug}One event + its categories
GET /api/v1/subevents/{id}/drawLocked knockout bracket + results (null until published)
GET /api/v1/subevents/{id}/standingsLive group standings (ITTF 3.7.5, computed on read)
GET /api/v1/subevents/{id}/matchesEvery match of a category with scores
GET /api/v1/matches/{id}One match with live score + pre-match prediction
GET /api/v1/rankingsPublic rating leaderboard
Sample — GET /api/v1/events
{
  "data": {
    "events": [
      {
        "id": "c3c98379-0cc1-40cb-863f-8f56548918f7",
        "slug": "uncle-cheng-friendly-king-of-pips-2-season-10",
        "name": "Uncle Cheng Friendly King of Pips 2 - Season 10",
        "venue": "V2TT",
        "startDate": "2026-10-04",
        "endDate": "2026-10-04",
        "status": "published",
        "description": "Open to all players. This is a single event. Only pips player can play this event. Requirement one of rubber must be pips. Or you can have 2 rubbers pips. Or one pips one smooth rubber.",
        "posterUrl": null,
        "categoryCount": 2
      }
    ]
  },
  "meta": {
    "generatedAt": "2026-10-02T00:44:24.350Z"
  }
}
Sample — GET /api/v1/rankings
{
  "data": {
    "rankings": [
      {
        "rank": 1,
        "playerId": "45fb0b5d-c22e-4594-b800-052629f43361",
        "displayName": "Uncle Cheng",
        "rating": 999,
        "matchCount": 18,
        "association": "Uncle Cheng Table Tennis Group"
      },
      {
        "rank": 2,
        "playerId": "f9aa46e1-2043-4f6f-8177-bd76692d2824",
        "displayName": "Weiming",
        "rating": 912,
        "matchCount": 3,
        "association": "Taman Jurong Table Tennis Club"
      },
      {
        "rank": 3,
        "playerId": "1ac3dfde-0c3b-41de-806d-1276d760f60e",
        "displayName": "Lee Choon khiang",
        "rating": 908,
        "matchCount": 2,
        "association": null
      }
    ]
  },
  "meta": {
    "generatedAt": "2026-10-02T00:44:24.351Z"
  }
}
Sample — GET /api/v1/matches/{id}
{
  "data": {
    "match": {
      "id": "aa1b2c3d-0000-4000-8000-000000000002",
      "status": "in_progress",
      "matchKind": "tournament",
      "stage": "knockout",
      "roundNo": 3,
      "bracketPos": 0,
      "stageLabel": "Final",
      "homeName": "Ada Loop",
      "awayName": "Bruno Net",
      "homeId": "e-home",
      "awayId": "e-away",
      "winnerId": null,
      "resultReasonCode": null,
      "retirementAfterGame": null,
      "gamesWon": {
        "home": 1,
        "away": 1
      },
      "currentGame": {
        "home": 7,
        "away": 5
      },
      "gameScores": [
        {
          "home": 11,
          "away": 8
        },
        {
          "home": 9,
          "away": 11
        }
      ],
      "startedAt": "2026-09-05T03:00:00.000Z",
      "completedAt": null,
      "updatedAt": "2026-09-05T03:24:00.000Z"
    },
    "live": {
      "started": true,
      "gamesWon": {
        "home": 1,
        "away": 1
      },
      "currentGame": {
        "home": 7,
        "away": 5
      },
      "currentServer": "home",
      "bestOf": 5
    },
    "prediction": {
      "pHome": 0.64,
      "ciLow": 0.55,
      "ciHigh": 0.72,
      "confidenceLabel": "medium",
      "createdAt": "2026-09-05T02:00:00Z",
      "outcome": null
    }
  },
  "meta": {
    "generatedAt": "2026-10-02T00:44:24.352Z"
  }
}

CORS

All /api/v1 responses carry Access-Control-Allow-Origin: *, so you can call them straight from a browser on any site — club pages, widgets, stream dashboards. There is no auth and no cookie involved, which is what makes the wildcard safe.

Honest predictions

Match payloads include the frozen pre-match prediction with its interval and confidence label. If you display the probability, display the confidence with it — and when the label is insufficient, show words, not a number.

OBS / stream overlays

Transparent scoreboard pages built for OBS, vMix and Streamlabs browser sources. They poll the live score once a second and never paint errors over your stream.

  1. In OBS: Sources → + → Browser.
  2. URL: /overlay/match/{matchId} (the id from any match page URL, /m/{matchId}). Width/height: your canvas, e.g. 1920×1080.
  3. The page background is transparent — no chroma key. Position the source; the bar sits centred on the chosen edge.
ParamValuesMeaning
posbottom (default) · topEdge the scoreboard hugs
scale0.8–1.5Size multiplier (CSS transform)
debug1Checkerboard behind the bar for testing

/overlay/subevent/{subeventId} is a compact category board (live matches + latest results) for a picture-in-picture corner — same params, same transparent ground.

Venue TV mode

Point a hall TV or projector at /t/uncle-cheng-friendly-king-of-pips-2-season-10/tv — full-screen, no interaction needed. It rotates every ~12 seconds between the live board, latest results, group standings and a “scan to follow” QR code. Pairs with the printable /t/{event-slug}/poster.

Questions or an integration that needs more? Ask via the organiser of your event — the API grows with real use cases.