# How to check CS2 skin liquidity and time-to-sell

> Check how liquid a CS2 skin is and how long it takes to sell, per market and across markets, one item or thousands at once. With runnable API code.

*Liquidity & depth · Intermediate · Updated 2026-09-25*

A price is only half of what you need to know about a skin. The other half is how long it takes to turn that skin back into money. Two items can both be worth $60 while one sells in an hour and the other sits for three weeks. For inventory valuation, trading bots, and anything that holds positions, time-to-sell is the difference between a price and a realistic exit.

SkinPricer estimates it with a liquidity model built from each market's live listings and its observed sales rate. You get a badge, a median and 90th-percentile time-to-sell in hours, the market where the item clears fastest, and a per-market breakdown. This guide covers the single-item read, the slim summary, batch scoring for up to 100 items, and the Enterprise whole-catalog map.

## How the estimate works

For each supported market the model takes the listings you would have to wait behind and divides by how fast that market has been selling the item over the last day. If you list below the market's current best ask, you go to the front of the queue. If you list at or above it, you wait behind the current supply. The time comes from observed sales velocity only: a crossed book or a pile of bids never manufactures a fast estimate on its own.

The badge is set by the fastest market's median time-to-sell, using fixed thresholds so it stays explainable:

| Badge | Median time-to-sell on the best market |
| --- | --- |
| `EXTREMELY_LIQUID` | Under 2 hours |
| `VERY_LIQUID` | 2 to 12 hours |
| `LIQUID` | 12 to 48 hours |
| `ILLIQUID` | 2 to 7 days |
| `VERY_ILLIQUID` | 7 to 30 days |
| `EXTREMELY_ILLIQUID` | 30 days or more |
| `INSUFFICIENT_DATA` | Not enough recent sales to support an estimate (older snapshots may say `UNTRADED`) |

Two safety rails sit on top. When the sales data behind the best market is older than six hours, the badge is capped at `VERY_LIQUID`, and past a day it is capped at `LIQUID`, because a "sells within hours" claim needs recent evidence. And `confidence` (`HIGH`, `MEDIUM`, `LOW`, `CRITICAL`) plus `coverage` (`full`, `partial`, `none`) tell you how many markets corroborate the estimate.

## Step 1: score one item

Call [`GET /v1/liquidity/:marketHashName`](/api-docs/liquidity) with the URL-encoded item name. The liquidity endpoints require the Pro plan. Two optional query parameters change the question: `askPrice` (integer cents) asks "how long if I list at this price?", and `market` restricts the estimate to one venue.

**curl**

```bash
export SKINPRICER_API_KEY="sp_live_•••"   # from your account's API keys page

curl -s "https://pricing.skinpricer.com/v1/liquidity/AK-47%20%7C%20Redline%20(Field-Tested)" \
  -H "Authorization: ApiKey $SKINPRICER_API_KEY"
```

**Node (SDK)**

```ts
// npm i @skinpricer/sdk
import { SkinpricerClient } from "@skinpricer/sdk";

const client = new SkinpricerClient({
  apiKey: process.env.SKINPRICER_API_KEY!,
  authScheme: "ApiKey",
});

const liq = await client.liquidity.get("AK-47 | Redline (Field-Tested)");

if (liq.medianTimeToSellHours === null) {
  // No supported estimate. This is missing evidence, not proof of zero trades.
  console.log(`${liq.liquidityBadge}: not enough recent sales to estimate`);
} else {
  console.log(
    `${liq.liquidityBadge} (${liq.confidence}): median ${liq.medianTimeToSellHours}h,`,
    `p90 ${liq.p90TimeToSellHours}h, list on ${liq.bestMarketToList ?? "n/a"}`,
  );
}
```

**Python**

```python
import os
from urllib.parse import quote

import requests

name = "AK-47 | Redline (Field-Tested)"
res = requests.get(
    f"https://pricing.skinpricer.com/v1/liquidity/{quote(name, safe='')}",
    headers={"Authorization": f"ApiKey {os.environ['SKINPRICER_API_KEY']}"},
    timeout=10,
)
res.raise_for_status()
liq = res.json()

print(liq["liquidityBadge"], liq["confidence"], liq["medianTimeToSellHours"])
for m in liq["metrics"]:
    print(m["market"], m["salesPerDay"], m["medianTimeToSellHours"])
```

*The item name is a path segment here, so encode it fully (including `|`). The SDK does this for you.*

**Response 200** (`GET /v1/liquidity/:marketHashName`)

```json
{
  "canonicalItemId": "cmlofca920lka01yozajhixt3",
  "marketHashName": "Glock-18 | Water Elemental (Factory New)",
  "liquidityBadge": "VERY_ILLIQUID",
  "confidence": "LOW",
  "coverage": "full",
  "medianTimeToSellHours": 244.92,
  "p90TimeToSellHours": 273.19,
  "bestMarketToList": "csfloat",
  "salesPerDay": 13,
  "activeListings": 132,
  "referenceAskUsdCents": 6500,
  "marketWideSalesPerDay": 20,
  "marketWideListings": 233,
  "marketsWithSales": 3,
  "marketsWithDepth": 3,
  "marketWideMedianTimeToSellHours": 280.4,
  "marketWideP90TimeToSellHours": 304.57,
  "metrics": [
    {
      "market": "skinport",
      "salesPerDay": 3,
      "activeListings": 40,
      "bestAskUsdCents": 6516,
      "medianTimeToSellHours": 325.34,
      "p90TimeToSellHours": 395.1,
      "salesDataAgeMinutes": 7,
      "listingsDataAgeMinutes": 15
    }
  ],
  "notes": [
    "no_sales_data:dmarket",
    "sales_data_stale:csfloat",
    "sales_data_stale:marketcsgo",
    "thin_sales:marketcsgo",
    "thin_sales:skinport"
  ],
  "calculatedAt": "2026-06-23T22:07:16.813Z"
}
```

