Skip to main content
Loading market data…
SoccerverseSoccerverse

These developer docs are available in English only. View in English

Datapack and Assets

The Soccerverse datapack: JSON structure, customization and share flow, asset URL rules, cache behaviour and the default-pack fetch.

This page documents the Soccerverse datapack JSON and the frontend asset lookup path. It is a read-only/off-chain customization surface, not a game-state API: a datapack maps numeric game IDs to names, colours, and image metadata so clients can render player, club, league, cup, and stadium labels.

Purpose

Game and chain APIs expose IDs. The datapack turns those IDs into human-facing presentation data:

  • player ID -> first name, surname, player image URL;
  • club ID -> club name, primary RGB colour, club badge/shirt image URL;
  • country/division -> league name and league image;
  • country/cup ID -> cup name and cup image;
  • stadium ID -> stadium name and optional stadium image.

The canonical frontend default is:

https://downloads.soccerverse.com/svpack/packv2/default.json

The older URL https://downloads.soccerverse.com/svpack/default.json is superseded; the packv2/default.json URL above is current.

Access notes

ItemValue
ProtocolPlain HTTPS GET for a static JSON document and static asset URLs.
AuthNone observed for the default datapack URL.
Client overrideUsers can select the default datapack or enter a custom HTTPS URL in Settings.
Server overrideNEXT_PUBLIC_DATAPACK_URL controls the default frontend URL; backend/updater code uses DATAPACK_URL in the updater environment.
Asset basesThe pack carries section-level baseImageUrl values; frontend deployment also provides NEXT_PUBLIC_CLUB_SHIRT_BASE_URL and image-base env vars.
WritesNone in this doc. Hosting a custom pack is done outside Soccerverse; selecting it in the frontend only changes local client settings.

Top-level JSON structure

A datapack is a JSON object. Consumers accept either a top-level PackData object or, in some frontend code paths, a bare object shaped like PackData.

{
  "PackData": {
    "PlayerData": {
      "baseImageUrl": "https://downloads.soccerverse.io/svpack/packv2/player_webp/",
      "P": [{ "id": "1100", "f": "Erling", "s": "Braut Haaland" }]
    },
    "ClubData": {
      "baseImageUrl": "#",
      "C": [{ "id": "50", "n": "Manchester Blue", "rgb": "132,187,255" }]
    },
    "LeagueData": {
      "baseImageUrl": "https://downloads.soccerverse.io/svpack/packv2/leagues/",
      "L": [{ "c": "ENG", "d": "1", "n": "Premier League", "i": "ENG1.png" }]
    },
    "CupData": {
      "baseImageUrl": "https://downloads.soccerverse.io/svpack/packv2/cups/",
      "C": [{ "id": "ENG", "n": "FA Cup", "i": "ENG.png" }]
    },
    "StadiumData": {
      "baseImageUrl": "#",
      "S": [{ "id": "556", "n": "Old Trafford", "i": "556.jpg" }]
    }
  }
}

Field table

SectionRow keyMain fieldsNotes
PlayerDataPid, f, sid is parsed as an integer. Frontend displays f + " " + s; missing data falls back to Player #<id>.
ClubDataCid, n, rgbrgb is a decimal triplet string such as 132,187,255. If baseImageUrl is #, frontend builds shirt URLs from NEXT_PUBLIC_CLUB_SHIRT_BASE_URL.
LeagueDataLc, d, n, iDatapack divisions are 1-based. Some API responses use 0-based divisions, so frontend lookup adds 1.
CupDataCid, n, iCup id is usually a country/region-style code; image URL is baseImageUrl + i where present.
StadiumDataSid, n, iFrontend can return null image URL when no real stadium image is configured.

Fetching the default pack

This fetch is read-only and does not require any secret.

python3 - <<'PY'
import json, urllib.request, hashlib
url='https://downloads.soccerverse.com/svpack/packv2/default.json'
req=urllib.request.Request(url, headers={'User-Agent':'Soccerverse-docs-readonly/1.0'})
with urllib.request.urlopen(req, timeout=30) as r:
    status=r.status
    content_type=r.headers.get('content-type')
    data=r.read()
print('status', status)
print('content_type', content_type)
print('bytes', len(data))
print('sha256_12', hashlib.sha256(data).hexdigest()[:12])
obj=json.loads(data)
pack=obj.get('PackData', obj)
for section, arrkey in [('PlayerData','P'),('ClubData','C'),('LeagueData','L'),('CupData','C'),('StadiumData','S')]:
    rows=pack.get(section,{}).get(arrkey,[])
    print(section, len(rows), 'baseImageUrl=', pack.get(section,{}).get('baseImageUrl'))

def find(section, arrkey, key, val):
    for row in pack.get(section,{}).get(arrkey,[]):
        if str(row.get(key))==str(val): return row
print('player_1100', find('PlayerData','P','id',1100))
print('club_50', find('ClubData','C','id',50))
PY

The script prints per-section row counts and baseImageUrl values, then spot-checks a player and club row.

Frontend loading and lookup behaviour

The browser datapack manager is a singleton. It:

  1. reads the active URL from useSettingsStore.getState().getCurrentDatapackUrl();
  2. fetches the JSON with cache: 'force-cache' for normal initialization;
  3. parses PackData and builds in-memory maps for players, clubs, leagues, cups, and stadiums;
  4. exposes async lookup helpers such as getPlayerInfo, getClubInfo, getLeagueInfo, getCupInfo, and getStadiumInfo;
  5. leaves initialized=false on fetch/parse failure so the next call can retry.

Important lookup conventions:

