How to Get CSFloat Sales History with an API
Updated
cs2.sh's
POST /v1/archive/csfloatreturns one row per UTC day per item:price, the arithmetic average of every CSFloat sale that day, andvolume, 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
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
{
"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.