How to get CS2 Doppler phase prices, from Phase 1 to Emerald
Price CS2 Doppler and Gamma Doppler by phase: Phase 1 to 4, Ruby, Sapphire, Black Pearl, and Emerald, with live buckets and history. Runnable code.
On this page
Doppler and Gamma Doppler knives are the clearest case where one Steam market name hides several very different items. A ★ Karambit | Doppler (Factory New) can be any of four phases or one of three gems, and those trade at very different prices, yet the market hash name is identical for all of them. A price quoted for the plain name is a blend, and it is wrong for every specific knife you might hold.
SkinPricer prices each phase and gem as its own item, so you can ask for exactly the variant you care about. This guide shows how to list an item's phases, get a cross-market price for each, break a phase down by float band, and pull a band's history.
How phases are identified#
Every phase and gem is a separate catalog item whose name is the base name plus a suffix. The plain base name still exists and represents the blended market; use it only when you really mean "any phase".
| Finish | Variant item names |
|---|---|
| Doppler | <base> - Phase 1 to <base> - Phase 4, plus the gems <base> - Ruby, <base> - Sapphire, and <base> - Black Pearl |
| Gamma Doppler | <base> - Phase 1 to <base> - Phase 4, plus the gem <base> - Emerald |
For example, ★ Karambit | Doppler (Factory New) - Phase 2 and ★ Karambit | Gamma Doppler (Factory New) - Emerald. Not every knife and wear has every gem, so do not build the names by hand; list them from the catalog as shown next.
Step 1: list the phases of an item#
Look up the base item in the item schema. Its record carries a variants array with one entry per phase or gem: the variant's exact marketHashName, its family (doppler or gamma_doppler), its display name, and a numeric phase for Phase 1 to 4. The schema lookup is available from the Developer plan up. It is not wrapped by the published SDK yet, so the TypeScript sample calls it with fetch.
export SKINPRICER_API_KEY="sp_live_•••" # from your account's API keys page
curl -s "https://pricing.skinpricer.com/v1/schema/items/%E2%98%85%20Karambit%20%7C%20Doppler%20(Factory%20New)" \
-H "Authorization: ApiKey $SKINPRICER_API_KEY"interface SchemaVariant {
marketHashName: string;
family: "doppler" | "gamma_doppler";
name: string;
phase?: number;
}
const base = "★ Karambit | Doppler (Factory New)";
const res = await fetch(
`https://pricing.skinpricer.com/v1/schema/items/${encodeURIComponent(base)}`,
{ headers: { Authorization: `ApiKey ${process.env.SKINPRICER_API_KEY}` } },
);
if (!res.ok) throw new Error(`schema lookup failed: ${res.status}`);
const { item } = (await res.json()) as { item: { variants?: SchemaVariant[] } };
const variants = item.variants ?? [];
console.log(variants.map((v) => v.name).join(", "));import os
from urllib.parse import quote
import requests
base = "★ Karambit | Doppler (Factory New)"
res = requests.get(
f"https://pricing.skinpricer.com/v1/schema/items/{quote(base, safe='')}",
headers={"Authorization": f"ApiKey {os.environ['SKINPRICER_API_KEY']}"},
timeout=10,
)
res.raise_for_status()
variants = res.json()["item"].get("variants") or []
print([v["name"] for v in variants]){
"schemaVersion": 1783069455007,
"item": {
"id": "cmlofcu700pem01yop1965ua2",
"marketHashName": "★ Karambit | Doppler (Factory New)",
"slug": "karambit-doppler-factory-new",
"category": "skin",
"baseName": "★ Karambit | Doppler",
"weapon": "Karambit",
"finish": "Doppler",
"rarity": "covert",
"wear": "Factory New",
"wears": ["Factory New", "Minimal Wear"],
"stattrak": false,
"souvenir": false,
"hasStattrak": true,
"hasSouvenir": false,
"variants": [
{
"marketHashName": "★ Karambit | Doppler (Factory New) - Phase 1",
"family": "doppler",
"name": "Phase 1",
"phase": 1,
"image": "https://community.akamai.steamstatic.com/economy/image/..."
},
{
"marketHashName": "★ Karambit | Doppler (Factory New) - Phase 2",
"family": "doppler",
"name": "Phase 2",
"phase": 2,
"image": "https://community.akamai.steamstatic.com/economy/image/..."
},
{
"marketHashName": "★ Karambit | Doppler (Factory New) - Phase 3",
"family": "doppler",
"name": "Phase 3",
"phase": 3,
"image": "https://community.akamai.steamstatic.com/economy/image/..."
},
{
"marketHashName": "★ Karambit | Doppler (Factory New) - Phase 4",
"family": "doppler",
"name": "Phase 4",
"phase": 4,
"image": "https://community.akamai.steamstatic.com/economy/image/..."
},
{
"marketHashName": "★ Karambit | Doppler (Factory New) - Black Pearl",
"family": "doppler",
"name": "Black Pearl",
"image": "https://community.akamai.steamstatic.com/economy/image/..."
},
{
"marketHashName": "★ Karambit | Doppler (Factory New) - Ruby",
"family": "doppler",
"name": "Ruby",
"image": "https://community.akamai.steamstatic.com/economy/image/..."
},
{
"marketHashName": "★ Karambit | Doppler (Factory New) - Sapphire",
"family": "doppler",
"name": "Sapphire",
"image": "https://community.akamai.steamstatic.com/economy/image/..."
}
],
"image": "https://community.akamai.steamstatic.com/economy/image/...",
"ids": { "buff163": "42998", "marketcsgo": "★ Karambit | Doppler (Factory New)" }
},
"registries": {
"rarities": [{ "key": "covert", "name": "Covert", "tier": 6, "color": "#eb4b4b" }],
"collections": {},
"containers": {}
}
}Step 2: get a cross-market price for every phase#
Each variant name works anywhere the API takes a market hash name. The NBBO for a phase returns the best ask and best bid across every venue that lists that phase separately, with the usual freshness and confidence signals. Venues that only list the blended item simply do not contribute a quote.
// npm i @skinpricer/sdk
import { SkinpricerClient, formatUsd } from "@skinpricer/sdk";
const client = new SkinpricerClient({
apiKey: process.env.SKINPRICER_API_KEY!,
authScheme: "ApiKey",
});
for (const variant of variants) {
const q = await client.nbbo.get({ marketHashName: variant.marketHashName });
const ask = q.bestAsk ? `${formatUsd(q.bestAsk.price)} on ${q.bestAsk.market}` : "no asks";
console.log(variant.name.padEnd(12), ask, `(${q.freshMarketCount} fresh venues)`);
}Step 3: break a phase down by float#
Within one phase, float still moves the price, and the lowest bands usually carry a premium. The attribute price endpoint returns BUFF163's phase buckets for a variant: a phase-wide bucket that covers every float, and one bucket per float band within the phase. Both are tagged with the phase in dimensions.variant. Request attributes=float,tag; the phase-wide bucket is a pure tag bucket and is omitted by the default float-only filter.
curl -s "https://pricing.skinpricer.com/v1/attribute-prices/latest?marketHashName=%E2%98%85%20Karambit%20%7C%20Doppler%20(Factory%20New)%20-%20Phase%202&attributes=float,tag" \
-H "Authorization: ApiKey $SKINPRICER_API_KEY"const phase = await client.attributePrices.latest({
marketHashName: "★ Karambit | Doppler (Factory New) - Phase 2",
attributes: ["float", "tag"],
});
for (const item of phase.items) {
for (const bucket of item.buckets) {
const band = bucket.dimensions.float;
const label = band ? `float ${band.min} to ${band.max}` : "all floats";
const ask = bucket.askCents === null ? "no asks" : formatUsd(bucket.askCents);
console.log(item.market, bucket.dimensions.variant, label, ask);
}
}{
"bucketSchemaVersion": 1,
"markets": ["buff163", "steamcommunity", "marketcsgo", "csfloat", "dmarket"],
"currency": "USD",
"asOf": "2026-06-23T09:18:45.864Z",
"items": [
{
"canonicalItemId": "cmlofa7dh08ww01yo9lrpej3e",
"marketHashName": "★ Karambit | Doppler (Factory New) - Phase 2",
"market": "buff163",
"buckets": [
{
"bucketId": "buff163:cmlofa7dh08ww01yo9lrpej3e:tag:446954:float:0-0.01",
"bucketKey": "tag:446954:float:0-0.01",
"bucketType": "attribute",
"attributeType": "tag",
"attributeTypes": ["tag", "float"],
"dimensions": {
"variant": "Phase2",
"float": { "min": 0, "max": 0.01, "minInclusive": true, "maxInclusive": false },
"fade": null,
"pattern": null,
"paintSeed": null
},
"sourceItemId": "44102",
"sourceBucketId": "buff163:44102:tag446954:f0-0.01",
"askCents": 197795,
"avgAskCents": 201340,
"medianAskCents": 199500,
"bidCents": 179787,
"askListingCount": 82,
"bidOrderCount": null,
"sourceUpdatedAt": "2026-06-23T09:10:00.000Z",
"collectedAt": "2026-06-23T09:11:35.446Z"
},
{
"bucketId": "buff163:cmlofa7dh08ww01yo9lrpej3e:tag:446954:float:0.01-0.02",
"bucketKey": "tag:446954:float:0.01-0.02",
"bucketType": "attribute",
"attributeType": "tag",
"attributeTypes": ["tag", "float"],
"dimensions": {
"variant": "Phase2",
"float": { "min": 0.01, "max": 0.02, "minInclusive": true, "maxInclusive": false },
"fade": null,
"pattern": null,
"paintSeed": null
},
"sourceItemId": "44102",
"sourceBucketId": "buff163:44102:tag446954:f0.01-0.02",
"askCents": 189676,
"avgAskCents": 191220,
"medianAskCents": 190100,
"bidCents": 179344,
"askListingCount": 66,
"bidOrderCount": null,
"sourceUpdatedAt": "2026-06-23T09:10:00.000Z",
"collectedAt": "2026-06-23T09:11:35.446Z"
}
]
}
],
"nextCursor": null,
"sourceGaps": []
}attributeTypes shows each bucket bands on both the phase tag and a float range.Step 4: track a phase band over time#
Every bucket has a stable bucketId. Pass it to attribute price history to get that band's open, high, low, and close asks per interval, plus the closing bid. History requires the Pro plan. Daily spacing over a quarter is a good default for charts; switch to 1h or 10m for recent, fast-moving windows.
const history = await client.attributePrices.history({
bucketId: "buff163:cmlofa7dh08ww01yo9lrpej3e:tag:446954:float:0-0.01",
granularity: "1d",
from: new Date(Date.now() - 90 * 24 * 60 * 60 * 1000),
});
for (const point of history.items[0]?.buckets[0]?.points ?? []) {
console.log(point.bucketStart.slice(0, 10), point.closeAskCents, point.closeBidCents);
}Pitfalls that cost money#
- Never value a specific knife from the plain base name. The blended price mixes cheap and expensive phases and fits none of them.
dimensions.variantuses the source market's own label (for examplePhase2orEmerald). Treat the variant item name from Step 1 as the authoritative label and the bucket tag as confirmation.- Gems are thin. A Ruby or Emerald may have only a handful of listings across all venues, so check
isStale,freshMarketCount, andaskListingCountbefore trusting a single quote. nullbids are common on gem bands. No buy order is not the same as a zero bid.- On knives, a listing's
paintIndexidentifies the phase when the market exposes it: Doppler Ruby is 415, Sapphire 416, Black Pearl 417, and Phases 1 to 4 are 418 to 421; Gamma Doppler Emerald is 568 and Phases 1 to 4 are 569 to 572.
When to reach for a different endpoint#
To price ordinary skins by float, without a phase, follow the float range guide. To inspect the individual listings behind a phase price, including exact floats and paint indexes, use the listing data guide. And for BUFF's full bucket set and history in one place, see BUFF163 prices and history.
Endpoints used in this guide
Plans start at $50/month on the Developer tier. Want to try first? Ask in our Discord for a free 2-day Developer key.
Related guides
- Floats & attributesIntermediate
How to price a CS2 skin by its float range
Price a CS2 skin by its exact float: how float-range buckets work per market, how to query them, and how to spot low-float premiums. Runnable API code.
GET /v1/attribute-prices/latestGET /v1/attribute-prices/history6 min read - History & backtestingIntermediate
How to get BUFF163 prices, float buckets, and history in USD
Get BUFF163 prices, native float buckets, and price history for CS2 items in USD cents, without scraping or converting currency. Runnable API code included.
GET /v1/markets/buff163/latestGET /v1/markets/buff163/history6 min read - Floats & attributesBeginner
How to get float, paint seed, and sticker data for live CS2 listings
Get per-listing CS2 data: float value, paint seed, paint index, and stickers for live listings across marketplaces, with runnable curl and TypeScript.
GET /v1/pricing/:marketHashName/listings5 min read - Prices & quotesBeginner
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.
GET /v1/nbbo6 min read