Skip to Content
Canonical source of truth stays in the repository docs. This site is the polished presentation layer.
API Coverage Matrix

API Coverage

Status legend:

  1. implemented — available in csfloat-node-sdk
  2. discovered — confirmed live, but not yet implemented in the SDK
  3. validated — confirmed either by official docs, local source analysis, or live account checks
  4. account-gated — endpoint exists, but live availability depends on account state or platform eligibility
  5. source — where the endpoint evidence comes from

Known Endpoint Surface

EndpointMethodSDK StatusValidation SourceNotes
/listingsGETimplementedofficial docs + livesupports query params
/listings/price-listGETimplementedlivepublic price index; returns { market_hash_name, quantity, min_price }[]
/listings/{id}GETimplementedofficial docs + livesingle listing fetch
/listingsPOSTimplemented + account-gatedofficial docs + liveexplicit buy_now supported; current live retest hit account-state gate for further listings
/listings/bulk-listPOSTimplemented + account-gatedbrowser bundle + liveconfirmed 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-modifyPATCHimplementedbrowser bundle + liveconfirmed happy-path with body { modifications:[{ contract_id, price }] }; current SDK keeps this route price-focused until broader field semantics are proven
/listings/bulk-delistPATCHimplementedbrowser bundle + liveconfirmed happy-path with body { contract_ids:[...] } and response { "message":"contracts delisted" }
/listings/{id}PATCHimplementedliveupdate listing price
/listings/{id}DELETEimplementedlive + python cloneunlist / delist behavior
/users/{id}GETimplementedpython clone + livepublic user profile
/users/{id}/stallGETimplementedpython clone + livepublic user stall; current live validation confirms meaningful listing-style params including sort_by, filter, type, and min_ref_qty
/meGETimplementedlive + python cloneauthenticated 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/inventoryGETimplementedlive + python cloneauthenticated inventory
/history/{market_hash_name}/salesGETimplementedlivesales history
/schemaGETimplementedlive + public wrapper sourcepublic item schema; both /schema and /schema/ resolve
/schema/browseGETimplementedbrowser bundle + livepublic grouped browse route; current live validation confirmed type=stickers and bundle uses lowercased category labels such as stickers, keychains, and music kits
/schema/images/screenshotGETimplementedbrowser bundle + liveauthenticated example-screenshot route; current live sample returned { id, sides:{ playside:{path}, backside:{path} } } for a schema-targeted item query
/meta/exchange-ratesGETimplementedlive + public wrapper sourcepublic exchange rate map
/meta/appGETimplementedbrowser bundle + liveapp bootstrap metadata; current live response returned { min_required_version: "9.0.0" }
/meta/locationGETimplementedlive + public wrapper sourcepublic inferred location data
/meta/notaryGETimplementedbrowser bundle + livereturns current notary availability flags such as { rollback:{enabled,background}, accepted:{enabled,background} }
https://api.csfloat.com/?url={inspectLink}GETimplementedbrowser tool network + liveexternal 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}/loadoutsGETimplementedbrowser-auth network + livepublic external CSFloat loadout service; returns { loadouts: [...] }
https://loadout-api.csfloat.com/v1/loadout/{id}GETimplementedbrowser-auth network + livepublic loadout detail route; returns { loadout: ... }
https://loadout-api.csfloat.com/v1/loadoutGETimplementedbundle semantics + livepublic/global loadout list route; confirmed `sort_by=created_at
https://loadout-api.csfloat.com/v1/user/favoritesGETimplementedbundle semantics + liverequires Authorization: Bearer <recommender-token>; confirmed response shape { favorites:[{ added_at, loadout_id, loadout }] }
https://loadout-api.csfloat.com/v1/loadoutPOSTimplementedbrowser-auth network + liverequires Authorization: Bearer <recommender-token>; confirmed 201 create with body { name, ct, t } and response { loadout: ... }
https://loadout-api.csfloat.com/v1/loadout/{id}PUTimplementedbrowser-auth network + liverequires Authorization: Bearer <recommender-token>; confirmed 200 update with body { name, ct, t } and response { loadout: ... }
https://loadout-api.csfloat.com/v1/loadout/{id}DELETEimplementedbrowser-auth network + liverequires Authorization: Bearer <recommender-token>; confirmed 200 {"message":"Loadout deleted successfully"}
https://loadout-api.csfloat.com/v1/recommendPOSTimplementedbrowser-auth network + liverequires 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/stickersPOSTimplementedbundle semantics + liverequires 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/generatePOSTimplementedbundle semantics + liverequires 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}/favoritePOSTimplementedbundle semantics + liverequires Authorization: Bearer <recommender-token>; returns {"loadout":{"social_stats":{"favorites":N}},"message":"Loadout added to favorites"}
https://loadout-api.csfloat.com/v1/loadout/{id}/favoriteDELETEimplementedbundle semantics + liverequires Authorization: Bearer <recommender-token>; returns {"loadout":{"social_stats":{"favorites":N}},"message":"Loadout removed from favorites"}
/me/account-standingGETimplementedlive + public wrapper sourceauthenticated account standing
/me/transactionsGETimplementedlive + public wrapper sourcereturns { transactions, count }; current live validation confirms meaningful page, limit, `order=asc
/me/offers-timelineGETimplementedlive + public wrapper sourceauthenticated offers timeline
/offersPOSTimplementedliveconfirmed happy-path create on buyer account with body { contract_id, price }
/offers/{id}GETimplementedlivesingle offer fetch by valid offer id
/offers/{id}/acceptPOSTimplementedlive invalid probe + browser-auth discoverycurrent SDK exposes account.acceptOffer(); route existence is confirmed, but happy-path response semantics remain only partially mapped
/offers/{id}/historyGETimplementedlive + public wrapper sourcehistorical thread for the offer chain; confirmed with valid declined/counter-offer ids
/offers/{id}/counter-offerPOSTimplementedliveconfirmed happy-path on seller account with body { price }
/offers/{id}DELETEimplementedliveconfirmed happy-path cancellation with response offer canceled; exact actor/state semantics may vary by thread state
/me/notifications/timelineGETimplementedlive + public wrapper sourceauthenticated notifications timeline; current live validation confirms cursor-based pagination while limit appears ignored
/me/buy-ordersGETimplementedlive + public wrapper sourcereturns { orders, count }; current live validation confirms page, limit, and validated `order=asc
/buy-ordersPOSTimplementedlive + public wrapper sourceconfirmed 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}PATCHimplementedliveconfirmed happy-path update with body { max_price }
/buy-orders/{id}DELETEimplementedlive + public wrapper sourceconfirmed happy-path delete with successfully removed the order
/buy-orders/similar-ordersPOSTimplementedbrowser bundle + livesafe 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-bidsGETimplementedlive + public wrapper sourceauthenticated auto-bids list
/me/auto-bids/{id}DELETEimplementedbrowser-auth network + liveconfirmed happy-path delete with {"message":"deleted auto-bid"}
/me/recommender-tokenPOSTimplementedlive + browser-auth networkreturns { token, expires_at }
/me/notary-tokenPOSTimplementedbrowser bundle + livereturns { token, expires_at } for the notary/companion flow
/me/gs-inspect-tokenPOSTimplementedbrowser bundle + livereturns { token, expires_at } for the external gs-api.csfloat.com inspect/equip companion flow
/me/payments/max-withdrawableGETimplementedlive + browser bundlereturns { max_withdrawable } for the current account payout state
/me/payments/pending-depositsGETimplementedbrowser bundle + liveauthenticated pending-deposit list; current live sample returned [], while bundle/UI usage reads fields such as created, amount, currency, and payment_method_types
/me/pending-withdrawalsGETimplementedlive + browser bundlereturns the authenticated pending-withdrawal list; current live sample was an empty array
/me/pending-withdrawals/{id}DELETEimplementedlive invalid probe + browser bundleinvalid probe on id=0 returned 200 with an empty body, confirming the route/method despite the currently opaque response shape
/me/extension/statusGETimplementedlive + browser bundlereturns extension version/permission metadata for the authenticated account
/me/mobile/statusGETimplementedlive + public wrapper sourceauthenticated mobile status
/me/transactions/exportGETimplementedlive + browser bundlereturns 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/acceptPOSTimplementedlive + public wrapper sourceconfirmed 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}/acceptPOSTimplementedbrowser bundle + liveconfirmed 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/cancelPOSTimplementedbrowser bundle + live invalid probebundle-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}GETimplementedbrowser bundle + livetrade 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-detailsGETimplementedbrowser bundle + livebuyer-details route now captured from a real queued cross-account trade; current live sample shape { steam_level, persona_name, avatar_url }
/trades/{id}DELETEimplementedbrowser bundle + live invalid probebundle-mapped seller-side cancel route; live invalid probe on id=0 returned 500 record not found, confirming path/method
/mePATCHimplementedlive + public wrapper sourceconfirmed 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-receiptPOSTimplementedlive + public wrapper sourcemark notifications read via last_read_id
/me/mobile/statusPOSTimplementedlive + public wrapper sourceconfirmed live with payload { "version": "8.0.0" }
/listings/{id}/buy-ordersGETimplementedlive + public wrapper sourcepublic without extra query params; authenticated callers can also use limit
/listings/{id}/similarGETimplementedlive + public wrapper sourcereturns similar live listings
/listings/{id}/bidPOSTimplementedbrowser bundle + browser-auth network + liveauction 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}/watchlistPOSTimplementedliveconfirmed happy-path add with added to watchlist
/listings/{id}/watchlistDELETEimplementedliveconfirmed happy-path remove with removed from watchlist
/listings/buyPOSTimplementedlive + public wrapper sourceconfirmed happy-path with body { contract_ids: string[], total_price } and response all listings purchased
/history/{market_hash_name}/graphGETimplementedlive + public wrapper sourcesupports 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:

