GameUpdate API
$19/ month
- 60 requests / minute, 10,000 / day
- Up to 5 keys, revocable anytime
- Versioned JSON, CORS enabled
Developers
The same normalized patch notes, releases and maintenance notices that power our alerts, as JSON for your bot, launcher, dashboard or community site. Or skip the polling and have every alert pushed to Discord or your own webhook.
Both are monthly add-ons to a GameUpdate membership (trials included). Cancel anytime from your dashboard.
$19/ month
$2.99/ month
Create a key in Dashboard → Integrations, then:
curl https://gameupdate.org/api/v1/updates?app=730,570 \
-H "Authorization: Bearer gu_api_…"{
"api_version": "2026-10-01",
"object": "list",
"data": [
{
"object": "game_update",
"id": "730-5131204920834",
"app_id": 730,
"game": "Counter-Strike 2",
"title": "Release Notes for 10/20/2026",
"kind": "patch",
"summary": "Fixed a case where players could see through smokes…",
"highlights": ["Smoke grenades now…", "Updated Inferno…"],
"no_downtime": true,
"url": "https://store.steampowered.com/news/app/730/view/…",
"image": "https://cdn.cloudflare.steamstatic.com/steam/apps/730/header.jpg",
"published_at": "2026-10-20T21:14:00.000Z"
}
],
"has_more": false,
"next_before": null,
"unsupported_apps": []
}Node.js
const res = await fetch(
"https://gameupdate.org/api/v1/updates?app=730&since=" + encodeURIComponent(lastSeen),
{ headers: { Authorization: `Bearer ${process.env.GAMEUPDATE_API_KEY}` } },
);
if (res.status === 429) await sleep(Number(res.headers.get("Retry-After") ?? 60) * 1000);
const { data } = await res.json();
for (const update of data) console.log(update.game, update.kind, update.title);Python
import os, requests
r = requests.get(
"https://gameupdate.org/api/v1/updates",
params={"app": "553850,2694490", "limit": 20},
headers={"Authorization": f"Bearer {os.environ['GAMEUPDATE_API_KEY']}"},
timeout=10,
)
r.raise_for_status()
for update in r.json()["data"]:
print(update["published_at"], update["game"], update["title"])Send your key as Authorization: Bearer gu_api_…. Keys are shown once when created and stored only as a hash, so keep a copy in your secret manager. Keep keys on your server: anyone with a key can spend your quota. Revoking a key takes effect within 30 seconds.
GET/api/v1/updates
Normalized updates for the games you ask for, newest first. Each item has a stable id, a kind(patch, release, maintenance, event, news), an extractive summary quoted from the publisher's post, highlights and a link to the source.
| Parameter | Description |
|---|---|
| app | Required. Steam app ids, comma separated (up to 25). |
| since | Only updates published after this instant (ISO 8601 or unix seconds). Use your newest published_at to poll. |
| before | Only updates published before this instant. Pass next_before to page back through history. |
| limit | 1 to 100, default 50. Results are newest first. |
unsupported_appslists ids we don't cover yet. Following a game from your account starts coverage for it.
GET/api/v1/games
Every Steam app id the API currently covers, as { app_id, name }.
Errors share one shape: { "object": "error", "error": { "code", "message" } }.
| Status | Codes | What to do |
|---|---|---|
| 400 | missing_app · invalid_app · too_many_apps · invalid_since · invalid_before · invalid_limit | Fix the query string. |
| 401 | missing_key · invalid_key | Send a valid, unrevoked key as a Bearer token. |
| 403 | addon_inactive · membership_inactive | The API add-on or your membership has lapsed. |
| 429 | rate_limited · daily_quota_exceeded | Wait Retry-After seconds. |
| 503 | unavailable | Temporary. Retry with backoff. |
60 requests per minute and 10,000 per UTC day, per account across all keys. Every response carries RateLimit-Policy, X-Quota-Remaining and, on 429, Retry-After. Polling every minute with since is plenty: we check sources every few minutes.
The path is versioned (/v1) and responses include api_version and a GameUpdate-Versionheader. We only add fields within v1; breaking changes ship as /v2 with at least 90 days' notice by email.
With the Discord & webhook add-on, every instant alert is also posted to up to 3 destinations. Discord gets a rich embed (no pings). Your own HTTPS endpoint gets a signed JSON POST:
POST https://your-server.example/gameupdate
Content-Type: application/json
X-GameUpdate-Event: game_update.published
X-GameUpdate-Delivery: dlv_…
X-GameUpdate-Signature: t=1792598400,v1=5f2b…
{ "id": "dlv_…", "type": "game_update.published", "api_version": "2026-10-01",
"created": "2026-10-21T12:00:00.000Z", "test": false, "data": { …game_update… } }Verify X-GameUpdate-Signature: an HMAC-SHA256 of <t>.<raw body>with the destination's signing secret, shown once when you add it. Reject stale timestamps.
import crypto from "node:crypto";
// rawBody: the exact request body string; secret: whsec_… shown when you added the webhook.
export function verify(rawBody, header, secret, toleranceSeconds = 300) {
const parts = Object.fromEntries(header.split(",").map((p) => p.trim().split("=")));
const t = Number(parts.t);
if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false;
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const given = Buffer.from(parts.v1 ?? "", "hex");
return given.length === 32 && crypto.timingSafeEqual(given, Buffer.from(expected, "hex"));
}Answer with any 2xx within 5 seconds. Redirects are not followed. After 5 failed deliveries in a row (or a deleted Discord webhook) the destination is paused; a successful test from the dashboard turns it back on.
Use the data in your own apps, bots and sites, with a visible "via GameUpdate" credit and a link to the original source post. Don't resell or redistribute the raw feed as a competing API, and don't share keys outside your team. Summaries quote publishers' own posts; respect their rights when you display them. Full terms: Terms of Service.