Engineering Blog

Adaptive live-sports polling under a fixed API quota.

How AceRank keeps live tennis scores and derived standings fresh while directing a finite request budget toward the matches most likely to change.

By AceRank Editorial Team8 minute readEditorial policy

I operate AceRank, a free unofficial tennis bracket and fantasy app for ATP men's tournaments. AceRank is not affiliated with the ATP, USTA, or the US Open. This article describes the live-score poller AceRank already runs without publishing vendor names, credentials, quota totals, endpoint paths, or user data.

AceRank shows live scoring from real match results. Bracket points, fantasy points, and private-group standings move when relevant match state changes. Those states come from an upstream sports API with a fixed request budget. Busy tournament weeks combine many courts in parallel, long idle gaps, and bursts of score events.

Flat polling wastes quota on courts that have not moved and can still miss the matches that just did. Polling too slowly makes standings look frozen. The useful middle ground is to spend the budget where state is changing while protecting enough capacity for the full day.

Different clocks

What has to stay fresh

The slate is not one firehose. Each kind of object changes on a different rhythm.

Live match

Set, game, and sometimes point state can move every few seconds, then sit still through a changeover.

Scheduled match

Start time and court can slip while there is no score to retrieve yet.

Completed match

The result is nearly stable, but official corrections, retirements, and walkovers still need careful handling.

Draw and order of play

These change when progression or scheduling changes, not on every point.

Derived bracket and fantasy standings are computed from ingested results. Clients read AceRank's store rather than triggering upstream requests themselves, and vendor credentials stay on the server.

Deployed policy

How the poller actually works

01

Telemetry

02

Adaptive policy

03

Ranked queue

04

Safe scoring

A scheduled worker may wake on its own clock without making a provider call every time. AceRank does not persist a per-match next_poll_at, per-match last_hash, or exponential backoff schedule. Each run considers local match state, quota usage, and recent provider-request telemetry.

on worker_tick:
  load local matches and provider-request telemetry
  policy = adaptive_policy(active_match_count, quota_usage, configured_max)

  candidates = local matches due for targeted detail
  rank candidates by overdue time, round, seed, and ranking
  fetch targeted details for top(candidates, policy.batch_limit)

  broad_interval = provider_cadence(active, imminent, tournament_state)
  if no targeted/recovery data and broad poll is not due:
    return without broad polling

  fetch broad live slate when eligible
  merge provider observations into candidate patches
  skip unchanged patches and throttle nonessential writes
  update matches and recompute affected scoring

Target eligibility interval

The base interval lengthens as more courts become active, protecting the day’s request budget during a crowded slate. Important matches can still receive a shorter target interval through round, seed, and ranking priority. Quota pressure stretches polling intervals further; it does not determine batch size.

Match prioritization

When only part of the slate can be refreshed, later rounds, higher seeds, and stronger rankings receive more weight. Overdue time is measured against the most recent recorded provider request, so a match that has waited longer can move ahead of one that was just fetched.

Dynamically limited batches

Batch size comes from active-match count and a configured per-run maximum. A broad live response may cover several matches, while capped targeted-detail requests cover selected individual events. A batch is a work limit, not necessarily one upstream call.

Provider-request telemetry

Operational telemetry records request timing and outcomes for scheduling and diagnostics. It is not a user-activity log. Missing telemetry makes a candidate eligible instead of stopping ingestion.

Correctness

How scoring stays idempotent

The ingestion gateway hashes provider observations for operational bookkeeping. That hash does not include score or winner and is not the scoring-idempotency mechanism.

After provider data arrives, the worker constructs a patch and skips unchanged records. It throttles nonessential live writes, while explicitly recognizing scoring-relevant terminal and winner changes such as completed, retired, walkover, default, or a winner appearing or changing.

Scoring functions recompute and upsert bracket and fantasy results from current match state. Replaying an already-applied terminal result therefore does not add a second copy of the same outcome.

Correctness over freshness

Retirement, walkover, and default can change who receives round points. Terminal results need stronger evidence than ambiguous live state, and the poller should never run faster than the upstream feed's useful granularity.

This architecture deliberately excludes vendor contract terms, API credentials, precise quota totals, endpoint paths, and individual user information. None of those details are necessary to explain why an honest eligibility interval, priority order, batch cap, and upsert path matter.

See it in motion

AceRank turns verified match results into live brackets, fantasy points, and private-group standings.

Try AceRank
Follow The Tournament

Live results in. Your standings move.

Keep live scores, bracket movement, fantasy points, and private-group standings close while the tournament unfolds.

Live scoresBracket progressFantasy standings