EndpointMethodSDK StatusValidation SourceNotes
/me/tradesGETimplementedlivereturns { trades, count }; supports limit
/me/offersGETimplementedlivereturns { offers, count }; current live validation confirms meaningful page + limit, while legacy cursor appears ignored
/me/watchlistGETimplementedlive + browser-auth UIreturns { 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=20GETimplementedlive + frontend networkexact unauthenticated public /search page bootstrap shape; exposed via listings.getListings(getPublicMarketPageParams())
/listings?limit=5&min_ref_qty=20&type=buy_now&min_price=500GETimplementedbrowser UI + livecurrent 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=500GETimplementedbrowser UI + livecurrent 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=500GETimplementedbrowser UI + livecurrent homepage Unique Items feed shape; exposed via listings.getListings(getHomepageFeedParams("unique")) and workflows.getPublicMarketFeeds()
/listings?filter=sticker_combosGETimplementedlive + browser UI + auth APIUI label Sticker Combos; requires auth; current SDK exposes this through the typed filter param on listings.getListings()
/listings?filter=uniqueGETimplementedlive + browser UI + auth APIUI label Unique Items; current SDK exposes this through the typed filter param on listings.getListings()
/listings/{auction_id}/bidsGETimplementedlivereturns bid array for auction listings; empty array when no bids
/buy-orders/itemGETimplementedbrowser bundle + liveinspect-link oriented route using query params { url:<inspectLink>, limit }; live happy-path returned an array like [{ expression, qty, price }]
/buy-orders/matching-items/floatdbPOSTdiscoveredbrowser bundle + live invalid proberoute exists but currently demands float-expression semantics; plain market_hash_name returned condition and rules are required for an expression
/trades/{id}/cannot-deliverPOSTimplementedbrowser bundle + live invalid probefrontend-confirmed no-body payload; SDK exposes account.cannotDeliverTrade() as a low-level, seller-side failure helper
/trades/{id}/disputePOSTimplementedbrowser bundle + live invalid probefrontend-confirmed no-body payload; SDK exposes account.disputeTrade() conservatively as a low-level dispute helper
/trades/{id}/receivedPOSTimplementedbrowser bundle + livefrontend-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/receivedPOSTimplementedbrowser bundle + livebundle-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}/rollbackPOSTimplementedbrowser bundle + live invalid probefrontend-confirmed no-body payload; SDK exposes account.rollbackTrade() conservatively because the happy-path remains trade-state-specific
/trades/{id}/manual-verificationPOSTimplementedbrowser bundle + live invalid probefrontend-confirmed no-body payload; SDK exposes account.manualVerifyTrade() as a low-level helper
/trades/{id}/rollback-verifyPOSTimplementedbrowser bundle + live invalid probefrontend-confirmed no-body payload; SDK exposes account.verifyTradeRollback() as a low-level helper
/trades/{id}/report-errorPOSTdiscoveredbrowser bundle + live invalid probeinvalid id=0 returned 500 record not found; likely support/reporting path
/trades/notaryPOSTdiscoveredbrowser bundle + live invalid probeempty payload returned 400 payload is required; exact contract still unmapped
/me/verify-emailPOSTimplementedbrowser bundle + live invalid probeinvalid email=not-an-email returned 400 invalid email format; SDK exposes account.verifyEmail() as a low-level request/confirm helper
/me/verify-smsPOSTimplementedbrowser bundle + live invalid probeinvalid 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-offerPOSTimplementedlive + browser bundlelow-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/offerPOSTimplementedlive + public wrapper sourcelow-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:

