How to Build a CS2 Skin Arbitrage Bot
Updated
A CS2 skin arbitrage bot should compare the current
askon one cash marketplace with the currentbidon another, subtract every fee and transfer cost, reject stale or illiquid markets, and alert or trade only when the remaining profit clears a safety margin. cs2.sh'sGET /v1/prices/latestsupplies the cross-market prices;GET /v1/liquidity/itemssupplies sale-activity context. Marketplace accounts and trading APIs handle the actual purchase and sale.
Scope: This is a broad, high-level v0 guide, not a complete application. cs2.sh is designed as a flexible market-data layer for whatever you want to build on top of it, so adapt the implementation to your own product, strategy, and integrations.
The first useful version is a scanner, not an automatic trader. It produces a ranked list of item, buy marketplace, sell marketplace, landed cost, expected proceeds, net profit, margin, freshness, and liquidity. Automation can be added after those candidates survive live manual checks.
The data each candidate needs
| Input | API field or source | Why it changes the result |
|---|---|---|
| Exact item | market_hash_name or variant version |
Prevents comparing a base Doppler with a specific phase |
| Buy price | Source ask |
Lowest current sell listing on the marketplace |
| Sell price | Different source bid |
Highest current generic buy order on the marketplace |
| Available orders | ask_volume, bid_volume where supported |
Shows whether a price is backed by more than one active order |
| Observation time | Source updated_at and collected_at |
Stops an old price from being paired with a current one |
| Sale activity | liquidity, estimated_sale_time |
Separates a tradable gap from a thin market that may not clear |
BUFF, Youpin, and Steam return both sell-listing and buy-order counts. CSFloat and C5Game return a highest buy order without bid_volume; Skinport has no buy-order field. A scanner must preserve those gaps instead of treating a missing count as zero.
Calculate executable profit
For every ordered pair of different marketplaces, calculate:
landed_buy_cost = ask + buy_fee + deposit_cost + transfer_cost
net_sale_proceeds = bid - sell_fee - withdrawal_cost
net_profit = net_sale_proceeds - landed_buy_cost
net_margin = net_profit / landed_buy_costFee inputs belong in your configuration because cs2.sh returns market data, not account-specific fee schedules. Store percentage fees and fixed costs separately. A fixed withdrawal fee can erase the apparent margin on a cheap item even when the percentage gap looks large.
Do not use an ask on both sides. The cheaper sell listing shows where the bot might buy, while a bid shows what a buyer currently offers on the destination marketplace. Comparing two asks measures a listing-price difference but does not prove an immediate sale exists.
Steam also requires a separate model. Steam transactions settle to Steam Wallet funds, not withdrawable cash. Keep Steam candidates out of a cash arbitrage ranking unless the strategy explicitly models the value and allowed use of wallet funds.
Build the fetch, filter, and act loop
- Fetch
GET /v1/schemaand retain tradable items, canonical names, marketplace ids, and supported variants. Refresh the local catalog whengeneration_idchanges. - Download and cache
GET /v1/prices/lateston the backend. It returns the current snapshot for every tracked item across BUFF, Youpin, CSFloat, Skinport, Steam, and C5Game. - For each item, enumerate every source with a non-null
askand every different source with a non-nullbid. - Reject either side when
collected_atis older than the strategy's freshness bound. If source freshness matters, also reject an oldupdated_ateven when collection was recent. - Apply fees, account limits, region rules, payment costs, and any transfer delay obtained from the relevant marketplace integrations.
- Join the daily liquidity snapshot. Require a minimum liquidity bucket or widen the required safety margin as liquidity weakens.
- Rank the survivors by net profit and net margin, then send candidates to an alert queue or a marketplace-specific execution adapter.
- Before submitting an order, re-read the exact listing or buy order from the marketplace. The price snapshot identifies a candidate; it does not reserve either side.
Use POST /v1/prices/latest for repeat checks after the full scan has produced a shortlist. It accepts up to 100 market_hash_name values and returns successful items alongside per-item errors[], so one missing item should not stop the entire cycle.
Filters that remove false arbitrage
Freshness mismatch. Sources refresh independently. Two prices in one response can have different observation times, so response_time is not a valid freshness check for either side.
Insufficient order support. A lowest ask backed by one listing can disappear before the bot acts. Where order counts exist, require enough ask_volume and bid_volume for the intended quantity. A top price and an order count still do not provide full depth on every marketplace.
Weak sale activity. A large gap on an illiquid item may reflect weeks of waiting instead of profit. estimated_sale_time assumes a competitive listing price; it is not a fill guarantee for an outlying bid or ask.
Variant mismatch. Variant source maps are sparse. If BUFF has a Phase 2 price and another source has only the base Doppler, there is no valid Phase 2 pair. Missing variant data is absence, not a base-price fallback.
Asset-specific premiums. Generic prices do not appraise a rare paint seed, applied stickers, or every exact float. A generic bid may undervalue the asset, while a conditional buy order may apply only to assets satisfying its conditions. Use the CSFloat orderbook endpoint when the destination decision depends on float, paint index, sticker, charm, or paint-seed requirements.
What cs2.sh does not execute
The public cs2.sh API does not log into marketplaces, inspect your balances, buy listings, transfer skins, place sell orders, or bypass trade restrictions. Those actions require official or authorized marketplace interfaces and compliance with each service's terms.
Keep the market-data adapter separate from execution. That boundary lets the same scanner run in alert-only mode, makes fee and account assumptions auditable, and prevents a temporary marketplace failure from corrupting the price model.
References: current prices, liquidity, item schema, and data coverage.