Status legend:
implemented— available incsfloat-node-sdkdegraded— still exposed incsfloat-node-sdk, but the current live surface is regressed, stale, or externally unavailablediscovered— confirmed live, but not yet implemented in the SDKvalidated— confirmed either by official docs, local source analysis, or live account checksaccount-gated— endpoint exists, but live availability depends on account state or platform eligibilitysource— where the endpoint evidence comes from
| Endpoint | Method | SDK Status | Validation Source | Notes |
|---|---|---|---|---|
/listings |
GET |
implemented | official docs + live | supports query params |
/listings/price-list |
GET |
implemented | live | public price index; returns { market_hash_name, quantity, min_price }[] |
/listings/{id} |
GET |
implemented | official docs + live | single listing fetch |
/listings |
POST |
implemented + account-gated | official docs + live | explicit buy_now supported; current live retest hit account-state gate for further listings |
/listings/bulk-list |
POST |
implemented + account-gated | browser bundle + live | confirmed happy-path on two private low-cost buy-now listings; body { items:[{ asset_id, price, type, private?, description?, max_offer_discount? }] }; overpriced batches can hit seller KYC/Stripe gating |
/listings/bulk-modify |
PATCH |
implemented | browser bundle + live | confirmed happy-path with body { modifications:[{ contract_id, price }] }; current SDK keeps this route price-focused until broader field semantics are proven |
/listings/bulk-delist |
PATCH |
implemented | browser bundle + live | confirmed happy-path with body { contract_ids:[...] } and response { "message":"contracts delisted" } |
/listings/{id} |
PATCH |
implemented | live | update listing price |
/listings/{id} |
DELETE |
implemented | live + python clone | unlist / delist behavior |
/users/{id} |
GET |
implemented | python clone + live | public user profile |
/users/{id}/stall |
GET |
implemented | python clone + live | public user stall; current live validation confirms meaningful listing-style params including sort_by, filter, type, and min_ref_qty |
/me |
GET |
implemented | live + python clone | authenticated account; current profile Earnings card is derived from user.statistics.total_sales and user.statistics.total_purchases in this payload, not from a separate earnings endpoint |
/me/inventory |
GET |
implemented | live + python clone | authenticated inventory |
/history/{market_hash_name}/sales |
GET |
implemented | live | sales history |
/schema |
GET |
implemented | live + public wrapper source | public item schema; both /schema and /schema/ resolve |
/schema/browse |
GET |
implemented | browser bundle + live | public grouped browse route; current live validation confirmed type=stickers and bundle uses lowercased category labels such as stickers, keychains, and music kits |
/schema/images/screenshot |
GET |
implemented | browser bundle + live | authenticated example-screenshot route; current live sample returned { id, sides:{ playside:{path}, backside:{path} } } for a schema-targeted item query |
/meta/exchange-rates |
GET |
implemented | live + public wrapper source | public exchange rate map |
/meta/app |
GET |
implemented | browser bundle + live | app bootstrap metadata; current live response returned { min_required_version: "9.0.0" } |
/meta/location |
GET |
implemented | live + public wrapper source | public inferred location data |
/meta/notary |
GET |
implemented | browser bundle + live | returns current notary availability flags such as { rollback:{enabled,background}, accepted:{enabled,background} } |
https://api.csfloat.com/?url={inspectLink} |
GET |
implemented + degraded | historical browser tool network + 2026-04-14 live retest | old external Float Checker companion route; March 2026 live replay worked, but the April 14 retest found api.csfloat.com unresolved, and the current /checker flow now decodes masked inspect links locally instead of emitting this request |
https://loadout-api.csfloat.com/v1/user/{steam_id}/loadouts |
GET |
implemented | browser-auth network + live | public external CSFloat loadout service; returns { loadouts: [...] } |
https://loadout-api.csfloat.com/v1/loadout/{id} |
GET |
implemented | browser-auth network + live | public loadout detail route; returns { loadout: ... } |
https://loadout-api.csfloat.com/v1/loadout |
GET |
implemented | bundle semantics + live | public/global loadout list route; confirmed `sort_by=created_at |
https://loadout-api.csfloat.com/v1/user/favorites |
GET |
implemented | bundle semantics + live | requires Authorization: Bearer <recommender-token>; confirmed response shape { favorites:[{ added_at, loadout_id, loadout }] } |
https://loadout-api.csfloat.com/v1/loadout |
POST |
implemented | browser-auth network + live | requires Authorization: Bearer <recommender-token>; confirmed 201 create with body { name, ct, t } and response { loadout: ... } |
https://loadout-api.csfloat.com/v1/loadout/{id} |
PUT |
implemented | browser-auth network + live | requires Authorization: Bearer <recommender-token>; confirmed 200 update with body { name, ct, t } and response { loadout: ... } |
https://loadout-api.csfloat.com/v1/loadout/{id} |
DELETE |
implemented | browser-auth network + live | requires Authorization: Bearer <recommender-token>; confirmed 200 {"message":"Loadout deleted successfully"} |
https://loadout-api.csfloat.com/v1/recommend |
POST |
implemented | browser-auth network + live | requires Authorization: Bearer <recommender-token> from /me/recommender-token; confirmed skin-only request shape { items:[{ type:\"skin\", def_index, paint_index }], count, def_whitelist?, def_blacklist? } and response { count, results:[{ def_index, paint_index, score }] } |
https://loadout-api.csfloat.com/v1/recommend/stickers |
POST |
implemented | bundle semantics + live | requires Authorization: Bearer <recommender-token>; confirmed request shape { items:[{ type:\"skin\", def_index, paint_index }], count, collection_whitelist? } and response { count, results:[{ sticker_index, score }] } |
https://loadout-api.csfloat.com/v1/generate |
POST |
implemented | bundle semantics + live | requires Authorization: Bearer <recommender-token>; confirmed request shape { items:[{ type:\"skin\", def_index, paint_index, wear_index? }], def_indexes, faction, max_price? } and response { remaining_budget, total_cost, results:[{ def_index, recommendations:[{ def_index, paint_index, wear_index, price, score, locked? }] }] } |
https://loadout-api.csfloat.com/v1/loadout/{id}/favorite |
POST |
implemented | bundle semantics + live | requires Authorization: Bearer <recommender-token>; returns {"loadout":{"social_stats":{"favorites":N}},"message":"Loadout added to favorites"} |
https://loadout-api.csfloat.com/v1/loadout/{id}/favorite |
DELETE |
implemented | bundle semantics + live | requires Authorization: Bearer <recommender-token>; returns {"loadout":{"social_stats":{"favorites":N}},"message":"Loadout removed from favorites"} |
/me/account-standing |
GET |
implemented | live + public wrapper source | authenticated account standing |
/me/transactions |
GET |
implemented | live + public wrapper source | returns { transactions, count }; current live validation confirms meaningful page, limit, `order=asc |
/me/offers-timeline |
GET |
implemented | live + public wrapper source | authenticated offers timeline |
/offers |
POST |
implemented | live | confirmed happy-path create on buyer account with body { contract_id, price } |
/offers/{id} |
GET |
implemented | live | single offer fetch by valid offer id |
/offers/{id}/accept |
POST |
implemented | live invalid probe + browser-auth discovery | current SDK exposes account.acceptOffer(); route existence is confirmed, but happy-path response semantics remain only partially mapped |
/offers/{id}/history |
GET |
implemented | live + public wrapper source | historical thread for the offer chain; confirmed with valid declined/counter-offer ids |
/offers/{id}/counter-offer |
POST |
implemented | live | confirmed happy-path on seller account with body { price } |
/offers/{id} |
DELETE |
implemented | live | confirmed happy-path cancellation with response offer canceled; exact actor/state semantics may vary by thread state |
/me/notifications/timeline |
GET |
implemented | live + public wrapper source | authenticated notifications timeline; current live validation confirms cursor-based pagination while limit appears ignored |
/me/buy-orders |
GET |
implemented | live + public wrapper source | returns { orders, count }; current live validation confirms page, limit, and validated `order=asc |
/buy-orders |
POST |
implemented | live + public wrapper source | confirmed happy-path create using both market_hash_name + max_price and expression-backed bodies { expression:{ condition, rules }, max_price, quantity }; quantity defaults to 1 when omitted |
/buy-orders/{id} |
PATCH |
implemented | live | confirmed happy-path update with body { max_price } |
/buy-orders/{id} |
DELETE |
implemented | live + public wrapper source | confirmed happy-path delete with successfully removed the order |
/buy-orders/similar-orders |
POST |
implemented | browser bundle + live | safe insight route returning { data:[{ market_hash_name, qty, price }] }; browser service passes limit as a query param and current live validation now confirms both { market_hash_name } and { expression } request bodies |
/me/auto-bids |
GET |
implemented | live + public wrapper source | authenticated auto-bids list |
/me/auto-bids/{id} |
DELETE |
implemented | browser-auth network + live | confirmed happy-path delete with {"message":"deleted auto-bid"} |
/me/recommender-token |
POST |
implemented | live + browser-auth network | returns { token, expires_at } |
/me/notary-token |
POST |
implemented | browser bundle + live | returns { token, expires_at } for the notary/companion flow |
/me/gs-inspect-token |
POST |
implemented | browser bundle + live | returns { token, expires_at } for the external gs-api.csfloat.com inspect/equip companion flow |
/me/payments/max-withdrawable |
GET |
implemented | live + browser bundle | returns { max_withdrawable } for the current account payout state |
/me/payments/pending-deposits |
GET |
implemented | browser bundle + live | authenticated pending-deposit list; current live sample returned [], while bundle/UI usage reads fields such as created, amount, currency, and payment_method_types |
/me/pending-withdrawals |
GET |
implemented | live + browser bundle | returns the authenticated pending-withdrawal list; current live sample was an empty array |
/me/pending-withdrawals/{id} |
DELETE |
implemented | live invalid probe + browser bundle | invalid probe on id=0 returned 200 with an empty body, confirming the route/method despite the currently opaque response shape |
/me/extension/status |
GET |
implemented | live + browser bundle | returns extension version/permission metadata for the authenticated account |
/me/mobile/status |
GET |
implemented | live + public wrapper source | authenticated mobile status |
/me/transactions/export |
GET |
implemented | live + browser bundle | returns CSV text for a full past month via year + month; current-month exports reject with 400 full month must be in the past |
/trades/bulk/accept |
POST |
implemented | live + public wrapper source | confirmed happy-path on at least one seller account; current live retest on the main seller account showed that visible queued trade IDs can still return invalid trade ids specified, so treat this as a bulk/helper route with account-state nuance |
/trades/{id}/accept |
POST |
implemented | browser bundle + live | confirmed happy-path on a real queued cross-account sale (10 cents) from the main seller account; current live response transitioned the trade to pending and returned the updated CsfloatTrade payload |
/trades/bulk/cancel |
POST |
implemented | browser bundle + live invalid probe | bundle-mapped seller-side bulk cancel route; live invalid probe with { trade_ids:[\"0\"] } returned 400 invalid trade ids specified on the correct path |
/trades/{id} |
GET |
implemented | browser bundle + live | trade detail route now captured from a real queued cross-account trade; current live sample included { buyer, seller, contract, trade_url, trade_token, wait_for_cancel_ping, is_settlement_period } |
/trades/{id}/buyer-details |
GET |
implemented | browser bundle + live | buyer-details route now captured from a real queued cross-account trade; current live sample shape { steam_level, persona_name, avatar_url } |
/trades/{id} |
DELETE |
implemented | browser bundle + live invalid probe | bundle-mapped seller-side cancel route; live invalid probe on id=0 returned 500 record not found, confirming path/method |
/me |
PATCH |
implemented | live + public wrapper source | confirmed with no-op patch for offers_enabled, max_offer_discount, stall_public, away, trade_url; also confirmed for background_url and username (2026-03-07 research pass 2) |
/me/notifications/read-receipt |
POST |
implemented | live + public wrapper source | mark notifications read via last_read_id |
/me/mobile/status |
POST |
implemented | live + public wrapper source | confirmed live with payload { "version": "8.0.0" } |
/listings/{id}/buy-orders |
GET |
implemented | live + public wrapper source | public without extra query params; authenticated callers can also use limit |
/listings/{id}/similar |
GET |
implemented | live + public wrapper source | returns similar live listings |
/listings/{id}/bid |
POST |
implemented | browser bundle + browser-auth network + live | auction max-price bid route; confirmed from item-page Bid / Auto Bid flow; body shape { max_price }; happy-path response observed as { id, created_at, max_price, contract_id } and records appear in /me/auto-bids |
/listings/{id}/watchlist |
POST |
implemented | live | confirmed happy-path add with added to watchlist |
/listings/{id}/watchlist |
DELETE |
implemented | live | confirmed happy-path remove with removed from watchlist |
/listings/buy |
POST |
implemented | live + public wrapper source | confirmed happy-path with body { contract_ids: string[], total_price } and response all listings purchased |
/history/{market_hash_name}/graph |
GET |
implemented | live + public wrapper source | supports explicit paint_index; also responds when paint_index is omitted |
These routes were confirmed live during the 2026-03-07 recon sweep:
| Endpoint | Method | SDK Status | Validation Source | Notes |
|---|---|---|---|---|
/me/trades |
GET |
implemented | live | returns { trades, count }; supports limit |
/me/offers |
GET |
implemented | live | returns { offers, count }; current live validation confirms meaningful page + limit, while legacy cursor appears ignored |
/me/watchlist |
GET |
implemented | live + browser-auth UI | returns { data, cursor }; currently confirmed with limit, state, sort_by, filter, category, type, min_price, min_ref_qty, stickers, keychains, and sticker_option; live-meaningful examples include `type=auction |
/listings?limit=40&min_ref_qty=20 |
GET |
implemented | live + frontend network | exact unauthenticated public /search page bootstrap shape; exposed via listings.getListings(getPublicMarketPageParams()) |
/listings?limit=5&min_ref_qty=20&type=buy_now&min_price=500 |
GET |
implemented | browser UI + live | current homepage-highlight feed shape; exposed via listings.getListings(getHomepageFeedParams("top_deals")) and workflows.getPublicMarketFeeds() |
/listings?limit=5&sort_by=most_recent&min_ref_qty=20&type=buy_now&min_price=500 |
GET |
implemented | browser UI + live | current homepage Newest Items feed shape; exposed via listings.getListings(getHomepageFeedParams("newest")) and workflows.getPublicMarketFeeds() |
/listings?limit=5&sort_by=most_recent&filter=unique&min_ref_qty=20&type=buy_now&min_price=500 |
GET |
implemented | browser UI + live | current homepage Unique Items feed shape; exposed via listings.getListings(getHomepageFeedParams("unique")) and workflows.getPublicMarketFeeds() |
/listings?filter=sticker_combos |
GET |
implemented | live + browser UI + auth API | UI label Sticker Combos; requires auth; current SDK exposes this through the typed filter param on listings.getListings() |
/listings?filter=unique |
GET |
implemented | live + browser UI + auth API | UI label Unique Items; current SDK exposes this through the typed filter param on listings.getListings() |
/listings/{auction_id}/bids |
GET |
implemented | live | returns bid array for auction listings; empty array when no bids |
/buy-orders/item |
GET |
implemented | browser bundle + live | inspect-link oriented route using query params { url:<inspectLink>, market_hash_name, sig, limit }; the current frontend passes listing.item.market_hash_name plus listing.item.gs_sig, and the April 14 retest returned 200 rows again when the full query contract was replayed |
/buy-orders/matching-items/floatdb |
POST |
discovered | browser bundle + live invalid probe | route exists but currently demands float-expression semantics; plain market_hash_name returned condition and rules are required for an expression |
/trades/{id}/cannot-deliver |
POST |
implemented | browser bundle + live invalid probe | frontend-confirmed no-body payload; SDK exposes account.cannotDeliverTrade() as a low-level, seller-side failure helper |
/trades/{id}/dispute |
POST |
implemented | browser bundle + live invalid probe | frontend-confirmed no-body payload; SDK exposes account.disputeTrade() conservatively as a low-level dispute helper |
/trades/{id}/received |
POST |
implemented | browser bundle + live | frontend-confirmed no-body payload; real pending buyer-side trade returned 400 missing steam offer ID, so the SDK keeps account.markTradeReceived() intentionally low-level and state-gated |
/trades/bulk/received |
POST |
implemented | browser bundle + live | bundle-confirmed buyer flow using { trade_ids }; invalid trade_ids:["0"] returned 400 invalid trade ids specified, and a real pending buyer-side trade returned 400 missing steam offer ID until a Steam offer exists |
/trades/{id}/rollback |
POST |
implemented | browser bundle + live invalid probe | frontend-confirmed no-body payload; SDK exposes account.rollbackTrade() conservatively because the happy-path remains trade-state-specific |
/trades/{id}/manual-verification |
POST |
implemented | browser bundle + live invalid probe | frontend-confirmed no-body payload; SDK exposes account.manualVerifyTrade() as a low-level helper |
/trades/{id}/rollback-verify |
POST |
implemented | browser bundle + live invalid probe | frontend-confirmed no-body payload; SDK exposes account.verifyTradeRollback() as a low-level helper |
/trades/{id}/report-error |
POST |
discovered | browser bundle + live invalid probe | invalid id=0 returned 500 record not found; likely support/reporting path |
/trades/notary |
POST |
discovered | browser bundle + live invalid probe | empty payload returned 400 payload is required; exact contract still unmapped |
/me/verify-email |
POST |
implemented | browser bundle + live invalid probe | invalid email=not-an-email returned 400 invalid email format; SDK exposes account.verifyEmail() as a low-level request/confirm helper |
/me/verify-sms |
POST |
implemented | browser bundle + live invalid probe | invalid phone_number=abc returned 400 twilio: Invalid phone number: abc; SDK exposes account.verifySms() as a low-level request/confirm helper |
/trades/steam-status/new-offer |
POST |
implemented | live + browser bundle | low-level Steam sync route keyed by { offer_id }, with the current frontend also sending optional { given_asset_ids, received_asset_ids }; safe live probes returned 200 {"message":"successfully updated offer state"} even for "0" |
/trades/steam-status/offer |
POST |
implemented | live + public wrapper source | low-level Steam sync route accepting { sent_offers } and optional { trade_id }; safe live probes returned 200 {"message":"successfully updated offer state"}, while empty-array syncs produced no observed trade-state change |
These routes appeared in public wrappers, but live probing on 2026-03-07 did not confirm them:
| Endpoint | Method | Live Result | Notes |
|---|---|---|---|
/listings/sell |
POST |
404 |
likely stale wrapper surface |
/listings/{id}/bit |
POST |
404 |
likely stale wrapper surface |
/me/trades/bulk/cancel |
POST |
404 |
outdated wrapper path; browser bundle and live invalid probe confirm the real route is /trades/bulk/cancel |
/listings/{id}/sales |
GET |
404 |
wrapper surface not confirmed live |
/account-standing |
GET |
400 invalid resource |
stale path; live route is /me/account-standing |
/me/payments/stripe/connect |
GET |
404 |
stale withdraw-route fetch still observed in browser-auth flow |
These params return 200 OK on /listings but produce no filtering effect — probed on 2026-03-07 with targeted item comparisons:
| Param | Probed Value | Evidence |
|---|---|---|
has_stickers |
true |
IDs identical to unfiltered result; sticker-less items appear |
has_keychains |
true |
IDs identical to unfiltered result |
has_fade |
true |
IDs identical to unfiltered result |
phase |
1, 2, 3, 4, sapphire |
All values return identical IDs; Doppler items unaffected |
is_stattrak |
true/false |
IDs identical; items mix StatTrak and non-StatTrak — use category=2 instead |
is_souvenir |
true/false |
IDs identical; use category=3 instead |
wear_name |
Factory New, etc. |
IDs identical regardless of value — use min_float/max_float instead |
num_stickers |
1–4 |
IDs identical; items have different sticker counts |
sticker_count |
1–4 |
Alias for num_stickers; same silently-ignored behavior |
min_paint_seed |
700 |
Items with seed <700 still returned |
max_paint_seed |
50 |
Items with seed >50 still returned |
badges |
hot, new, rare |
200 OK but no badge-based filtering; items have no matching badge field |
low_rank |
true |
IDs identical to unfiltered; not a filter |
has_low_rank |
true |
IDs identical to unfiltered; same as low_rank |
quality |
4, 9 |
IDs identical; quality values unaffected |
sticker |
55, 55|0, 55,73, 9999999 |
All forms silently ignored (2026-03-07 research pass 2): sticker=55 returns identical IDs to baseline; items in result do NOT have sticker 55; even sticker=9999999 returns same result set. Official docs show ID|POSITION?[,ID|POSITION?...] format but the param does not filter in practice on the API |
page |
0, 1, 2 |
All page values return identical IDs — pagination uses cursor exclusively |
user_id |
authenticated steam_id | 200 OK but seller IDs in result do not match the specified user_id; silently ignored |
source |
1–5, csfloat, p2p |
All values return identical IDs; no source field in listing response; silently ignored on standard accounts (2026-03-07 research pass 2) |
is_commodity |
true |
200 OK but no filtering observed |
Note: Items DO have
low_rankas a field (e.g. low_rank: 41, low_rank: 4). The param does not filter on it. Note: For stattrak/souvenir/category filtering usecategory(1=normal, 2=stattrak, 3=souvenir, 4=highlight) — that IS a confirmed real filter. Note: Thestickerparam formatID|POSITION?[,ID|POSITION?...]appears in official docs but the endpoint silently ignores it on the live API as of 2026-03-07.
These params return a non-200 error (not silently ignored):
| Param | Probed Value | Status | Notes |
|---|---|---|---|
type |
any, normal, stattrak, souvenir |
400 |
Only buy_now and auction are valid type values; other strings hard-fail |
Currently covered or typed:
limitcursortypefiltersourcemarket_hash_namedef_indexpaint_indexcategorymin_floatmax_floatsort_byuser_idcollectionraritymin_pricemax_pricepaint_seedsticker_indexkeychain_indexkeychain_highlight_reelmusic_kit_indexmin_keychain_patternmax_keychain_patternmin_bluemax_bluemin_fademax_fade
Live-confirmed search behaviors:
sort_byaccepts:lowest_pricehighest_pricemost_recentexpires_soonlowest_floathighest_floatbest_dealhighest_discountfloat_ranknum_bids
- invalid
sort_byreturns404 filter=sticker_combosandfilter=uniqueare both live; any otherfiltervalue returns400 invalid filter valuefiltervalues are derived from browser UI labels:Sticker Combos->sticker_combosUnique Items->unique
- wear filtering via
min_float/max_floatis live - browser wear shortcuts map like this:
FN->max_float=0.07MW->min_float=0.07&max_float=0.15FT->min_float=0.15&max_float=0.38WW->min_float=0.38&max_float=0.45BS->min_float=0.45
min_floatalone worksmax_floatalone works- reversed ranges such as
min_float=0.8&max_float=0.2return an empty result set - invalid ranges such as
min_float < 0ormax_float > 1do not hard-fail; they appear to be ignored/fallbacked by the backend sourceis live as both string and numeric forms:source=csfloat,source=p2p,source=1–5all return200; exact semantic mapping between string/numeric forms and result sets is not fully differentiated on standard accountscategoryis live and maps like this:
1-> Normal2-> StatTrak3-> Souvenir4-> Highlight
categoryis the confirmed correct filter for stattrak/souvenir;is_stattrakandis_souvenirparams exist but are silently ignored (see Confirmed Silently-Ignored section)def_index+paint_indexis live and can target a specific skin familypaint_seed(exact value) is live and can narrow family results to an exact seedcollectionis live; schema keys likeset_cobblestoneworkrarityis live; schema rarity values like6workmin_priceandmax_priceare both livemusic_kit_indexis live and can target music kit listings directlykeychain_highlight_reelis live and can target highlight charm listings directlymin_fade/max_fadeare live for fade-capable finishesmin_blue/max_blueare live for blue-percentage filtered searchesmin_keychain_pattern/max_keychain_patternare live and can further narrow charm listings when paired withkeychain_index; on 2026-03-08,keychain_index=29&min_keychain_pattern=0&max_keychain_pattern=10returned a smallerCharm | Semi-Preciousresult set than the broader0..10000probesticker_indexandkeychain_indexare accepted by the API; they currently behave as index filters for sticker/charm listings themselves, not yet a documented helper for applied attachments- only
category=1..4have confirmed semantics;category=5returned a mixed, effectively unfiltered result set on 2026-03-07 and should be treated as unsupported GET /me/buy-ordersacceptsmarket_hash_nameandsort_bywithout hard-failing on the current account, but temporary live orders did not show any filtering or sorting effect; these params should be treated as unconfirmed/ignored for nowGET /history/{name}/graphworks withoutpaint_index; it returns a broader series than an explicitpaint_indexquery, but the exact server-side aggregation semantics are not fully mapped yetGET /history/{name}/graphaccepts acategoryquery param (1=Normal, 2=StatTrak, 3=Souvenir); live probe on 2026-03-07 returnedtotal_count=1747for all values ofcategorybut with slightly differentavg_pricevalues per day — this behavior suggestscategorymay influence averaging, but it does not change the set of days returned; treat as weakly mappable, not a confirmed hard filterPATCH /meacceptsbackground_urlandusernameas undocumented fields — both return200 "user updated!"(confirmed 2026-03-07 research pass 2); exact validation rules forusernameare unknown; SDK adds these as optional typed fields inCsfloatUpdateMeRequestwith helpersupdateBackground()andupdateUsername()filter=sticker_combosandfilter=uniquerequire authentication — unauthenticated requests return403(not 401); these are actively blocked, not silently ignored- listing subroutes
/listings/{id}/offers,/listings/{id}/trades,/listings/{id}/history,/listings/{id}/price-history,/listings/{id}/buyer,/listings/{id}/seller,/listings/{id}/itemall return404— none confirmed live /me/*hidden routes probed and all return404: balance, preferences, settings, referrals, subscriptions, kyc, payment, payout, stall, bids, listings, cart, disputes, 2fa, extension, rate-limit, limits, offers/sent, offers/received, fees- all tested
/users/{id}/*extensions return404: offers, trades, buy-orders, statistics, reviews, reputation, watchlist, inventory GET /offersreturns405 Method Not Allowed— GET is not valid on this route;POST /offersis the only supported method- top-level routes probed and all return
400 "invalid resource": announcements, referrals, promotions, leaderboard, search (with q=), items, market, prices, trending, stats, buy-now - browser-auth auction detail flow confirmed that the
historybutton maps toGET /listings/{id}/bids, while bothBidandAuto Bidconverge on the same max-price routePOST /listings/{id}/bid POST /listings/{id}/bidis a stable happy-path route on cheap auctions; live API tests with{ "max_price": 9 },{ "max_price": 15 }, and{ "max_price": 17 }created bid records{ id, created_at, max_price, contract_id }that immediately appeared inGET /me/auto-bids- repeated
POST /listings/{id}/bidbehaves as replacement/update semantics for a listing-level auto-bid: the previous entry disappears fromGET /me/auto-bidsand a new record with a newidbecomes active - browser-auth modal state after a live bid shows
Your Max Bid: ...and aRemoveaffordance; clicking it revealed the actual cancel routeDELETE /me/auto-bids/{id}via browser-auth network capture - direct guesses
DELETE /auto-bids/{id}andDELETE /listings/{id}/bidboth returned405; the correct delete path is user-scoped under/me/auto-bids/{id} GET /offers/{id}returns the current offer snapshot, whileGET /offers/{id}/historyreturns the historical chain for that offer thread; this was confirmed live on 2026-03-07 with a declined buyer offer and a declined seller counter-offerPOST /offershappy-path is confirmed with body{ contract_id, price }on a buyer account; usinglisting_idinstead ofcontract_idfalls back tofailed to find contract with id '0'POST /offers/{id}/counter-offerhappy-path is confirmed with body{ price }on a seller accountDELETE /offers/{id}is the confirmed close route for both buyer-side cancel and seller-side decline flows; the server still returns the generic message{ "message": "offer canceled" }in both cases, while the historical offer state becomesdeclinedPOST /listings/buyhappy-path is confirmed with body{ contract_ids: string[], total_price }and returns{ "message": "all listings purchased" }PATCH /buy-orders/{id}happy-path is confirmed with body{ max_price };PUTandPOSTon the same route return405POST /offers/{id}/acceptexists and returnscode 91: failed to accept offerfor an invalid offer id; the SDK now exposes it as the low-level helperaccount.acceptOffer(), while the happy-path is still intentionally not executed because that would complete a real purchasePOST /trades/bulk/acceptis the confirmedaccept saleroute for queued seller trades; on a real$0.05queued sale it returned{ data: [trade] }, transitioned the trade fromqueuedtopending, and populatedaccepted_at,trade_url,trade_token, andsteam_offertiming fieldsGET /listings/price-listis a public market-wide price index that returned24653entries during the 2026-03-07 live check; each entry currently exposesmarket_hash_name,quantity, andmin_price- currently observed query params on
/listings/price-listsuch asmarket_hash_nameandlimitappear to be silently ignored; exact filtering/pagination semantics are not yet mapped POST /trades/steam-status/new-offerkeys off a stringoffer_idfield, nottrade_id;{ offer_id: "0" }returned200 {"message":"successfully updated offer state"}while{ trade_id: "0" }returnedinvalid offer id formatPOST /trades/steam-status/offeracceptssent_offersand optionaltrade_idpayloads; both{ sent_offers: [] }and{ trade_id, sent_offers: [] }returned200 {"message":"successfully updated offer state"}, while the empty-array sync produced no observed trade-state change on the current pending trade- browser-auth discovery on
/profile/tradesshowed that the UI uses two concrete trade queries:/me/trades?state=queued,pending&limit=5000for active seller-side work and/me/trades?role=seller&state=failed,cancelled,verified&limit=30&page=0for history - browser-auth discovery on
/profile/offersuses/me/offers-timeline?limit=40plus direct/offers/{id}/historyfetches for the selected thread - browser-auth discovery on
/stall/metriggersPOST /me/recommender-token, which returns{ token, expires_at }, and also calls the external public routehttps://loadout-api.csfloat.com/v1/user/{steam_id}/loadouts - browser-auth discovery on
/loadout/overviewand/loadout/{id}confirmed the public companion routeshttps://loadout-api.csfloat.com/v1/user/{steam_id}/loadoutsandhttps://loadout-api.csfloat.com/v1/loadout/{id} POST https://loadout-api.csfloat.com/v1/recommendrequiresAuthorization: Bearer <recommender-token>; a valid live skin request on 2026-03-08 was{ "items":[{ "type":"skin", "def_index":7, "paint_index":490 }], "count":5 }and returned{ "count":5, "results":[{ "def_index", "paint_index", "score" }, ...] }def_whitelistanddef_blacklistare accepted optional arrays on/v1/recommend; a live request withdef_whitelist:[7]returned only weapondef_index=7results- sticker-style recommendation requests are currently unsupported; live probes with
{ "items":[{ "type":"sticker", "sticker_index":3 }], "count":5 }and{ "type":"sticker", "sticker_index":55 }both returned400 {"error":"Item 1 unsupported type 'sticker'"} GET https://loadout-api.csfloat.com/v1/loadoutis the public/global list route;sort_by=created_at,sort_by=favorites, andsort_by=randomare all live and return different orderingsPOST https://loadout-api.csfloat.com/v1/loadout/{id}/favoriteandDELETE https://loadout-api.csfloat.com/v1/loadout/{id}/favoriteboth require the same recommender bearer token family; a live toggle on 2026-03-08 returned{"message":"Loadout added to favorites"}and{"message":"Loadout removed from favorites"}modeandpageonGET /v1/loadoutcurrently look ignored in public probes:mode=created,mode=favorites,mode=bogus,page=0, andpage=1all returned the same first-page ids during the 2026-03-08 check- invalid
sort_byonGET /v1/loadouthard-fails with400 {"error":"sort_by must be \"favorites\", \"random\", or \"created_at\""}; older UI-styledate-descis not accepted by the companion API POST https://loadout-api.csfloat.com/v1/loadoutis a stable bearer-token create route; a live request on 2026-03-08 with{ "name":"SDK Temp ...", "ct":{ "is_filled": false }, "t":{ "is_filled": false } }returned201and a full{ "loadout": ... }payloadPUT https://loadout-api.csfloat.com/v1/loadout/{id}is the stable update route; a live request on 2026-03-08 with{ "name":"... Updated", "ct":{ "is_filled": false }, "t":{ "is_filled": false } }returned200and persisted the renamed loadoutDELETE https://loadout-api.csfloat.com/v1/loadout/{id}is the stable delete route; a live request on 2026-03-08 returned200 {"message":"Loadout deleted successfully"}- negative method checks on the companion API:
PUT /v1/loadoutandPATCH /v1/loadout/{id}both returned405, so create/update should be modeled only asPOST /v1/loadoutandPUT /v1/loadout/{id} - live loadout item refs can include more than
def_index: current public list/detail payloads also exposepaint_index,wear_index,isLocked,stat_trak, andstickers GET https://loadout-api.csfloat.com/v1/user/favoritesrequires the same recommender bearer token family; a live favorite/unfavorite cycle on 2026-03-08 returned{ favorites:[{ added_at, loadout_id, loadout }] }and correctly included the toggled loadout- auction listing discovery on 2026-03-08 confirmed that
GET /listings?type=auction&sort_by=lowest_pricereturns the cheap-auction frontier, including repeatedprice=3listings withauction_details.min_next_bid=8 sort_by=num_bidsandsort_by=expires_soonare both live-meaningful ontype=auctionqueries:num_bidssurfaces high-activity auctions, whileexpires_soonsurfaces near-expiry auctions- the item spotlight route
/item/{id}currently bootstraps a stable read bundle around the selected listing:GET /listings/{id},GET /listings/{id}/buy-orders?limit=10,GET /listings/{id}/similar, andGET /history/{market_hash_name}/graph?paint_index=... GET https://loadout-api.csfloat.com/v1/loadoutalso supports browser-observed discover paramslimit,months,def_index, andpaint_index; live checks on 2026-03-08 showedmonths=1changes the top ids undersort_by=favorites, anddef_index=7&paint_index=490narrows the result set to AK-47 | Wasteland Rebel themed loadoutsany_filledonGET /v1/loadoutis now better mapped than the initial weak note suggested: the current discover UI usesany_filled=true, direct live probes on 2026-03-08 confirmed that literaltrueis accepted, and invalid values likefalse,0,1, orbogusall hard-fail with400 {"error":"any_filled must be \"true\""}; the current top-page favorites sample still matched the baseline ordering, so the param is best treated as a validated discover-mode flag rather than a proven reordering controlPOST https://loadout-api.csfloat.com/v1/recommend/stickersis live and safe:{ "items":[{ "type":"skin", "def_index":7, "paint_index":490 }], "count":10, "collection_whitelist":["Holo"] }returned200with{ "results":[{ "sticker_index", "score" }, ...] }POST https://loadout-api.csfloat.com/v1/generateis live and returns slot-level recommendations; a safe request on 2026-03-08 with{ "items":[{ "type":"skin", "def_index":7, "paint_index":490, "wear_index":2 }], "def_indexes":[7,13,39,9], "faction":"t", "max_price":3000 }returned200with{ "remaining_budget":30, "total_cost":2970, "results":[...] }POST /v1/generatehard-validates faction and budget: mismatcheddef_indexescan returnct-only/t-onlyerrors, and too-smallmax_pricecan returnLocked items cost (...) exceeds budget (...)GET /meta/appis live and currently returns a minimal bootstrap payload{ min_required_version }; on 2026-03-08 the value was"9.0.0"GET /schema/browseis live and groups schema-adjacent browse items under{ data:[{ type, user_visible_type, items:[...] }] };type=stickersreturned tournament-era sticker buckets such asDreamHack 2013andKatowice 2014- the browser bundle lowercases schema browse category labels directly before calling
/schema/browse, so the currently known query values arerifles,pistols,smgs,heavy,knives,gloves,agents,containers,stickers,keychains,patches,collectibles, andmusic kits POST /listings/bulk-listis live on authenticated seller accounts and returns{ data:[listing, ...] }; on 2026-03-08 a happy-path batch of two privateZeus x27 | Swamp DDPAT (Factory New)listings at$0.04succeeded, while the same route at$9.99hit400 "Listing is suspected to be overpriced. You need to complete KYC and onboard with Stripe to list this item."PATCH /listings/bulk-modifyis live and currently confirmed with price-only updates:{ modifications:[{ contract_id, price }] }returned{ data:[updated listings...] }on the same two-listing batchPATCH /listings/bulk-modifyfailure modes are already useful for validation:{ contract_id:"0", price:3 }returned500 "failed to fetch listing", while{ contract_id:"0", price:1 }failed earlier at the server price floor with422 "minimum allowed price is $0.03 USD"PATCH /listings/bulk-delistis live and reversible:{ contract_ids:[...] }returned200 {"message":"contracts delisted"}on the real two-listing batch, and{ contract_ids:["0"] }returned500 "failed to delist contracts"POST /me/gs-inspect-tokenis live and returns the same token-style contract as other companion flows:{ token, expires_at }; the current browser bundle uses it to authorize externalgs-api.csfloat.com/api/v1/players/equip*requests, while the SDK intentionally stops at the CSFloat-side token helper for nowGET /schema/images/screenshotis live behind authentication: on 2026-03-08,def_index=7&paint_index=490&min_float=0.15&max_float=0.38returned{ id:"1305328935500910839", sides:{ playside:{ path:"m/.../playside.png" }, backside:{ path:"m/.../backside.png" } } }, while the same request without auth returned401 code=27 authorization not set- the browser screenshot flow also has an external generator at
https://s-api.csfloat.com/api/v1/public/screenshot?sig=...&url=..., but the SDK currently only exposes the CSFloat-side/schema/images/screenshothelper because the external path depends on item-level screenshot signatures and is better kept as a documented companion detail for now - browser-auth discovery on
/profile/watchlistconfirmed that the watchlist page serializes filters into URL/query params on the same listing-style surface:Listed->state=listed,Newest->sort_by=most_recent,Sticker Combos->filter=sticker_combos, andStatTrak™->category=2 - direct authenticated live probes on 2026-03-08 confirmed that
GET /me/watchlistaccepts at leaststate,sort_by,filter,category,type, andmin_price;state=listed|sold|delistedall return200, whilestate=bogushard-fails with400 code 18andschema: error converting value for "state". Details: invalid state - current live watchlist state semantics are meaningful on the main account:
state=soldreturned onlysoldrows,state=delistedreturned onlydelistedrows, andsort_by=most_recentchanged the first-page ordering versus the default watchlist sort - current raw HTTP market-search behavior is mixed rather than uniformly public: on 2026-03-08, many explicit
/listings?...queries withsort_by,category,min_price,type, andfilterstill returned403 "You need to be logged in to search listings", but the small homepage-feed combinations documented below remained replayable without auth; unauthenticated market search should therefore be treated as a guarded surface with a limited public feed subset, not as a clean general-purpose public API contract - the current market/watchlist bundle serializes applied sticker filters into a JSON
stickersquery param and keychain filters into a JSONkeychainsquery param; the browser helper currently emits sticker entries like{ "i": <stickerId>, "s": <slot-1> }or{ "c": <customStickerId>, "s": <slot-1> }, and keychain entries like{ "i": <keychainIndex> } - direct authenticated live probes on 2026-03-08 confirmed that
GET /listings?stickers=[...]andGET /listings?keychains=[...]both return200and meaningfully narrow results:stickers=[{"i":3}]surfaced items whose payloads included matching applied sticker entries like{ stickerId: 3, slot: 1 },stickers=[{"c":"C10204271498"}]surfaced coldzera autograph rows via the lower-levelcustom_sticker_idcontract, andkeychains=[{"i":1}]surfaced items whose payloads included matching keychain entries like{ stickerId: 1, slot: 0, pattern, ... } sticker_option=skins|packagesis live-meaningful when paired with JSONstickers=[...]filters: on 2026-03-08,GET /listings?stickers=[{"i":3}]&sticker_option=skinspreserved the applied-skin result set, whileGET /listings?stickers=[{"i":85}]&sticker_option=packagesandGET /listings?stickers=[{"i":96}]&sticker_option=packagesboth returnedEMS One 2014 Souvenir Package; the current SDK exposessticker_optionas a low-level typed param, but package-side density still depends on the specific sticker id- direct authenticated live probes on 2026-03-08 confirmed that
GET /me/watchlistreuses the same JSON attachment-filter contract as/listings:stickers=[{"i":3}]returned the matching watchedSouvenir M4A1-S | VariCamo (Field-Tested)row with an applied{ stickerId: 3, slot: 3 }payload, and a targetedkeychains=[{"i":83}]probe returned the matching watchedSouvenir AUG | Spalted Wood (Field-Tested)row with an applied{ stickerId: 83, slot: 0, highlight_reel: 807, ... }payload;sticker_option=packageson watchlist is still only weakly mapped because the current account-side probe forstickers=[{"i":85}]returned200with an empty set rather than a watched package row min_ref_qtyis live-meaningful on both/listingsand/me/watchlist: the browserExclude Rare Itemstoggle maps tomin_ref_qty=20, higher floors such as100further narrow results, and invalid values likemin_ref_qty=bogushard-fail with400 code 18 schema: error converting value for "min_ref_qty"- current live limit ceilings on 2026-03-08 are
50for both/listingsand/me/watchlist:limit=50returned200, whilelimit=51and above returned400 {"code":4,"message":"limit is too high"} - the watchlist page currently reuses meaningful market-style sort and listing-mode params beyond
most_recent: on 2026-03-08, the default page ordering matchedsort_by=best_deal,sort_by=highest_discountreordered the first rows versus default,sort_by=lowest_pricesurfaced the cheapest watched rows first,type=auctionreturned only auction rows,type=buy_nowreturned buy-now rows, andfilter=uniquealso returned a distinct watchlist slice - the March 2026
/checkercompanion claim is no longer current: on 2026-04-14, live CLI probes foundapi.csfloat.comunresolved, and browser inspection plus bundle review showed/checkernow decoding current masked/protobuf inspect links locally while only fetching schema/browse side data for stickers and keychains; the SDK now follows that local path for masked links, while legacy unmasked links remain degraded on the old companion fallback - browser-auth discovery on
/selldid not reveal a new sell-only backend surface on 2026-03-08; the page currently bootstraps with the already-coveredGET /me,GET /schema,GET /meta/location,GET /meta/exchange-rates, andGET /me/inventoryroutes - browser-auth discovery on
/stall/melikewise stayed on the already-covered surface on 2026-03-08:POST /me/recommender-token,GET /users/{steam_id}/stall?limit=40, andGET https://loadout-api.csfloat.com/v1/user/{steam_id}/loadouts; no additional self-stall-only backend route was promoted from this pass - direct public live probes on 2026-03-08 confirmed that
GET /users/{id}/stallaccepts meaningful listing-style params beyondlimitandcursor:sort_by=lowest_price|highest_discount|most_recentall changed the first-page ordering,filter=uniquenarrowed the public stall from263to164rows,type=buy_nowreturned the active rows whiletype=auctionreturned an empty set on the current stall, andmin_ref_qty=20narrowed the total count from263to244 - the public stall route also accepts attachment-style listing filters: on 2026-03-08,
keychains=[{"i":83}]returned the matchingSouvenir Zeus x27 | Charged Up (Battle-Scarred)row, whilefilter=sticker_combosreturned200with an empty set on the current stall; invalidfilter=bogushard-failed with400 invalid filter value, while invalidsort_by=bogusreturned404 the given resource could not be found - unlike
/listingsand/me/watchlist, the current public stall route is not capped at50rows per page: on 2026-03-08,limit=51returned200, and the live audit already usesGET /users/{id}/stall?limit=500&type=buy_nowsuccessfully for mutation-safe inventory reconciliation GET /me/notifications/timelinecurrently supports cursor pagination but not meaningful limit control: on 2026-03-08, replaying the response cursor returned an older page with a different firstnotification_id, while bothlimit=1andlimit=5returned the same42rows as the default request;cursor=0simply fell back to the current first page rather than failing validation- direct authenticated retest on 2026-04-14 showed why the earlier replay broke:
GET /buy-orders/item?url=<fresh live listing inspect link>&limit=3still returns422 {"code":6,"message":"invalid signature"}when called bare, but the current browser bundle now passesmarket_hash_nameplussig, and the inspecturlshould normally come fromlisting.item.serialized_inspect ?? listing.item.inspect_link; replaying that trio withsig = listing.item.gs_sigrestored a normal200response with buy-order rows GET /me/transactionsis broader than the original SDK typing suggested: on 2026-03-08, browser-auth discovery and direct API probes confirmed meaningfulorder=asc|descplustype=deposit|withdrawal|fine|bid_posted|trade_verified; invalidtype=bogushard-failed with400 "you are not authorized to filter by this transaction type", and invalidorder=bogushard-failed with400 "bogus is not a valid order"GET /me/offersis currently page-oriented rather than cursor-oriented on the live profile UI: on 2026-03-08,/profileusedGET /me/offers?page=0&limit=10, direct API probes confirmedpage=0andpage=1are accepted,limit=1narrows the result set, invalidpage=bogushard-fails with400 "failed to deserialize query params", andcursor=abcappears ignored on the current backendGET /me/buy-ordersis also page-oriented on the live profile UI: on 2026-03-08,/profileusedGET /me/buy-orders?page=0&limit=10&order=desc, direct API probes confirmedorder=asc|descis accepted, invalidorder=bogushard-fails with400 "\"bogus\" is not a valid order", and invalidpage=bogushard-fails with400 schema: error converting value for "page"; the current accounts had zero active orders, so the actual asc/desc ordering difference remains only weakly mapped- current authenticated market probes on 2026-03-08 showed that
filter=sticker_combosandfilter=uniqueare not cosmetic aliases:filter=sticker_combosreturned sticker-heavy rows likeGlock-18 | Twilight Galaxy (Factory New)andAK-47 | X-Ray (Factory New)with4-5applied attachments, whilefilter=uniquereturned a distinct knife/glove-heavy slice led by★ StatTrak™ Bayonet | Blue Steel (Factory New)and★ Sport Gloves | Superconductor (Factory New) - the current public homepage bootstraps a highlighted buy-now feed via
GET /listings?limit=5&min_ref_qty=20&type=buy_now&min_price=500; browser replay and direct unauthenticated live probes both returned200on 2026-03-08, so this param combination is now tracked as a stable public page feed rather than just an incidental listing query - the current cart/checkout UI does not map to a dedicated CSFloat
/cartor/checkoutAPI route: the live bundle on 2026-03-08 stores cart state under local storage keycheckout_cart_contracts, refreshes entries through existing listing detail reads, and the item-cardbuyNow()path still delegates to the already-covered purchase flow (purchaseContracts(...)/POST /listings/buy); no separate cart backend endpoint was promoted from this pass - browser-observed homepage radio states are currently backed by three distinct public feed queries on 2026-03-08:
Top Deals->GET /listings?limit=5&min_ref_qty=20&type=buy_now&min_price=500,Newest Items-> the same route plussort_by=most_recent, andUnique Items-> the sameNewestroute plusfilter=unique; all three replayed directly without auth and returned200 - the SDK now exposes these public homepage feed combinations as
getHomepageFeedParams()/CSFLOAT_HOMEPAGE_FEED_PRESETSinstead of forcing consumers to hand-copy the current query-string contract - the current public loadout discover page is stable enough for a first-class helper: browser/network and direct live probes on 2026-03-08 confirmed
GET https://loadout-api.csfloat.com/v1/loadout?sort_by=favorites&limit=100&months=1&any_filled=true, so the SDK now exposes that baseline asloadout.getDiscoverLoadouts(),getDiscoverLoadoutParams(),buildLoadoutListParams(), andCSFLOAT_DISCOVER_LOADOUT_PARAMS monthsonGET https://loadout-api.csfloat.com/v1/loadoutcurrently validates only when it parses as a number:months=0andmonths=-1returned400 "months must be at least 1"on 2026-03-08, while non-numericmonths=boguscurrently fell back to a normal200baseline response instead of hard-failinglimitonGET https://loadout-api.csfloat.com/v1/loadoutis now tightly mapped:limit=200returned200whilelimit=201,limit=500,limit=1000,limit=0, andlimit=-1all returned400 "limit must be between 1 and 200"on 2026-03-08; the SDK now exportsCSFLOAT_LOADOUT_MAX_LIMIT = 200and validates the same range inbuildLoadoutListParams()- skin-scoped public loadout search requires
def_indexandpaint_indextogether:def_index=7alone andpaint_index=490alone both returned400 "Both def_index and paint_index must be provided together"on 2026-03-08, while paired requests likedef_index=7&paint_index=490&months=1returned200with AK-47 themed rows; the SDK now exposesbuildLoadoutSkinSearchParams()andloadout.getSkinLoadouts(defIndex, paintIndex, params?)for this contract - current extra query params on the public loadout list route remain mapped conservatively: on 2026-03-08, adding
wear_index=2orstattrak=trueto a paireddef_index=7&paint_index=490search still returned200, but the first-page ids matched the baseline paired search closely enough that these fields are still treated as weakly mapped/possibly ignored rather than promoted into the typed public surface - cursor pagination on
GET /me/watchlistis now strong enough for a high-level iterator: withlimit=5, direct live probes on 2026-03-08 returned distinct non-empty first, second, and third pages under successive response cursors, so the SDK now exposesaccount.iterateWatchlist()rather than forcing callers to hand-roll cursor loops - cursor pagination on
GET /users/{id}/stallis likewise strong enough for a high-level iterator: withlimit=20&type=buy_now, direct live probes on 2026-03-08 returned distinct first-page and second-page listing ids under successive cursors, so the SDK now exposesstall.iterateStall() - the unauthenticated market/search boundary is now tighter than the earlier broad note suggested: on 2026-03-08,
GET /listings?limit=40&min_ref_qty=20still returned200as the exact public/searchpage bootstrap, but addingsort_by=most_recent,filter=unique,type=buy_now|auction, ormin_price=500to that baseline all flipped back to403 "You need to be logged in to search listings"; the SDK now exposes this exact public page contract asgetPublicMarketPageParams()/CSFLOAT_PUBLIC_MARKET_PAGE_PARAMS POST https://loadout-api.csfloat.com/v1/recommendis more permissive than a strict contract would suggest: on 2026-03-08,count=0returned200with empty results, negativecount=-1also returned200but effectively clamped to0, non-numericcount="bogus"triggered a backend500, emptyitems:[]still returned a non-empty default recommendation set, and no hard cap was observed atcount=250; the SDK therefore keeps the raw route low-level but now exposesbuildLoadoutRecommendationRequest()/buildSingleSkinRecommendationRequest()andloadout.recommendForSkin()to validate sane caller input before the backend's inconsistent edge handlingPOST https://loadout-api.csfloat.com/v1/recommend/stickerscurrently hard-caps output at100: on 2026-03-08,count=101,150, and500all returned200withcount=100, while negativecount=-1returned a strange200withcount=198, and non-numericcount="bogus"triggered a backend500; invalid-lookingcollection_whitelist:["bogus"]was simply ignored and still returned normal results. The SDK now exposesCSFLOAT_STICKER_RECOMMENDATION_MAX_COUNT = 100,buildStickerRecommendationRequest(),buildSingleSkinStickerRecommendationRequest(), andloadout.recommendStickersForSkin()so callers can stay inside the stable boundaryPOST https://loadout-api.csfloat.com/v1/generatevalidates only a subset of its input shape: on 2026-03-08, emptydef_indexes:[]returned400 "def_indexes required", invalidfaction:"bogus"returned400 "faction is required and must be either \"ct\" or \"t\"", andmax_price=-1/max_price="bogus"returned400 "max_price must be a positive integer", but emptyitems:[]still returned a normal recommendation payload and negative ids insidedef_indexesdid not hard-fail; the SDK therefore exposesbuildGenerateLoadoutRecommendationsRequest()plusCSFLOAT_LOADOUT_FACTIONSfor the stable validated subset without pretending the whole backend contract is strict- the SDK now also exposes small composable builders for several already-confirmed visible market filters that were previously only raw fields on
CsfloatListParams:buildCollectionFilter(),buildRarityFilter(),buildPaintSeedFilter(), andbuildMusicKitFilter()map directly to the live query fieldscollection,rarity,paint_seed, andmusic_kit_indexthat are already validated in the market audit surface - expression-backed buy-order creation is now fully live-confirmed instead of being only an inferred AST path: on 2026-03-08, a direct
POST /buy-orderswith{ expression:{ condition:"and", rules:[{ field:"DefIndex", operator:"==", value:{ constant:"7" } }, { field:"PaintIndex", operator:"==", value:{ constant:"72" } }, { field:"StatTrak", operator:"==", value:{ constant:"false" } }, { field:"Souvenir", operator:"==", value:{ constant:"false" } }] }, max_price:3, quantity:1 }returned200with an order payload whoseexpressionstring serialized to(DefIndex == 7 and PaintIndex == 72 and StatTrak == false and Souvenir == false), and a follow-upDELETE /buy-orders/{id}removed it cleanly - expression-backed similar-order lookup is likewise now live-confirmed: on 2026-03-08,
POST /buy-orders/similar-orders?limit=3with the same AK-47Safari Meshexpression body returned200with non-empty rows likeAK-47 | Safari Mesh (Factory New)and distinct{ qty, price }values, so the SDK now treats the expression path as implemented rather than leaving it as a docs-only browser note
Current supported request shapes:
buy_nowasset_idpricetypeprivatedescriptionmax_offer_discount
auctionasset_idreserve_priceduration_daystypeprivatedescription
Important:
buy_nowcreate flow is live-validatedauctionrequest shape is supported from known API surface, but should be treated as not-yet-live-validated by this repository until explicitly testedPOST /listingsshould not be treated as universally available for every account state; a live retest on 2026-03-07 returned code134with a Stripe onboarding requirement for further listingsPOST /listings/bulk-listcurrently reuses the same per-item payload shapes under{ items:[...] }; the repository has live-validated thebuy_nowbatch path, but not yet a real auction batch happy-path
Live retesting on 2026-03-07 confirmed:
PATCH /listings/{id}still works and was verified with+1then revert on a real listingPOST /listingsstill exists, but the current account hit400 code 134with messageYou need to fully onboard with Stripe for payouts to list further items- Because of that gate, this repository should distinguish between:
- endpoint existence
- live behavior on a specific account
- unconditional public availability
Current live probing indicates:
- public routes include:
/schema/listings?limit=40&min_ref_qty=20/listings/{id}/listings/{id}/buy-orderswithout extra query params/users/{id}/users/{id}/stall/history/.../sales/history/.../graph/meta/exchange-rates/meta/location/listings/{auction_id}/bids/listings/{id}/similar
- authenticated routes include:
/me*- general
/listingssearch with normal query params /listings/{id}/buy-orderswhen using auth-only query params such aslimit- mutation routes such as
/buy-orders,/offers,/listings/buy
This repository includes a repeatable audit script:
ENV_FILE=/path/to/.env npm run audit:liveThe default script now runs the core scope, which keeps the stable read/mutation regression path and skips the heavier market-filter burst.
To include the extended market/candidate sweep as well:
ENV_FILE=/path/to/.env npm run audit:live:extendedTo opt into reversible live mutation checks:
ALLOW_LIVE_MUTATIONS=1 ENV_FILE=/path/to/.env npm run audit:liveThis repository does not claim:
- support for unknown private endpoints
- complete coverage of any surface not documented, source-inspected, or live-validated
- live validation of every mutation variant
The honest claim for public release should be:
csfloat-node-sdk covers the currently known CSFloat API surface that is documented, source-discovered, or live-validated.
Stronger but still honest positioning:
csfloat-node-sdk aims to be one of the most comprehensive public maps of the currently accessible CSFloat API surface, while clearly separating implemented, discovered, live-validated, stale, and account-gated behavior.