Inventory API
GET /api/inventory-lookup looks up
the CS2 items on a public Steam inventory. Nothing is persisted between requests beyond a
short-lived cache; this answers per request.
Request
GET /api/inventory-lookup?steamid=76561197960287930&key=cs2pk_your_key_here
| Param | Type / bounds | Notes |
|---|---|---|
| steamid | ≤200 characters, no whitespace or quote characters | A SteamID64, a vanity profile name, or a full profile URL: any of the three works. |
| key | /^[a-z0-9_-]{4,64}$/i |
Your publishable key. See Error codes for what happens without one. |
Response
{
"steamid": "76561197960287930",
"count": 214,
"items": [ /* … */ ],
"cached": true,
"ageMs": 42130
}
cached and ageMs tell you whether this answer came from the 5-minute
per-account cache and, if so, how stale it might be. That's useful if your UI wants to show a
"loaded moments ago" state versus a "showing a cached copy" state.
Caching and rate limits
Steam rate-limits inventory lookups hard, per IP, and every lookup this service makes leaves from the same server address, so unmetered traffic here could lock out every other customer. Three things keep that from happening:
- Per-account cache, 5 minutes. A reload of the same
steamidinside that window never reaches Steam at all. A vanity name and its resolved SteamID64 are cached as two separate keys (looking up both costs one extra Steam fetch), and the cache holds at most 200 distinct accounts, oldest evicted first. - One lookup in flight at a time, server-wide, through a small queue (up to 12 waiting). This turns a burst of new lookups into a line rather than a flood against Steam. A cache hit never enters this queue.
- A per-IP allowance: 5 cache misses per 60 seconds. Only misses count: repeatedly viewing an inventory that's already cached is free and doesn't spend your allowance.
If Steam itself answers with a rate limit (429), every lookup, not only yours, is held for a
60-second cooldown before any more are attempted, and your request gets a 503
immediately rather than waiting it out.
Errors
| Status | Body | Cause |
|---|---|---|
| 400 | { error: "Enter a SteamID, a profile name, or a link to your profile." } |
Missing, empty, too long, or contains whitespace/quote characters. |
| 403 | { error: "That inventory is private. …" } |
The account's inventory privacy is not set to public. |
| 404 | { error: "Couldn't find a Steam profile for \"…\"." } |
The SteamID, vanity name or URL didn't resolve to an account. |
| 429 | { error: "Busy right now …" }, Retry-After: 15 |
The server-wide lookup queue is full (12 waiting). |
| 429 | { error: "That is a lot of lookups …" }, Retry-After: 60 |
Your IP exceeded 5 cache misses in the last 60 seconds. |
| 503 | { error: "Steam is rate-limiting us right now. …" } |
Steam itself answered 429; a global 60-second cooldown is now in effect. |
| 502 | { error }, Steam's own error text |
Any other failure fetching from Steam (including a request that ran longer than our 60-second internal timeout). |
Access-control refusals (bad key, wrong origin, over quota) use the same JSON error shape at
401/403/429, checked before any of the above. See
Error codes.