# Quickstart

The CS Deals API gives you the marketplace: live listings and prices, buying,
withdrawing to Steam, and a WebSocket feed of market activity.

## Get an API key [#get-an-api-key]

Keys are created from **Settings → API** on the website. Creating and revoking
a key is deliberately browser-only: those routes reject requests that don't
come from the site, so a leaked key can never mint another one.

Two things are required before a key is issued:

* **A linked Discord or Telegram account**, so we have a way to reach you.
  Link one from **Settings → Connected Accounts**.
* **A short description of what you'll use the key for.** Keep it accurate;
  it's what we read first if your traffic ever looks unusual.

<Callout type="warn">
  Providing a fake Discord/Telegram will lead to API termination in the event we
  need to reach out and verify the legitimacy of your requests.
</Callout>

The key is shown **once**. Store it somewhere safe.

## Authenticate [#authenticate]

Send it as a bearer token on every request:

```bash
curl "https://api.cs.deals/public/v1/listings?page=1&limit=500" \
  -H "Authorization: Bearer csd_your_key_here"
```

`GET /public/v1` is the only route that works without a key. To confirm a key
is live, call [`GET /auth/api-key`](/docs/reference/account/api-key).

<Callout type="warn">
  An API key carries your full account authority, including buying and
  withdrawing. Treat it like a password: never ship it in client-side code, and
  revoke it from Settings the moment it leaks.
</Callout>

## Money is always integer cents [#money-is-always-integer-cents]

Every price, balance and amount in this API is an integer in cents. `4250`
means $42.50. There are no floats and no currency codes to pass around. If
you are porting from the v1 API, this is the single most common source of bugs.

Field names are `snake_case` everywhere. CS Deals ids are integers; external
Steam identifiers are strings and always carry a `steam_` prefix
(`steam_asset_id`, `steam_offer_id`). Timestamps are ISO 8601 strings, on
REST and WebSocket alike.

## Errors [#errors]

Failures return a JSON body with a stable machine-readable code:

```json
{ "error": "LISTING_PRICE_CHANGED", "data": { "listing_ids": [12345] } }
```

Branch on `error`, never on the HTTP status or the message text. `data` is
present when the error carries detail: which listings were out of stock, which
were too expensive, and so on.

## Rate limits [#rate-limits]

Limits are per endpoint, counted per minute:

| Endpoint                   | Limit |
| -------------------------- | ----- |
| `GET /public/v1/listings`  | 60    |
| `GET /public/v1/book`      | 6     |
| `GET /public/v1/prices`    | 60    |
| `GET /public/v1/sales`     | 60    |
| `POST /public/v1/purchase` | 30    |
| `GET /public/v1/user`      | 60    |
| `GET /public/v1/orders`    | 60    |
| `GET /public/v1/backpack`  | 30    |
| `GET /public/v1/trades`    | 60    |

`POST /public/v1/withdraw` is not rate-limited. Exceeding a limit returns
`RATE_LIMITED` with HTTP 429. Back off and retry.

## Next [#next]

The guides follow the life of an item on the platform:

* [Buying items](/docs/buying): find listings and place an order
* [Selling items](/docs/selling): list from your backpack, reprice, delist
* [Withdrawing to Steam](/docs/withdrawing): get your items out
* [Migrating from the v1 API](/docs/migrating-from-v1): endpoint-by-endpoint mapping

Bought items sit in your **backpack** until you either sell them again or
withdraw them to Steam. There is no cart: an order is one call that fills
completely or not at all.
