# Inventory output: currency, fields, pages and exports

These options work on `GET /v1/inventory` and
`GET /steam/api/inventory`. Keep your WOK key in the
`Authorization: Bearer YOUR_WOK_KEY` header.

The default response remains JSON: the native route returns an object with
totals and items; the compatibility route returns an item array. Existing
`top`, game, search, grouping and pricing options retain their meaning.
Currency conversion, field selection and export formatting run locally.
A normal inventory request can still fetch Steam when its cache is missing;
these output options do not add a separate Steam or proxy request.

## Currency

`currency=USD` is the default. Use a three-letter currency present in WOK's
saved FX snapshot, for example `currency=EUR`. There is no per-request FX
download. A non-USD snapshot must be at most 24 hours old; an unavailable or
stale snapshot returns `503 fx_unavailable` with `Retry-After: 60`.
An unsupported currency returns `400 unsupported_currency`.

WOK converts its USD valuation fields and native inventory totals. Explicit
provider-native price records keep their original currency and meaning.
Missing prices remain `null`.

Response headers include `X-Wok-Price-Currency`. Non-USD responses also expose
`X-Wok-FX-Rate`, `X-Wok-FX-Updated-At` and `X-Wok-FX-Age`.

## Select the fields you need

`select` is a comma-separated list, for example:

```text
select=assetid,pricemedian,pricesource,tradable,image
```

`markethashname`, `count` and `game` remain when available so projected rows
still identify the item. Field selection changes returned item rows; it does
not recalculate whole-inventory totals. Unsupported fields return
`400 invalid_select`.

Supported item fields:

```text
assetid, markethashname, marketname, prices_native, count, game,
tradable, marketable, tradelocked, tradeprotected, source, context_id,
visibility, protection_status, trade_view_assetids, tradelockuntil,
pricemedian, pricesafe, priceavg, pricelatest, pricemax, price_statistics,
source_statistics, price_stat, pricesource, priceupdatedat, price_basis,
prices, volume, image, image_url, type, attachments_known, stickers, charms,
attachment_value, unpriced_attachments, estimated_value, resale_estimates,
resale_estimate, inspect_link, inspect_links, inspect_items, paint_index,
paint_seed, float_value, doppler_phase, currency
```

Selecting a field does not request new inspect data or bypass source privacy.
Use `include_inspect=1` when you need the existing inspect enrichment.

## Pagination

Set `limit` from 1 to 1,000. It overrides native `top`. Optional `offset` is
0–100,000; a nonzero offset requires `limit`. Search and grouping are applied
before rows are paged.

With `limit`, native JSON adds:

```json
{
  "pagination": {
    "returned_rows": 100,
    "matched_rows": 240,
    "offset": 0,
    "limit": 100,
    "next_cursor": "SIGNED_CURSOR",
    "has_more": true
  }
}
```

The compatibility route remains an array. Pagination is exposed in headers
on both routes and export formats:

| Header | Meaning |
| --- | --- |
| `X-Wok-Returned-Rows` | Rows returned on this page |
| `X-Wok-Matched-Rows` | Rows matching this request before pagination |
| `X-Wok-Page-Offset` | Current row offset |
| `X-Wok-Has-More` | `1` when another page exists, otherwise `0` |
| `X-Wok-Next-Cursor` | Signed cursor, absent on the last page |

For the next page send the returned `cursor` with the same SteamID64, game,
filters, price options, currency, selected fields, limit and format. A cursor
requires `limit`; it cannot be combined with a nonzero offset,
`no_cache=1` or `refresh_prices=1`. It reads only an existing valid cached
inventory snapshot and does not resolve a vanity name or fetch Steam.

Expired or changed snapshots return HTTP 409. Restart from the first page of
a normal inventory read. A cursor preserves inventory source ordering;
local catalog prices may change between page reads.

## CSV and NDJSON

Use `format=csv` or `format=ndjson`. Without `limit`, an export includes all
matched rows, regardless of the native default `top`. With `limit`, it
exports that page. These formats contain item rows, not the native totals
envelope.

CSV uses UTF-8 with a BOM and a header row. Nested lists and objects are JSON
cells; unavailable scalar values are blank. NDJSON contains one JSON item
per line. API failures keep their structured JSON error body and non-success
HTTP status in every format.

## JavaScript

```js
const options = {
  game: "cs2", limit: 100, currency: "EUR",
  select: ["assetid", "pricemedian", "tradable"],
};
let page = await client.inventoryPage(steamId, options);
const cursor = page.headers["X-Wok-Next-Cursor"];
if (cursor) page = await client.inventoryPage(steamId, { ...options, cursor });

const csv = await client.exportInventory(steamId, { game: "cs2" }); // string
const bytes = await client.downloadInventory(steamId, {
  game: "cs2", format: "ndjson",
}); // Uint8Array
```

## Python

```python
options = {
    "game": "cs2", "limit": 100, "currency": "EUR",
    "select": ["assetid", "pricemedian", "tradable"],
}
page = client.inventory_page(steam_id, **options)
cursor = page["headers"].get("X-Wok-Next-Cursor")
if cursor:
    page = client.inventory_page(steam_id, cursor=cursor, **options)

csv_text = client.export_inventory(steam_id, game="cs2")  # str
download = client.download_inventory(steam_id, game="cs2", format="ndjson")  # bytes
```

`inventoryPage` / `inventory_page` returns `{data, headers}` for JSON, CSV
and NDJSON. Set `compatible: true` / `compatible=True` to use the
compatibility route. Export/download helpers accept the same output options
and raise `WokApiError` on HTTP failures; they do not save an error response
as a successful export. Each inventory read still consumes its normal quota
unit, including cached pages.

## Repeated checks and inventory privacy

Normal reads reuse fresh inventory membership. Default lifetimes are 30 minutes
for a populated inventory, 6 hours for an empty inventory, 1 hour for confirmed
private access, and 10 minutes for a missing inventory. Server settings may
change these lifetimes; response cache headers describe the applied window.
Reading cached access information does not renew its confirmation time.

Access observations come from requests WOK already performed. They survive
restarts. A public profile alone does not prove its inventory is public.
Network failures, HTML denials and HTTP 429 do not mark the account private.
Confirmed private access invalidates earlier public inventory snapshots.

`no_cache=1` requests a new source check. Simultaneous ordinary and forced
reads of the same SteamID/game share one physical inventory download; each
logical request still uses its normal quota. A transient source failure can
be reused for up to 2 seconds, including a failure after the first page, without
replacing a useful complete inventory snapshot. Failure responses include
`Retry-After` while that short retry window applies. A partial download never
becomes a successful complete inventory.

If a client disconnects, its subscription to the download is removed. The
source read stops when no other callers still need it. Use sensible client
timeouts and honour `Retry-After` instead of immediately sending the same batch.

Vanity-name resolution is shared and cached briefly. Official Steam rate,
budget and source errors return a typed error instead of an invalid-SteamID
error, and do not trigger paid XML fallback. Use SteamID64 in repeated checks
to avoid alias-resolution work altogether.
