Data Access¶
pyrf includes a client for the FootyIQ watcher data API and the bet3 ratings API.
Setup¶
Set your API tokens as environment variables (or place them in a .env file):
export FOOTYIQ_TOKEN=your_footyiq_token_here
export BET3_TOKEN=your_bet3_token_here
Fetching Scores¶
from pyrf_data import PyRfDataClient
client = PyRfDataClient()
# All scores
df = client.fetch_scores()
# Only games where watchers agree on the score (recommended)
df = client.fetch_scores_agreed()
# Games with score discrepancies (data quality investigation)
df = client.fetch_scores_disagreed()
Full Match Export¶
Detailed match statistics including goals, chances, half-chances, deliveries, and more:
df = client.fetch_full_export()
The full export includes per-team, per-half breakdowns of:
- Goals and assists
- Chances and half-chances
- Ooohs and choos (near-miss events)
- Deliveries into the box
Team Ratings¶
Fetch model-derived team ratings (alpha/beta/lambda parameters, supremacy, total goals) for upcoming matchups:
df = client.fetch_ratings_club()
Data Quality¶
Use fetch_scores_agreed() for reliable score data. The agreed/disagreed split helps
identify matches where watcher observations may be unreliable.
API Reference¶
pyrf_data
¶
Client for accessing Ringfinity match data (FootyIQ watcher API + bet3 ratings).
PyRfDataClient
dataclass
¶
Client for the FootyIQ watcher data API.
Returns DataFrames with flattened data by default. Use *_raw methods
for the original nested JSON response.
__post_init__() -> None
¶
Initialize tokens and paths from environment if not provided.
fetch_scores_raw(limit: int = 1000, from_date: pd.Timestamp | None = None, to_date: pd.Timestamp | None = None, scores_agree: bool | None = None) -> list[dict[str, Any]]
¶
Fetch game scores from the API as raw nested JSON.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
limit
|
int
|
Maximum number of records to fetch. |
1000
|
from_date
|
Timestamp | None
|
Start date as pd.Timestamp (defaults to 2000-07-07). |
None
|
to_date
|
Timestamp | None
|
End date as pd.Timestamp (defaults to 2029-08-07). |
None
|
scores_agree
|
bool | None
|
If True, only return games where watcher and NowGoal agree. If False, only return games where they disagree. If None, return all games regardless. |
None
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
Raw nested JSON response (country -> comps -> seasons -> rounds -> fixtures). |
fetch_full_export_raw(limit: int = 500000, from_date: pd.Timestamp | None = None, to_date: pd.Timestamp | None = None) -> dict[str, Any]
¶
Fetch full export data as raw JSON.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Raw JSON with 'fixtures' list and metadata. |
fetch_scores(limit: int = 1000, from_date: pd.Timestamp | None = None, to_date: pd.Timestamp | None = None, scores_agree: bool | None = None) -> pd.DataFrame
¶
Fetch game scores as a flattened DataFrame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
limit
|
int
|
Maximum number of records to fetch. |
1000
|
from_date
|
Timestamp | None
|
Start date as pd.Timestamp (defaults to 2000-07-07). |
None
|
to_date
|
Timestamp | None
|
End date as pd.Timestamp (defaults to 2029-08-07). |
None
|
scores_agree
|
bool | None
|
If True, only return games where watcher and NowGoal agree. If False, only return games where they disagree. If None, return all games regardless. |
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with columns: fixture_id, start_time, country, comp_id, comp_name, |
DataFrame
|
comp_short, season, round, team1_id, team1_name, team2_id, team2_name, |
DataFrame
|
score_ft, score_ht, team1_goals, team2_goals, scores_agree, pitch, |
DataFrame
|
weather, temp. |
fetch_scores_agreed(limit: int = 500000, from_date: pd.Timestamp | None = None, to_date: pd.Timestamp | None = None) -> pd.DataFrame
¶
Fetch all trusted data where watcher and NowGoal scores agree.
This is the recommended method for fetching reliable historical data.
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with one row per fixture. |
fetch_scores_disagreed(limit: int = 1000, from_date: pd.Timestamp | None = None, to_date: pd.Timestamp | None = None) -> pd.DataFrame
¶
Fetch games where watcher score differs from NowGoal score.
Useful for investigating data quality or discrepancies.
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with one row per fixture. |
fetch_full_export(limit: int = 500000, from_date: pd.Timestamp | None = None, to_date: pd.Timestamp | None = None) -> pd.DataFrame
¶
Fetch full export with detailed match statistics as DataFrame.
Includes per-team stats: goals, chances, half-chances, deliveries, ooohs, choos - broken down by half.
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with detailed match statistics. |
fetch_ratings_club_raw() -> list[dict[str, Any]]
¶
Fetch club team ratings as raw JSON.
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
List of rating objects, one per home/away club matchup, each |
list[dict[str, Any]]
|
containing alpha/beta/lambda parameters, supremacy (sup), total |
list[dict[str, Any]]
|
goals (tg), and team/league metadata. |
fetch_ratings_club() -> pd.DataFrame
¶
Fetch club team ratings as a DataFrame.
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with one row per club matchup, including alpha/beta/lambda |
DataFrame
|
parameters, supremacy, total goals, and team/league metadata. |
DataFrame
|
The |
fetch_ratings_international_raw() -> list[dict[str, Any]]
¶
Fetch international (national-team) ratings as raw JSON.
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
List of rating objects, one per nation, each containing supremacy |
list[dict[str, Any]]
|
(sup), total goals (tg, goals), per-side goal expectations, |
list[dict[str, Any]]
|
normalised alpha/beta, and team/NowGoal metadata. |
fetch_ratings_international() -> pd.DataFrame
¶
Fetch international (national-team) ratings as a DataFrame.
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with one row per nation, including supremacy, total goals, |
DataFrame
|
per-side goal expectations, normalised alpha/beta, and team/NowGoal |
DataFrame
|
metadata. The |
load_wcplayer_export_raw() -> dict[str, Any]
¶
Load the local wcplayerexport.json file as raw JSON.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Dict with keys |
dict[str, Any]
|
|
load_wcplayer_export() -> pd.DataFrame
¶
Load the local wcplayerexport.json as a flat DataFrame.
Nested fs / ng / tm sub-objects are flattened with
dot-separated column names (e.g. tm.cpm, ng._id).
Date columns (createdAt, updatedAt, v1StarRatingChangedAt,
v2StarRatingChangedAt) are parsed to datetime.
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with one row per player. |
fetch_wcplayer_export_raw(limit: int = 5000) -> dict[str, Any]
¶
Fetch the wcteamplayers export from the API as raw JSON.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Dict with keys |
dict[str, Any]
|
|
fetch_wcplayer_export(limit: int = 5000, *, cache: bool = True) -> pd.DataFrame
¶
Fetch the wcteamplayers export from the API as a flat DataFrame.
When cache is True (default) and rf_local_path is set, the raw
JSON response is written to <rf_local_path>/wcplayerexport.json,
overwriting any existing file.
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with one row per player, matching |
fetch_wc2026_squads(*, update: bool = False, cache_path: Path | str | None = None, url: str = WC2026_SQUADS_URL) -> pd.DataFrame
¶
Return the 2026 World Cup squads as one row per (nation, player).
Reads data/wc2026_players_flashscore.parquet (or rf_local_path equivalent)
by default. Pass update=True to re-scrape the FlashScore article
at url and overwrite the cache.
See docs/specs/wc2026_v2.md for the column contract.