Skip to main content

How to get live CS2 skin prices from every marketplace

Pull live CS2 skin prices from every major marketplace in one call: per-market asks, bids, and aggregate stats in USD cents, with runnable curl and TypeScript.

Updated 6 min readBeginnercurl · TypeScript · PythonMarkdown
On this page
  1. What one pricing call returns
  2. Step 1: price a single item
  3. Step 2: narrow it to the venues you trade on
  4. Step 3: search the catalog and price many items
  5. Read the numbers correctly
  6. When to reach for a different endpoint

"What is this skin worth right now?" sounds like one question, but every marketplace answers it differently. Each venue has its own listings, its own buy orders, its own fees, and its own refresh cadence. Scraping them one by one means maintaining a dozen integrations, normalizing a dozen currencies, and still not knowing which quotes are current.

SkinPricer does that collection for you and serves the result from one endpoint, GET /v1/pricing/:marketHashName. A single call returns every covered venue's representative ask, every standing buy order, an aggregate over the asks, and a best bid and ask summary, all in integer USD cents. This guide covers the single-item read, narrowing it to the venues you trade on, and searching the catalog when you need many items at once.

What one pricing call returns#

FieldWhat it tells you
markets[]One representative ask per venue: market, price, fetchedAt (when SkinPricer last fetched it from the venue), ingestedAt (when it reached our price store), quoteSize (listings behind the quote), and isStale.
buyOrders[]Standing buy orders per venue, with totalQuantity. Often shorter than markets[]: only some venues run a bid book.
aggregateMin, max, average, median, and standard deviation over the asks, plus marketCount, freshMarketCount, and listingCount.
bestAsk / bestBidThe cheapest ask and the highest bid across venues, each naming its market. In the default view the bid only counts cash-executable venues.
spreadBpsThe best ask versus best bid gap in basis points. null when a side is missing, when the book is crossed, or when the gap would exceed 10000 bps.

Step 1: price a single item#

Put the exact Steam market_hash_name in the path, URL-encoded, and send your key in the Authorization header. The endpoint is part of every plan, starting with Developer.

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

curl -s "https://pricing.skinpricer.com/v1/pricing/AK-47%20%7C%20Redline%20(Field-Tested)" \
  -H "Authorization: ApiKey $SKINPRICER_API_KEY"
// npm i @skinpricer/sdk
import { SkinpricerClient, formatUsd } from "@skinpricer/sdk";

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

// The SDK URL-encodes the name for you.
const item = await client.pricing.get("AK-47 | Redline (Field-Tested)");

for (const quote of item.markets) {
  console.log(quote.market.padEnd(16), formatUsd(quote.price), quote.isStale ? "(stale)" : "");
}

const median = item.aggregate?.medianPrice;
if (median != null) console.log("Median ask across venues:", formatUsd(median));
import os
from urllib.parse import quote

import requests

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

for q in item["markets"]:
    flag = " (stale)" if q["isStale"] else ""
    print(f"{q['market']:<16} {q['price'] / 100:>9.2f} USD{flag}")
The name goes in the path, so encode it fully (spaces, the pipe, and parentheses). In Python, quote(name, safe='') does that.
Response 200GET /v1/pricing/:marketHashName
Field reference
{
  "canonicalItemId": "cmlofca920lka01yozajhixt3",
  "name": "Glock-18 | Water Elemental (Factory New)",
  "aggregate": {
    "minPrice": 6500,
    "maxPrice": 8012,
    "avgPrice": 7102,
    "medianPrice": 7119,
    "stdDev": 439.12,
    "marketCount": 10,
    "freshMarketCount": 9,
    "listingCount": 710,
    "updatedAt": "2026-06-23T21:59:48.717Z"
  },
  "markets": [
    {
      "market": "csfloat",
      "price": 6500,
      "currency": "USD",
      "fetchedAt": "2026-06-23T21:52:27.427Z",
      "ingestedAt": "2026-06-23T21:55:56.923Z",
      "quoteSize": 132,
      "ingestLatencyMs": 209496,
      "isStale": false,
      "sourceUpdatedAt": "2026-06-23T21:01:13.000Z",
      "sourceTimeKind": "last_modified",
      "sourceAgeSeconds": 3074
    }
  ],
  "buyOrders": [
    {
      "market": "buff163",
      "price": 6440,
      "currency": "USD",
      "fetchedAt": "2026-06-23T21:59:48.717Z",
      "ingestedAt": "2026-06-23T21:59:48.000Z",
      "quoteSize": 47,
      "totalQuantity": 47,
      "ingestLatencyMs": 0,
      "isStale": false
    }
  ],
  "bestAsk": {
    "market": "csfloat",
    "price": 6500,
    "updatedAt": "2026-06-23T21:52:27.427Z",
    "isStale": false
  },
  "bestBid": {
    "market": "buff163",
    "price": 6440,
    "updatedAt": "2026-06-23T21:59:48.717Z",
    "isStale": false
  },
  "spreadBps": 93,
  "marketCount": 10,
  "freshMarketCount": 9,
  "calculatedAt": "2026-06-23T22:07:13.752Z"
}
The documented 200 response, trimmed to one venue per list. A real response carries one entry per covered venue.
Live
Try it live

