DEVELOPERS

The Steam BANGERS API.

A free, read-only JSON API for the BANG Score: one 0-to-100 rating of whether a Steam game's price matches what you get. Rank games, look one up, or search by name. The exact scoring formula stays private; the score and its public inputs are yours to use.

Get a key

The games, single-game and search endpoints need a key. The verdict endpoint (/api/v1/verdict), the addon config, the badge and the API root at /api/v1 do not. Keys are tied to your Steam account, free to create, and shown only once, so save each somewhere safe. Sign in, then create and manage your keys here.

Loading your keys.

Price

The API is free for non-commercial use, with attribution. A key covers 1,000 requests a day. For anything else, write to me.

Authentication

Send your key as a bearer token (preferred), or as an api_key query parameter:

curl https://steambangers.com/api/v1/games \
  -H "Authorization: Bearer sb_live_your_key_here"

# or, for quick tests:
curl "https://steambangers.com/api/v1/games?api_key=sb_live_your_key_here"

Endpoints

GET /api/v1/games: games ranked by BANG Score. Query params (all optional): genre (comma-separated: rpg, shooter, action, strategy, narrative, roguelike, multiplayer, indie_short, other), tag (comma-separated Steam user tags as SteamSpy recorded them, AND'd together, e.g. Rogue-like,Roguelike Deckbuilder), year_from, year_to, on_sale=true, page (25 per page).

The next-discount list. discount_soon=14 or 90 keeps only the paid games whose own discount record puts a graded discount window opening within that many days: the same games as the Discount soon view on /browse. Games on sale today are left out, so on_sale is ignored with it. banger_next=true keeps only those that would be a BANGER at the graded price. sort orders the list: next_soonest (the window that opens first), next_cut (the biggest graded cut), next_price (the lowest graded price) or next_surest (the call that came true most often); without it the list is by BANG Score. Each game then carries next_discount, the same object as the single-game endpoint below, and graded.rate in it is the number to quote beside any next-discount claim. The response adds forecast: window_opens_within_days, banger_at_predicted_price, as_of (the daily run), state (ok, or paused until the next daily run after a Steam sale opens or closes, or disabled) and dropped. Any other value of the three is a 400 that names the valid ones. It is cached to the end of the hour, at most 60 seconds. MCP top_games takes the same discount_soon and banger_next.

GET /api/v1/games?discount_soon=14&banger_next=true&sort=next_surest
GET /api/v1/games?genre=rpg&on_sale=true&page=1

{
  "page": 1, "pages": 16, "total": 385, "per_page": 25,
  "games": [
    {
      "appid": 413150,
      "name": "Stardew Valley",
      "bang_score": 97,
      "tier": "banger",
      "genre": "rpg",
      "price_usd": 14.99,
      "discount_percent": 0,
      "review_score": 0.98,
      "steam_positive_pct": 98,
      "review_count": 1037488,
      "reviews_updated_at": "2026-09-10",
      "length_hours": 53.4,
      "finish_rate": null,
      "refunded_review_share": 0.0004,
      "refunded_review_sample": 799,
      "refunded_review_flagged": 15,
      "release_year": 2016,
      "url": "https://steambangers.com/game/413150/stardew-valley"
    }
  ]
}

review_score (0 to 1) is the score's review input, not Steam's figure: Steam's positive share pulled toward 0.70 as if every game had 50 more reviews at that rate, so a game with 12 reviews cannot score like one with 12,000. To quote a game as percent positive, use steam_positive_pct (0 to 100, Steam's own ratio of positive reviews over review_count). reviews_updated_at is the date we last fetched those reviews.