HelperInputOutput notes
getPlayerInfo(playerId)numberReturns datapack name/image or Player #<id> plus /images/player-placeholder.svg.
getClubInfo(clubId)numberReturns datapack club name/image/RGB or Club #<id> plus /images/club-placeholder.svg.
getLeagueInfo(countryId, division)country string plus API divisionAdds 1 to the API division before lookup because datapack divisions are 1-based.
getCupInfo(countryId, compType)country/cup code and competition typeHandles special World Club Cup / Cup Winners Cup cases and otherwise uses CupData.C.
getStadiumInfo(stadiumId)numberBuilds a stadium image URL only when StadiumData.baseImageUrl is configured and not #.
getMatchClubImages(homeClubId, awayClubId, options)club IDs plus size/kit optionsUses RGB similarity to choose away kit automatically when home and away colours are too close.

Club/player/competition assets

Asset URL handling is split between datapack base URLs and frontend environment config.

AssetSourceURL construction
Player imagePlayerData.baseImageUrl${baseImageUrl}${playerId}.webp when the base contains webp, otherwise .png.
Club image / shirtClubData.baseImageUrl or NEXT_PUBLIC_CLUB_SHIRT_BASE_URLIf ClubData.baseImageUrl === "#", frontend uses ${NEXT_PUBLIC_CLUB_SHIRT_BASE_URL}${clubId}_${kit}_${size}.webp.
League imageLeagueData.baseImageUrl plus row i${baseImageUrl}${i}.
Cup imageCupData.baseImageUrl plus row i${baseImageUrl}${i}, with special fallbacks for some competition types.
Stadium imageStadiumData.baseImageUrl plus stadium ID${baseImageUrl}${stadiumId}.jpg when configured.

Production frontend deployment source currently sets:

NEXT_PUBLIC_DATAPACK_URL=https://downloads.soccerverse.com/svpack/packv2/default.json
NEXT_PUBLIC_CLUB_SHIRT_BASE_URL=https://downloads.soccerverse.com/svpack/packv2/clubs/
NEXT_PUBLIC_GK_SHIRT_BASE_URL=https://downloads.soccerverse.com/shirts/webp/

The values above are public asset URLs from deployment config, not secrets.

Customization, import, and sharing

The frontend supports a user-selected datapack URL in Settings:

  • customDatapackUrl stores the active custom URL, or null for the default pack.
  • datapackUrlHistory stores up to 3 recent custom URLs, de-duplicated with newest first.
  • getCurrentDatapackUrl() returns customDatapackUrl || DEFAULT_DATAPACK_URL.
  • Adding a custom URL performs basic new URL(...) validation before applying it.
  • Selecting a URL calls datapackManager.refresh() and then reloads the page so rendered names/assets come from the newly selected pack.

For pack authors, "import" in the current frontend means "enter the hosted JSON URL"; it is not a file upload. To share a custom pack:

  1. Start from the current default pack as a baseline.
  2. Edit only the sections you want to customize.
  3. Validate JSON parsing and field shapes.
  4. Host the JSON at an HTTPS URL with CORS suitable for browser fetches.
  5. Give users that URL; they add it in Settings -> Datapack Source -> Add Custom URL.

Cache and refresh behaviour

There are two cache layers in frontend code:

LayerBehaviour
Browser HTTP cacheNormal initialization fetches with cache: 'force-cache'. Browser cache storage/eviction is managed by the browser.
Datapack manager memory mapsParsed players/clubs/leagues/cups/stadiums live in singleton Map objects for fast lookup during the session.

datapackManager.refresh() clears in-memory maps, fetches with cache: 'reload', rebuilds maps, and the Settings modal reloads the page after success. datapackManager.clearCache() clears only the in-memory maps and initialization flag; the code comments explicitly note that HTTP cache is managed by the browser.

getCacheInfo() reports:

{
  "type": "HTTP Cache",
  "note": "Cache managed by browser. Use force refresh to bypass cache.",
  "inMemory": {
    "players": 171376,
    "clubs": 5350,
    "leagues": 322,
    "cups": 159,
    "stadiums": 5350
  }
}

The counts above reflect the current default pack and will change as the pack is updated.

Backend/updater import behaviour

The backend updater can import the datapack into name tables for Datacentre use. This is a service-side operation:

  1. requests.get(DATAPACK_URL, timeout=15) downloads the JSON.
  2. Missing PackData raises ValueError.
  3. The updater creates/updates these tables: dc_player_names, dc_club_names, dc_league_names, dc_cup_names, dc_venue_names.
  4. Rows are inserted with INSERT ... ON DUPLICATE KEY UPDATE, so re-importing updates existing names/colours.
  5. Removed rows are not deleted automatically.

Notes

  • LeagueData.d is 1-based, while some API responses use 0-based divisions. Frontend adds 1 before looking up league names.
  • ClubData.baseImageUrl = "#" is meaningful: it tells the frontend to use shirt assets from NEXT_PUBLIC_CLUB_SHIRT_BASE_URL, not to append club IDs to #.
  • Name decoding in frontend replaces ~ with a space and ` with an apostrophe for league/cup/stadium names.
  • Browser clear cache UI only clears the datapack manager memory maps. A full browser HTTP-cache clear is outside this code path.
  • The server-side datapack manager currently marks itself initialized even after a failed fetch to avoid retry loops; browser datapack manager does not.
  • The updater import is merge/update only. If a row disappears from a custom pack, old database rows can remain until manually removed.
  • The datapack has no explicit version field. Use URL versioning, file hashing, or release notes if you need cache-busting/auditability for custom packs.

Our Partners