Skip to main content

How to get Rust skin prices with an API

Get Rust skin prices from the same API as CS2: game-scoped pricing, NBBO, history, and item search with one game parameter. Runnable code included.

Updated 6 min readBeginnercurl · TypeScript · PythonMarkdown
On this page
  1. How the game-scoped routes work
  2. Step 1: find the item
  3. Step 2: read the best bid and ask
  4. Step 3: pull price history
  5. What is different about Rust data
  6. Errors and readiness
  7. When to reach for a different endpoint

Rust skins trade on their own set of marketplaces, with their own listing formats and their own quirks, and there is far less tooling for them than for CS2. If you already price CS2 items with SkinPricer, you do not need a second integration: the same API serves Rust through game-scoped /v2 routes that take one extra parameter. If you are starting with Rust, the same routes are the fastest way to get normalized, cross-market prices without collecting them yourself.

This guide covers how the game-scoped routes work, how to find a Rust item, how to read its best bid and ask and its price history, and what is genuinely different about Rust data compared with CS2.

How the game-scoped routes work#

Every /v1 route is CS2 and stays exactly as documented. The core reads also exist as /v2 routes that require a game query parameter, game=rust or game=cs2; any other value is a 400. The response shapes are identical to their v1 counterparts, authentication is the same Authorization: ApiKey <key> header, and Rust is included in every plan with no add-on. CS2 and Rust calls draw from the same per-minute and monthly budget across all your keys. The Games: CS2 & Rust page is the full reference.

GET routeReturnsPlan
/v2/itemsCatalog search with live pricing, paginatedDeveloper and up
/v2/pricing/:marketHashNamePer-market asks, buy orders, aggregate, best bid and askDeveloper and up
/v2/nbboCross-market best bid and offerDeveloper and up
/v2/nbbo/depthAggregated order-book depthDeveloper and up
/v2/pricing/:marketHashName/listingsIndividual listingsDeveloper and up
/v2/pricing/:marketHashName/historyTime-bucketed price historyPro and up
/v2/pricing/:marketHashName/history/marketsHistory split per marketPro and up

Liquidity (/v2/liquidity/:marketHashName, /v2/liquidity/summary/:marketHashName, and POST /v2/liquidity/batch) and the item schema (/v2/schema, /v2/schema/changes, and /v2/schema/items/:idOrName) accept the same game parameter and keep their v1 plan requirements. Marketplace health for Rust is keyless at https://api.skinpricer.com/v2/markets/health?game=rust.

Step 1: find the item#

A Rust item is identified by its plain market hash name, which is the display name you see on the Steam market, for example Tempered AK47 or Big Grin. There are no wear suffixes to append. When you only know part of a name, search the Rust catalog: /v2/items takes search, markets, page, and perPage (up to 100 per page). Search text is limited to 512 characters, and there is no /v2/items/all, so page through the search instead.

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

curl -s "https://pricing.skinpricer.com/v2/items?game=rust&search=tempered&perPage=20" \
  -H "Authorization: ApiKey $SKINPRICER_API_KEY"
import os

import requests

res = requests.get(
    "https://pricing.skinpricer.com/v2/items",
    params={"game": "rust", "search": "tempered", "perPage": 20},
    headers={"Authorization": f"ApiKey {os.environ['SKINPRICER_API_KEY']}"},
    timeout=10,
)
res.raise_for_status()
for item in res.json()["data"]:
    ask = item.get("bestAsk")
    print(item["name"], ask and f"{ask['price'] / 100:.2f} USD on {ask['market']}")

Step 2: read the best bid and ask#

Call /v2/nbbo with game=rust and the item name. The response has the same shape as the CS2 NBBO: bestAsk and bestBid each name their venue, spreadBps measures the gap, and confidence, freshMarketCount, and the stale flags tell you how much to trust it. As with CS2, the default best bid only counts venues that pay sellers in cash. The TypeScript SDK wraps the v1 (CS2) routes today; game-scoped SDK methods arrive in a future SDK release, so call /v2 over plain HTTP for now. The SDK's exported types still describe these responses exactly.

