Skip to main content

How to get Steam Community Market prices without scraping

Get Steam Community Market prices for CS2 without scraping or IP bans: current asks, per-market history, and a comparison with cash marketplaces.

Updated 5 min readBeginnercurl · TypeScript · PythonMarkdown
On this page
  1. What you get for Steam from the API
  2. Step 1: read Steam's current ask and buy order
  3. Step 2: pull Steam-only history
  4. Step 3: put Steam next to cash markets
  5. Scraping versus an API, in practice
  6. When to reach for a different endpoint

The Steam Community Market is the reference price many CS2 players know, and it is the first thing most developers try to automate. The usual route is to call the same endpoints the Steam website uses in the browser. Those endpoints are part of Valve's website, not a published API: there is no documented contract, no quota you can plan against, and no notice when a response changes. Our own ingestion had to be updated in August 2026 when Steam changed the format of its order-book responses, which is exactly the kind of break a hand-rolled scraper finds out about in production.

SkinPricer already collects Steam Community Market data alongside every other venue and serves it under one key, in the same shape as everything else. This guide shows how to read Steam's current ask and buy orders, pull Steam-only price and sale history, and put Steam next to cash markets without mixing up the currencies.

What you get for Steam from the API#

You wantCallPlan
Steam's current lowest ask and highest buy orderGET /v1/pricing/:marketHashName?markets=steamcommunityDeveloper and up
Steam ask, buy-order, or sale history over timeGET /v1/pricing/:marketHashName/history/markets?markets=steamcommunityPro and up
Steam alongside cash venues, fee-adjustedGET /v1/nbbo?markets=steamcommunity,...Developer and up

Steam's market id is steamcommunity, the same join key the market health endpoint uses. Items are identified by their exact Steam market_hash_name, so the names you already store keep working. Every price comes back as integer USD cents, regardless of which wallet currency the listing was shown in.

Step 1: read Steam's current ask and buy order#

Restrict the pricing snapshot to Steam. The markets[] entry is Steam's representative ask; the buyOrders[] entry is its highest standing buy order with the quantity behind it. Each row carries fetchedAt, ingestedAt, and isStale, so you always know how old the number is.

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)?markets=steamcommunity" \
  -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",
});

const name = "AK-47 | Redline (Field-Tested)";
const steam = await client.pricing.get(name, { markets: ["steamcommunity"] });

const ask = steam.markets.find((q) => q.market === "steamcommunity");
const bid = steam.buyOrders?.find((q) => q.market === "steamcommunity");

console.log("Steam lowest ask:", ask ? formatUsd(ask.price) : "no listings");
console.log("Steam highest buy order:", bid ? formatUsd(bid.price) : "no buy orders");
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='')}",
    params={"markets": "steamcommunity"},
    headers={"Authorization": f"ApiKey {os.environ['SKINPRICER_API_KEY']}"},
    timeout=10,
)
res.raise_for_status()
data = res.json()

ask = next((q for q in data["markets"] if q["market"] == "steamcommunity"), None)
bid = next((q for q in data.get("buyOrders") or [] if q["market"] == "steamcommunity"), None)
print("ask:", ask and ask["price"] / 100, "bid:", bid and bid["price"] / 100)
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 pricing shape. With markets=steamcommunity, markets[] and buyOrders[] carry the Steam rows.
Live
Try it live

Fetch the full cross-market snapshot for AK-47 | Redline (Field-Tested) live, Steam included.

Step 2: pull Steam-only history#

Per-market history returns one series per venue, so filtering it to steamcommunity gives you a Steam-only chart. listingType picks what you are charting: SELL_OFFER for asks (the default), BUY_ORDER for bids, or SALE_HISTORY for completed-sale statistics, which Steam publishes and SkinPricer ingests as rolling windows. With SALE_HISTORY, salesWindow (24h, 7d, 30d, or 90d, default 30d) selects the window and each bucket's volume is the number of sales in that window.

const weekAgo = new Date(Date.now() - 7 * 24 * 3600 * 1000);

const sales = await client.history.byMarket(name, {
  markets: ["steamcommunity"],
  listingType: "SALE_HISTORY",
  salesWindow: "7d",
  from: weekAgo,
  interval: "DAY_1",
});

for (const series of sales.series) {
  for (const bucket of series.history) {
    console.log(bucket.timestamp, "median sale:", bucket.medianPrice, "sales in 7d:", bucket.volume);
  }
}
curl -s "https://pricing.skinpricer.com/v1/pricing/AK-47%20%7C%20Redline%20(Field-Tested)/history/markets?markets=steamcommunity&listingType=SALE_HISTORY&salesWindow=7d&interval=DAY_1" \
  -H "Authorization: ApiKey $SKINPRICER_API_KEY"
History endpoints need the Pro plan. The response also echoes effectiveInterval, the bucket width actually served.
  • Bucket widths snap to stored rollups: MINUTE_5 and MINUTE_15 serve 10-minute buckets, HOUR_1 and HOUR_6 serve hourly buckets, DAY_1 serves daily buckets.
  • Without interval, the width follows your range: up to 7 days gets 10-minute buckets, up to 180 days hourly, anything longer daily.
  • maxPrice on ask history includes extreme listings with no outlier cap. It is not a fair-value estimate.

Step 3: put Steam next to cash markets#

Steam prices settle in wallet credit, so they are usually higher than cash-market prices for the same skin. That is why the default cross-market best bid on the NBBO excludes Steam: a wallet-credit bid is not money you can withdraw. To see Steam compete, pass an explicit markets list that includes steamcommunity; the executionAdjusted block then shows what the Steam bid nets after an estimated seller fee.

Node (SDK)
const book = await client.nbbo.get({
  marketHashName: name,
  markets: ["steamcommunity", "csfloat", "buff163", "skinport"],
});

const adjustedBid = book.executionAdjusted?.bestBid;
if (adjustedBid) {
  console.log("Best bid nets", formatUsd(adjustedBid.price), "on", adjustedBid.market,
    adjustedBid.market === "steamcommunity" ? "(wallet credit)" : "(cash)");
}

Scraping versus an API, in practice#

  • Maintenance: a scraper breaks when the page or response changes. An API contract is versioned, and changes are announced in the changelog.
  • Identity: SkinPricer joins Steam rows to the same canonical item as every other venue, so one name resolves across markets.
  • Currency: Steam shows prices in the viewer's wallet currency. The API normalizes every quote to USD cents.
  • Freshness: each quote carries fetch and ingest timestamps and a stale flag, instead of a number of unknown age.
  • Budget: your plan's per-minute and monthly quotas are published up front and reported on every response in X-RateLimit-* headers.

When to reach for a different endpoint#

For the full cross-market view rather than Steam alone, start with live CS2 prices. For longer charts and aggregate history across every venue, see CS2 price history. The field-level reference for everything above is on the pricing, per-market history, and NBBO 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.