API Coverage
Status legend:
implemented— available incsfloat-node-sdkdiscovered— 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
Known Endpoint Surface
| 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 | browser tool network + live | external Float Checker companion route; currently requires Origin: https://csfloat.com and returns { iteminfo:{ defindex, paintindex, floatvalue, paintseed, full_item_name, ... } } |
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 |
Expanded Live Endpoint Surface
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>, limit }; live happy-path returned an array like [{ expression, qty, price }] |
/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 |
Likely Stale Or Wrapper-Only Routes
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 |
Confirmed Silently-Ignored Query Params
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.
Confirmed Hard-Rejected Query Params
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 |
Query/Behavior Surface Covered
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
/checkerpage uses an external companion route rather than/api/v1: on 2026-03-08, browser-auth network and direct live replay confirmedGET https://api.csfloat.com/?url=<inspectLink>withOrigin: https://csfloat.comreturning{ iteminfo:{ origin, quality, rarity, paintseed, defindex, paintindex, floatvalue, min, max, weapon_type, item_name, rarity_name, quality_name, wear_name, full_item_name, s, a, d, m } }; the same request without a validOriginheader returned400 {"error":"Invalid Origin"} - 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 validationGET /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
Listing Creation Surface
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
Current Account-State Notes
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
Auth Notes
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
Repeatable Live Audit
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:liveClaims This Repository Does Not Make
This 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
Release-Ready Claim
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.