curl -s "https://pricing.skinpricer.com/v2/nbbo?game=rust&market_hash_name=Tempered%20AK47" \
  -H "Authorization: ApiKey $SKINPRICER_API_KEY"
// Types only: npm i -D @skinpricer/sdk
import type { NbboResponse } from "@skinpricer/sdk";

const BASE = "https://pricing.skinpricer.com";
const headers = { Authorization: "ApiKey " + process.env.SKINPRICER_API_KEY };

async function rustGet<T>(path: string, params: Record<string, string>): Promise<T> {
  const url = new URL(path, BASE);
  url.searchParams.set("game", "rust");
  for (const [key, value] of Object.entries(params)) url.searchParams.set(key, value);

  const res = await fetch(url, { headers });
  if (res.status === 503) throw new Error("Rust reads are temporarily disabled; retry later");
  if (!res.ok) throw new Error("HTTP " + res.status + " on " + url.pathname);
  return (await res.json()) as T;
}

const nbbo = await rustGet<NbboResponse>("/v2/nbbo", { market_hash_name: "Tempered AK47" });

if (nbbo.bestAsk) console.log("Lowest ask:", nbbo.bestAsk.price / 100, "USD on", nbbo.bestAsk.market);
if (nbbo.bestBid) console.log("Best cash bid:", nbbo.bestBid.price / 100, "USD on", nbbo.bestBid.market);
console.log("Fresh venues:", nbbo.freshMarketCount, "| confidence:", nbbo.confidence);
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 NBBO response. This sample is a CS2 item; a Rust response has exactly the same fields.

Step 3: pull price history#

History works like CS2: /v2/pricing/:marketHashName/history?game=rust returns time buckets with avgPrice, medianPrice, minPrice, maxPrice, and the best bid and ask seen in each bucket, filtered by from, to, listingType, and interval. It needs the Pro plan. On Rust, pay attention to which field you chart.

import type { PriceHistoryResponse } from "@skinpricer/sdk";

const name = "Tempered AK47";
const history = await rustGet<PriceHistoryResponse>(
  "/v2/pricing/" + encodeURIComponent(name) + "/history",
  { interval: "HOUR_1" },
);

for (const bucket of history.history) {
  // On thin Rust items, chart the executable best ask, not the median.
  console.log(bucket.timestamp, bucket.bestAskCents, bucket.bestBidCents);
}
curl -s "https://pricing.skinpricer.com/v2/pricing/Tempered%20AK47/history?game=rust&interval=HOUR_1" \
  -H "Authorization: ApiKey $SKINPRICER_API_KEY"

What is different about Rust data#

  • No wear semantics. Rust items have no float value, wear tier, paint seed, StatTrak, or souvenir variants. On Rust listings the floatValue, paintSeed, paintIndex, and sticker fields are always null or empty; see Nulls & partial data.
  • CS2-only endpoints stay on v1. Attribute and float bucket prices, BUFF163 buckets, recommendations, and the batch min and max price reads have no game variant.
  • Plain names. The market hash name is the item's display name, so there is nothing to append for condition.
  • Budgets are shared. A Rust call costs the same as a CS2 call and comes out of the same quotas, reported in the X-RateLimit-* headers.

Errors and readiness#

StatusMeaning on /v2 game routes
400Missing or invalid game, or invalid input such as an over-long search.
404Unknown item in the requested game, or no pricing where the route requires it.
409The item selector is ambiguous. Use the exact name.
503Rust reads are temporarily disabled. Authentication and plan checks run first, so a 503 means your key and plan are fine. Retry later.
401 / 403 / 429The same key, plan, and quota rules as every key-gated route.

When to reach for a different endpoint#

Everything in the CS2 guides carries over to Rust once you switch to the /v2 route and add game=rust. The best bid and ask guide goes deeper on reading and trusting the NBBO, live prices covers the full per-market snapshot, and price history explains intervals and bucket semantics in detail.

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.