Skip to main content

How to find the best bid and ask for a CS2 skin across every market

Find the highest buy order and lowest ask for any CS2 skin across every marketplace at once, with freshness, spread, and confidence. Runnable code included.

Updated 6 min readBeginnercurl · TypeScript · PythonMarkdown
On this page
  1. What you get back
  2. Step 1: make the request
  3. Step 2: read the bid the way the market pays it
  4. Step 3: use the execution-adjusted book for real decisions
  5. Step 4: decide whether to trust the quote
  6. When to reach for a different endpoint

Every CS2 marketplace shows you its own lowest listing and, if it has a buy-order book, its own highest bid. None of them shows you the best price across all of them. If you are pricing a trade, routing a purchase, or deciding where to sell, you need both sides of the book across every venue at once, with enough metadata to know whether each side is still real.

That cross-market top of book is called the NBBO (national best bid and offer, a term borrowed from equities). SkinPricer computes it for every tracked item and serves it from one endpoint, GET /v1/nbbo. This guide shows how to call it, how to read every field, and which checks to run before you act on the number.

What you get back#

One request returns the lowest ask and the highest bid across every integrated marketplace, each naming the venue it came from, plus the spread between them and a few quality signals. All prices are integer USD cents, so 6500 means $65.00.

FieldWhat it tells you
bestAskThe cheapest live listing across all venues: market, price, updatedAt, isStale. null when nothing is listed.
bestBidThe highest standing buy order. In the default view only cash-executable venues count (see below). null when no venue has a bid.
spreadBps(ask − bid) ÷ bid, in basis points (100 bps = 1%). null when a side is missing or the spread would exceed 10000 bps.
marketCount / freshMarketCountHow many venues quote the item, and how many of those quotes are fresh.
confidenceA 0 to 1 score built from coverage, freshness, and cross-market agreement.
locked / crossedZero spread, or a bid that nets above the ask after the selling venue's fees.
executionAdjustedThe same book re-ranked by what you would actually pay and receive after fees.

Step 1: make the request#

Identify the item by its exact Steam market_hash_name (or by its canonical id if you already store one). Send your API key in the Authorization header. The endpoint is included in 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/nbbo?market_hash_name=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",
});

const quote = await client.nbbo.get({
  marketHashName: "AK-47 | Redline (Field-Tested)",
});

if (quote.bestAsk && quote.bestBid) {
  console.log(`Buy on ${quote.bestAsk.market} at ${formatUsd(quote.bestAsk.price)}`);
  console.log(`Sell on ${quote.bestBid.market} at ${formatUsd(quote.bestBid.price)}`);
  console.log(`Spread: ${quote.spreadBps ?? "n/a"} bps`);
}
import os
import requests

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

ask, bid = nbbo["bestAsk"], nbbo["bestBid"]
if ask and bid:
    print(f"Buy on {ask['market']} at ${ask['price'] / 100:.2f}")
    print(f"Sell on {bid['market']} at ${bid['price'] / 100:.2f}")
    print(f"Spread: {nbbo['spreadBps']} bps")
Names contain spaces and a pipe, so URL-encode them in raw HTTP. The SDK and requests encode for you.
Response 200GET /v1/nbbo
Field reference
{
  "marketHashName": "Glock-18 | Water Elemental (Factory New)",
  "canonicalItemId": "cmlofca920lka01yozajhixt3",
  "bestAsk": {
    "market": "csfloat",
    "price": 4373,
    "updatedAt": "2026-09-25T11:20:00.981Z",
    "isStale": false
  },
  "bestBid": {
    "market": "marketcsgo",
    "price": 4486,
    "updatedAt": "2026-09-25T11:20:03.070Z",
    "isStale": false
  },
  "spreadBps": -252,
  "marketCount": 16,
  "freshMarketCount": 15,
  "locked": false,
  "crossed": false,
  "confidence": 0.88,
  "executionAdjusted": {
    "bestAsk": {
      "market": "csfloat",
      "price": 4373,
      "rawPrice": 4373,
      "appliedFeeBps": 0,
      "executability": 0.51,
      "updatedAt": "2026-09-25T11:20:00.981Z",
      "isStale": false,
      "reasons": [
        "unknown_executable_depth"
      ]
    },
    "bestBid": {
      "market": "buff163",
      "price": 4256,
      "rawPrice": 4365,
      "appliedFeeBps": 250,
      "withdrawalFlatUsdCents": 0,
      "executability": 0.51,
      "updatedAt": "2026-09-25T11:20:11.402Z",
      "isStale": false,
      "reasons": [
        "unknown_executable_depth"
      ]
    },
    "spreadBps": 275,
    "locked": false,
    "crossed": false,
    "executableConfidence": 0.7,
    "reasons": [
      "unknown_executable_depth"
    ],
    "feeModelVersion": "exec-fees-v1-estimated",
    "calculatedAt": "2026-09-25T11:28:40.236Z"
  },
  "calculatedAt": "2026-09-25T11:28:40.236Z"
}
The documented 200 response. Note that each side names its venue, and the adjusted bid is lower than the raw bid because it is net of the seller's fees.
Live
Try it live

