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 Team
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.
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.
How the poller actually works
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.
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.
A wrong completed result is more dangerous to a prediction game than a point arriving a little late.
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.
AceRank Editorial Team
https://acerank.app/blog/adaptive-live-sports-polling
Top comments (0)