Datapack and Assets
The Soccerverse datapack: JSON structure, customization and share flow, asset URL rules, cache behaviour and the default-pack fetch.
The datapack is a static JSON file that maps Soccerverse's numeric IDs to names, colours and image files, so your app can show player, club, league, cup and stadium labels. It is off-chain and read-only: no API key, and no game state in it.
This page shows how to fetch and read the pack, how to build image URLs, and how to make and share your own pack.
Purpose
Game and chain APIs expose IDs. Most hosted responses on https://play.soccerverse.com give IDs, not club, player, league, cup or stadium names. The one exception is /v1/portfolio/discover, which includes club and player names from the default pack. A game node you run yourself (POST http://<node>:8600/) gives IDs only, see Running your own node. The datapack is how you label them:
- player ID -> first name, surname, and a photo if the player has one;
- club ID -> club name, primary RGB colour, and club shirt image;
- country and division -> league name and league icon;
- country or cup code -> cup icon (the pack has no real cup names, so give cups your own names);
- stadium ID -> stadium name (the default pack has no stadium images).
The default pack lives at:
https://downloads.soccerverse.com/svpack/packv2/default.json
Player names are real. Most club names are real town or club names, and some are changed (for example Manchester Blue). Leagues, cups and stadiums have generic names such as ENG Division 1, ENG Cup and Manchester Blue Stadium, so real names such as "Premier League", "FA Cup" or "Old Trafford" never appear in it.
The older URL https://downloads.soccerverse.com/svpack/default.json still answers, but it is an out-of-date copy. Use the packv2 URL for anything new.
Example: name a club
GET https://play.soccerverse.com/v1/club/50 gives you IDs, not club, stadium or league names: clubId 50, details.stadium_id 555, details.country_id ENG and details.division 0. The pack turns them into names:
| From the API | Look it up in the pack | You get |
|---|---|---|
clubId 50 | ClubData, the row with id "50" | Manchester Blue, colour 132,187,255 |
details.stadium_id 555 | StadiumData, the row with id "555" | Manchester Blue Stadium |
details.country_id ENG, details.division 0 | LeagueData, the row with c "ENG" and d "1" (division 0 + 1) | ENG Division 1, icon div1.png |
The league lookup adds 1 to the division, see Division numbers. The club shirt and the league icon are plain files, and both of these URLs work:
https://downloads.soccerverse.com/svpack/packv2/clubs/50_home_medium.webp
https://downloads.soccerverse.com/svpack/packv2/leagues/div1.png
Going the other way, from a name to an ID: there is no general name-to-ID lookup for clubs or players in the hosted APIs (/v1/search only finds user names). Search the pack yourself, or use the resolve_player_name, resolve_club_name, resolve_league_name, resolve_competition_name and resolve_stadium_name tools of the Soccerverse MCP server. The MCP counts divisions from 1, see Division numbers.
Access notes
| Item | Value |
|---|---|
| Protocol | Plain HTTPS GET for one static JSON file and static image files. |
| Auth | No API key or login. Send your own User-Agent header, see Fetching the default pack. |
| Custom packs | Players can switch the game to your own pack in Settings or with a share link, see Custom packs and sharing. |
| Writes | None. You host a custom pack yourself, outside Soccerverse. Choosing one only changes that player's own browser settings. |
What the server sends for the pack and for the image files:
Access-Control-Allow-Origin: *, so any website can use them from a browser.Cache-Control: public, max-age=86400(24 hours).ETagandLast-Modified. Conditional requests work: sendIf-None-MatchorIf-Modified-Sinceand you get304 Not Modifiedwhile the file is unchanged. Use them to spot a new pack.- For the pack,
Content-Type: application/jsonand a size of about 9 MB (about 3 MB compressed: sendAccept-Encoding: gzip).
Top-level JSON structure
A datapack is a JSON object with a PackData wrapper. Always keep that wrapper.
This is the shape, with real rows from the default pack. name and desc are the pack's title and description, and the only metadata it has. (The default pack's desc says the clubs are fictional; that text is out of date.)
{
"PackData": {
"name": "Fictional clubs and leagues",
"desc": "All players are real, but clubs, leagues, and cups are fictional.",
"PlayerData": {
"baseImageUrl": "https://downloads.soccerverse.com/svpack/packv2/player_webp/",
"imageFlags": true,
"P": [{ "id": "1100", "f": "Erling", "s": "Braut Haaland", "i": 1 }]
},
"ClubData": {
"baseImageUrl": "#",
"C": [{ "id": "50", "n": "Manchester Blue", "rgb": "132,187,255" }]
},
"LeagueData": {
"baseImageUrl": "https://downloads.soccerverse.com/svpack/packv2/leagues/",
"L": [{ "id": "88", "n": "ENG Division 1", "c": "ENG", "d": "1", "i": "div1.png" }]
},
"CupData": {
"baseImageUrl": "https://downloads.soccerverse.com/svpack/packv2/cups/",
"C": [{ "id": "ENG", "n": "ENG Cup", "i": "national_cup.png" }]
},
"StadiumData": {
"baseImageUrl": "#",
"S": [{ "id": "555", "n": "Manchester Blue Stadium" }]
}
}
}
Field table
| Section | Row key | Main fields | Notes |
|---|---|---|---|
PlayerData | P | id, f, s, optional i | f (first name) and s (surname) can be null: about 2,000 rows have both null. i is 1 when the player has a photo and is missing otherwise, see Image URLs. The section also has "imageFlags": true. |
ClubData | C | id, n, rgb | rgb is a decimal triplet string such as 132,187,255. baseImageUrl is # in the default pack, which means "use the shirts folder", see Image URLs. |
LeagueData | L | id, n, c, d, i | Look a league up by c (country) plus d (division). d is 1-based, see Division numbers. id is only a row number in the pack, not the game's league_id, which changes every season (the top English division has league_id 1665 in season 4 but pack id "88"). i is an icon in the leagues folder. |
CupData | C | id, n, i | id is a country code such as ENG or a region code (EUR, AFR, AME, ASI, WOR). n is only a placeholder such as ENG Cup, not a real cup name, so use i (an icon in the cups folder) and give cups your own names. |
StadiumData | S | id, n | No i: the default pack has no stadium images, and its baseImageUrl is #. |
id and d are strings in the JSON, while the player i is a number.
Division numbers
Pack divisions (d) are 1-based: "1" is the top division. The Datacentre REST API, the /v1 API and GSP are 0-based (0 is the top division) in every division or level field and every division filter, apart from the two exceptions below. So add 1 before you look up the pack row: d = division + 1.
The two exceptions already count from 1, so use them as they are:
- the
{division}in the/v1/league/{country_id}/{division}URL, and thedivisionit echoes at the top of its response./v1/league/ENG/1is the top division, and/v1/league/ENG/0is rejected with422. The otherdivisionfields in that response, such asleague.divisionandtable[].division, are 0-based, so add 1 to those. - the
divisionin/datacentre/dumps/league_tables?country_ids=..., where the top flight isdivision: 1.
The Soccerverse MCP tools count from 1 too, like the pack. Use their division values as they are and do not add 1. Many of them also return a ready-made league_name.
Fetching the default pack
This fetch is read-only and needs no key or login. The script downloads the pack, prints the row count and image folder of each section, then looks up player 1100 and club 50.
python3 - <<'PY'
import json, urllib.request
url='https://downloads.soccerverse.com/svpack/packv2/default.json'
# Use your own app name here: Python's default User-Agent is blocked (see below).
req=urllib.request.Request(url, headers={'User-Agent':'my-app/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))
obj=json.loads(data)
pack=obj['PackData']
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
Two things to know before you script it:
- Send your own
User-Agent. The host rejects Python's stockurllibagent (Python-urllib/3.x) with HTTP403and the bodyerror code: 1010. curl, python-requests, Node and Go work as they are, and no key or bypass header is needed. - Go easy on it. No rate limit is published. Download the pack once and cache it. If you get HTTP
429, wait a second and try again.
How the game looks things up
The game's website (https://play.soccerverse.com) loads the pack like this, and you can copy the same rules in your own client:
- It takes the active pack URL: the player's custom pack if they chose one, otherwise the default.
- It downloads the JSON and lets the browser reuse a saved copy.
- It reads
PackDataand builds in-memory lookup tables for players, clubs, leagues, cups and stadiums.
The lookup rules:
| You have | The game shows |
|---|---|
| A player ID | f, a space, then s. A null part is replaced: the first name by Player, the surname by the ID, so a row with both null shows as Player 47399. An ID that is not in the pack at all shows Player #<id> and a placeholder image. Always null-check f and s, because a plain f + " " + s fails on those rows. |
| A club ID | The club name, colour and shirt image. An ID that is not in the pack shows Club #<id> and a placeholder image. |
| A country and a 0-based division | The league name and icon. It adds 1 to the division first, see Division numbers. If the pack has no such league it shows <CC> Division <n>, for example ENG Division 1. |
| A cup code | The pack's cup names are only placeholders (ENG Cup), so the game does not use them. It names each cup itself from the competition type and country (for example World Club Cup or English Cup), and takes only the icon i from the pack. |
| A stadium ID | The stadium name, or Stadium #<id>. There is no image unless a custom pack sets a real StadiumData.baseImageUrl. |
| Two club IDs for a match | The home club in its home kit. The away club uses its away kit when the two club colours are too similar, otherwise its home kit. |
In league and stadium names, write ~ for a space and a backtick for an apostrophe. This does not apply to player or club names.
Image URLs
The pack gives you a folder (baseImageUrl) for each kind of image, and its rows give the file names. The default pack keeps everything under https://downloads.soccerverse.com/svpack/packv2/. All of these URLs work today:
https://downloads.soccerverse.com/svpack/packv2/player_webp/1100.webp
https://downloads.soccerverse.com/svpack/packv2/clubs/50_home_medium.webp
https://downloads.soccerverse.com/svpack/packv2/leagues/div1.png
https://downloads.soccerverse.com/svpack/packv2/cups/national_cup.png
| Image | How to build the URL |
|---|---|
| Player photo | ${PlayerData.baseImageUrl}${playerId}.webp. The game uses .webp when the base URL contains webp, otherwise .png. Only some players have a photo, see below. |
| Club shirt | ${shirtBase}${clubId}_${kit}_${size}.webp, where kit is home or away and size is small, medium or large. All six files exist for a club. shirtBase is explained below. |
| League icon | ${LeagueData.baseImageUrl}${i}, where i is div1.png to div5.png, or divgeneric.png. |
| Cup icon | ${CupData.baseImageUrl}${i}, where i is national_cup.png for national cups, or EUR.png, AFR.png, AME.png, ASI.png or WOR.png for continental and world cups. |
| Stadium image | None in the default pack. If a custom pack sets a real StadiumData.baseImageUrl, the game requests ${baseImageUrl}${stadiumId}.jpg. |
Check i before you request a player photo. Only players with "i": 1 have one, which is about 4 in 10 players. For every other player the URL returns 404, so use your own placeholder instead of requesting it.
Club shirts and the # base. In the default pack ClubData.baseImageUrl is #. It does not mean "append the club ID to #". It means the shirts live in https://downloads.soccerverse.com/svpack/packv2/clubs/, which is your shirtBase. A custom pack can set a real base URL instead. The club image is then ${base}${clubId} plus .webp if the base contains webp, otherwise .png.
Custom packs and sharing
The game can use a pack other than the default. A custom pack replaces the default and nothing is merged, so start from a full copy of the default pack and keep every section. A missing section shows placeholders (Player #<id>, Club #<id>, <CC> Division <n>, Stadium #<id>). A custom pack also only changes what that person's browser shows: the parts of pages that Soccerverse's servers render still use the default pack.
To make and share one:
- Download the default pack (see Fetching the default pack) and change the values you want. Keep the
PackDatawrapper. - Check that the file still parses as JSON and that the fields keep the shapes above.
- Host it at an HTTPS URL that allows browser requests from other websites (
Access-Control-Allow-Origin: *). - Give people a share link:
https://play.soccerverse.com/?pack=followed by your pack's URL, URL-encoded. It works on any game page. For example:
https://play.soccerverse.com/?pack=https%3A%2F%2Fexample.com%2Fmy-pack.json
When someone opens the link, the game checks that the address is a valid URL and asks Add Custom Datapack?. The prompt shows the pack URL and warns that a malicious datapack could show incorrect or misleading player and club information, so people should only add packs from sources they trust. Yes, Add It makes it the active pack and reloads the page. No, Cancel removes ?pack= from the address and changes nothing.
Test your share link before you send it. If the download fails (for example, the host does not send Access-Control-Allow-Origin: *), the player sees "Failed to add datapack. Please try again." The broken URL is already saved as their active pack and ?pack= stays in the address, so pages cannot load names and images until the player chooses Default in Settings -> Datapack Source.
Players can also add a pack by hand: Settings -> Datapack Source -> + Add Custom URL. There is no file upload, because the game always loads a pack from a URL. It keeps up to 3 custom packs, newest first, and drops the oldest when a fourth is added. The + Add Custom URL button is hidden while 3 are saved, so remove one first. A share link still works then.
Cache and refresh behaviour
The game keeps the pack in the browser's own cache. After you change your pack, click Settings -> Refresh Datapack: it downloads the pack again and reloads the page. Clear Cache does not force a new download.
If you fetch the pack yourself, keep the ETag from your last download and send it back in If-None-Match. A 304 means nothing changed, so you can skip the download. The pack has no version field, only name and desc. For your own packs, put a version in the URL or compare a file hash.
Running your own node
A game node you run yourself never reads the datapack. Fetch the pack from downloads.soccerverse.com like any other client.