# SteamGPT API - full guide for AI agents > Base URL: https://steamgpt.net | no API key, no registration | no cookies, no tracking | soft limit 120 req/min per IP > SteamGPT is an independent service - not affiliated with Valve Corporation or Steam. ## Endpoints | Endpoint | Returns | When to use | | --- | --- | --- | | `GET /summary/{id}[.md\|.json\|.ai]` | Steam summary + Steam bans + FACEIT + first 100 entries of the public friend graph | need everything in one request (~0.5-1.5k tokens; preset=competitive ~350) | | `GET /profile/{id}[.md\|.json\|.ai]` | Steam summary only | cheapest (~200 tokens; .ai ~80) | | `GET /friends/{id}[.md\|.json\|.ai]` | public Steam friend graph, platform snapshot (?limit=N max 5000 default 100; ?detail=short\|medium\|full) | only the friend graph (short: ~5 tokens/friend) | | `GET /faceit/{id}[.md\|.json\|.ai]` | FACEIT player object + bans | only FACEIT (~100-200 tokens) | | `GET /batch/{id1,id2,...}[.md\|.json\|.ai]` | up to 100 Steam profiles at once (any SteamID format except vanity names) | bulk lookups, 1 request instead of 100 | | `GET /converter/{id}[.md\|.json\|.ai]` | SteamID converter: steamid64 (dec + hex), SteamID, legacy STEAM_0, SteamID3, 32-bit account id, steam:hex, profile URL - all at once | convert ids instantly (~120 tokens) | | `GET /identity/{id}[.md\|.json\|.ai]` | identifier resolver THROUGH Steam: adds vanity resolution to the conversions above | resolve vanity names (~100 tokens) | | `GET /bans/{id}[.md\|.json\|.ai]` | Steam bans: VACBanned, NumberOfVACBans, NumberOfGameBans, DaysSinceLastBan, CommunityBanned, EconomyBan | cheating reports, trust checks (~100 tokens) | | `GET /compare/{id1}/{id2}[.md\|.json\|.ai]` | two players side by side: identity, account, bans, FACEIT, shared entries of the public friend graph | head-to-head checks | `{id}` accepts ANY SteamID form: steamid64 (17 digits), STEAM_1:0:x / STEAM_1:1:x, [U:1:x], a steamcommunity.com profile link (/profiles/ or /id/) or a vanity name. Resolution goes through Steam. `/batch` accepts comma-separated ids in ANY computable SteamID format (steamid64, STEAM_1:0:x, STEAM_0:0:x, [U:1:x], hex, steam:hex, a 32-bit account id) - only vanity names are excluded, since each one would need its own Steam lookup. It returns `{"count", "players": [{"steamid64", "ids", "steam"}], "not_found": [...], "invalid": [...]}`. Over 100 ids - HTTP 400. Also available as POST /batch with JSON body {"ids": ["765611...", ...]} - same limits, better for applications; GET stays for agents. `/summary` has agent presets: `?preset=identity` (Steam block only), `?preset=competitive` (FACEIT + bans, no friends), `?preset=full` (= no preset). Explicit `?include=` wins over preset. `/summary` response size is controlled by `?include=` (comma list of faceit, friends, bans - omit to get all three), `?friends_limit=N` (alias `?limit=`, default 100, max 5000) and `?friends_detail=short|medium|full`. Example: `/summary/{id}.json?include=faceit,bans` returns no friends at all; `?include=steam` is the leanest summary. AI agents: default to `?friends_detail=short` unless friend metadata is required. `/batch` accepts `?include=faceit,bans` too: FACEIT for the whole batch is one query, Steam bans for the whole batch are one upstream call - the cheapest way to review a full match roster. `/identity` performs resolution only (no profile fetch): `{"steamid64", "steamid", "steamid3", "steam_hex", "vanity", "profile_url", "resolved": true}`. `vanity` is the custom URL name when known to the cache, `null` otherwise. ## Quick start from code ```python import requests data = requests.get("https://steamgpt.net/summary/76561197960287930.json?preset=competitive").json()["data"] ``` ```js const { data } = await ( await fetch("https://steamgpt.net/summary/76561197960287930.json?preset=competitive") ).json() ``` More snippets (LangChain, MCP for Claude/Cursor/VS Code): https://steamgpt.net/ai.md ## Authentication (optional) No credential is required - every call above works anonymously. A free self-service token (no registration, no client_id) raises the per-IP limit from 120 to 600 requests/min: ```bash TOKEN=$(curl -s -X POST https://steamgpt.net/oauth/token -d "grant_type=client_credentials" | jq -r .access_token) curl -H "Authorization: Bearer $TOKEN" https://steamgpt.net/summary/76561197960287930.json ``` The token is a 1-hour JWT with scope `rate-boost`; mint a new one when it expires. An invalid token returns HTTP 401 `invalid_token` - drop the header and the API keeps working. Full discovery document: https://steamgpt.net/auth.md ## Format selection 1. Suffix wins: `.md`, `.json` or `.ai` 2. Then `?format=md|json|ai` 3. Then `Accept: text/markdown` / `application/json` / `text/html` 4. No browser Accept header - markdown by default (token-cheapest) `.ai` returns a deterministic plain-text rendering (text/plain) built by a serializer, not an LLM - fast, stable and the cheapest to read for a model. Available on every data endpoint: /summary /profile /bans /faceit /friends /identity /batch /compare. ## Example: request ``` curl https://steamgpt.net/profile/76561197960287930.md ``` ## Example: response shape (JSON) ```json { "result": "success", "data": { "steamid64": "76561197960287930", "ids": {"steamid64": "...", "steamid": "STEAM_1:0:...", "steamid3": "[U:1:...]"}, "steam": {"steamid": "...", "personaname": "...", "profileurl": "...", "avatarfull": "...", "timecreated": 0, "personastate": 0} } } ``` TypeScript types for every response: https://steamgpt.net/types.d.ts (optional = field absent, | null = present but null). Steam data freshness: `/profile` and `/summary` serve a cache no older than 48 hours - stale or unknown profiles are refreshed live from Steam on request. `/batch` and `/friends` are cache-only (fastest, no live refresh). JSON responses of /profile and /summary include machine trust blocks: `provenance` ({source, retrieved_at unixtime|null, age_seconds|null, fresh boolean, cache_ttl seconds|null} per data source; fresh = age within TTL) and `canonical` ({steamid64, url}) - /profile/{vanity} and /profile/{steamid64} are the same resource. /faceit and /bans carry `provenance` too (no canonical block). Versioning: current major is v1. /v1/{endpoint} is an alias of every unversioned path; breaking changes will only land under /v2. Conditional GET: every response carries a strong ETag and Cache-Control: public, max-age=60. Send If-None-Match to get 304 Not Modified. All timestamps are unixtime. Markdown responses show dates as `YYYY-MM-DD HH:MM:SS UTC | unixtime: N`. Friend detail levels (`?detail=`): `short` - plain steamid64 array (cheapest), `medium` - `{"steamid64", "personaname"}`, `full` (default) - `{"steamid64", "steam": {raw Steam summary object}}`. FACEIT data is the FACEIT API player object: `/faceit` returns `{"steamid64", "faceit": {player object}, "bans": [...]}`; `/summary` mirrors it as `data.faceit` + `data.faceit_bans`. ## Use with AI tools - ChatGPT: Custom GPT -> Actions -> import https://steamgpt.net/openapi.json (no auth) - Claude: paste https://steamgpt.net/llms.txt into the chat, or let the agent GET any endpoint directly - Cursor / IDE agents: add https://steamgpt.net/llms-full.txt to project docs; typed clients via /types.d.ts - OpenAI Agents / tool use: generate a typed tool from /openapi.json; responses match /types.d.ts - MCP: Streamable HTTP endpoint at https://steamgpt.net/mcp - `claude mcp add --transport http steamgpt https://steamgpt.net/mcp`. 9 read-only tools: steam_convert, steam_identity, steam_bans, steam_profile, steam_faceit, steam_friends, steam_summary, steam_batch, steam_compare (listed cheapest first). No auth. Manifest: https://steamgpt.net/.well-known/mcp-server.json. Official MCP Registry entry: net.steamgpt/steamgpt (verified on Glama: glama.ai/mcp/servers/SteamGPTnet/steamgpt-mcp). Stdio-only clients: npx -y steamgpt-mcp. JS SDK: npm install steamgpt - Context7 (docs in coding-agent context): libraries `/steamgptnet/steamgpt-js` and `/steamgptnet/steamgpt-mcp` ## The .ai format `.ai` is a DETERMINISTIC flat-text serializer (not an LLM summary): short labeled lines, no markdown tables, no JSON braces - the cheapest representation to read for any model. Same data every time for the same input. Real example, `GET /profile/76561197960287930.ai`: ``` Steam user Rabscuttle. Identity: SteamID64: 76561197960287930 SteamID: STEAM_1:0:11101 SteamID3: [U:1:22202] Account: Profile is private. Currently offline. ``` Use `.ai` when the model just needs facts, `.md` when a human may read the output too, `.json` when code parses it. ## /friends detail levels - `detail=short` - bare array of steamid64 strings: `["76561197960287930", ...]` (~5 tokens/friend) - `detail=medium` - `[{"steamid64", "personaname"}]` - id + current nickname - `detail=full` (default) - `[{"steamid64", "steam": {...}}]` - raw Steam player object per friend (avatar, profile url, visibility, country...), the same shape as `data.steam` of /profile ## Errors Errors follow the same format selection as success responses: `.md` suffix - markdown error, `.json` - JSON, browser Accept - HTML page; no format hints - markdown, same default as success. JSON error shape: `{"result": "error", "code": 404, "error": "player_not_found", "message": "..."}`. Switch on the `error` field: Real error bodies: `GET /profile/zzz-no-such-user.json` -> HTTP 404 ```json {"result": "error", "code": 404, "error": "player_not_found", "message": "Player not found. Use steamid64 (17 digits), STEAM_1:0:x, [U:1:x], a steamcommunity.com link or a vanity name."} ``` `GET /profile/zzz-no-such-user.md` -> the SAME error in the format you asked for: ``` # Error 404: player_not_found > Player not found. Use steamid64 (17 digits), STEAM_1:0:x, [U:1:x], a steamcommunity.com link or a vanity name. Docs: https://steamgpt.net/docs.md | https://steamgpt.net/llms.txt ``` 121st request in a minute -> HTTP 429 with a `Retry-After: 60` header: ```json {"result": "error", "code": 429, "error": "rate_limited", "message": "Soft fair-use limit: 120 requests/min per IP. Responses are cached - please slow down."} ``` 101 ids in /batch -> HTTP 400 `{"result": "error", "code": 400, "error": "batch_too_large", "message": "Max 100 steamid64 per batch request."}` - `player_not_found` (404) - id did not resolve or profile unknown to Steam - `no_faceit_data` (404) - no FACEIT profile for this player - `batch_too_large` (400) / `batch_no_valid_ids` (404) - /batch input problems - `no_bans_data` (404) - Steam did not return ban data (upstream unavailable), retry later - `bad_request` (400; 413 when the body is over 16kb) - malformed body, unknown include value or oversized body - `unknown_endpoint` (404) - wrong path, see /llms.txt - `invalid_token` (401) - the OPTIONAL Bearer boost-token is invalid or expired: mint a fresh one (POST /oauth/token) or drop the Authorization header, the API works without it - `rate_limited` (429) - honor Retry-After (soft limit 120 req/min per IP; an optional free self-service token raises it to 600 - see /auth.md) - `internal` (500) - server error, retry later Private profiles return 200 with the public subset of fields, not 404. ## Partial responses `/summary` merges several sources, so one of them can be down while the rest are fine. The response always carries a `sources` map and a `partial` flag: ```json {"result": "success", "partial": true, "data": {"sources": {"steam": "ok", "faceit": "unavailable", "friends": "ok", "bans": "excluded"}}} ``` - `ok` - source answered, data is present - `empty` - source answered, this player simply has no such data (a real fact) - `unavailable` - source did not answer: the block in `data` is NOT evidence of absence, retry the narrow endpoint - `excluded` - you did not request this block via include Markdown and .ai renders say the same in words instead of silently showing an empty section. `result` stays `success` so existing clients keep working - branch on `partial` / `sources` when correctness matters. ## Freshness (trust the data) Every JSON provenance block answers "can I trust this NOW": `{"source", "retrieved_at", "age_seconds", "fresh", "cache_ttl"}`. `fresh: true` means the snapshot age is within its TTL; `fresh: false` with a large `age_seconds` means treat online status, current game and bans as possibly stale. ## Versioning policy Unversioned paths are a STABLE alias of `/v1` - pin `/v1/...` in long-lived tools if you want to be explicit. Breaking changes (renamed fields, changed shapes) only ever land in a future `/v2`; `/v1` semantics are frozen. ## Data caveats (read before drawing conclusions) - The friend graph is the platform snapshot of the public Steam graph, not a live Steam query: it can be partial, so an empty intersection in /compare is NOT proof that two players are unrelated. - `/batch` and `/friends` are cache-only: a real account the platform has never seen lands in `not_found` - re-fetch those ids with /profile, which does go to Steam. - `DaysSinceLastBan` is 0 both for "banned today" and for "never banned" - read it together with `NumberOfVACBans` / `NumberOfGameBans`, exactly as Steam returns it. - `provenance.retrieved_at` is the age of the cached snapshot; ban data lives with the profile snapshot (`cache_ttl` 432000 = 5 days), a stale snapshot triggers a background refresh - check `provenance.fresh`.