Six daemons that parse real offers (listings) from skin marketplaces — actual sell offers with prices, float values, paint seeds, etc. This is the core data source for arbitrage, order books, and price charts.
| Repository | Description |
|---|---|
| cs2_skins_trading_database | Database schemas (required) |
| cs2_skins_trading_items_parse | Items catalog parser (required for DMarket/WM) |
cd cs2_skins_trading_offers_parse
npm install
cp .env.example .env # fill in API keys# 1. Initialize databases first
cd ../database && npm i && node offers_dmarket.js && node offers_whitemarket.js && cd ../offers_parse
# 2. Make sure items catalog is populated
# (items_parse daemons should have run at least once)
# 3. Start offer parsers
pm2 start offers.config.cjs # DMarket + WhiteMarket
# Or start individually
node dmarket.js
node whitemarket.js
node bitskins.js
node waxpeer.js
node lootfarm.js
node skinport.js- Go to dmarket.com/settings
- Navigate to Trading API section
- Click Generate Keys
- Copy Public Key (hex with
0xprefix) and Secret Key (hex) - Paste into
.envasDMARKET_PUBLIC_KEYandDMARKET_SECRET_KEY
Signing: ed25519 via tweetnacl library. Each request is signed with method + path + body + timestamp.
- Go to white.market/profile/api
- Generate Partner Token
- Paste into
.envasWHITE_PARTNER_TOKEN
Auth flow: Partner Token → JWT access token (cached 23 hours).
BitSkins, Waxpeer, Loot.Farm, Skinport — public APIs, no authentication required.
API: https://api.dmarket.com/exchange/v1/offers-by-title
How it works:
- Takes a batch of items from
items_dmarket.dbqueue (offers_parsed=0) - For each item, fetches all offers with pagination (up to MAX_PAGES × 100)
- Stores each offer:
id,price_usd,float_val,paint_seed,paint_index,inspect_url - Tracks changes via history table:
- New offer →
seenevent - Price changed →
price_changedevent - Offer gone →
removedevent + DELETE
- New offer →
- Marks item as
offers_parsed=1 - When queue empty → resets all to
offers_parsed=0, starts over - Runs
incremental_vacuum(1000)after each batch
Speed: 100 items/batch, 200ms delay. Full cycle ~12,600 items in ~8 hours.
API: https://api.white.market/graphql/partner (GraphQL)
How it works:
- Takes batch of 500 items from
items_whitemarket.dbqueue - For each item, fetches offers via GraphQL with pagination (up to 10,000 per item)
- Extracts:
id,price_usd,float_val,paint_seed,paint_index,phase,asset_id,seller_id - Upsert + history tracking (same as DMarket)
- Parallel: 2 concurrent workers (configurable)
Speed: 500 items/batch, 2 parallel. Full cycle ~27,000 items in ~5 minutes.
API: https://api.bitskins.com/market/insell/730
Single request → all items with min price and quantity. Cycle every 5 minutes.
API: https://api.waxpeer.com/v1/prices?game=csgo
Single request → all items. Price in 1/1000 USD.
API: https://loot.farm/fullprice.json
Single request → all items. Price in 1/1000 USD.
API: https://api.skinport.com/v1/items?app_id=730
Single request → all items with min price and quantity.
| Parameter | Default | Description |
|---|---|---|
DMARKET_API_BASE |
https://api.dmarket.com |
API base URL |
DMARKET_OFFERS_PATH |
/exchange/v1/offers-by-title |
Offers endpoint path |
DMARKET_GAME_ID |
a8db |
CS2 game ID |
DMARKET_PUBLIC_KEY |
— | Required. Ed25519 public key (hex) |
DMARKET_SECRET_KEY |
— | Required. Ed25519 secret key (hex) |
DMARKET_OFFERS_BATCH_SIZE |
50 |
Items per batch from queue |
DMARKET_OFFERS_LIMIT |
100 |
Offers per page (max 100) |
DMARKET_OFFERS_MAX_PAGES |
200 |
Max pages per item. 200 × 100 = 20,000 offers max |
DMARKET_OFFERS_REQUEST_DELAY_MS |
200 |
Delay between API requests (ms) |
DMARKET_OFFERS_HTTP_MAX_RETRIES |
8 |
Max retries on 429/5xx |
DMARKET_OFFERS_FETCH_TIMEOUT_MS |
30000 |
Request timeout (ms) |
DMARKET_OFFERS_MAX_WAIT_MS |
15000 |
Max retry backoff (ms) |
DMARKET_OFFERS_LOOP_SLEEP_MS |
5000 |
Sleep between batches (ms) |
DMARKET_OFFERS_RESET_SLEEP_MS |
60000 |
Sleep after queue reset (ms) |
DMARKET_ITEMS_DMARKET_DB_PATH |
../database/items_dmarket.db |
Items queue DB |
DMARKET_OFFERS_DMARKET_DB_PATH |
../database/offers_dmarket.db |
Offers output DB |
| Parameter | Default | Description |
|---|---|---|
WHITE_ENDPOINT |
https://api.white.market/graphql/partner |
GraphQL endpoint |
WHITE_PARTNER_TOKEN |
— | Required. Partner API token |
WHITE_APP_ID |
CSGO |
Application ID |
WHITE_OFFERS_BATCH_SIZE |
500 |
Items per batch |
WHITE_OFFERS_PAGE_SIZE |
100 |
Offers per GraphQL page |
WHITE_OFFERS_MAX_PER_ITEM |
10000 |
Max offers per item |
WHITE_OFFERS_CONCURRENCY |
2 |
Parallel workers. Keep ≤3 to avoid 429 |
WHITE_OFFERS_REQUEST_DELAY_MS |
300 |
Delay between requests (ms) |
WHITE_OFFERS_HTTP_MAX_RETRIES |
6 |
Max retries |
WHITE_OFFERS_FETCH_TIMEOUT_MS |
30000 |
Request timeout (ms) |
WHITE_OFFERS_MAX_WAIT_MS |
15000 |
Max retry backoff (ms) |
WHITE_OFFERS_LOOP_SLEEP_MS |
1000 |
Sleep between batches (ms) |
WHITE_OFFERS_RESET_SLEEP_MS |
60000 |
Sleep after queue reset (ms) |
WHITEMARKET_ITEMS_WHITEMARKET_DB_PATH |
../database/items_whitemarket.db |
Items queue DB |
WHITE_OFFERS_WHITEMARKET_DB_PATH |
../database/offers_whitemarket.db |
Offers output DB |
| Parameter | Default | Description |
|---|---|---|
WAXPEER_DB_PATH |
../database/offers_waxpeer.db |
Waxpeer DB path |
WAXPEER_LOOP_SLEEP_MS |
300000 |
Cycle interval (5 min) |
BITSKINS_DB_PATH |
../database/offers_bitskins.db |
BitSkins DB path |
BITSKINS_LOOP_SLEEP_MS |
300000 |
Cycle interval (5 min) |
LOOTFARM_DB_PATH |
../database/offers_lootfarm.db |
Loot.Farm DB path |
LOOTFARM_LOOP_SLEEP_MS |
300000 |
Cycle interval (5 min) |
SKINPORT_DB_PATH |
../database/offers_skinport.db |
Skinport DB path |
SKINPORT_LOOP_SLEEP_MS |
300000 |
Cycle interval (5 min) |
- History tracking — every price change or offer removal is recorded
- auto_vacuum = INCREMENTAL — prevents DB file bloat (was 66 GB before fix)
- Hot-reload .env — change parameters without restart
- Retry with exponential backoff — handles 429 and network errors
- Queue-based parsing —
offers_parsedfield ensures even coverage of all items
# Check daemon status
pm2 logs dmarket-offers-daemon --lines 20
pm2 logs whitemarket-offers-daemon --lines 20
# Check offers count
sqlite3 ../database/offers_dmarket.db "SELECT COUNT(DISTINCT name), COUNT(*) FROM offers_dmarket;"
sqlite3 ../database/offers_whitemarket.db "SELECT COUNT(DISTINCT name), COUNT(*) FROM offers_whitemarket;"
# Check parsing queue
sqlite3 ../database/items_dmarket.db "SELECT SUM(offers_parsed=0) as pending, SUM(offers_parsed=1) as done FROM items_dmarket;"
# Check DB size (should stay reasonable with auto_vacuum)
ls -lh ../database/offers_*.db
# Check 429 errors
pm2 logs whitemarket-offers-daemon --err --lines 50 | grep 429
# Force re-parse all items
sqlite3 ../database/items_dmarket.db "UPDATE items_dmarket SET offers_parsed=0;"offers_parse/
├── dmarket.js # DMarket offers daemon (auth: ed25519)
├── whitemarket.js # WhiteMarket offers daemon (auth: Partner Token)
├── bitskins.js # BitSkins offers daemon (public API)
├── waxpeer.js # Waxpeer offers daemon (public API)
├── lootfarm.js # Loot.Farm offers daemon (public API)
├── skinport.js # Skinport offers daemon (public API)
├── env.js # Configuration with hot-reload
├── offers.config.cjs # PM2 ecosystem config
├── .env.example # Environment template
├── package.json
├── README.md # English
└── README.uk.md # Ukrainian
better-sqlite3— SQLite driverdotenv— Environment variablestweetnacl— Ed25519 signing for DMarket API