Fetch the current NBBO for AK-47 | Redline (Field-Tested) right now, in your browser.

Step 2: read the bid the way the market pays it#

Not every bid is cash. Steam Community Market buy orders settle in Steam wallet credit, and some trade-bot venues pay in site balance. If those bids were mixed into the book, a wallet-denominated bid would routinely sit above a cash ask and every popular item would look like free money. So in the default all-markets view, bestBid only considers cash-executable venues.

If you do want every venue's bid considered, pass an explicit markets list (for example markets=steamcommunity,buff163,csfloat). The filter is then off and each requested venue's bid is taken verbatim. The Ask, bid & NBBO concept page explains the rule in full.

Step 3: use the execution-adjusted book for real decisions#

The executionAdjusted block re-ranks the same quotes by what a trade would actually cost. Its ask price is the total a buyer pays, its bid price is the net a seller receives after commission and withdrawal, and both carry the untouched rawPrice, the appliedFeeBps, and an executability score that discounts stale or thin quotes. Its spreadBps is the realistic round-trip cost; the top-level spreadBps is the display spread.

Node (SDK)
const adj = quote.executionAdjusted;

if (adj?.bestAsk && adj.bestBid) {
  console.log(`Display spread: ${quote.spreadBps} bps`);
  console.log(`After fees:     ${adj.spreadBps} bps`);
  console.log(
    `A ${formatUsd(adj.bestBid.rawPrice)} bid nets ${formatUsd(adj.bestBid.price)}`,
    `on ${adj.bestBid.market} (${adj.bestBid.appliedFeeBps} bps in fees)`,
  );
}

Step 4: decide whether to trust the quote#

A top-of-book number is only as good as the quotes behind it. Every side carries its own updatedAt and isStale flag (stale means older than a uniform 30-minute window), and the response as a whole carries confidence, freshMarketCount, and the crossed flag. A sensible gate before acting looks like this:

Node (SDK)
import type { NbboResponse } from "@skinpricer/sdk";

/** True when both sides are live, fresh, and backed by more than one venue. */
export function isActionable(q: NbboResponse): boolean {
  if (!q.bestAsk || !q.bestBid) return false;
  if (q.bestAsk.isStale || q.bestBid.isStale) return false;
  if (q.crossed) return false; // one leg probably moved first
  return q.freshMarketCount >= 2 && q.confidence >= 0.6;
}
Tune the thresholds to your risk. On thinly traded items a single stale quote may be the only one, so treat staleness as a soft signal there.
  • null is not zero. A missing bid means nobody is bidding, not that the bid is $0. Keep nulls out of averages.
  • A crossed book usually means one venue refreshed before the other. Re-query before you trade it.
  • spreadBps uses the bid as its denominator here, and is clamped to null above 10000 bps because a spread that wide almost always means a stale bid.
  • Cache for your polling interval rather than calling per page view. Your plan's per-minute budget is shared across every key on the account.

When to reach for a different endpoint#

The NBBO is the top of the book: one price per side. If you need to know how many units sit behind that price, read order-book depth. If you want every venue's quote side by side for a comparison table, use the full pricing snapshot. And to turn the quote into an estimate of what the item is worth, see estimating fair value.

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.