# POST /v1/archive/youpin

Youpin sale history API for CS2 - sale prices at 1h/4h/12h intervals in USD, 12h data since November 12, 2025, updated ~1-2x per day.

Returns Youpin sale price history at three independent intervals.

- `12h` frequency from November 12, 2025
- `4h` frequency from June 27, 2026
- `1h` frequency from July 20, 2026

Youpin publishes one sale per sampling bucket and does not provide sale volume. A 1h series can therefore contain at most 24 points per day, a 4h series 6, and a 12h series 2.

The same sale can appear in more than one series:

```text
sale at 12:42
  -> 1h series:  bucket 12:00
  -> 4h series:  bucket 12:00
  -> 12h series: bucket 12:00
```

Since the same sale can appear in multiple series, I'd recommend treating `1h`, `4h`, and `12h` as separate views of the data.

About 16,000 liquid items are collected, plus mapped Doppler, Gamma Doppler, and Case Hardened variants. Data updates ~1-2x per day. Max 100 items per request.

## Access

Requires a Scale or Enterprise API key.

## Supported intervals

| Interval | Sampling bucket |
| --- | --- |
| `1h` | One sale per hour |
| `4h` | One sale per 4 hours |
| `12h` | One sale per 12 hours |

All observed intervals return together under each item's `intervals` map.

## Request

`POST https://api.cs2.sh/v1/archive/youpin`

**curl**

```bash
curl -X POST https://api.cs2.sh/v1/archive/youpin \
  -H "Authorization: Bearer <<YOUR_API_KEY>>" \
  -H "Accept-Encoding: gzip" --compressed \
  -H "Content-Type: application/json" \
  -d '{
  "items": [
    "★ Karambit | Doppler (Factory New)"
  ],
  "start": "2026-01-29",
  "end": "2026-07-28"
}'
```

**Python**

```python
import requests

headers = {
    "Authorization": "Bearer <<YOUR_API_KEY>>",
    "Accept-Encoding": "gzip",
    "Content-Type": "application/json",
}

payload = {
    "items": ["★ Karambit | Doppler (Factory New)"],
    "start": "2026-01-29",
    "end": "2026-07-28",
}

response = requests.post(
    "https://api.cs2.sh/v1/archive/youpin",
    headers=headers,
    json=payload,
)

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

**Node**

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

const response = await fetch("https://api.cs2.sh/v1/archive/youpin", {
  method: "POST",
  headers,
  body: JSON.stringify({
    "items": [
      "★ Karambit | Doppler (Factory New)"
    ],
    "start": "2026-01-29",
    "end": "2026-07-28"
  }),
});

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

**Go**

```go
package main

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

