# 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.

*Prices & quotes · Beginner · Updated 2026-09-25*

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`](/api-docs/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.

| Field | What it tells you |
| --- | --- |
| `bestAsk` | The cheapest live listing across all venues: `market`, `price`, `updatedAt`, `isStale`. `null` when nothing is listed. |
| `bestBid` | The 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` / `freshMarketCount` | How many venues quote the item, and how many of those quotes are fresh. |
| `confidence` | A 0 to 1 score built from coverage, freshness, and cross-market agreement. |
| `locked` / `crossed` | Zero spread, or a bid that nets above the ask after the selling venue's fees. |
| `executionAdjusted` | The 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.

**curl**

```bash
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"
```

**Node (SDK)**

```ts
// 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`);
}
```

**Python**

```python
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 200** (`GET /v1/nbbo`)

```json
{
  "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.*

## 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](/api-docs/reading-prices) concept page explains the rule in full.

> **Raw prices are sticker prices:** `bestAsk.price` is what the listing shows, not what you pay, and `bestBid.price` is the gross bid, not what you receive. Buyer fees, seller commission, and withdrawal costs differ by venue. Use `executionAdjusted` whenever the decision involves money changing hands.

## 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.

```ts
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:

```ts
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](/resources/cs2-order-book-depth). If you want every venue's quote side by side for a comparison table, use the full [pricing snapshot](/resources/live-cs2-skin-prices). And to turn the quote into an estimate of what the item is worth, see [estimating fair value](/resources/cs2-skin-fair-value).

## Endpoints used

- [GET /v1/nbbo](/api-docs/nbbo.md)
- [Reading prices: ask, bid & NBBO](/api-docs/reading-prices.md)
- [Freshness & timestamps](/api-docs/freshness.md)

## Related guides

- [How to get live CS2 skin prices from every marketplace](/resources/live-cs2-skin-prices.md)
- [How to read CS2 order-book depth across every marketplace](/resources/cs2-order-book-depth.md)
- [Steam Market vs cash-market prices: how to compare them correctly](/resources/steam-vs-cash-market-prices.md)
- [How to estimate what a CS2 skin is really worth](/resources/cs2-skin-fair-value.md)
