@writeshh/fotmob-api
v0.1.0
Published
Unofficial FotMob API client — match data, live scores and xG. TypeScript, zero dependencies.
Maintainers
Readme
fotmob-api
Unofficial FotMob client for Node 20+. TypeScript, zero dependencies, native fetch.
Usage guide with real response samples →
npm install @writeshh/fotmob-apiimport { getMatchesByDate, getMatchDetails, search } from "@writeshh/fotmob-api";
const today = await getMatchesByDate(new Date()); // grouped by league
const match = await getMatchDetails(5795432); // facts, lineups, stats, shotmap
const hits = await search("arsenal");| function | endpoint |
| --- | --- |
| getMatchesByDate(date) | matches?date=YYYYMMDD |
| getMatchDetails(matchId) | matchDetails?matchId= |
| getLeagueDataForMatch(matchId, leagueId) | leagueDataForMatch?matchId=&leagueId= — other fixtures in the round, no table |
| getLeague(id, season?) | leagues?id=&season= |
| getLeagueSeasons(id) | leagueseasondeepstats?id= |
| getTable(leagueId) | tltable?leagueId= |
| getFixtures(id) | fixtures?id= |
| getTeam(id) | teams?id= |
| getPlayer(id) | playerData?id= |
| getAllLeagues() | allLeagues |
| search(term) | search/suggest?term= |
| getTransfers(page) | transfers?page= |
| watchMatch(matchId) | polls matchDetails — see below |
Live matches
import { watchMatch } from "@writeshh/fotmob-api";
for await (const { match, changed, finished } of watchMatch(4123517)) {
if (changed) console.log(match.header); // score or events moved
if (finished) console.log("full time");
}Polls until full time, then returns. It paces itself off each response's cache
max-age — FotMob drops that to 10s while a match is ongoing — so it never
outruns the CDN. Transient network errors are retried (maxRetries, default 5)
instead of ending the loop. Pass signal to stop early, intervalMs to override
the pacing, untilFinished: true to keep polling past full time.
matchDetails cache TTL tracks match state: 10s ongoing, 900s upcoming,
3600s finished. Polling faster returns the same cached body.
Anything not wrapped: fotmob<T>(path, params, opts). Every call takes
{ signal, headers, baseUrl }. Failures throw FotmobError with url and status.
Base is https://www.fotmob.com/api/data/, which needs no auth header today —
the apigw.fotmob.com host and its signed x-mas / x-fm-req header are not
involved. If FotMob locks these paths down, requests start returning the SPA's
HTML instead of JSON and FotmobError reports it.
npm test # type-check + deterministic tests, no network
npm run test:live # 15 tests against the real API
npm run test:allsrc/client.test.ts runs against a local stand-in server, so it is offline and
deterministic. src/live.test.ts hits FotMob and resolves its own match ids at
runtime rather than pinning ones that age out.
Unofficial and unaffiliated. Data belongs to FotMob; keep request volume sane.
Note: fixtures returns ids as strings while matches returns them as numbers
for the same entities. Coerce before comparing across the two.
