# Quickstart

Make your first cs2.sh API request.

Make your first cs2.sh API requests. This guide fetches the item schema, downloads the complete latest-price snapshot, and introduces the dedicated BUFF and Steam snapshots.

## Get an API key

All `/v1` endpoints require an API key.

You can view your key from the dashboard after subscribing to a plan. To try the API first, sign up and open a ticket in our [Discord server](https://discord.gg/5AJemzwBtq) for a free 2-day Developer key.

The public API base URL is:

```text
https://api.cs2.sh
```

Every `/v1` request needs these headers:

| Header | Value |
| --- | --- |
| `Authorization` | `Bearer YOUR_API_KEY` |
| `Accept-Encoding` | `gzip` |

Keep your API key on your backend. Do not expose it in browser or client-side code.

## Fetch the item schema

`GET /v1/schema` returns the complete Counter-Strike 2 item schema.

`GET https://api.cs2.sh/v1/schema`

**curl**

```bash
curl https://api.cs2.sh/v1/schema \
  -H "Authorization: Bearer <<YOUR_API_KEY>>" \
  -H "Accept-Encoding: gzip" --compressed
```

**Python**

```python
import requests

headers = {
    "Authorization": "Bearer <<YOUR_API_KEY>>",
    "Accept-Encoding": "gzip",
}

response = requests.get(
    "https://api.cs2.sh/v1/schema",
    headers=headers,
)

response.raise_for_status()
data = response.json()
```

**Node**

```javascript
const headers = {
  "Authorization": "Bearer <<YOUR_API_KEY>>",
  "Accept-Encoding": "gzip",
};

const response = await fetch("https://api.cs2.sh/v1/schema", { headers });

if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
```

**Go**

```go
package main

import (
    "compress/gzip"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
)

func main() {
    req, _ := http.NewRequest("GET", "https://api.cs2.sh/v1/schema", nil)
    req.Header.Set("Authorization", "Bearer <<YOUR_API_KEY>>")
    req.Header.Set("Accept-Encoding", "gzip")

    resp, err := http.DefaultClient.Do(req)
    if err != nil { panic(err) }
    defer resp.Body.Close()

    if resp.StatusCode < 200 || resp.StatusCode >= 300 {
        body, _ := io.ReadAll(resp.Body)
        panic(fmt.Sprintf("HTTP %d: %s", resp.StatusCode, body))
    }

    var reader io.Reader = resp.Body
    if resp.Header.Get("Content-Encoding") == "gzip" {
        gz, err := gzip.NewReader(resp.Body)
        if err != nil { panic(err) }
        defer gz.Close()
        reader = gz
    }

    var data any
    if err := json.NewDecoder(reader).Decode(&data); err != nil { panic(err) }
    fmt.Printf("%#v\n", data)
}
```

**R**

```r
library(httr2)

resp <- request("https://api.cs2.sh/v1/schema") |>
  req_headers(
    Authorization = "Bearer <<YOUR_API_KEY>>",
    `Accept-Encoding` = "gzip"
  ) |>
  req_perform()

data <- resp_body_json(resp)
```

The schema contains approximately 47,500 items keyed by `market_hash_name`, including:

- Item categories and images
- Rarities and collections
- Wears and float ranges
- Marketplace ids
- Doppler variants

The schema and latest-price snapshot use the same keys:

```text
schema.items["USP-S | Printstream (Factory New)"]
prices.items["USP-S | Printstream (Factory New)"]
```

This lets you use the schema for item metadata and `/v1/prices/latest` for current marketplace prices without maintaining your own item-name mapping.

The schema updates automatically when Counter-Strike 2 game files update.

See [GET /v1/schema](/docs/schema) for all available item metadata.

## Fetch all latest prices

`GET /v1/prices/latest` returns current prices for every tracked item across every supported marketplace.

`GET https://api.cs2.sh/v1/prices/latest`

**curl**

```bash
curl https://api.cs2.sh/v1/prices/latest \
  -H "Authorization: Bearer <<YOUR_API_KEY>>" \
  -H "Accept-Encoding: gzip" --compressed
```

**Python**

```python
import requests

headers = {
    "Authorization": "Bearer <<YOUR_API_KEY>>",
    "Accept-Encoding": "gzip",
}

response = requests.get(
    "https://api.cs2.sh/v1/prices/latest",
    headers=headers,
)

response.raise_for_status()
data = response.json()
```

**Node**

```javascript
const headers = {
  "Authorization": "Bearer <<YOUR_API_KEY>>",
  "Accept-Encoding": "gzip",
};

const response = await fetch("https://api.cs2.sh/v1/prices/latest", { headers });

if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
```

**Go**

```go
package main

import (
    "compress/gzip"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
)

func main() {
    req, _ := http.NewRequest("GET", "https://api.cs2.sh/v1/prices/latest", nil)
    req.Header.Set("Authorization", "Bearer <<YOUR_API_KEY>>")
    req.Header.Set("Accept-Encoding", "gzip")

    resp, err := http.DefaultClient.Do(req)
    if err != nil { panic(err) }
    defer resp.Body.Close()

    if resp.StatusCode < 200 || resp.StatusCode >= 300 {
        body, _ := io.ReadAll(resp.Body)
        panic(fmt.Sprintf("HTTP %d: %s", resp.StatusCode, body))
    }

    var reader io.Reader = resp.Body
    if resp.Header.Get("Content-Encoding") == "gzip" {
        gz, err := gzip.NewReader(resp.Body)
        if err != nil { panic(err) }
        defer gz.Close()
        reader = gz
    }

    var data any
    if err := json.NewDecoder(reader).Decode(&data); err != nil { panic(err) }
    fmt.Printf("%#v\n", data)
}
```

**R**

```r
library(httr2)

resp <- request("https://api.cs2.sh/v1/prices/latest") |>
  req_headers(
    Authorization = "Bearer <<YOUR_API_KEY>>",
    `Accept-Encoding` = "gzip"
  ) |>
  req_perform()

data <- resp_body_json(resp)
```

The response contains an `items` object keyed by `market_hash_name`. Each item contains one object for every marketplace with available data.

A truncated response looks like this:

```json
{
  "response_time": "2026-07-26T18:54:04.041256216Z",
  "currency": "USD",
  "items": {
    "USP-S | Printstream (Factory New)": {
      "market_hash_name": "USP-S | Printstream (Factory New)",
      "buff": {
        "updated_at": "2026-07-26T18:50:53Z",
        "collected_at": "2026-07-26T18:53:10.67Z",
        "ask": 109.72,
        "ask_volume": 463,
        "bid": 106.17,
        "bid_volume": 41
      },
      "csfloat": {
        "updated_at": "2026-07-26T18:53:05.094Z",
        "collected_at": "2026-07-26T18:53:05.134Z",
        "ask": 107.99,
        "ask_volume": 227,
        "bid": 105
      }
    }
  }
}
```

`ask` is the current lowest sell listing. `bid` is the current highest generic buy order. `ask_volume` and `bid_volume` are the number of active listings or buy orders.

`updated_at` is when the marketplace last updated the price. `collected_at` is when cs2.sh collected it.

All prices are returned in USD.

This endpoint returns every tracked item, so the response can be large. Download it from your backend and cache it when you need to perform repeated price lookups.

See [GET /v1/prices/latest](/docs/prices-latest) for every marketplace field and the complete response schema.

## Dedicated marketplace snapshots

The general latest-price snapshot contains the current best prices and volumes from each marketplace. Dedicated endpoints provide additional marketplace-specific data.

### BUFF float and fade ranges

`GET /v1/market/buff/latest` returns BUFF prices split into float ranges.

`GET https://api.cs2.sh/v1/market/buff/latest`

**curl**

```bash
curl https://api.cs2.sh/v1/market/buff/latest \
  -H "Authorization: Bearer <<YOUR_API_KEY>>" \
  -H "Accept-Encoding: gzip" --compressed
```

**Python**

```python
import requests

headers = {
    "Authorization": "Bearer <<YOUR_API_KEY>>",
    "Accept-Encoding": "gzip",
}

response = requests.get(
    "https://api.cs2.sh/v1/market/buff/latest",
    headers=headers,
)

response.raise_for_status()
data = response.json()
```

**Node**

```javascript
const headers = {
  "Authorization": "Bearer <<YOUR_API_KEY>>",
  "Accept-Encoding": "gzip",
};

const response = await fetch("https://api.cs2.sh/v1/market/buff/latest", { headers });

if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
```

**Go**

```go
package main

import (
    "compress/gzip"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
)

func main() {
    req, _ := http.NewRequest("GET", "https://api.cs2.sh/v1/market/buff/latest", nil)
    req.Header.Set("Authorization", "Bearer <<YOUR_API_KEY>>")
    req.Header.Set("Accept-Encoding", "gzip")

    resp, err := http.DefaultClient.Do(req)
    if err != nil { panic(err) }
    defer resp.Body.Close()

    if resp.StatusCode < 200 || resp.StatusCode >= 300 {
        body, _ := io.ReadAll(resp.Body)
        panic(fmt.Sprintf("HTTP %d: %s", resp.StatusCode, body))
    }

    var reader io.Reader = resp.Body
    if resp.Header.Get("Content-Encoding") == "gzip" {
        gz, err := gzip.NewReader(resp.Body)
        if err != nil { panic(err) }
        defer gz.Close()
        reader = gz
    }

    var data any
    if err := json.NewDecoder(reader).Decode(&data); err != nil { panic(err) }
    fmt.Printf("%#v\n", data)
}
```

**R**

```r
library(httr2)

resp <- request("https://api.cs2.sh/v1/market/buff/latest") |>
  req_headers(
    Authorization = "Bearer <<YOUR_API_KEY>>",
    `Accept-Encoding` = "gzip"
  ) |>
  req_perform()

data <- resp_body_json(resp)
```

Each range includes its own ask, bid, listing volume, and buy-order volume. Doppler and Case Hardened variants can have their own ranges.

See [GET /v1/market/buff/latest](/docs/market-buff-latest) for the available range types and response fields.

### Steam orderbooks

`GET /v1/market/steam/latest` returns the latest full Steam bid and ask orderbooks for every tracked regular item.

`GET https://api.cs2.sh/v1/market/steam/latest`

**curl**

```bash
curl https://api.cs2.sh/v1/market/steam/latest \
  -H "Authorization: Bearer <<YOUR_API_KEY>>" \
  -H "Accept-Encoding: gzip" --compressed
```

**Python**

```python
import requests

headers = {
    "Authorization": "Bearer <<YOUR_API_KEY>>",
    "Accept-Encoding": "gzip",
}

response = requests.get(
    "https://api.cs2.sh/v1/market/steam/latest",
    headers=headers,
)

response.raise_for_status()
data = response.json()
```

**Node**

```javascript
const headers = {
  "Authorization": "Bearer <<YOUR_API_KEY>>",
  "Accept-Encoding": "gzip",
};

const response = await fetch("https://api.cs2.sh/v1/market/steam/latest", { headers });

if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
```

**Go**

```go
package main

import (
    "compress/gzip"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
)

func main() {
    req, _ := http.NewRequest("GET", "https://api.cs2.sh/v1/market/steam/latest", nil)
    req.Header.Set("Authorization", "Bearer <<YOUR_API_KEY>>")
    req.Header.Set("Accept-Encoding", "gzip")

    resp, err := http.DefaultClient.Do(req)
    if err != nil { panic(err) }
    defer resp.Body.Close()

    if resp.StatusCode < 200 || resp.StatusCode >= 300 {
        body, _ := io.ReadAll(resp.Body)
        panic(fmt.Sprintf("HTTP %d: %s", resp.StatusCode, body))
    }

    var reader io.Reader = resp.Body
    if resp.Header.Get("Content-Encoding") == "gzip" {
        gz, err := gzip.NewReader(resp.Body)
        if err != nil { panic(err) }
        defer gz.Close()
        reader = gz
    }

    var data any
    if err := json.NewDecoder(reader).Decode(&data); err != nil { panic(err) }
    fmt.Printf("%#v\n", data)
}
```

**R**

```r
library(httr2)

resp <- request("https://api.cs2.sh/v1/market/steam/latest") |>
  req_headers(
    Authorization = "Bearer <<YOUR_API_KEY>>",
    `Accept-Encoding` = "gzip"
  ) |>
  req_perform()

data <- resp_body_json(resp)
```

The response includes the best bid and ask alongside the full orderbook depth. Ask prices are sorted from lowest to highest, while bid prices are sorted from highest to lowest.

This snapshot is approximately 100 MB. Download and cache it from your backend instead of requesting it for individual item lookups.

Steam does not distinguish between individual Doppler or Case Hardened variants, so variants are not included.

See [GET /v1/market/steam/latest](/docs/market-steam-latest) for the complete orderbook format.

## Fetch specific items

If you only need current prices for a known set of items, use [POST /v1/prices/latest](/docs/prices-latest#post-v1priceslatest) instead. It accepts up to 100 `market_hash_name` values and returns the same marketplace price objects for those items.

## What's next

- Read [Data Coverage](/docs/data-coverage) for supported marketplaces, available fields, refresh rates, variants, and historical coverage.
- Read [Using the API](/docs/using-the-api) for shared request conventions, timestamps, missing data, partial success, errors, and limits.
- Use the [interactive API demo](/demo) to inspect responses without writing code.
