ProtonDB
Check game compatibility on Steam Deck / Linux via ProtonDB APIs.
Rating Tiers (best → worst)
platinum > gold > silver > bronze > borked
- Platinum: Runs perfectly out of the box
- Gold: Runs after tweaks
- Silver: Runs with minor issues
- Bronze: Runs but with significant issues
- Borked: Doesn't run or unplayable
API 1: Official Summary
GET https://www.protondb.com/api/v1/reports/summaries/{appid}.json
Status codes:
200 application/json— game has ≥1 report404 text/html— game has 0 reports (returns Netlify 404 page, NOT empty JSON). Always check status before parsing.
Response fields:
tier— official rating (platinum/gold/silver/bronze/borked), or"pending"whenconfidence: "inadequate"(typically <5 reports)provisionalTier— tier estimate used whiletierispending. Fall back to this whentier === "pending".bestReportedTier— highest tier anyone reported (optimistic)trendingTier— recent reports' tier (may differ fromtier→ indicates regression/fix). Also can be"pending".confidence—inadequate/low/moderate/high/strongscore(0-1),total(report count)
Verdict logic:
effective_tier = tier if tier != "pending" else provisionalTier
A 404 here ≠ the game page is missing. The ProtonDB web page /app/{appid} always exists for any valid Steam app and shows metadata + Deck Verified status even with 0 reports. Don't conflate the two — verify by browsing the page when in doubt.
⚠️ Pitfall: summary 404 ≠ page is empty
The summary API returns 404 (Netlify "Page not found" HTML) when a game has zero user reports. This does NOT mean the ProtonDB page itself is empty or useless. https://www.protondb.com/app/{appid} still renders and carries data even with 0 reports:
- Deck Verified Status (Valve's official judgement: Verified / Playable / Unsupported / Unknown) — independent of community reports, often the most important signal
- Chromebook Ready Status
- Native platform support icons
- External links: Steam, SteamDB, Steamcharts, PCGamingWiki, GitHub Proton issue search
When summary API 404s, DO NOT tell the user "the page is 404 / has no info". Instead:
- Open the
/app/{appid}page (browser tool) to read Deck Verified status from the rendered HTML - Report: "0 community reports, but Valve's Deck Verified status is
<status>" - Surface the PCGamingWiki and Proton GitHub issue search links — those are often where niche-game tweaks actually live
Always give the user the https://www.protondb.com/app/{appid} URL even when reports are empty.
⚠️ Pitfall: check every appid in a game family independently
Games often have multiple Steam appids — base game, DLC, Deluxe/GOTY edition, demo. Each appid has independent ProtonDB summary results and independent Valve Deck Verified status. Never generalize one appid's result to its siblings. Example seen in the wild: Monobeno (758090) → 0 reports, Deck Unsupported; Monobeno -HAPPY END- Deluxe (831660) → 2 reports, Deck Verified, provisionalTier platinum. The Deluxe edition is the playable one; the base appid would have steered the user wrong. Always enumerate the family via Steam store search and check each appid before answering.
API 2: Community Reports (Detailed)
ProtonDB serves report data as pre-built static JSON with hashed URLs. To access them:
Step 1: Get the token from counts.json
curl -s "https://www.protondb.com/data/counts.json"
# Returns: { "reports": 411787, "timestamp": 1774011569, ... }
Step 2: Compute the hash
The URL hash is computed from appId, reports count, timestamp, and page number:
# Usage: compute_hash <appId> <reports> <timestamp> <page>
node -e "
const R=(e,t,n)=>''+t+'p'+(e*(t%n));
const I=e=>Math.abs((''+e+'m').split('').reduce((a,c)=>(a<<5)-a+c.charCodeAt(0)|0,0));
const D=(e,t,n,r,o)=>I('p'.concat(R(e,t,n),'*vRT').concat(R(r,e,n)).concat(o));
console.log(D(${APPID},'${REPORTS}','${TIMESTAMP}',${PAGE}));
"
Step 3: Fetch reports
GET https://www.protondb.com/data/reports/{device}/app/{hash}.json
Device options: all-devices, steam-deck, pc, chrome-os
Returns paginated results (40 per page):
{
"page": 1,
"perPage": 40,
"reports": [...],
"total": 1994
}
Each report contains:
responses.verdict: "yes" (works) / "no" (issues)responses.notes.verdict: Free-text user review (the actual feedback)responses.protonVersion: e.g. "10.0-3"responses.graphicalFaults,audioFaults,inputFaults, etc.: "yes"/"no"device.hardwareType: "pc" or "steamDeck"device.inferred.steam.os: OS namedevice.inferred.steam.gpu: GPU namedevice.inferred.steam.cpu: CPU namecontributor.steam.nickname: Steam usernametimestamp: Unix timestamp
Complete Example (Shell)
#!/bin/bash
APPID=1245620 # Elden Ring
# Get token
COUNTS=$(curl -s "https://www.protondb.com/data/counts.json")
REPORTS=$(echo "$COUNTS" | python3 -c "import json,sys; print(json.load(sys.stdin)['reports'])")
TIMESTAMP=$(echo "$COUNTS" | python3 -c "import json,sys; print(json.load(sys.stdin)['timestamp'])")
# Compute hash for page 1
HASH=$(node -e "
const R=(e,t,n)=>''+t+'p'+(e*(t%n));
const I=e=>Math.abs((''+e+'m').split('').reduce((a,c)=>(a<<5)-a+c.charCodeAt(0)|0,0));
const D=(e,t,n,r,o)=>I('p'.concat(R(e,t,n),'*vRT').concat(R(r,e,n)).concat(o));
console.log(D($APPID,'$REPORTS','$TIMESTAMP',1));
")
# Fetch
curl -s "https://www.protondb.com/data/reports/all-devices/app/$HASH.json"
Pagination
To get page N, change the page parameter in the hash computation:
HASH_PAGE2=$(node -e "...D($APPID,'$REPORTS','$TIMESTAMP',2)...")
curl -s "https://www.protondb.com/data/reports/all-devices/app/$HASH_PAGE2.json"
Total pages = ceil(total / perPage).
Usage Guidelines
- Always fetch the summary first for a quick overview.
- Fetch detailed reports when user needs specifics (what issues exist, workarounds, Steam Deck specific notes).
- For Steam Deck queries, use
device=steam-deckto filter SD-specific reports. - Report both the overall tier AND the trending tier — they tell different stories.
- When summarizing reports, highlight: common issues, working Proton versions, and any required tweaks.
- The
counts.jsontoken changes periodically (when new reports are processed). Cache it for a few minutes at most.
Finding App IDs
Use the Steam store search:
curl -s "https://store.steampowered.com/api/storesearch/?term=game+name&l=english&cc=us" | python3 -c "
import json, sys
data = json.load(sys.stdin)
for item in data.get('items', []):
print(f\"{item['id']} — {item['name']}\")
"
Fallback: Bulk Data Dump
If the hash-based API changes or breaks, ProtonDB publishes monthly data dumps under ODbL license:
https://github.com/bdefore/protondb-data — reports/ directory contains monthly .tar.gz files (latest ~59MB).
Download, extract, and grep by appId. Not suitable for per-game queries but useful for bulk analysis.
ProtonDB Page Link
Link to the game's ProtonDB page: https://www.protondb.com/app/{appid}