The v2 API returns items as ItemV2 — a leaner shape than v1. Instead of always fetching every field, you pick an intent: a preset that controls which fields come back, how expensive the query is, and how long responses stay cached.Pass it as a query param (or in the JSON body for POST /api/v2/items/many):Invalid values return 400. If you omit intent, each endpoint uses its own default (see each endpoint in the reference — usually minimal for item lookups, card for search and list itemdata).v2 coexists with v1. Existing v1 clients keep working; new integrations should prefer /api/v2.
Intents#
| Intent | Best for | Includes (high level) | Cache TTL |
|---|
minimal | Lookups, widgets, matching IDs | ids, name, slug, image, type, description, status | 2 hours |
card | Lists, search results, UI cards | minimal + flags, colorHex, price, ncValue, rarity, category, estVal | 15 minutes |
pricer | Pricing tools | ids, image, name, slug, type, status, rarity, price, ncValue, saleStatus, flags | 1 hour |
full | Item detail pages | every ItemV2 field | 30 minutes |
Prefer the smallest intent that fits. minimal is cheaper for you (quota) and for us (fewer JOINs, longer cache). Don't request full if card is enough.
The pricer intent is probably the most popular (used by SDB Pricer script) so it has a higher cache hit rate
Field notes#
price — acquisition price: NP market (type: "np"), active NC Mall (type: "ncMall"), or null.
ncValue — NC secondary-market trade value (caps). Only present for NC items with a known value; may be omitted entirely.
saleStatus — slim liquidity badge (ets / regular / hts).
flags — e.g. wearable, neohome, bd, missingInfo, plus other free-form tags.
Each intent is a separate cache entry. Asking for card then minimal for the same item does not derive one from the other — they are stored and served independently.
Cache#
Where caching applies, v2 uses a two-layer model:1.
CDN (Cloudflare) — for eligible successful GET responses
2.
Redis — per-item, keyed by lookup type + id + intent
TTL follows the intent table above. Endpoint-specific cache behaviour (search, lists, POST, etc.) is documented on each operation in the API reference.How it interacts with rate limits#
We still use an item-based quota (data points returned):Cache hits do not consume quota (on routes that use the item cache)
Only responses that actually hit the database count toward your limit
So caching helps both latency and your rate limit.This is why picking a stable intent and reusing it matters — repeated minimal lookups for the same items will often be free hits after the first miss.
Cache-Control (GET)#
On cacheable item GETs, successful responses typically look like:Cache-Control: public, s-maxage=<TTL>, stale-while-revalidate=<4× TTL>
Where <TTL> is the intent TTL from the table above. 404s are not cached (no-store).Bypass with ?fresh=1#
GET /api/v2/items/123?intent=card&fresh=1
Forces a fresh database load
Returns Cache-Control: no-store
Still counts toward your quota
Still writes back to Redis for later requests
You probably never need to use this. So use sparingly — when you know the data just changed and you can't wait for TTL.
Don't put fresh=1 in a tight loop. You'll burn quota and load as if there were no cache at all.
Quick examples#
GET /api/v2/items/many?type=id&data[]=1&data[]=2&intent=minimal
Force refresh for a pricing toolGET /api/v2/items/negg?intent=pricer&fresh=1
Cache is TTL-only — there is no “invalidate this item now” API. After a price or metadata change, values can stay stale until the intent TTL expires (or until you call with fresh=1).