# Create a buy order with CS2 filters (v2)

`POST /v2/create-buy-order`

Place an order to buy up to amount copies of one item at up to price each. The order is a JSON body; api stays in the query.


How an order fills. Every listing of the item at or below your price is bought for you automatically, at the listing&#39;s price, and sent to your saved tradelink, as POST /v1/buy would. Listings already for sale are checked as soon as the order is placed, new and updated listings as they appear. When several orders want the same listing, the highest price wins, then the oldest order; editing an order keeps its place. Each fill buys one listing and adds 1 to filled; the order closes when filled reaches amount. Trade-locked listings are bought only when allow_trade_locked is true.


Filters (CS2 only). filters limits which copies the order buys: a float range, paint seeds, a Doppler phase, stickers (each optionally in a slot) and a charm (optionally with a pattern range). Leave filters out or send null for a plain order; inside it, leave out what you do not filter on. An unknown key in filters is refused, so a typo never places a plain order at a filtered price. Stickers are matched once the listing&#39;s stickers are known; check them on the trade offer.


Limits. amount is 1 to 250 and price a positive whole number (1000 = $1). The unit price must not exceed your balance, all your open orders together (Σ price × (amount − filled)) must not exceed 10 × your balance, and you can have at most 1000 open orders. The price must be at least 30% of the item&#39;s Steam average. KYC and a saved tradelink that can receive the game are required. You can place any number of orders for the same item.


Not enough balance. When a fill finds that your balance cannot pay for the listing, the order is cancelled and the listing goes to the next order. The cancellation is in [GET /v2/buy-order-history](/api/buy-order/buy-order-history-v2) with kind = cancelled and reason = insufficient_funds.


Notifications. A fill and a no-funds cancellation each send one message: to Telegram, else Discord (whichever you marked main goes first), else a mobile push. A fill also sends an email when your notification settings allow it. A run of no-funds cancellations sends one message, not one per order, until your balance changes.


Rate limit. Create, edit and both removals share one budget: 10 requests per 10 seconds per account, on the v1 and v2 paths together. Over it the answer is HTTP 429 with Retry-After and msBeforeNext.


Errors. Refusals are HTTP 200 with {"success": false, "msg": "…", "code": "…"}. Branch on code: it is stable, msg is human text. A missing or wrong API key is HTTP 403.





code
msg
When



invalid_request
Invalid request, or the field at fault (Missing name, Missing id, Missing ids, Invalid game, Invalid skip, Invalid limit, Invalid kind, Invalid sort, Too many items)
A required field is missing or has the wrong type. A body that is not a JSON object is HTTP 400 with this code.


invalid_price
Invalid price
price is not a positive whole number


invalid_amount
Amount must be between 1 and 250
amount is not a whole number from 1 to 250


invalid_filters
Invalid filters, Filters are available for CS2 only, Invalid float range, Invalid paint seeds, Invalid phase, Invalid stickers, Invalid charm, Invalid charm pattern, Invalid allow_trade_locked
A filter is out of range, unknown, or sent for a game other than csgo


filters_not_editable
Filters cannot be edited
An edit sent filters or allow_trade_locked


item_not_found
No item found
No item with this name in this game


price_too_low
Please increase your buy order price
price is below 30% of the item&#39;s Steam average


kyc_required
You need to pass KYC verification
Your account needs KYC


tradelink_required
Please save your tradelink
No saved tradelink, or your Steam account cannot receive items of this game


price_above_balance
Price exceeds your balance
price is above your balance


exposure_limit
Your open orders exceed 10x your balance
Your open orders would exceed 10 × your balance


too_many_orders
You can have up to 1000 active buy orders
You already have 1000 open orders


order_not_found
Offer not found
No open order of yours with this id


too_many_ids
Too many ids
More than 1000 ids


busy
Please try again
Another write of your orders held them too long; retry


error
Something went wrong
Any other failure