EndpointMethodLive ResultNotes
/listings/sellPOST404likely stale wrapper surface
/listings/{id}/bitPOST404likely stale wrapper surface
/me/trades/bulk/cancelPOST404outdated wrapper path; browser bundle and live invalid probe confirm the real route is /trades/bulk/cancel
/listings/{id}/salesGET404wrapper surface not confirmed live
/account-standingGET400 invalid resourcestale path; live route is /me/account-standing
/me/payments/stripe/connectGET404stale 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:

ParamProbed ValueEvidence
has_stickerstrueIDs identical to unfiltered result; sticker-less items appear
has_keychainstrueIDs identical to unfiltered result
has_fadetrueIDs identical to unfiltered result
phase1, 2, 3, 4, sapphireAll values return identical IDs; Doppler items unaffected
is_stattraktrue/falseIDs identical; items mix StatTrak and non-StatTrak — use category=2 instead
is_souvenirtrue/falseIDs identical; use category=3 instead
wear_nameFactory New, etc.IDs identical regardless of value — use min_float/max_float instead
num_stickers14IDs identical; items have different sticker counts
sticker_count14Alias for num_stickers; same silently-ignored behavior
min_paint_seed700Items with seed <700 still returned
max_paint_seed50Items with seed >50 still returned
badgeshot, new, rare200 OK but no badge-based filtering; items have no matching badge field
low_ranktrueIDs identical to unfiltered; not a filter
has_low_ranktrueIDs identical to unfiltered; same as low_rank
quality4, 9IDs identical; quality values unaffected
sticker55, 55|0, 55,73, 9999999All 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
page0, 1, 2All page values return identical IDs — pagination uses cursor exclusively
user_idauthenticated steam_id200 OK but seller IDs in result do not match the specified user_id; silently ignored
source15, csfloat, p2pAll values return identical IDs; no source field in listing response; silently ignored on standard accounts (2026-03-07 research pass 2)
is_commoditytrue200 OK but no filtering observed

Note: Items DO have low_rank as a field (e.g. low_rank: 41, low_rank: 4). The param does not filter on it. Note: For stattrak/souvenir/category filtering use category (1=normal, 2=stattrak, 3=souvenir, 4=highlight) — that IS a confirmed real filter. Note: The sticker param format ID|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):

ParamProbed ValueStatusNotes
typeany, normal, stattrak, souvenir400Only buy_now and auction are valid type values; other strings hard-fail

Query/Behavior Surface Covered

Currently covered or typed:

  1. limit
  2. cursor
  3. type
  4. filter
  5. source
  6. market_hash_name
  7. def_index
  8. paint_index
  9. category
  10. min_float
  11. max_float
  12. sort_by
  13. user_id
  14. collection
  15. rarity
  16. min_price
  17. max_price
  18. paint_seed
  19. sticker_index
  20. keychain_index
  21. keychain_highlight_reel
  22. music_kit_index
  23. min_keychain_pattern
  24. max_keychain_pattern
  25. min_blue
  26. max_blue
  27. min_fade
  28. max_fade

