CSGO-API: a JSON mirror of the Counter-Strike 2 item catalogue
An unofficial JSON API for Counter-Strike 2 in multiples languages. List of skins, cases, stickers, collections, collectibles, agents, graffiti, keys, patches and music kits.
At a glance
- What is it?
- CSGO-API is an unofficial JSON API for Counter-Strike 2 that publishes skins, cases, stickers, collections, collectibles, agents, graffiti, keys, patches and music kits as static JSON files, in 28 possible languages of which two are hosted. The data is regenerated by scripts in the repository rather than served live, which is the design decision that explains both its speed and its staleness.
- Who is it for?
- CSGO-API suits a developer building a price tracker, a collection viewer or a marketplace front end who wants structured item data without scraping the game or calling Valve's endpoints on every request, and who is content to work with a catalogue that refreshes on the upstream's schedule rather than on demand.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
An item catalogue, not a live game service
CSGO-API is an unofficial JSON API for Counter-Strike 2 that provides access to various data aspects of the game, parsed into JSON for easier integration and use. The project description lists what it covers: skins, cases, stickers, collections, collectibles, agents, graffiti, keys, patches and music kits.
It is worth being clear about the scope from the start. This is a catalogue of items and their properties, in a machine readable form. It is not a service that tells you what is in your inventory, what a skin costs on the market, or what is currently in a case. Everything in the dataset is static information about items: their names, their rarity, the collections they belong to, the cases they can come out of, the float ranges they exist in.
That distinction matters because it determines what you can build on it. A collection browser, a catalogue for a trading site, a tool that reconciles your inventory against the full item list, a statistics page: all of those are within reach. A price tracker is not, unless you combine this with a separate market data source.
The implementation language is JavaScript, the project is written as an ES module with axios and sha1 as its only runtime dependencies, and the licence is MIT with the licence file at the root of the repository. The homepage is bymykel.com/CSGO-API/, and the last push was on 2026-10-22.
The hosted API is static files served from a repository
The architecture is the first surprising thing, and it is a good decision.
The hosted API is served over plain HTTP from the repository's raw content path, with a language folder in the URL:
GET https://raw.githubusercontent.com/ByMykel/CSGO-API/main/public/api/{language}There is no server, no query endpoint, no API key and no rate limit, because there is nothing to rate limit: you are fetching a file that already exists. A CDN edge serves it, and your code can cache it for as long as it likes.
That has consequences for how you design a client. A request is cheap but not free, so the sensible pattern is to fetch a language folder once, or fetch the specific file you need, and cache the result locally. And because the data is a snapshot, your application has to decide how stale it is willing to be.
The endpoint structure is per resource. The full item list is one file:
GET https://raw.githubusercontent.com/ByMykel/CSGO-API/main/public/api/en/all.jsonand that file is an object with all items accessible by their id. Then there is a file per category, such as skins.json for skins, stickers.json for stickers and sticker_slabs.json for sticker slabs, each of which is an array.
The host currently ships two languages, en and zh-CN, out of 28 available folder codes. The full set of codes is listed in the documentation and the display names live in constants.js: en, zh-CN, pt-BR, ru, es-ES, bg, cs, da, nl, fi, fr, de, el, hu, it, ja, ko, es-MX, no, pl, pt-PT, ro, sv, zh-TW, th, tr, uk and vi.
Grouped and ungrouped skins are two different shapes
The most useful thing to understand about this dataset is that skins exist in two groupings, and the two files are not the same data with a different order.
The grouped file, skins.json, is organised by wear and by state. One entry describes the skin itself, and the wear variants and the state variants hang off it as arrays: wears contains the wear names such as Factory New, and the stattrak and souvenir flags sit on the parent object.
id: "skin-65604",
name: "Desert Eagle | Urban DDPAT",
min_float: 0.06,
max_float: 0.8,
stattrak: false,
souvenir: true,
paint_index: "17",The ungrouped file, skins_not_grouped.json, is the same information flattened so that each individual variant is its own entry, with the wear folded into the name and into a market_hash_name field:
id: "skin-65604_0",
skin_id: "skin-65604",
name: "Desert Eagle | Urban DDPAT (Factory New)",
market_hash_name: "Desert Eagle | Urban DDPAT (Factory New)",That difference is a real engineering decision on the consumer side. The grouped shape is right if you are rendering a catalogue with wear as a facet and you want to show one page per skin. The ungrouped shape is right if you are matching against market listings, because the market hash name is the key that listings are actually indexed by, and because a row per variant is what a database table wants.
The ungrouped entries also carry fields the grouped ones do not, including a wear object and a style object with a URL pointing at the workshop finishes reference.
The documentation states the distinction plainly: the ungrouped file is similar to the previous one but is not grouped by wear and by state, where state means normal, stattrak or souvenir.
The fields that downstream tools actually use
Looking at a single skin entry shows what a consumer gets.
The object carries an id such as skin-65604, a name and a description, and then the relationships: a weapon object with the weapon id and name, a category object such as Pistols, and a pattern object naming the finish. It has the float range the skin exists in, min_float and max_float, and a rarity object with an id, a name and a hex colour, which is what a client needs to draw the colour chip next to the name.
It has the flags that separate the market variants, stattrak and souvenir, both booleans, and a paint_index.
And it has the containment relationships, collections and crates arrays, each entry carrying an id, a name and an image URL, plus a team object with values such as Both Teams, a legacy_model boolean and an image URL.
Two details are worth knowing if you plan to build on it. The image URLs point at a separate repository, ByMykel/counter-strike-image-tracker, so the image host is a different project with its own availability. And the description field carries HTML rather than plain text, with markup like span elements and italics inline, which means a client rendering it has to decide whether to sanitise it.
The sticker entries follow the same shape with a few extra fields for the esports side: a tournament_event, a tournament_team, a type, an effect and a market_hash_name, plus a rarity and a crates array pointing at the sticker packs the sticker comes from.
Regenerating the data yourself with update.js and group.js
Because the published files are generated artefacts, the repository ships the generators, and you can run them locally.
npm install
node update.js --languages en,ru,uk
node group.js --languages en,ru,ukThe two scripts have distinct jobs, and the second name hints at it. update.js pulls the upstream data, and group.js produces the grouped and ungrouped variants of the skin files.
Two flags matter. Passing --languages all runs every available language instead of the ones you list, and --force skips the manifest cache check.
That last flag explains a good deal of the repository layout. The root contains imagesShaGroup.txt, imagesShaUpdate.txt, manifestIdGroup.txt and manifestIdUpdate.txt, four plain text files, and the sha1 dependency is there to compute the content hashes they hold. The manifest cache is what lets an update run skip work that has not changed upstream, and --force is the escape hatch when you know the cache is wrong or you have changed the grouping logic locally.
This is also the answer to the alternative approach. Instead of consuming the hosted snapshot, you can generate the catalogue yourself on your own schedule, which means the data is as fresh as your last run and you control which languages exist. What you give up is the free hosting and the CDN, and you take on the upstream fetch, the rate limits that come with it, and the need to run the pipeline on a schedule. For most consumers the snapshot is the better trade, because the item catalogue changes slowly compared with how often anyone reads it.
Adding a language through a GitHub Actions variable
The documentation explains how to get a language you need, and it does so by making the project a fork-and-configure workflow rather than a code contribution.
In a fork, you set the LANGUAGES variable under Settings, then Secrets and variables, then Actions, then Variables. The value is a comma separated list such as en,ru,uk, or all for every language. Scheduled runs use that variable automatically.
For a one-off override, you go to Actions, then Update and Group JSON API, then Run workflow, and fill in the languages input. The documentation notes that this input takes priority over the variable.
That workflow structure also tells you how the project is maintained. The upstream repository runs a scheduled job that updates and groups the data for the hosted languages, which keeps the hosted set small. A fork is free to add more, at the cost of its own Actions minutes.
The fork workflow has a practical consequence beyond CI. The hosted files live under public/api in the main branch of the upstream repository, so a fork that adds languages publishes them under its own raw URL. If you want your own languages in production, you either point your application at your fork's raw URLs, or you run the pipeline yourself and host the output wherever you like.
The repository also carries a scripts directory with a generate-docs script wired to a package script, which is how the endpoint list in the documentation stays in step with the files that actually exist.
Editorial conclusion
CSGO-API suits a developer building a price tracker, a collection viewer or a marketplace front end who wants structured item data without scraping the game or calling Valve's endpoints on every request, and who is content to work with a catalogue that refreshes on the upstream's schedule rather than on demand. It is the wrong choice if you need real time inventory or price data, since this is an item catalogue and nothing more, and it is the wrong choice if you need a language other than English or Simplified Chinese without running the generation scripts yourself. Verify three things before you depend on it: that the hosted language you need exists, that the fields you care about survive the grouping step, and that the snapshot age is acceptable for your use case, because the repository publishes no versioned releases and no changelog for the data.
Frequently asked questions
What does the CSGO-API project provide?
It is an unofficial JSON API for Counter-Strike 2 that exposes the game's item data, covering skins, cases, stickers, collections, collectibles, agents, graffiti, keys, patches and music kits. The data is item metadata rather than live inventory or pricing.
Which languages does the hosted CSGO-API serve?
The hosted API currently ships two languages, en and zh-CN, out of 28 available folder codes. The full code list is in the documentation and the display names are in constants.js, and the other languages can be generated by running the update and group scripts.
What is the difference between skins.json and skins_not_grouped.json?
skins.json groups skins by wear and by state, with the wear variants and the stattrak and souvenir flags attached to the parent entry. skins_not_grouped.json gives every variant its own entry, with the wear in the name and in a market_hash_name field, which suits matching against market listings.
How do I generate the CSGO-API data locally?
Run npm install, then node update.js --languages en,ru,uk followed by node group.js --languages en,ru,uk. Passing --languages all runs every available language, and --force skips the manifest cache check.
How do I add a language to CSGO-API in a fork?
Set the LANGUAGES variable under Settings, Secrets and variables, Actions, then Variables to a comma separated list or all, and scheduled runs will use it. For a single run you can use Actions, Update and Group JSON API, Run workflow and fill in the languages input, which takes priority over the variable.
What licence does CSGO-API use?
The project is MIT licensed, with the licence file at the root of the repository. The package manifest itself is minimal, declaring only axios and sha1 as runtime dependencies, plus a documentation generation script and a Prettier formatting script.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/bymykel-csgo-api)Was this useful?