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

How to Get Historical CS2 Skin Prices

There is no single historical price series for CS2. cs2.sh publishes three kinds: high-frequency OHLC of active orders from December 24, 2025, long-term archive prices with supply from 2023, and marketplace sale archives reaching back to 2013. Pick by the question, not by which one starts earliest.

The most common mistake in CS2 price analysis is treating a listing series and a sale series as the same thing. A sell listing is what someone is asking. A completed sale is what someone paid. Charting them together without labels produces conclusions that do not survive scrutiny.

Pick the dataset that answers your question#

Question Endpoint Coverage
How did asks or bids move intraday? POST /v1/prices/history From December 24, 2025
How did BUFF, Youpin, or C5Game prices move over years? POST /v1/archive/history From 2023
What did items sell for on BUFF, and how much supply existed? POST /v1/archive/buff From September 6, 2024
What did items sell for on CSFloat? POST /v1/archive/csfloat From 2022
What was Steam's median sale price and purchase count? POST /v1/archive/steam Daily from April 26, 2013
What sale samples did Youpin publish? POST /v1/archive/youpin 12h from November 12, 2025

Every one of these requires a Scale or Enterprise API key and accepts up to 100 items per request. GET /v1/schema is available on all plans and resolves the exact market_hash_name values the others expect.

Three shapes, not one#

The endpoints differ in more than their start dates, and the differences change how you write the join.

OHLC aggregation. POST /v1/prices/history builds open, high, low, and close from the same snapshots that feed current prices, at 5m, 30m, 1h, or 1d. Each bucket carries sample_count and the real open_time and close_time inside it.

Last-observed value. POST /v1/archive/history records the last reading in each bucket rather than aggregating it, plus hourly_volume and total_supply on the aggregate source. It updates about 1-2x per day.

Native marketplace series. The sale archives return each marketplace's own published statistic. Steam gives a median sale price and purchase count. CSFloat gives an arithmetic average and a sale count. BUFF gives sampled sale prices from its own chart plus the daily total supply it reports, and no volume. Youpin gives one sampled sale price per bucket and no volume at all.

Those three statistics are not interchangeable. A Steam median and a CSFloat average describe different centres of different distributions, and comparing their levels without labelling them is a category error.

Request a multi-year series#

bash
curl -X POST https://api.cs2.sh/v1/archive/history \
  -H "Authorization: Bearer <<YOUR_API_KEY>>" \
  -H "Accept-Encoding: gzip" --compressed \
  -H "Content-Type: application/json" \
  -d '{
  "items": ["USP-S | Printstream (Factory New)"],
  "start": "2023-01-01",
  "interval": "1d"
}'
json
"aggregate": {
  "time": "2026-04-27T23:59:07Z",
  "ask": 130.05,
  "ask_volume": 1225,
  "bid": 130.19,
  "bid_volume": 133,
  "hourly_volume": 27,
  "total_supply": 31291,
  "sample_count": 24
}

sources defaults to aggregate only, so BUFF, Youpin, or C5Game must be named explicitly. hourly_volume is an approximate sale count: the hourly estimate in 1h responses, and the sum of those estimates in each 1d bucket. total_supply is an approximate count of copies in circulation and appears on aggregate only.

Interval limits#

Endpoint Interval Maximum request range
prices/history 5m 14 days
prices/history 30m 90 days
prices/history 1h 365 days
prices/history 1d Unlimited
archive/history 1h, 1d Unlimited
archive/steam 1h, 1d Unlimited

The limits are per request. A year of 30m candles is four requests combined, which is expected rather than an error.

Export a reusable dataset#

For a file you will query repeatedly, write one row per item, source, bucket, and interval so nothing loses its meaning. Include market_hash_name, variant identity, source, interval, bucket, the observation time, the price fields, order counts, sample_count, and a dataset version.

Resolve the item universe through GET /v1/schema, batch names in groups of at most 100, request one source and interval per bounded range, and flatten source objects into rows without replacing nulls. Keep hourly_volume and total_supply nullable, because they do not populate on marketplace sources. Write item-level errors to a companion report rather than dropping them.

If several sources are exported, use separate rows rather than columns that imply simultaneous observation. Sources refresh independently and were not read at the same instant.

Reading the data correctly#

bucket aligns the interval. open_time, close_time, and archive time say when observations actually happened. When exact timing affects the result, use the latter.

A missing bucket is a gap. It is not a zero and not permission to carry the previous value forward. Forward-filling before summing volume inflates activity in exactly the quiet periods where the error matters most.

Steam sale history supports no variants, because the Community Market does not distinguish a phase from its base item. Variant history exists on the price endpoints and on the BUFF, CSFloat, and Youpin archives, and each variant series is independent of its base item.

sample_count is how much evidence sits behind a bucket. A one-sample bucket has identical OHLC values, which is a single reading rather than a still market.

When items fail#

Every endpoint here validates items independently and returns 200 with both items and errors[]. The codes differ by dataset: not_in_archive on the archives for a valid item with no rows, unsupported_variant on Steam for any variant request, and unsupported_source where an item has no identity on the requested marketplace.

A 404 not_found means no requested valid item has data in that dataset. Request-level failures carry error, message, and a request_id to quote when contacting support. Correct 400, 401, 403, and 404 before retrying; retry 429 and temporary 5xx with a bounded delay.

Worked example: thirteen years in one chart#

Take Steam as the long spine, since POST /v1/archive/steam reaches back to April 26, 2013. Request 1d buckets with no end to pull the full series, then layer POST /v1/archive/history from 2023 for BUFF and Youpin, POST /v1/archive/buff from September 6, 2024 for BUFF sale samples and supply, and POST /v1/prices/history from December 24, 2025 where intraday detail matters.

Store each series separately with its statistic recorded: Steam median sale, CSFloat average sale, archive last-observed listing, intraday OHLC close. Plot them as distinct lines rather than one merged history, and mark every point where a series begins. The dataset boundaries are real features of the data, and hiding them is how a chart starts lying.

Full references: POST /v1/prices/history, POST /v1/archive/history, POST /v1/archive/buff, POST /v1/archive/steam, GET /v1/schema. Start dates and refresh rates for everything: data coverage.