Prices are integers. `1000` = `1 USD`.

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `api` | query | string | required | Your [WAXPEER API key](https://waxpeer.com/profile/user?tab=security) |

## Request body

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | required | Full item market hash name |
| `game` | enum | optional | csgo (default), dota2, rust or tf2 |
| `price` | integer | required | Highest price to pay per item, a positive whole number (1000 = $1) |
| `amount` | integer | required | How many items to buy, 1 to 250 |
| `allow_trade_locked` | boolean | optional | true: also buy trade-locked listings. Default false |
| `filters` | object | optional | CS2 only. Which copies to buy; null or absent for any copy |
| `filters.float_min` | number | optional | Lowest float, 0 to 1 |
| `filters.float_max` | number | optional | Highest float, 0 to 1, not below float_min |
| `filters.paint_seeds` | array<integer> | optional | 1 to 50 paint seeds (patterns), each 0 to 1000 |
| `filters.phase` | enum | optional | Doppler phase: Phase 1 to Phase 4, Ruby, Sapphire, Emerald or Black Pearl |
| `filters.stickers` | array<object> | optional | 1 to 5 stickers the item must have |
| `filters.stickers.id` | integer | required | Sticker id |
| `filters.stickers.slot` | integer | optional | Sticker slot 0 to 4; absent = any slot |
| `filters.charm_id` | integer | optional | Charm id |
| `filters.charm_pattern_min` | integer | optional | Lowest charm pattern, 0 to 100000 |
| `filters.charm_pattern_max` | integer | optional | Highest charm pattern, 0 to 100000, not below charm_pattern_min |

```json
{
  "name": "AK-47 | Redline (Field-Tested)",
  "game": "csgo",
  "price": 12500,
  "amount": 3,
  "allow_trade_locked": false,
  "filters": {
    "float_min": 0.15,
    "float_max": 0.18,
    "paint_seeds": [
      661,
      670
    ],
    "stickers": [
      {
        "id": 4777,
        "slot": 0
      },
      {
        "id": 55
      }
    ]
  }
}
```

## Example (curl)

```bash
curl -X POST 'https://api.waxpeer.com/v2/create-buy-order?api=%7B%7BAPI_KEY%7D%7D' \
  -H 'Content-Type: application/json' \
  -d '{
  "name": "AK-47 | Redline (Field-Tested)",
  "game": "csgo",
  "price": 12500,
  "amount": 3,
  "allow_trade_locked": false,
  "filters": {
    "float_min": 0.15,
    "float_max": 0.18,
    "paint_seeds": [
      661,
      670
    ],
    "stickers": [
      {
        "id": 4777,
        "slot": 0
      },
      {
        "id": 55
      }
    ]
  }
}'
```

## Example response

```json
{
  "success": true,
  "order": {
    "id": 123,
    "name": "AK-47 | Redline (Field-Tested)",
    "game": "csgo",
    "price": 12500,
    "amount": 3,
    "filled": 1,
    "allow_trade_locked": false,
    "filters": {
      "float_min": 0.15,
      "float_max": 0.18,
      "paint_seeds": [
        661,
        670
      ],
      "stickers": [
        {
          "id": 4777,
          "slot": 0
        },
        {
          "id": 55
        }
      ]
    },
    "created": "2026-10-19T10:00:00Z",
    "last_update": "2026-10-19T10:05:00Z"
  }
}
```

## Responses

- `200` Successful operation or refusal

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `success` | boolean | optional | Whether the operation succeeded |
| `order` | object | optional | The placed order |
| `order.id` | integer | optional | Order id |
| `order.name` | string | optional | Full item market hash name |
| `order.game` | string | optional | csgo (default), dota2, rust or tf2 |
| `order.price` | integer | optional | Highest price to pay per item, a positive whole number (1000 = $1) |
| `order.amount` | integer | optional | How many items to buy, 1 to 250 |
| `order.filled` | integer | optional | How many items were bought so far |
| `order.allow_trade_locked` | boolean | optional | true: also buy trade-locked listings. Default false |
| `order.filters` | object | optional | CS2 only. Which copies to buy; null or absent for any copy |
| `order.filters.float_min` | number | optional | Lowest float, 0 to 1 |
| `order.filters.float_max` | number | optional | Highest float, 0 to 1, not below float_min |
| `order.filters.paint_seeds` | array<integer> | optional | 1 to 50 paint seeds (patterns), each 0 to 1000 |
| `order.filters.phase` | enum | optional | Doppler phase: Phase 1 to Phase 4, Ruby, Sapphire, Emerald or Black Pearl |
| `order.filters.stickers` | array<object> | optional | 1 to 5 stickers the item must have |
| `order.filters.stickers.id` | integer | required | Order id |
| `order.filters.stickers.slot` | integer | optional | Sticker slot 0 to 4; absent = any slot |
| `order.filters.charm_id` | integer | optional | Charm id |
| `order.filters.charm_pattern_min` | integer | optional | Lowest charm pattern, 0 to 100000 |
| `order.filters.charm_pattern_max` | integer | optional | Highest charm pattern, 0 to 100000, not below charm_pattern_min |
| `order.by` | string | optional | Waxpeer user ID (found in /user) |
| `order.created` | string | optional | When the order was placed (RFC 3339) |
| `order.last_update` | string | optional | When the order last changed (RFC 3339) |
| `msg` | string | optional | Why the request was refused (only when success is false) |
| `code` | enum | optional | Stable refusal code (only when success is false) |

- `400` Body is not a JSON object

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `success` | boolean | optional | Whether the operation succeeded |
| `msg` | string | optional | Why the request was refused (only when success is false) |
| `code` | enum | optional | Stable refusal code (only when success is false) |

- `429` Too many requests. Rate limit exceeded

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `success` | boolean | optional | Whether the operation succeeded |
| `msg` | string | optional | Why the request was refused (only when success is false) |
| `msBeforeNext` | number | optional | Milliseconds before next allowed request |


## Notes for agents

- Auth is the query parameter `api`, not an Authorization header.
- See [/statuses.md](/statuses.md) for numeric trade status plus `is_released` / `release_date`. `escrow_status` is site-only.
- Prefer GET /v2/search-items-by-name over v1.
- Prefer GET /v2/get-items-list over v1 (cursor keyset pagination without the 5000 row cap). Pass next_cursor only while has_more is true.
- Prefer POST /v2/my-history over v1 (faster on long histories, ordered by trade id). On buy rows, price is the amount you paid.
- POST /v2/my-history: status (number or array) and reason (string or array) filters need start and end at most 31 days apart. Every trade has reason, reversed_by (buyer / seller / null) and penalties (null when nothing was charged)
- POST /v1/check-availability: up to 1000 item_ids in a JSON body (send strings). Each listing for sale returns its CSV snapshot fields plus float, paint_index, phase, sticker_names, read live. include_hold / include_manual as on the CSV
- Socket.IO game item events carry inspect (Steam inspect link, null when none) and delivery (instant / regular / hold / manual)
- Buy orders: use the /v2 routes. POST /v2/create-buy-order takes a JSON body (name, game, price, amount 1 to 250, allow_trade_locked, and CS2 filters: float_min/float_max, paint_seeds, phase, stickers (id, slot), charm_id, charm_pattern_min/max); api stays in the query. Orders fill automatically from listings at or below the price.
- Buy-order refusals are HTTP 200 `success: false` + `msg` + `code`: branch on `code` (price_above_balance, exposure_limit when open orders would exceed 10 × balance, too_many_orders at 1000 open, invalid_amount, invalid_filters, …). Create, edit and both removals share 10 writes per 10 seconds (HTTP 429).
- The v1 buy-order paths are deprecated and answer with the v2 contract; successful v1 answers also keep v1 field names (`offers` next to `orders`, `id`, `count`, `item_name`). Public lists include filtered orders: compare `filters` with the item before reading an order price as an offer for it. In GET /v2/buy-order-history, kind=cancelled with reason=insufficient_funds means the balance could not pay.
- Trade-locked listings are omitted unless you pass include_hold=1. They then include unlock_at and send_until (RFC 3339 UTC).
- Buy a trade-locked listing only by exact item_id with allow_trade_locked (query 1 on GET /v1/buy-one-p2p, body true on POST /v1/buy). GET /v1/buy-one-p2p-name never sells hold items.
- CSV snapshot: GET /v1/prices/snapshot?format=csv (gzip). Shared header `item_id,name,price,auto,inspect,unlock_at,send_until,class_id,instance_id,steam_id,merchant,delivery`. include_hold=1 adds trade-locked rows (fills unlock_at/send_until; delivery=hold). include_manual=1 adds 12h manual P2P rows (delivery=manual; merchant empty on those rows). compact is JSON-only.
- Do not use GET /v1/ready-to-transfer-p2p; use the [trade websocket](/websocket.md).