Fetch the current cross-market snapshot for AK-47 | Redline (Field-Tested) in your browser.

Step 2: narrow it to the venues you trade on#

Most integrations only care about a handful of markets: the ones you can deposit on, withdraw from, or hold inventory at. Pass a markets list (a comma-separated query parameter, or an array in the SDK) and the response is restricted to those venues. Market ids are the same join keys the market health endpoint returns, such as csfloat, buff163, skinport, and steamcommunity.

curl -s "https://pricing.skinpricer.com/v1/pricing/AK-47%20%7C%20Redline%20(Field-Tested)?markets=csfloat,buff163,skinport" \
  -H "Authorization: ApiKey $SKINPRICER_API_KEY"
const venues = await client.pricing.get("AK-47 | Redline (Field-Tested)", {
  markets: ["csfloat", "buff163", "skinport"],
});

if (venues.bestAsk && venues.bestBid) {
  console.log("Cheapest:", venues.bestAsk.market, formatUsd(venues.bestAsk.price));
  console.log("Best bid:", venues.bestBid.market, formatUsd(venues.bestBid.price));
}

Step 3: search the catalog and price many items#

When you do not know the exact name, or you want prices for a whole weapon or collection, use GET /v1/items. Every result is the same cross-market snapshot as the single-item read, and results are paginated. Search is tokenized: each word is matched as a case-insensitive prefix and every word must match, so redline ft narrows on both words rather than looking for that exact phrase. perPage goes up to 100.

curl -s "https://pricing.skinpricer.com/v1/items?search=redline&perPage=50&page=1" \
  -H "Authorization: ApiKey $SKINPRICER_API_KEY"
// searchEach walks every page for you.
for await (const item of client.items.searchEach({ search: "redline", perPage: 100 })) {
  const ask = item.bestAsk;
  console.log(item.name, ask ? formatUsd(ask.price) + " on " + ask.market : "no listings");
}
Each page costs one request against your per-minute budget, so a 100-item page is far cheaper than 100 single-item calls.

For a full catalog sync there is also GET /v1/items/all, which returns every tracked item with its latest per-market prices in one unpaginated array (capped at 50,000 rows). It is heavy, so run it on a schedule and cache the result rather than calling it per request.

Read the numbers correctly#

  • Every price is an integer number of USD cents. 1550 is $15.50. Divide by 100 only when you display it, so rounding never accumulates.
  • aggregate describes asks, not trades. The maxPrice is often one optimistic listing, and a single outlier can drag avgPrice. For a headline number, prefer minPrice (what you could buy at) or medianPrice (the typical ask).
  • Judge freshness by fetchedAt, not ingestedAt. isStale flips when fetchedAt is more than 30 minutes old; treat it as a soft signal on thinly traded items where an older quote may be the only one.
  • null means unknown, not zero. A venue with no listings is simply absent from markets[], and a missing bid side leaves bestBid and spreadBps as null.
  • quoteSize is how many listings back a venue's quote. A price backed by one listing can disappear the moment someone buys it.

When to reach for a different endpoint#

If you only need the top of the book, one best ask and one best bid with fee-adjusted execution prices, the NBBO guide is lighter and more precise. For charts and trend lines, price history returns time buckets instead of a snapshot. And if you are building a page that shows every venue side by side, the comparison site guide combines search, per-market quotes, and market health into one workflow. The full field reference lives on the pricing and item search API reference pages.

Endpoints used in this guide

Run this guide against live data

Plans start at $50/month on the Developer tier. Want to try first? Ask in our Discord for a free 2-day Developer key.