Live-confirmed search behaviors:

  1. sort_by accepts:
    • lowest_price
    • highest_price
    • most_recent
    • expires_soon
    • lowest_float
    • highest_float
    • best_deal
    • highest_discount
    • float_rank
    • num_bids
  2. invalid sort_by returns 404
  3. filter=sticker_combos and filter=unique are both live; any other filter value returns 400 invalid filter value
  4. filter values are derived from browser UI labels:
    • Sticker Combos -> sticker_combos
    • Unique Items -> unique
  5. wear filtering via min_float / max_float is live
  6. browser wear shortcuts map like this:
    • FN -> max_float=0.07
    • MW -> min_float=0.07&max_float=0.15
    • FT -> min_float=0.15&max_float=0.38
    • WW -> min_float=0.38&max_float=0.45
    • BS -> min_float=0.45
  7. min_float alone works
  8. max_float alone works
  9. reversed ranges such as min_float=0.8&max_float=0.2 return an empty result set
  10. invalid ranges such as min_float < 0 or max_float > 1 do not hard-fail; they appear to be ignored/fallbacked by the backend
  11. source is live as both string and numeric forms: source=csfloat, source=p2p, source=15 all return 200; exact semantic mapping between string/numeric forms and result sets is not fully differentiated on standard accounts
  12. category is live and maps like this:
  • 1 -> Normal
  • 2 -> StatTrak
  • 3 -> Souvenir
  • 4 -> Highlight
  1. category is the confirmed correct filter for stattrak/souvenir; is_stattrak and is_souvenir params exist but are silently ignored (see Confirmed Silently-Ignored section)
  2. def_index + paint_index is live and can target a specific skin family
  3. paint_seed (exact value) is live and can narrow family results to an exact seed
  4. collection is live; schema keys like set_cobblestone work
  5. rarity is live; schema rarity values like 6 work
  6. min_price and max_price are both live
  7. music_kit_index is live and can target music kit listings directly
  8. keychain_highlight_reel is live and can target highlight charm listings directly
  9. min_fade / max_fade are live for fade-capable finishes
  10. min_blue / max_blue are live for blue-percentage filtered searches
  11. min_keychain_pattern / max_keychain_pattern are live and can further narrow charm listings when paired with keychain_index; on 2026-03-08, keychain_index=29&min_keychain_pattern=0&max_keychain_pattern=10 returned a smaller Charm | Semi-Precious result set than the broader 0..10000 probe
  12. sticker_index and keychain_index are accepted by the API; they currently behave as index filters for sticker/charm listings themselves, not yet a documented helper for applied attachments
  13. only category=1..4 have confirmed semantics; category=5 returned a mixed, effectively unfiltered result set on 2026-03-07 and should be treated as unsupported
  14. GET /me/buy-orders accepts market_hash_name and sort_by without 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 now
  15. GET /history/{name}/graph works without paint_index; it returns a broader series than an explicit paint_index query, but the exact server-side aggregation semantics are not fully mapped yet
  16. GET /history/{name}/graph accepts a category query param (1=Normal, 2=StatTrak, 3=Souvenir); live probe on 2026-03-07 returned total_count=1747 for all values of category but with slightly different avg_price values per day — this behavior suggests category may influence averaging, but it does not change the set of days returned; treat as weakly mappable, not a confirmed hard filter
  17. PATCH /me accepts background_url and username as undocumented fields — both return 200 "user updated!" (confirmed 2026-03-07 research pass 2); exact validation rules for username are unknown; SDK adds these as optional typed fields in CsfloatUpdateMeRequest with helpers updateBackground() and updateUsername()
  18. filter=sticker_combos and filter=unique require authentication — unauthenticated requests return 403 (not 401); these are actively blocked, not silently ignored
  19. listing subroutes /listings/{id}/offers, /listings/{id}/trades, /listings/{id}/history, /listings/{id}/price-history, /listings/{id}/buyer, /listings/{id}/seller, /listings/{id}/item all return 404 — none confirmed live
  20. /me/* hidden routes probed and all return 404: balance, preferences, settings, referrals, subscriptions, kyc, payment, payout, stall, bids, listings, cart, disputes, 2fa, extension, rate-limit, limits, offers/sent, offers/received, fees
  21. all tested /users/{id}/* extensions return 404: offers, trades, buy-orders, statistics, reviews, reputation, watchlist, inventory
  22. GET /offers returns 405 Method Not Allowed — GET is not valid on this route; POST /offers is the only supported method
  23. top-level routes probed and all return 400 "invalid resource": announcements, referrals, promotions, leaderboard, search (with q=), items, market, prices, trending, stats, buy-now
  24. browser-auth auction detail flow confirmed that the history button maps to GET /listings/{id}/bids, while both Bid and Auto Bid converge on the same max-price route POST /listings/{id}/bid
  25. POST /listings/{id}/bid is 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 in GET /me/auto-bids
  26. repeated POST /listings/{id}/bid behaves as replacement/update semantics for a listing-level auto-bid: the previous entry disappears from GET /me/auto-bids and a new record with a new id becomes active
  27. browser-auth modal state after a live bid shows Your Max Bid: ... and a Remove affordance; clicking it revealed the actual cancel route DELETE /me/auto-bids/{id} via browser-auth network capture
  28. direct guesses DELETE /auto-bids/{id} and DELETE /listings/{id}/bid both returned 405; the correct delete path is user-scoped under /me/auto-bids/{id}
  29. GET /offers/{id} returns the current offer snapshot, while GET /offers/{id}/history returns 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-offer
  30. POST /offers happy-path is confirmed with body { contract_id, price } on a buyer account; using listing_id instead of contract_id falls back to failed to find contract with id '0'
  31. POST /offers/{id}/counter-offer happy-path is confirmed with body { price } on a seller account
  32. DELETE /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 becomes declined
  33. POST /listings/buy happy-path is confirmed with body { contract_ids: string[], total_price } and returns { "message": "all listings purchased" }
  34. PATCH /buy-orders/{id} happy-path is confirmed with body { max_price }; PUT and POST on the same route return 405
  35. POST /offers/{id}/accept exists and returns code 91: failed to accept offer for an invalid offer id; the SDK now exposes it as the low-level helper account.acceptOffer(), while the happy-path is still intentionally not executed because that would complete a real purchase
  36. POST /trades/bulk/accept is the confirmed accept sale route for queued seller trades; on a real $0.05 queued sale it returned { data: [trade] }, transitioned the trade from queued to pending, and populated accepted_at, trade_url, trade_token, and steam_offer timing fields
  37. GET /listings/price-list is a public market-wide price index that returned 24653 entries during the 2026-03-07 live check; each entry currently exposes market_hash_name, quantity, and min_price
  38. currently observed query params on /listings/price-list such as market_hash_name and limit appear to be silently ignored; exact filtering/pagination semantics are not yet mapped
  39. POST /trades/steam-status/new-offer keys off a string offer_id field, not trade_id; { offer_id: "0" } returned 200 {"message":"successfully updated offer state"} while { trade_id: "0" } returned invalid offer id format
  40. POST /trades/steam-status/offer accepts sent_offers and optional trade_id payloads; both { sent_offers: [] } and { trade_id, sent_offers: [] } returned 200 {"message":"successfully updated offer state"}, while the empty-array sync produced no observed trade-state change on the current pending trade
  41. browser-auth discovery on /profile/trades showed that the UI uses two concrete trade queries: /me/trades?state=queued,pending&limit=5000 for active seller-side work and /me/trades?role=seller&state=failed,cancelled,verified&limit=30&page=0 for history
  42. browser-auth discovery on /profile/offers uses /me/offers-timeline?limit=40 plus direct /offers/{id}/history fetches for the selected thread
  43. browser-auth discovery on /stall/me triggers POST /me/recommender-token, which returns { token, expires_at }, and also calls the external public route https://loadout-api.csfloat.com/v1/user/{steam_id}/loadouts
  44. browser-auth discovery on /loadout/overview and /loadout/{id} confirmed the public companion routes https://loadout-api.csfloat.com/v1/user/{steam_id}/loadouts and https://loadout-api.csfloat.com/v1/loadout/{id}
  45. POST https://loadout-api.csfloat.com/v1/recommend requires Authorization: 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" }, ...] }
  46. def_whitelist and def_blacklist are accepted optional arrays on /v1/recommend; a live request with def_whitelist:[7] returned only weapon def_index=7 results
  47. 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 returned 400 {"error":"Item 1 unsupported type 'sticker'"}
  48. GET https://loadout-api.csfloat.com/v1/loadout is the public/global list route; sort_by=created_at, sort_by=favorites, and sort_by=random are all live and return different orderings
  49. POST https://loadout-api.csfloat.com/v1/loadout/{id}/favorite and DELETE https://loadout-api.csfloat.com/v1/loadout/{id}/favorite both 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"}
  50. mode and page on GET /v1/loadout currently look ignored in public probes: mode=created, mode=favorites, mode=bogus, page=0, and page=1 all returned the same first-page ids during the 2026-03-08 check
  51. invalid sort_by on GET /v1/loadout hard-fails with 400 {"error":"sort_by must be \"favorites\", \"random\", or \"created_at\""}; older UI-style date-desc is not accepted by the companion API
  52. POST https://loadout-api.csfloat.com/v1/loadout is 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 } } returned 201 and a full { "loadout": ... } payload
  53. PUT 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 } } returned 200 and persisted the renamed loadout
  54. DELETE https://loadout-api.csfloat.com/v1/loadout/{id} is the stable delete route; a live request on 2026-03-08 returned 200 {"message":"Loadout deleted successfully"}
  55. negative method checks on the companion API: PUT /v1/loadout and PATCH /v1/loadout/{id} both returned 405, so create/update should be modeled only as POST /v1/loadout and PUT /v1/loadout/{id}
  56. live loadout item refs can include more than def_index: current public list/detail payloads also expose paint_index, wear_index, isLocked, stat_trak, and stickers
  57. GET https://loadout-api.csfloat.com/v1/user/favorites requires 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
  58. auction listing discovery on 2026-03-08 confirmed that GET /listings?type=auction&sort_by=lowest_price returns the cheap-auction frontier, including repeated price=3 listings with auction_details.min_next_bid=8
  59. sort_by=num_bids and sort_by=expires_soon are both live-meaningful on type=auction queries: num_bids surfaces high-activity auctions, while expires_soon surfaces near-expiry auctions
  60. 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, and GET /history/{market_hash_name}/graph?paint_index=...
  61. GET https://loadout-api.csfloat.com/v1/loadout also supports browser-observed discover params limit, months, def_index, and paint_index; live checks on 2026-03-08 showed months=1 changes the top ids under sort_by=favorites, and def_index=7&paint_index=490 narrows the result set to AK-47 | Wasteland Rebel themed loadouts
  62. any_filled on GET /v1/loadout is now better mapped than the initial weak note suggested: the current discover UI uses any_filled=true, direct live probes on 2026-03-08 confirmed that literal true is accepted, and invalid values like false, 0, 1, or bogus all hard-fail with 400 {"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 control
  63. POST https://loadout-api.csfloat.com/v1/recommend/stickers is live and safe: { "items":[{ "type":"skin", "def_index":7, "paint_index":490 }], "count":10, "collection_whitelist":["Holo"] } returned 200 with { "results":[{ "sticker_index", "score" }, ...] }
  64. POST https://loadout-api.csfloat.com/v1/generate is 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 } returned 200 with { "remaining_budget":30, "total_cost":2970, "results":[...] }
  65. POST /v1/generate hard-validates faction and budget: mismatched def_indexes can return ct-only / t-only errors, and too-small max_price can return Locked items cost (...) exceeds budget (...)
  66. GET /meta/app is live and currently returns a minimal bootstrap payload { min_required_version }; on 2026-03-08 the value was "9.0.0"
  67. GET /schema/browse is live and groups schema-adjacent browse items under { data:[{ type, user_visible_type, items:[...] }] }; type=stickers returned tournament-era sticker buckets such as DreamHack 2013 and Katowice 2014
  68. the browser bundle lowercases schema browse category labels directly before calling /schema/browse, so the currently known query values are rifles, pistols, smgs, heavy, knives, gloves, agents, containers, stickers, keychains, patches, collectibles, and music kits
  69. POST /listings/bulk-list is live on authenticated seller accounts and returns { data:[listing, ...] }; on 2026-03-08 a happy-path batch of two private Zeus x27 | Swamp DDPAT (Factory New) listings at $0.04 succeeded, while the same route at $9.99 hit 400 "Listing is suspected to be overpriced. You need to complete KYC and onboard with Stripe to list this item."
  70. PATCH /listings/bulk-modify is live and currently confirmed with price-only updates: { modifications:[{ contract_id, price }] } returned { data:[updated listings...] } on the same two-listing batch
  71. PATCH /listings/bulk-modify failure modes are already useful for validation: { contract_id:"0", price:3 } returned 500 "failed to fetch listing", while { contract_id:"0", price:1 } failed earlier at the server price floor with 422 "minimum allowed price is $0.03 USD"
  72. PATCH /listings/bulk-delist is live and reversible: { contract_ids:[...] } returned 200 {"message":"contracts delisted"} on the real two-listing batch, and { contract_ids:["0"] } returned 500 "failed to delist contracts"
  73. POST /me/gs-inspect-token is live and returns the same token-style contract as other companion flows: { token, expires_at }; the current browser bundle uses it to authorize external gs-api.csfloat.com/api/v1/players/equip* requests, while the SDK intentionally stops at the CSFloat-side token helper for now
  74. GET /schema/images/screenshot is live behind authentication: on 2026-03-08, def_index=7&paint_index=490&min_float=0.15&max_float=0.38 returned { id:"1305328935500910839", sides:{ playside:{ path:"m/.../playside.png" }, backside:{ path:"m/.../backside.png" } } }, while the same request without auth returned 401 code=27 authorization not set
  75. 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/screenshot helper because the external path depends on item-level screenshot signatures and is better kept as a documented companion detail for now
  76. browser-auth discovery on /profile/watchlist confirmed 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, and StatTrak™ -> category=2
  77. direct authenticated live probes on 2026-03-08 confirmed that GET /me/watchlist accepts at least state, sort_by, filter, category, type, and min_price; state=listed|sold|delisted all return 200, while state=bogus hard-fails with 400 code 18 and schema: error converting value for "state". Details: invalid state
  78. current live watchlist state semantics are meaningful on the main account: state=sold returned only sold rows, state=delisted returned only delisted rows, and sort_by=most_recent changed the first-page ordering versus the default watchlist sort
  79. current raw HTTP market-search behavior is mixed rather than uniformly public: on 2026-03-08, many explicit /listings?... queries with sort_by, category, min_price, type, and filter still returned 403 "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
  80. the current market/watchlist bundle serializes applied sticker filters into a JSON stickers query param and keychain filters into a JSON keychains query 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> }
  81. direct authenticated live probes on 2026-03-08 confirmed that GET /listings?stickers=[...] and GET /listings?keychains=[...] both return 200 and 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-level custom_sticker_id contract, and keychains=[{"i":1}] surfaced items whose payloads included matching keychain entries like { stickerId: 1, slot: 0, pattern, ... }
  82. sticker_option=skins|packages is live-meaningful when paired with JSON stickers=[...] filters: on 2026-03-08, GET /listings?stickers=[{"i":3}]&sticker_option=skins preserved the applied-skin result set, while GET /listings?stickers=[{"i":85}]&sticker_option=packages and GET /listings?stickers=[{"i":96}]&sticker_option=packages both returned EMS One 2014 Souvenir Package; the current SDK exposes sticker_option as a low-level typed param, but package-side density still depends on the specific sticker id
  83. direct authenticated live probes on 2026-03-08 confirmed that GET /me/watchlist reuses the same JSON attachment-filter contract as /listings: stickers=[{"i":3}] returned the matching watched Souvenir M4A1-S | VariCamo (Field-Tested) row with an applied { stickerId: 3, slot: 3 } payload, and a targeted keychains=[{"i":83}] probe returned the matching watched Souvenir AUG | Spalted Wood (Field-Tested) row with an applied { stickerId: 83, slot: 0, highlight_reel: 807, ... } payload; sticker_option=packages on watchlist is still only weakly mapped because the current account-side probe for stickers=[{"i":85}] returned 200 with an empty set rather than a watched package row
  84. min_ref_qty is live-meaningful on both /listings and /me/watchlist: the browser Exclude Rare Items toggle maps to min_ref_qty=20, higher floors such as 100 further narrow results, and invalid values like min_ref_qty=bogus hard-fail with 400 code 18 schema: error converting value for "min_ref_qty"
  85. current live limit ceilings on 2026-03-08 are 50 for both /listings and /me/watchlist: limit=50 returned 200, while limit=51 and above returned 400 {"code":4,"message":"limit is too high"}
  86. the watchlist page currently reuses meaningful market-style sort and listing-mode params beyond most_recent: on 2026-03-08, the default page ordering matched sort_by=best_deal, sort_by=highest_discount reordered the first rows versus default, sort_by=lowest_price surfaced the cheapest watched rows first, type=auction returned only auction rows, type=buy_now returned buy-now rows, and filter=unique also returned a distinct watchlist slice
  87. the /checker page uses an external companion route rather than /api/v1: on 2026-03-08, browser-auth network and direct live replay confirmed GET https://api.csfloat.com/?url=<inspectLink> with Origin: https://csfloat.com returning { 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 valid Origin header returned 400 {"error":"Invalid Origin"}
  88. browser-auth discovery on /sell did not reveal a new sell-only backend surface on 2026-03-08; the page currently bootstraps with the already-covered GET /me, GET /schema, GET /meta/location, GET /meta/exchange-rates, and GET /me/inventory routes
  89. browser-auth discovery on /stall/me likewise stayed on the already-covered surface on 2026-03-08: POST /me/recommender-token, GET /users/{steam_id}/stall?limit=40, and GET https://loadout-api.csfloat.com/v1/user/{steam_id}/loadouts; no additional self-stall-only backend route was promoted from this pass
  90. direct public live probes on 2026-03-08 confirmed that GET /users/{id}/stall accepts meaningful listing-style params beyond limit and cursor: sort_by=lowest_price|highest_discount|most_recent all changed the first-page ordering, filter=unique narrowed the public stall from 263 to 164 rows, type=buy_now returned the active rows while type=auction returned an empty set on the current stall, and min_ref_qty=20 narrowed the total count from 263 to 244
  91. the public stall route also accepts attachment-style listing filters: on 2026-03-08, keychains=[{"i":83}] returned the matching Souvenir Zeus x27 | Charged Up (Battle-Scarred) row, while filter=sticker_combos returned 200 with an empty set on the current stall; invalid filter=bogus hard-failed with 400 invalid filter value, while invalid sort_by=bogus returned 404 the given resource could not be found
  92. unlike /listings and /me/watchlist, the current public stall route is not capped at 50 rows per page: on 2026-03-08, limit=51 returned 200, and the live audit already uses GET /users/{id}/stall?limit=500&type=buy_now successfully for mutation-safe inventory reconciliation
  93. GET /me/notifications/timeline currently supports cursor pagination but not meaningful limit control: on 2026-03-08, replaying the response cursor returned an older page with a different first notification_id, while both limit=1 and limit=5 returned the same 42 rows as the default request; cursor=0 simply fell back to the current first page rather than failing validation
  94. GET /me/transactions is broader than the original SDK typing suggested: on 2026-03-08, browser-auth discovery and direct API probes confirmed meaningful order=asc|desc plus type=deposit|withdrawal|fine|bid_posted|trade_verified; invalid type=bogus hard-failed with 400 "you are not authorized to filter by this transaction type", and invalid order=bogus hard-failed with 400 "bogus is not a valid order"
  95. GET /me/offers is currently page-oriented rather than cursor-oriented on the live profile UI: on 2026-03-08, /profile used GET /me/offers?page=0&limit=10, direct API probes confirmed page=0 and page=1 are accepted, limit=1 narrows the result set, invalid page=bogus hard-fails with 400 "failed to deserialize query params", and cursor=abc appears ignored on the current backend
  96. GET /me/buy-orders is also page-oriented on the live profile UI: on 2026-03-08, /profile used GET /me/buy-orders?page=0&limit=10&order=desc, direct API probes confirmed order=asc|desc is accepted, invalid order=bogus hard-fails with 400 "\"bogus\" is not a valid order", and invalid page=bogus hard-fails with 400 schema: error converting value for "page"; the current accounts had zero active orders, so the actual asc/desc ordering difference remains only weakly mapped
  97. current authenticated market probes on 2026-03-08 showed that filter=sticker_combos and filter=unique are not cosmetic aliases: filter=sticker_combos returned sticker-heavy rows like Glock-18 | Twilight Galaxy (Factory New) and AK-47 | X-Ray (Factory New) with 4-5 applied attachments, while filter=unique returned a distinct knife/glove-heavy slice led by ★ StatTrak™ Bayonet | Blue Steel (Factory New) and ★ Sport Gloves | Superconductor (Factory New)
  98. 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 returned 200 on 2026-03-08, so this param combination is now tracked as a stable public page feed rather than just an incidental listing query
  99. the current cart/checkout UI does not map to a dedicated CSFloat /cart or /checkout API route: the live bundle on 2026-03-08 stores cart state under local storage key checkout_cart_contracts, refreshes entries through existing listing detail reads, and the item-card buyNow() path still delegates to the already-covered purchase flow (purchaseContracts(...) / POST /listings/buy); no separate cart backend endpoint was promoted from this pass
  100. 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 plus sort_by=most_recent, and Unique Items -> the same Newest route plus filter=unique; all three replayed directly without auth and returned 200
  101. the SDK now exposes these public homepage feed combinations as getHomepageFeedParams() / CSFLOAT_HOMEPAGE_FEED_PRESETS instead of forcing consumers to hand-copy the current query-string contract
  102. 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 as loadout.getDiscoverLoadouts(), getDiscoverLoadoutParams(), buildLoadoutListParams(), and CSFLOAT_DISCOVER_LOADOUT_PARAMS
  103. months on GET https://loadout-api.csfloat.com/v1/loadout currently validates only when it parses as a number: months=0 and months=-1 returned 400 "months must be at least 1" on 2026-03-08, while non-numeric months=bogus currently fell back to a normal 200 baseline response instead of hard-failing
  104. limit on GET https://loadout-api.csfloat.com/v1/loadout is now tightly mapped: limit=200 returned 200 while limit=201, limit=500, limit=1000, limit=0, and limit=-1 all returned 400 "limit must be between 1 and 200" on 2026-03-08; the SDK now exports CSFLOAT_LOADOUT_MAX_LIMIT = 200 and validates the same range in buildLoadoutListParams()
  105. skin-scoped public loadout search requires def_index and paint_index together: def_index=7 alone and paint_index=490 alone both returned 400 "Both def_index and paint_index must be provided together" on 2026-03-08, while paired requests like def_index=7&paint_index=490&months=1 returned 200 with AK-47 themed rows; the SDK now exposes buildLoadoutSkinSearchParams() and loadout.getSkinLoadouts(defIndex, paintIndex, params?) for this contract
  106. current extra query params on the public loadout list route remain mapped conservatively: on 2026-03-08, adding wear_index=2 or stattrak=true to a paired def_index=7&paint_index=490 search still returned 200, 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
  107. cursor pagination on GET /me/watchlist is now strong enough for a high-level iterator: with limit=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 exposes account.iterateWatchlist() rather than forcing callers to hand-roll cursor loops
  108. cursor pagination on GET /users/{id}/stall is likewise strong enough for a high-level iterator: with limit=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 exposes stall.iterateStall()
  109. 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=20 still returned 200 as the exact public /search page bootstrap, but adding sort_by=most_recent, filter=unique, type=buy_now|auction, or min_price=500 to that baseline all flipped back to 403 "You need to be logged in to search listings"; the SDK now exposes this exact public page contract as getPublicMarketPageParams() / CSFLOAT_PUBLIC_MARKET_PAGE_PARAMS
  110. POST https://loadout-api.csfloat.com/v1/recommend is more permissive than a strict contract would suggest: on 2026-03-08, count=0 returned 200 with empty results, negative count=-1 also returned 200 but effectively clamped to 0, non-numeric count="bogus" triggered a backend 500, empty items:[] still returned a non-empty default recommendation set, and no hard cap was observed at count=250; the SDK therefore keeps the raw route low-level but now exposes buildLoadoutRecommendationRequest() / buildSingleSkinRecommendationRequest() and loadout.recommendForSkin() to validate sane caller input before the backend’s inconsistent edge handling
  111. POST https://loadout-api.csfloat.com/v1/recommend/stickers currently hard-caps output at 100: on 2026-03-08, count=101, 150, and 500 all returned 200 with count=100, while negative count=-1 returned a strange 200 with count=198, and non-numeric count="bogus" triggered a backend 500; invalid-looking collection_whitelist:["bogus"] was simply ignored and still returned normal results. The SDK now exposes CSFLOAT_STICKER_RECOMMENDATION_MAX_COUNT = 100, buildStickerRecommendationRequest(), buildSingleSkinStickerRecommendationRequest(), and loadout.recommendStickersForSkin() so callers can stay inside the stable boundary
  112. POST https://loadout-api.csfloat.com/v1/generate validates only a subset of its input shape: on 2026-03-08, empty def_indexes:[] returned 400 "def_indexes required", invalid faction:"bogus" returned 400 "faction is required and must be either \"ct\" or \"t\"", and max_price=-1 / max_price="bogus" returned 400 "max_price must be a positive integer", but empty items:[] still returned a normal recommendation payload and negative ids inside def_indexes did not hard-fail; the SDK therefore exposes buildGenerateLoadoutRecommendationsRequest() plus CSFLOAT_LOADOUT_FACTIONS for the stable validated subset without pretending the whole backend contract is strict
  113. 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(), and buildMusicKitFilter() map directly to the live query fields collection, rarity, paint_seed, and music_kit_index that are already validated in the market audit surface
  114. 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-orders with { 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 } returned 200 with an order payload whose expression string serialized to (DefIndex == 7 and PaintIndex == 72 and StatTrak == false and Souvenir == false), and a follow-up DELETE /buy-orders/{id} removed it cleanly
  115. expression-backed similar-order lookup is likewise now live-confirmed: on 2026-03-08, POST /buy-orders/similar-orders?limit=3 with the same AK-47 Safari Mesh expression body returned 200 with non-empty rows like AK-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:

  1. buy_now
    • asset_id
    • price
    • type
    • private
    • description
    • max_offer_discount
  2. auction
    • asset_id
    • reserve_price
    • duration_days
    • type
    • private
    • description

Important:

  1. buy_now create flow is live-validated
  2. auction request shape is supported from known API surface, but should be treated as not-yet-live-validated by this repository until explicitly tested
  3. POST /listings should not be treated as universally available for every account state; a live retest on 2026-03-07 returned code 134 with a Stripe onboarding requirement for further listings
  4. POST /listings/bulk-list currently reuses the same per-item payload shapes under { items:[...] }; the repository has live-validated the buy_now batch path, but not yet a real auction batch happy-path

Current Account-State Notes

Live retesting on 2026-03-07 confirmed:

  1. PATCH /listings/{id} still works and was verified with +1 then revert on a real listing
  2. POST /listings still exists, but the current account hit 400 code 134 with message You need to fully onboard with Stripe for payouts to list further items
  3. 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:

  1. public routes include:
    • /schema
    • /listings?limit=40&min_ref_qty=20
    • /listings/{id}
    • /listings/{id}/buy-orders without extra query params
    • /users/{id}
    • /users/{id}/stall
    • /history/.../sales
    • /history/.../graph
    • /meta/exchange-rates
    • /meta/location
    • /listings/{auction_id}/bids
    • /listings/{id}/similar
  2. authenticated routes include:
    • /me*
    • general /listings search with normal query params
    • /listings/{id}/buy-orders when using auth-only query params such as limit
    • 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:live

The 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:extended

To opt into reversible live mutation checks:

ALLOW_LIVE_MUTATIONS=1 ENV_FILE=/path/to/.env npm run audit:live

Claims This Repository Does Not Make

This repository does not claim:

  1. support for unknown private endpoints
  2. complete coverage of any surface not documented, source-inspected, or live-validated
  3. 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.

Last updated on