steamdataapi
v1.0.0
Published
Steam Market API and Steam Inventory API client for Node.js — live CS2 and Rust skin prices from Steam and 10 marketplaces, daily price history back to 2013, and whole-inventory valuation with floats. Typed, zero dependencies.
Maintainers
Readme
steamdataapi
Steam Market API and Steam Inventory API client for Node.js and TypeScript.
Live CS2 and Rust skin prices from the Steam Community Market and 10 third-party marketplaces (Skinport, CSFloat, Buff163, DMarket, Waxpeer, Lis-Skins, SkinBaron, WhiteMarket, YouPin, C5Game), daily price history back to 2013, Doppler phase prices, case drop tables, and whole-inventory valuation with floats and patterns — through one typed client with zero dependencies.
Backed by steamdataapi.com. Free API key, no card.
npm install steamdataapiWorks on Node 18+, Bun, Deno and browsers (see the note on keys below).
Quick start
import { SteamDataApi } from 'steamdataapi';
const api = new SteamDataApi('sdk_your_api_key'); // https://steamdataapi.com/app
// One item — every price field, plus where it is cheapest right now
const item = await api.items.get('AK-47 | Redline (Field-Tested)', { game: 'cs2' });
console.log(item.prices.best, item.prices.bestSource); // 1690 'csfloat' (integer cents)
// A whole public inventory, valued — with floats, pattern seeds and Doppler phases
const inv = await api.inventory('76561198305185709', { game: 'cs2', currency: 'EUR' });
console.log(inv.summary.totalValue.steamPrice, inv.items.length);
// 30 days of daily prices on every marketplace
const history = await api.items.history('★ Karambit | Doppler (Factory New)', { source: 'markets', days: 30 });
console.log(Object.keys(history.markets)); // ['steam', 'skinport', 'csfloat', …]All prices are integers in minor units (cents) of the response's currency, never floats. A price is null when a market has no current listing.
What's covered
| Method | Endpoint | Notes |
| --- | --- | --- |
| api.items.get(name, opts) | GET /items/:name | Metadata, all price fields, Doppler variants; markets: true embeds per-marketplace rows |
| api.items.all(opts) | GET /items/all | The price sheet — every item for a game in one call (~4 MB) |
| api.items.prices(names, opts) | POST /items/prices | Up to 500 items in one request |
| api.items.markets(name, opts) | GET /items/:name/markets | Every marketplace's current price, the best one and the spread |
| api.items.marketsBulk(names, opts) | POST /items/markets | Same, up to 100 items |
| api.items.history(name, opts) | GET /items/:name/history | source: 'steam' \| 'markets' \| 'phases'; days or from/to; metric: 'close' \| 'low' \| 'avg' |
| api.items.historyBulk(names, opts) | POST /items/history | Per-market daily series, up to 100 items |
| api.inventory(steamid, opts) | GET /inventory/:steamid | SteamID64, profile URL or vanity name; fresh: true bypasses the cache; markets: true adds per-market rows |
| api.collections.list(opts) / .get(name) | GET /collections[/:name] | Collections with set icons; items inside one collection |
| api.crates.list(opts) / .get(name) | GET /crates[/:name] | Cases, capsules and packages with full drop tables; rare: 'only' for the knife/glove pool |
| api.currencies() · api.plans() | GET /currencies · /plans | Accepted currency codes; plan limits |
Full reference with every parameter and a sample response for each: steamdataapi.com/docs.
Examples
Cheapest marketplace for a list of items — one request, not a hundred:
const { data, missing } = await api.items.marketsBulk(
['AWP | Asiimov (Field-Tested)', 'M4A4 | Howl (Minimal Wear)'],
{ game: 'cs2' },
);
for (const row of data) {
if (row.found) console.log(row.marketHashName, row.best?.market, row.best?.price, 'spread', row.spread);
}
console.log('not in catalog:', missing);Mirror every price locally — poll the sheet, join by marketHashName:
const sheet = await api.items.all({ game: 'cs2' });
const byName = new Map(sheet.data.map((r) => [r.marketHashName, r.prices]));
console.log(sheet.count, 'items as of', sheet.cachedAt);Value an inventory in your currency, with the uncapped third-party total:
const inv = await api.inventory('https://steamcommunity.com/id/someone', { currency: 'EUR', markets: true });
console.log('Steam:', inv.summary.totalValue.steamPrice / 100, 'EUR');
console.log('Markets:', (inv.summary.totalValue.realAvg ?? 0) / 100, 'EUR');
for (const it of inv.items) {
console.log(it.marketHashName, it.float, it.phase ?? '', it.prices.value);
}Rust works the same way — pass game: 'rust':
const rust = await api.items.all({ game: 'rust' }); // every Rust item, priced
const door = await api.items.get('Metal Door', { game: 'rust' });Errors and retries
Every non-2xx response throws a SteamDataApiError carrying the API's error code and message, plus a few helpers:
import { SteamDataApiError } from 'steamdataapi';
try {
await api.items.history('x', { source: 'markets' });
} catch (err) {
if (err instanceof SteamDataApiError) {
err.status; // 429
err.code; // 'quota_exceeded'
err.quotaGroup; // 'history' — which endpoint group ran out
err.isQuotaExceeded; // true — do not retry until it resets
err.isRateLimited; // per-minute limiter — the client already retried this
err.isPlanForbidden; // endpoint not in your plan
err.reason; // for 401: 'missing' | 'scheme' | 'api_key' | 'session'
}
}The client retries automatically — honouring retry-after — on the per-minute rate limit, on Steam's transient 503 rate_limited during inventory reads, and on 502/504. It never retries an exhausted daily or monthly quota. Tune with maxRetries (default 2) and timeoutMs (default 30 000).
Options
const api = new SteamDataApi('sdk_…', {
currency: 'EUR', // default for every price-bearing call; per-call `currency` wins
timeoutMs: 30_000,
maxRetries: 2,
baseUrl: 'https://steamdataapi.com/api/v1', // self-hosted instances
fetch: myFetch, // bring your own (defaults to the global fetch)
headers: { 'X-Trace': '…' },
});TypeScript
Every response is typed (Item, PriceSheet, InventoryResponse, MarketsHistoryResponse, …) from the published reference. items.history narrows its return type on source. Fields this version does not know about are still passed through.
Keys in browsers
The client runs in browsers, but an API key in a browser bundle is public — anyone can read it and spend your quota. Call the API from your own backend and send your frontend the results.
Links
- Docs: https://steamdataapi.com/docs
- Get a key: https://steamdataapi.com/app
- Pricing and per-endpoint limits: https://steamdataapi.com/pricing
- CS2 skin API: https://steamdataapi.com/cs2-api · Rust skin API: https://steamdataapi.com/rust-api
- Steam market API: https://steamdataapi.com/steam-market-api · Steam inventory API: https://steamdataapi.com/steam-inventory-api
- Python client: https://pypi.org/project/steamdataapi/
License
MIT © Steam Data API. Not affiliated with Valve or Steam.
