BUFF sale history is now available - POST /v1/archive/buff returns sale prices and daily total supply since September 2024.

How to Get CSFloat Sales History with an API

Updated

Endpoints used: POST /v1/archive/csfloat & POST /v1/prices/history

Related pages: CSFloat price API & CS2 price history API

cs2.sh's POST /v1/archive/csfloat returns one row per UTC day per item: price, the arithmetic average of every CSFloat sale that day, and volume, the number of sales. Coverage begins in 2022 and includes Doppler, Gamma Doppler, and Case Hardened variants.

CSFloat is one of the few CS2 marketplaces that exposes what items actually sold for rather than only what sellers are asking. cs2.sh archives that series per day and per variant, so a Phase 2 Doppler has its own sale history rather than being blended into the base item.

What cs2.sh provides for CSFloat sales#

Property Value
Endpoint POST /v1/archive/csfloat
Coverage Daily from 2022
Interval Daily only. There is no interval parameter.
Refresh About 1-2x per day
Items per request 100
Variants Supported, returned under variants
Currency Always USD
Access Requires a Scale or Enterprise API key

Two details separate this from POST /v1/archive/steam, and mixing them up produces wrong charts. CSFloat price is an arithmetic average of that day's sales; Steam price is a median. CSFloat identifies a day with date as a YYYY-MM-DD string; Steam identifies a bucket with bucket as a full timestamp. CSFloat supports variants; Steam supports none.

Request the series#

bash
curl -X POST https://api.cs2.sh/v1/archive/csfloat \
  -H "Authorization: Bearer <<YOUR_API_KEY>>" \
  -H "Accept-Encoding: gzip" --compressed \
  -H "Content-Type: application/json" \
  -d '{
  "items": [
    "USP-S | Printstream (Factory New)"
  ],
  "start": "2026-04-27",
  "end": "2026-07-25"
}'
Field Type Required Description
items string[] Yes market_hash_name values, max 100.
start string No YYYY-MM-DD or RFC3339. Defaults to 2020-01-01.
end string No YYYY-MM-DD or RFC3339. Defaults to now.

Both dates are optional, so omitting them returns the item's full archived history.

The response shape#

json
{
  "response_time": "2026-07-26T18:54:19.632716841Z",
  "currency": "USD",
  "start": "2026-04-27T00:00:00Z",
  "end": "2026-07-25T00:00:00Z",
  "items": {
    "USP-S | Printstream (Factory New)": {
      "market_hash_name": "USP-S | Printstream (Factory New)",
      "count": 89,
      "data": [
        {
          "date": "2026-04-27",
          "price": 149.76,
          "volume": 8
        },
        {
          "date": "2026-04-28",
          "price": 189.81,
          "volume": 10
        }
      ]
    }
  }
}
Field Type Description
items.<name>.count integer Number of daily aggregates returned.
items.<name>.data[] array Daily aggregates.
data[].date string Day as YYYY-MM-DD, UTC.
data[].price number | null Arithmetic average of all sale prices that day, USD.
data[].volume integer Number of sales that day.
items.<name>.variants object The same shape, keyed per variant.
errors ItemError[] Per-item failures returned alongside successful results.

price can be null on a day that recorded volume but no usable price. That combination is real data, not a malformed row, so keep the volume and drop the price rather than discarding the day.

Reading the data correctly#

A daily arithmetic average hides its own distribution. It does not expose individual transactions or the spread of prices inside the day, so on a low-volume day a single unusual float, paint seed, or sticker premium moves the whole figure. Carry volume beside price everywhere the average is displayed, and weight accordingly: to combine several items, multiply each item's daily average by its own volume before summing, never average the averages.

Variant series are independent. A Phase 2 daily average is not a fallback for a missing Phase 4 day, and neither is the base item. When comparing two phases, divide one variant's price by the other's on the same date, and show both volume values next to the ratio: a one-sale Ruby average and a fifty-sale Phase 1 average do not carry equal weight.

This endpoint answers what sold. POST /v1/prices/history with sources: ["csfloat"] answers how the active ask (the lowest sell listing) and bid (the highest buy order) moved, from December 24, 2025. Joining them on date gives the gap between what sellers listed at and what buyers actually paid. Note that CSFloat carries ask and bid OHLC but no bid_volume, so a rising bid never comes with depth behind it.

A day absent from data[] is a gap. It is not a zero-sale day unless the response explicitly returns one.

When items are missing or fail#

Code Meaning
not_in_archive A valid item has no CSFloat archive data.
unknown_item The market_hash_name does not resolve.
invalid_format The item string is malformed.
404 not_found None of the requested valid items have CSFloat archive data.

Partial results return 200 with both items and errors[]. Read errors[] on every response rather than only on failures, or items silently missing from a basket will look like zero activity.

Worked example: listed price against realised price#

Request an item's CSFloat sale archive and its CSFloat listing history over the same window. For each date, take the archive price and the close_ask from the matching 1d history bucket. The percentage gap between them, close_ask / price - 1, is how far above realised sales the cheapest listing sat that day.

Read it carefully. A wide gap can mean sellers are optimistic, or it can mean the day's sales happened to include a high-float or sticker-heavy copy that the listing series never identified. Keep volume and sample_count beside the figure so a two-sale day is visibly weaker evidence than a forty-sale day.

Full request, response, and error reference: POST /v1/archive/csfloat. How variants are named and versioned: variants. Archive endpoints need a Scale or Enterprise key.