Teams, players & transfers#
Teams#
GET /api/v2/teams/
| Param | Type | Description |
|---|---|---|
country_code |
string | ISO country code, e.g. AR |
league_id / season_id |
int | Teams competing in a league/season |
in_competition |
bool | Only teams currently in an active competition |
is_women |
bool | Women's teams |
name |
string | Name search |
limit / offset |
int | Pagination |
GET /api/v2/teams/{id}/ # profile: venue, coach, colours, country
GET /api/v2/teams/{id}/squad/ # current squad, ordered by position then shirt number
GET /api/v2/teams/{id}/fixtures/ # date_from, date_to, league_id, status
GET /api/v2/teams/{id}/social/ # curated tweets/videos (type filter)
curl -H "Authorization: Token YOUR_API_KEY" \
"https://sports.bzzoiro.com/api/v2/teams/755/fixtures/?status=upcoming&limit=5"
Squad availability#
Every player in the squad response carries their current availability:
| Field | Type | Description |
|---|---|---|
availability |
string | available · injured · doubtful · suspended |
injury_type |
string | Reason, e.g. Knee Injury. Empty when available or unpublished |
injury_expected_return |
date | Expected return, or null |
For an injured flag, test availability == "injured" — a boolean would fold
doubtful and suspended into "not injured".
The status is refreshed several times a day from the team news published for each
side's next fixture. available is the default value, so it means not currently
listed as missing rather than confirmed fit, and a club with no upcoming
fixture in a covered competition is not re-checked until it has one.
Players#
GET /api/v2/players/
| Param | Type | Description |
|---|---|---|
name |
string | Name search |
team_id |
int | Current club |
national_team_id |
int | Current national-team squad |
nationality_code |
string | ISO code, e.g. AR |
position |
string | G · D · M · F |
limit / offset |
int | Pagination |
GET /api/v2/players/{id}/ # bio, market value, skills
GET /api/v2/players/{id}/stats/ # per-match log — season_id, team_id, league_id, date_from/to
GET /api/v2/players/{id}/transfers/ # transfer history with fees
GET /api/v2/players/{id}/career/ # season-by-season summary
GET /api/v2/players/{id}/national-team/ # international record
GET /api/v2/players/{id}/social/ # tweets/videos
The full workflow with examples: Get a football player profile.
Transfers feed#
GET /api/v2/transfers/
| Param | Type | Description |
|---|---|---|
league_id |
int | Either club has played in this league |
player_id |
int | One player's transfer history |
team_id |
int | In or out of this club |
from_team_id / to_team_id |
int | Direction-specific |
date_from / date_to |
date | Transfer date window |
min_fee |
int | Minimum fee (EUR) |
has_fee |
bool | Only transfers with a disclosed fee |
ordering |
string | Sort field, prefix - for descending |
league_id takes the id from /api/v2/leagues/ and matches a transfer when
either side of the move is a club from that league — so an Argentine club
selling to Europe and buying from Europe both show up:
GET /api/v2/transfers/?league_id=85&limit=10
Unknown parameters are rejected. This endpoint answers
400if you pass a filter it does not recognise, listing what it accepts. A typo like?league=85fails loudly instead of quietly returning the unfiltered global feed.Note: the dataset includes forward-dated moves (announced loans/returns that haven't happened yet). For a "latest transfers" widget, always cap
date_toat today.
Transfer ledger#
The feed answers who moved. The ledger answers what it cost.
GET /api/v2/transfers/ledger/?team_id=…
GET /api/v2/transfers/ledger/?league_id=… # every club in the league, max 40
| Param | Type | Description |
|---|---|---|
team_id |
int | One club. Mutually exclusive with league_id |
league_id |
int | Every club in that league |
date_from |
date | Window start (ISO) |
date_to |
date | Window end. Never reaches past today whatever you pass |
Each club comes back with incoming and outgoing blocks — moves, how many
were permanent, loans, free and undisclosed, the fees that were published, and
the average age — plus gross_spend_eur, gross_income_eur, net_spend_eur,
loan_dependency_pct, squad_size, turnover_pct, age_in and age_out.
Three things decide these numbers, and you should know them before quoting one:
Money only counts where a fee was published. A move with no figure lands in
undisclosed, not as a zero. Summing the blanks as zeroes reports a tenth of
what the big leagues actually spend. free is the separate, known case of a
fee of nothing.
A loan return is not a signing. A player coming back from a loan is the same player returning to where he already belonged. Counting him as an arrival inflates the arrivals of every club that lends anyone out, and the raw feed gives you no warning that it happened.
turnover_pct is measured against today's squad. We do not keep historical
squad sizes, so a long window can exceed 100%. That is not a bug — it means the
club moved more players than it currently holds.
Transfer routes#
GET /api/v2/transfers/routes/?team_id=…&min_moves=3
| Param | Type | Description |
|---|---|---|
team_id / league_id |
int | Scope, same rules as the ledger |
date_from / date_to |
date | Window |
min_moves |
int | Minimum moves for a pair to count (default 2) |
limit |
int | Default 50, max 200 |
The club pairs that repeat, ranked: moves, loans, fees_eur and up to five
of the players who walked it.
Only moves with a real club at both ends are counted. "No team" is how the dataset spells free agency, not a destination, and it is excluded — without that it is the busiest route of almost every club on earth.
A club's own academy does count as a route, so the top line for a big club is often its own under-21s. That is the same question asked of the one feeder it controls.
World Cup squads#
GET /api/v2/worldcup/squads/ # team, group, status, has_player
GET /api/v2/worldcup/squads/{team_id}/
Tournament squad lists with per-player status — built for tournament hub pages.
Images#
Badges and photos come from ids, no extra calls:
/img/team/{id}/, /img/player/{id}/?sor=true&bg=transparent — see
Working with images.