Monitors Steam Market and Skinport prices in real time and surfaces buy opportunities — skins where buying on Skinport and relisting on Steam yields a net profit after both platforms' fees.
Each refresh cycle the script:
- Fetches the lowest Steam Market listing price for each skin (one request per skin, 3s apart)
- Pulls all CS2 listings from Skinport in a single bulk request
- Calculates net profit after fees and sorts by gap, largest first
- Highlights any opportunity above the 12% threshold in green
Gap formula:
cost = skinport_price × 1.12 (Skinport 12% buyer fee)
revenue = steam_price × 0.85 (Steam 15% seller fee)
gap % = (revenue − cost) / cost × 100
python -m venv venv
venv\Scripts\activate # Windows
# source venv/bin/activate # macOS / Linux
pip install -r requirements.txt
python arbitrage.pyAll tunable values are at the top of arbitrage.py:
| Constant | Default | Description |
|---|---|---|
SKINS |
5 skins | List of market_hash_name strings to monitor |
GAP_THRESHOLD |
12.0 |
Minimum net % to flag as an opportunity |
REFRESH_INTERVAL |
300 |
Seconds between full refreshes (5 min) |
STEAM_DELAY |
3.0 |
Seconds between Steam API calls |
STEAM_SELLER_FEE |
0.15 |
Steam + CS2 game fee (15%) |
SKINPORT_BUY_FEE |
0.12 |
Skinport buyer fee (12%) |
| API | Limit | Our usage |
|---|---|---|
| Steam Market | ~20 req/min (unofficial) | 1 req per 3s — safe up to ~65 skins at 5-min refresh |
Skinport /v1/items |
8 req / 5 min, cached 5 min | 1 call per refresh cycle regardless of skin count |
Skinport is a single bulk call so adding more skins costs nothing there. To watch more skins on Steam, increase REFRESH_INTERVAL proportionally — every extra minute of refresh window buys you ~15 more skins.
Names must match Steam's market_hash_name exactly:
- Wear is spelled out:
Factory New,Minimal Wear,Field-Tested,Well-Worn,Battle-Scarred - Knives require the
★prefix:★ Karambit | Doppler (Factory New) - Doppler knives have multiple entries per phase on both platforms — the tracker automatically picks the cheapest
| Skin | Notes |
|---|---|
| AK-47 | Redline (Field-Tested) | |
| AWP | Asiimov (Field-Tested) | |
| M4A4 | Howl (Minimal Wear) | Contraband — no longer case-droppable |
| ★ Karambit | Doppler (Factory New) | Cheapest phase selected automatically |
| AK-47 | Case Hardened (Field-Tested) |
- Expand skin list — the architecture supports up to ~65 skins at the current 5-min refresh with no other changes
- Skinport API auth — add Basic auth (Client ID + Secret from Skinport account settings) for higher rate limit headroom
- Price history — log each refresh to a local SQLite database to track gap trends over time and spot recurring opportunities
- Alerts — send a desktop notification or Discord webhook when a new opportunity crosses the threshold
- Fee configurability — Skinport's buyer fee varies by item (up to 12%); pulling the per-item fee from the API would make gap calculations more precise
- Multi-currency support — the APIs both support EUR and other currencies; useful if operating from outside the US
- StatTrak / souvenir variants — currently only tracks base skins; StatTrak versions often show different gap profiles
- CLI skin management — add/remove skins from the watched list without editing the source file