refunded_review_share is the share of the game's sampled Steam-purchase reviews whose writer refunded it, from Steam's own Product refunded label, weighted to the game's positive/negative review mix. refunded_review_flagged is the raw count over refunded_review_sample reviews. Only reviewers are counted, so a refund with no review behind it never appears, and the field is null until the game is sampled. The single-game endpoint adds refunded_review_percentile (0 to 1, where that share ranks among paid games sampled to the same depth, catalog-wide), refunded_review_genre_percentile (the same rank inside the game's genre, over refunded_review_genre_pool games), refunded_review_over_2h, the sample's date window, and the first-30-days split, plus Steam's own summary of the reviews written inside the two-hour refund window: early_window_reviews, early_window_positive_share (0 to 1) and early_window_review_share (0 to 1, of the game's Steam-purchase reviews). Those count reviewers who had under two hours on the clock at review time, never buyers or refunds, and all three are null until fetched. On list rows every single-game field reads null because the list does not fetch it, not because the game is unsampled.

price_rise is null unless the game's regular price went up shortly before its current discount. Then it carries regular_before_usd (the old regular price, as the store sold it at full price), raised_by (the day we first saw the higher price; the rise happened on or before it) and real_saving_percent, the percent off measured against the old price (0 when the sale price is at or above it).

GET /api/v1/games/{appid}: one game, with extra fields: developer, publisher, metacritic, completionist_hours, and the price it becomes a BANGER / worth-it at.

GET /api/v1/games/413150

{ "appid": 413150, "name": "Stardew Valley", "bang_score": 97,
  "becomes_banger_at_usd": 14.99, "worth_it_at_usd": 14.99,
  "buy_or_wait": {
    "record_through": "2026-11-08",
    "seasonal_record": { "sales": 8, "discounted": 8, "base_price_usd": 14.99, "cut_sales": 8,
      "typical_discount_pct": 50, "typical_price_usd": 7.49, "deepest_discount_pct": 50,
      "deepest_price_usd": 7.49, "deepest_sale": "Summer Sale 2026", "by_sale": [ ... ] },
    "last_discount": { "start": "2026-10-01", "end": "2026-10-08", "days_since": 31 },
    "last_price_increase": null,
    "next_sale": { "name": "Winter Sale 2026", "start": "2026-12-17", "end": "2027-01-04",
      "starts_at": "2026-12-17T18:00:00.000Z", "ends_at": "2027-01-04T18:00:00.000Z",
      "live": false, "days_until": 39, "eligible": true, "eligible_from": "2026-12-17", ... },
    "sources": { ... } }, ... }

buy_or_wait is the game page's "Buy now or wait" block as data. seasonal_record counts the game's last seasonal sales on its IsThereAnyDeal and Steam price record and how many it was discounted in. The typical and deepest cut, with the US price at each, count only the sales cut from today's base price (base_price_usd, over cut_sales sales), so a cut from an older, higher price never reads as a price this game has today; they are null under two such sales, and the whole record is null until enough sales are on record. next_sale opens and closes at Valve's 10am Pacific (starts_at, ends_at), and live is true only between the two. next_sale is Valve's next seasonal sale, from Valve's own calendar, and eligible applies Valve's published discount rules (no discount for 30 days after a release or a price increase in any currency; seasonal sales waive only the gap between discounts) to the game's release date and US price record, so a price rise in another currency can still keep it out. It is history and rules, never a forecast: a developer chooses whether to join a sale. The whole object is null when the price record is not current or the store price is older than 36 hours: a price nobody has checked says nothing about today.

next_discount is the game page's next-discount row as data: a forecast of when the game's next discount most likely starts, either the next Valve sale (kind sale, with its name) or a 14-day window (window), with opens_at, the cut and US price where its grading allows one, p7, p30 and p90 (the chance a discount starts within 7, 30 and 90 days) and as_of, when the daily run made it. graded says how often calls like it came true: rate is the one the page prints, basis says what it counts (lenient: discounted at any time during the sale; strict: a discount opened inside the window), both rates ride along, with the calls behind them and whether they come from the backtest or from live grading. It is null with next_discount_reason when no window passes the graded-rate gate, its window has opened, the forecast predates a sale opening or closing, the game is on sale or in one of Valve's cooldowns, or the game is free. It is not buy_or_wait.next_sale, which is Valve's next seasonal sale.

players_online is Steam's own count of the players in the game when we last checked: count, checked_at (the UTC instant Steam was asked) and source. It is one moment's count, never a daily peak or an average. We read each game at the same hour of the week, every week for games with 1,000+ reviews and for multiplayer and roguelike games, every four weeks for the rest, so quote it with its time. It is null until the game has been read, and on list rows.

GET /api/v1/search?q={query}: search games by name.

The API root GET /api/v1 returns this list as JSON (no key needed) so your client can introspect it.

Rate limits

Terms of use