*The documented 200 response. `notes` flags thin or stale inputs per market, and every per-market metric is null when that market has no data.*

## Step 2: read the fields that matter

- `medianTimeToSellHours` / `p90TimeToSellHours`: the per-listing estimate on the best market. The p90 is the pessimistic case: nine in ten comparable listings sell within it.
- `bestMarketToList`: where the item clears fastest at the reference price. It can be `null` when no market supports an estimate.
- `referenceAskUsdCents`: the price the estimate was made against, either your `askPrice` or the best ask.
- `marketWide*` fields: how long it would take the whole current supply to clear at the combined sales rate. That is a saturation signal, not the time for your one listing.
- `metrics[]`: one row per supported market with `salesPerDay`, `activeListings`, `bestAskUsdCents`, and data ages, so you can see which venues drive the estimate.
- `notes[]`: flags such as `sales_data_stale:<market>` or `thin_sales:<market>`. Surface them next to the badge in your UI.

> **Null is not zero:** When the badge says `INSUFFICIENT_DATA`, the time-to-sell fields and the summary `score` are null. It means we cannot support an estimate from recent sales, not that nobody trades the item. Never map it to "never sells" or to zero value in a portfolio.

## Step 3: ask "how long at my price?"

The most practical use of `askPrice` is pricing a sale. Undercutting the current best ask by one cent moves you to the front of the queue, so the estimate at that price is the fastest exit the market currently supports. Listing above the best ask puts you behind the whole current supply. Comparing both tells you what speed costs.

```ts
const name = "AK-47 | Redline (Field-Tested)";
const current = await client.liquidity.get(name);
const ref = current.referenceAskUsdCents;

if (ref !== null) {
  const undercut = await client.liquidity.get(name, { askPrice: ref - 1 });
  const premium = await client.liquidity.get(name, { askPrice: Math.round(ref * 1.05) });
  console.log(`At best ask minus 1c: ${undercut.medianTimeToSellHours ?? "n/a"}h`);
  console.log(`At best ask plus 5%:  ${premium.medianTimeToSellHours ?? "n/a"}h`);
}
```

## Step 4: score many items

For badges in a list, [`GET /v1/liquidity/summary/:marketHashName`](/api-docs/liquidity-summary) returns just the badge, a lowercase label, a human `estimatedSaleTime` such as "2 - 6 hours", `confidence`, and a 0 to 100 `score`. To score a watchlist or an inventory, [`POST /v1/liquidity/batch`](/api-docs/liquidity-batch) takes up to 100 names in one call and returns the full shape per item, plus a `notFound` list for names we do not track.

**curl**

```bash
curl -s -X POST "https://pricing.skinpricer.com/v1/liquidity/batch" \
  -H "Authorization: ApiKey $SKINPRICER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"marketHashNames": ["AK-47 | Redline (Field-Tested)", "AWP | Asiimov (Field-Tested)"]}'
```

**Node (SDK)**

```ts
import { chunk } from "@skinpricer/sdk";

const names: string[] = [/* your watchlist */];
for (const batch of chunk(names, 100)) {
  const { results, notFound } = await client.liquidity.batch({ marketHashNames: batch });
  for (const r of results) console.log(r.marketHashName, r.liquidityBadge);
  if (notFound.length > 0) console.warn("Untracked:", notFound);
}
```

*Each batch call counts as one request against your per-minute budget, which makes it the efficient way to score lists.*

On Enterprise, [`GET /v1/liquidity/items`](/api-docs/liquidity-bulk) returns the whole catalog as one map keyed by market hash name, recomputed every 10 minutes. Probe [`GET /v1/liquidity/items/manifest`](/api-docs/liquidity-manifest) first and compare its `version` with the copy you hold, or send `If-None-Match` for a 304, so you only download the full map when it has changed. Both return 503 until the first snapshot is built after a deploy; honor `Retry-After`.

## When to reach for a different endpoint

Liquidity tells you how fast a listing clears. To know how much size the book can absorb right now, walk the ladder in the [order-book depth guide](/resources/cs2-order-book-depth). To turn badges into a realistic portfolio number, combine them with bids in the [inventory valuation guide](/resources/cs2-inventory-value). The [liquidity API reference](/api-docs/liquidity) documents every field and error.

## Endpoints used

- [GET /v1/liquidity/:marketHashName](/api-docs/liquidity.md)
- [GET /v1/liquidity/summary/:marketHashName](/api-docs/liquidity-summary.md)
- [POST /v1/liquidity/batch](/api-docs/liquidity-batch.md)
- [GET /v1/liquidity/items](/api-docs/liquidity-bulk.md)
- [GET /v1/liquidity/items/manifest](/api-docs/liquidity-manifest.md)

## Related guides

- [How to read CS2 order-book depth across every marketplace](/resources/cs2-order-book-depth.md)
- [How to value a CS2 inventory at live market prices](/resources/cs2-inventory-value.md)
- [How to estimate what a CS2 skin is really worth](/resources/cs2-skin-fair-value.md)