func main() {
    payload := map[string]any{
        "items": []any{
            "★ Karambit | Doppler (Factory New)",
        },
        "start": "2026-01-29",
        "end": "2026-07-28",
    }
    body, _ := json.Marshal(payload)
    req, _ := http.NewRequest("POST", "https://api.cs2.sh/v1/archive/youpin", bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    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)

payload <- list(
  items = list(
    "★ Karambit | Doppler (Factory New)"
  ),
  start = "2026-01-29",
  end = "2026-07-28"
)

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

data <- resp_body_json(resp)
```

## Parameters

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `items` | `string[]` | Yes | List of market_hash_name values (max 100) |
| `start` | `string` | No | Start date (YYYY-MM-DD or RFC3339). Default 180 days ago. Floored to the hour. |
| `end` | `string` | No | End date (YYYY-MM-DD or RFC3339). Default now. Ceiled to the hour. |

## Response

```json
{
  "response_time": "2026-07-28T16:20:41.318204951Z",
  "currency": "USD",
  "start": "2026-01-29T00:00:00Z",
  "end": "2026-07-28T00:00:00Z",
  "items": {
    "★ Karambit | Doppler (Factory New)": {
      "market_hash_name": "★ Karambit | Doppler (Factory New)",
      "intervals": {
        "1h": {
          "count": 120,
          "data": [
            {
              "bucket": "2026-07-21T16:00:00Z",
              "time": "2026-07-21T16:52:38.307Z",
              "price": 1314.88
            },
            {
              "bucket": "2026-07-21T23:00:00Z",
              "time": "2026-07-21T23:42:05.008Z",
              "price": 1920.6
            }
          ]
        },
        "4h": {
          "count": 180,
          "data": [
            {
              "bucket": "2026-06-27T16:00:00Z",
              "time": "2026-06-27T18:37:02.696Z",
              "price": 1971.09
            },
            {
              "bucket": "2026-06-27T20:00:00Z",
              "time": "2026-06-27T23:59:41.06Z",
              "price": 1995.59
            }
          ]
        },
        "12h": {
          "count": 353,
          "data": [
            {
              "bucket": "2026-01-29T16:00:00Z",
              "time": "2026-01-30T03:34:33.138Z",
              "price": 2256.76
            },
            {
              "bucket": "2026-01-30T16:00:00Z",
              "time": "2026-01-31T03:51:57.426Z",
              "price": 1438.64
            }
          ]
        }
      },
      "variants": {
        "Phase 2": {
          "market_hash_name": "★ Karambit | Doppler (Factory New)",
          "name": "★ Karambit | Doppler (Factory New) | Phase 2",
          "display_name": "Phase 2",
          "version": "p2",
          "intervals": {
            "12h": {
              "count": 356,
              "data": [
                {
                  "bucket": "2026-01-29T04:00:00Z",
                  "time": "2026-01-29T15:46:01.526Z",
                  "price": 2296.22
                }
              ]
            }
          }
        }
      }
    }
  }
}
```

## Response fields

[ArchiveYoupinResponse](/docs/objects#archiveyoupinresponse) fields:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `response_time` | `string (date-time)` | Yes | When the response was generated. |
| `currency` | `string` | Yes | Currency code (always `USD`). |
| `start` | `string (date-time)` | Yes | Effective start of the queried range, floored to the 1h boundary. |
| `end` | `string (date-time)` | Yes | Effective end of the queried range, ceiled to the 1h boundary. Exclusive. |
| `items` | [`Record<string, ArchiveYoupinItem>`](/docs/objects#archiveyoupinitem) | Yes | Map of `market_hash_name` to Youpin sale series. |
| `errors` | [ItemError[]](/docs/objects#itemerror) | No | Per-item failures alongside successful results (partial success). |

## Series

| Field | Description |
| --- | --- |
| `items.<name>.intervals` | Map keyed `1h`, `4h`, and `12h`; each an independent series. |
| `intervals.<width>.count` | Number of sale points in the window. |
| `intervals.<width>.data[]` | Sampled sale points. |
| `data[].bucket` | Sampling bucket boundary. |
| `data[].time` | The actual sale time. |
| `data[].price` | Sale price, USD, converted at the sale date's exchange rate. |
| `items.<name>.variants` | The same shape per variant. |

- An interval key appears only when that width was observed in the window; `count: 0` means observed with no sales.
- `count` is the number of points in that one series. It is not sale volume.
- Valid requested items with no Youpin sale history return `not_in_archive`.

Full schemas: [ArchiveYoupinItem](/docs/objects#archiveyoupinitem), [ArchiveYoupinSeries](/docs/objects#archiveyoupinseries), [ArchiveYoupinPoint](/docs/objects#archiveyoupinpoint).

### ArchiveYoupinItem

Youpin sale history for a single item, served as one independent series per provider sampling interval.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `market_hash_name` | `string` | Yes | Steam market hash name. |
| `intervals` | `object` | Yes | Independent series keyed by provider sampling interval, emitted in `1h`, `4h`, `12h` order. A key is present only when that interval was observed inside the requested window, so an entry that exists only to carry requested variants returns `{}`. |
| `variants` | `Record<string, object>` | No | Per-variant Youpin sale history for items with Doppler or Gamma Doppler phases or Case Hardened tiers. Keyed by display name, and omitted when empty. A variant series never falls back to base history. |

### ArchiveYoupinSeries

One independent Youpin sale series at a single provider sampling interval, carrying exactly `count` and `data`. Series are never stitched, summed, or interleaved with one another.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `count` | `integer` | Yes | Number of points in this series, equal to the length of `data`. This is not a sale volume, and `count` of 0 with an empty `data` means the interval was observed and no sales landed in the window. |
| `data` | [ArchiveYoupinPoint[]](/docs/objects#archiveyoupinpoint) | Yes | Sales at this interval, ordered by bucket. |

### ArchiveYoupinPoint

One sale on Youpin. Youpin keeps one sale per sampling bucket, so a point is a real sale, not an aggregate. The sampling interval is the series key, never a field on the point.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `bucket` | `string (date-time)` | Yes | Start of the provider sampling bucket the point represents, at the width of the series holding it. |
| `time` | `string (date-time)` | Yes | Actual sale time inside the bucket. |
| `price` | `number \| null` | Yes | Sale price (USD), converted from CNY at the sale date's own historical rate, or `null` when no rate for that date could be resolved. |

## Errors

`404 not_found` means none of the requested valid items have Youpin history in the window. Partial results return `200` with `errors[]`; possible item codes are `unknown_item`, `invalid_format`, `not_in_archive`, `unsupported_source`, and `unsupported_variant`. See [Partial success](/docs/using-the-api#partial-success) and [Request errors](/docs/using-the-api#request-errors).
