# Gaming Stats API: Playnite Version: 3.8.3 ## Overview Base path: /playnite Read the latest complete library, local artwork, per-game metadata, tracked playtime, and short-lived now-playing state uploaded by the Gaming Stats Sync extension on a Windows PC. The cached library remains readable while the PC is off. Examples are illustrative. ## Authentication Every read and artwork endpoint requires X-API-Key from a trusted backend. Documentation is public. The Windows extension uses a separate X-Playnite-Key only on private /playnite/sync routes. Never put either credential in browser JavaScript. Read responses use Cache-Control: private, no-store. ## Requests GET /playnite/library - complete selected Playnite library and totals GET /playnite/presence - online, playing, or expired-to-offline presence GET /playnite/games/:playniteId - one game selected by its Playnite GUID GET /playnite/assets/:assetId - cached JPEG, PNG, WebP, or AVIF artwork selected by SHA-256 ID No read request accepts a body or triggers the Windows PC. ## Request example PLAYNITE_API_KEY is an existing API_KEYS read key, not the extension upload key. ```sh curl --fail-with-body "$API_BASE_URL/playnite/library" \ -H "X-API-Key: $PLAYNITE_API_KEY" ``` ## Library response The extension publishes a complete selected snapshot. playtimeSeconds is Playnite's tracked/imported total and playtimeMinutes is derived using whole elapsed minutes. Artwork paths require the same read key. The record also retains source and library IDs, play count, timestamps, install/running/favorite/custom flags, install size, release date, completion status, descriptive metadata, scores, and safe HTTPS links. Hidden games are excluded by default. A direct Epic record and its Playnite record remain separate and their playtime must not be added together. ```json { "schemaVersion": 1, "provider": "playnite", "accountRef": "owner", "status": "ready", "stale": false, "lastAttemptAt": "2026-09-20T12:00:00.000Z", "lastSuccessAt": "2026-09-20T12:00:00.000Z", "nextRefreshAt": null, "deviceId": "be30ba39-154f-4c93-9830-781979722243", "deviceName": "Gaming PC", "sequence": 1726833600000, "generatedAt": "2026-09-20T12:00:00.000Z", "playniteVersion": "10.35", "extensionVersion": "1.0.0", "coverage": { "kind": "complete-playnite-library", "gameCount": 1, "artworkUploaded": true }, "totals": { "gameCount": 1, "installedGameCount": 1, "playedGameCount": 1, "totalPlaytimeSeconds": 5460 }, "games": [ { "playniteId": "a85aec7d-447d-4c0c-9506-f8b27c2ad310", "providerGameId": "example-provider-id", "libraryPluginId": null, "source": { "id": null, "name": "Epic" }, "name": "Example Game", "sortingName": null, "playtimeSeconds": 5460, "playtimeMinutes": 91, "playCount": 3, "lastActivityAt": "2026-09-20T12:00:00.000Z", "addedAt": null, "modifiedAt": null, "isInstalled": true, "isRunning": true, "hidden": false, "favorite": true, "isCustomGame": false, "installSizeBytes": null, "releaseDate": { "year": 2026, "month": 1, "day": 1 }, "completionStatus": "Playing", "platforms": [ "PC (Windows)" ], "genres": [ "Action" ], "categories": [], "tags": [], "features": [], "ageRatings": [], "regions": [], "series": [], "developers": [ "Example Studio" ], "publishers": [ "Example Publisher" ], "scores": { "user": null, "critic": null, "community": null }, "links": [], "artwork": { "icon": { "assetId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "path": "/playnite/assets/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "contentType": "image/png", "width": 256, "height": 256 }, "cover": null, "background": null } } ] } ``` ## Presence response Presence reports online or playing while extension updates arrive. A playing response includes the stable Playnite ID, provider game ID, source, name, and session start. When no update arrives within PLAYNITE_PRESENCE_TTL_SECONDS, the API returns stale/offline and clears currentGame so a crashed or sleeping PC cannot remain playing forever. ```json { "schemaVersion": 1, "provider": "playnite", "accountRef": "owner", "status": "ready", "stale": false, "lastAttemptAt": "2026-09-20T12:00:00.000Z", "lastSuccessAt": "2026-09-20T12:00:00.000Z", "nextRefreshAt": null, "deviceId": "be30ba39-154f-4c93-9830-781979722243", "deviceName": "Gaming PC", "sequence": 1726833600001, "generatedAt": "2026-09-20T12:00:00.000Z", "playniteVersion": "10.35", "extensionVersion": "1.0.0", "state": "playing", "currentGame": { "playniteId": "a85aec7d-447d-4c0c-9506-f8b27c2ad310", "providerGameId": "example-provider-id", "source": { "id": null, "name": "Epic" }, "name": "Example Game", "startedAt": "2026-09-20T11:55:00.000Z" } } ``` ## Game response A malformed Playnite GUID returns HTTP 400. A valid GUID absent from the current cached library returns HTTP 404. A known game repeats snapshot freshness and device metadata and returns the same normalized game object. ```json { "schemaVersion": 1, "provider": "playnite", "accountRef": "owner", "status": "ready", "stale": false, "lastAttemptAt": "2026-09-20T12:00:00.000Z", "lastSuccessAt": "2026-09-20T12:00:00.000Z", "nextRefreshAt": null, "deviceId": "be30ba39-154f-4c93-9830-781979722243", "deviceName": "Gaming PC", "sequence": 1726833600000, "generatedAt": "2026-09-20T12:00:00.000Z", "playniteVersion": "10.35", "extensionVersion": "1.0.0", "game": { "playniteId": "a85aec7d-447d-4c0c-9506-f8b27c2ad310", "providerGameId": "example-provider-id", "libraryPluginId": null, "source": { "id": null, "name": "Epic" }, "name": "Example Game", "sortingName": null, "playtimeSeconds": 5460, "playtimeMinutes": 91, "playCount": 3, "lastActivityAt": "2026-09-20T12:00:00.000Z", "addedAt": null, "modifiedAt": null, "isInstalled": true, "isRunning": true, "hidden": false, "favorite": true, "isCustomGame": false, "installSizeBytes": null, "releaseDate": { "year": 2026, "month": 1, "day": 1 }, "completionStatus": "Playing", "platforms": [ "PC (Windows)" ], "genres": [ "Action" ], "categories": [], "tags": [], "features": [], "ageRatings": [], "regions": [], "series": [], "developers": [ "Example Studio" ], "publishers": [ "Example Publisher" ], "scores": { "user": null, "critic": null, "community": null }, "links": [], "artwork": { "icon": { "assetId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "path": "/playnite/assets/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "contentType": "image/png", "width": 256, "height": 256 }, "cover": null, "background": null } } } ``` ## Freshness and errors HTTP 200 serves ready or stale library/presence and known game records. HTTP 400 means a malformed identifier. HTTP 401 means a missing or invalid read key. HTTP 404 means the game or artwork is absent. HTTP 503 means disabled, pending, corrupt, or unavailable storage. Library status becomes stale after PLAYNITE_LIBRARY_STALE_HOURS. Presence becomes offline after its TTL. Never render pending or unavailable as an empty library or zero playtime. ## Extension behavior The extension sends a complete library on Playnite start, when games are added, removed, or edited, after library updates, after install/uninstall events, and after a game stops. Related database changes are debounced into one upload. It sends presence on application and game state changes plus a 60-second heartbeat while playing. Manual Sync now sends both presence and library data. It uploads only content-addressed local artwork that the server does not already have. Launch games through Playnite for reliable session tracking. ## Owner setup Create an independent random upload key, set ENABLE_PLAYNITE=true and PLAYNITE_UPLOAD_KEYS on the server, and keep GAMING_DATA_DIR on persistent storage. Install GamingStatsSync-1.0.1.pext on Windows. In Add-ons > Extension settings > Generic > Gaming Stats Sync, enter the HTTPS deployment origin and upload key, save, then choose Sync now. The extension protects the key for the current Windows user and excludes paths, launch commands, notes, scripts, cookies, and store credentials. ```sh node -e "process.stdout.write(require('node:crypto').randomBytes(32).toString('hex'))" # Set the result privately as PLAYNITE_UPLOAD_KEYS, then: ENABLE_PLAYNITE=true npm start ```