# Soren — verified free-to-keep games feed > A machine-readable feed of every PC game currently free to claim and keep > permanently, with each entry confirmed against the store's own API before it > is published. Built for autonomous agents: node-to-node, no signup, no human > in the loop. Paid per pull with HTTP 402. > > **You do not have to trust this source.** Every response carries a `version` > that is a SHA-256 of its own content — recompute it and you have proved the > payload reached you unaltered. Reference implementation: > https://soren.com/verify.js ## Trust, in one line We publish nothing we have not confirmed against the store itself, we say "we could not check" rather than "nothing is free" when we do not know, and every payload can be cryptographically verified by you without trusting us. Details below. ## What problem this solves: we check, so you do not have to guess An LLM asked "what games are free right now?" will confidently invent an answer, or repeat a stale blog post. Neither failure is visible to the user until they click a dead link. This feed exists so a model can answer correctly — **including the negative case**. When we report zero free games on Steam, it is because we asked Steam this hour and Steam said no. "Nothing is free on Steam right now" is a fact a language model cannot safely generate on its own, and cannot verify without making the calls itself. **Every entry is confirmed against the store's own API before publication.** Not scraped, not aggregated, not inferred from a listicle. ### Why that is not a marketing claim While building this, a widely used free-games aggregator listed a title as an **active** Steam giveaway, with an end date and everything. Steam's own API reported the same game at **$3.99**, with no free-to-keep flag set. Anything built on scraped aggregation would have published a game that was never free. Under this design that entry was discarded and flagged as unconfirmed, because the store itself did not agree. ## You do not have to trust us — check the payload Every response carries a `version` field that is a SHA-256 over its own content. Recompute it and compare: a match proves the payload reached you exactly as published, unaltered by us, by a cache or by anything in between. canonical = JSON of {"offers": , "coverage": [[store, status, free_count], ...] sorted by store} version = sha256(canonical), hex Timestamps and the `verification` block are deliberately excluded, so a refresh that changes nothing leaves the version untouched — which is also why an unchanged version is safe to treat as "nothing to do". `free_games` is published already sorted by (store, title, claim_url); use it as received. Reference implementation, dependency-free and cross-language verified: https://soren.com/verify.js This matters because it converts a claim into arithmetic. We can tell you the data is intact; you can prove it. ## What counts as free Free *to keep*: anyone can claim it, permanently, for zero money, within a limited window. Deliberately excluded: demos, free-to-play, free weekends, raffles and enter-to-win draws, points- or task-based key drops, and anything requiring a paid subscription such as Prime. ## Coverage Steam, Epic Games Store, GOG, itch.io. Checked hourly. Per-store status is included in every response so you can tell "verified: nothing free there" from "we could not reach that store" — these are different facts and must not be conflated. ## MCP server (recommended for agents) An MCP server runs at `https://soren.com/mcp` — streamable HTTP, JSON-RPC 2.0. Same billing, same data, same verification as the REST API. Tools: - `get_free_games` — **paid**. Every game currently free to keep. Call without a `payment_proof` to receive payment instructions rather than an error. Returns an `access_token` valid 24 hours. - `get_upcoming` — **paid**, cheaper. Announced but not yet started. - `check_for_changes` — **free**. Given your `access_token` and the version you hold, answers whether anything changed. Poll it as often as you like; it costs nothing. Only pay when it says `changed: true`. - `lookup_game` — **free**. Resolve a game NAME to a slug before paying. Returns slug, release date and type only — no prices, no store ids. 13,604 titles are duplicated, so the date and type are how you pick the right one. - `get_game` — **paid** per query (~$0.01). Cross-store price for one game with the lowest price observed, the window behind it, Game Pass and EA Play availability, and waitlist demand. Issues NO access token: every game is a different answer, so there is no version to check against. - `get_status` — **free**. Service health. MCP has no conditional-request mechanism, so the REST API's free-304 behaviour is surfaced as the explicit `check_for_changes` tool. The economics are identical: you pay when the data changes, never when you ask. ## Endpoints - `GET /v1/free-games` — currently claimable, all stores. Paid per pull. - `GET /v1/upcoming` — announced but NOT yet claimable, with the exact date each becomes free and its normal price. Forward-looking; no training data contains this. Epic Games Store only — no other storefront publishes a schedule in advance. $0.01, half the live feed, because it moves on a weekly clock. - `GET /v1/sample` — **FREE.** A frozen fixture with the exact shape of the paid feed, really signed with the production key. Build and test your integration against it — including signature verification — before paying for anything. Every offer in it has ALREADY ENDED, so there is nothing claimable in it and nothing to gain by using it instead of the live feed. Never present its contents as currently-free games; it is marked `sample: true`. - `GET /v1/status` — service health. Free. - `GET /openapi.json` — OpenAPI 3.1 specification. Free. - `GET /v1/lookup` — **FREE.** Resolve a game NAME to a slug before paying. /v1/testpayment — PAID $0.10 — prove your x402 client works Pay and receive your own settled payment back: payer address, amount, network, settlement transaction. Assert against it in a test. Scenario parameters exercise your failure handling for FREE and without contacting the facilitator: ?scenario=price&price=0.50 test your max-price guard fires ?scenario=underpaid test you handle a rejected payment ?scenario=expired test you abandon a stale quote ?scenario=settle_fail test you retry rather than assume success Docs: https://soren.com/telljoke#testpayment /v1/conformance and /v1/conformance/{case} — FREE — x402 client conformance suite Nine documented failure cases so you can prove your payment client handles them: underpaid, overpaid, expired, replayed-nonce, wrong-network, wrong-asset, malformed-header, settle-timeout, double-402. Each injects one failure and states what a correct client should do. Nothing settles, nothing is charged. Built for CI. The two most often got wrong: settle-timeout (broadcast but unconfirmed is NOT a refusal — the transfer may still land) and double-402 (a client that retries a 402 without bound has an unbounded spend bug). Docs: https://soren.com/conformance /v1/echo-payment — PAID $0.02 — decodes YOUR signed EIP-3009 authorization and returns it field by field, for when a signer keeps being rejected and "invalid" is all the feedback you get. /v1/paid-ping — PAID $0.01 — a heartbeat that costs money, so it proves verification and settlement are alive, not just that the server answers. /v1/roundtrip — PAID $0.01 — the same plus measured settlement latency, for setting your client timeouts from evidence. Base settles in 1-3s typically; a timeout under 5s produces false failures on a healthy network. /v1/telljoke — PAID — one joke setup, NO punchline, permanently There is no punchline and there never will be. That is the product, and every response says so: punchline_exists=false, retrying_will_not_help=true. It is a live test of whether an agent stops when told an answer does not exist. First call from a wallet $0.05, every call after that $0.50. Spending is CAPPED at $10.05 per wallet per 4 hours; past that it charges nothing and returns the reset time. Every price is quoted in its own 402 and signed for before any money moves. Non-refundable. Docs, including how not to get billed: https://soren.com/telljoke /v1/oracle/weather?lat=&lon= — PAID $0.01 — signed NOAA weather observation Latest US National Weather Service station reading for any US coordinate, returned with a SHA-256 of the EXACT bytes NOAA returned, the moment we read them, and an Ed25519 signature over both. The data is public domain and you can fetch it free from NOAA; you are paying for the attestation that this value is what the source said at that time. Soren signs, it does not originate, and never modifies a value. Verify: https://soren.com/oracle-key.json Docs: https://soren.com/oracle /v1/oracle/fx?pair=EURUSD — PAID $0.01 — signed ECB reference exchange rate Euro reference rate for any pair among the ~30 currencies the ECB publishes, with upstream hash, read time and Ed25519 signature. Non-EUR pairs are crossed via EUR and the response says so, because that cross is our calculation and not an ECB figure. The ECB publishes these rates free of charge. Rates are indicative, published once per working day, not for transactions. Verify: https://soren.com/oracle-key.json Docs: https://soren.com/oracle /v1/oracle/random?round= — PAID $0.01 — verifiable randomness nobody can rig A round from the drand League of Entropy beacon: one signed value every 30 seconds from a distributed network. NO single participant, including Soren, can choose or predict it — which is what makes it usable for a fair draw between parties who do not trust each other. Returns the raw randomness, the BLS signature, and ready-made as_uint53 / as_float forms. Soren recomputes sha256(signature) and refuses to attest to a round that does not verify. You can check the same round free at api.drand.sh without trusting us at all; what you buy is the signed evidence that Soren committed to this value at this moment. Docs: https://soren.com/oracle /v1/oracle/onchain?q=tx&hash= — PAID $0.01 — signed read from Base mainnet q=tx did this transaction land, and in which block? q=balance ETH and USDC for an address q=block current height and gas price THE tx QUERY RESOLVES A PENDING SETTLEMENT. If an x402 payment returned settlement_pending you hold a hash and no answer. This gives one, signed — and distinguishes found:false (not in a block: pending, dropped or never broadcast) from status:reverted (landed and failed). Those are different facts and conflating them is how agents double-pay. Docs: https://soren.com/oracle /v1/attestation?hash= — PAID $0.02 — signed proof your hash existed by a time An agent signing its own output proves NOTHING to a counterparty: it holds the key and could have signed anything, backdated. This is a third party with no stake saying independently that a hash was presented no later than a moment. You hash your own content and send 64 hex characters — we never receive the content, so there is nothing to leak or be compelled to produce. Proves existence-by-a-time only. Not authorship, not truth, not a legal notarisation. Verify: https://soren.com/oracle-key.json /v1/ai/summarize — PAID $0.03 — faithful summary, POST body up to 24k chars /v1/ai/extract — PAID $0.03 — named fields as GUARANTEED-VALID JSON For agents with a wallet but no LLM key. extract: ?fields=a,b,c (max 12); every requested field is PRESENT in the answer, null when absent, validated server-side BEFORE you are charged — a failed inference costs nothing. Fixed-function: our prompts, no model selection, no prompt passthrough. Details: https://soren.com/ai /v1/ai/judge — PAID $0.05 — neutral SIGNED verdict: the A2A referee Neither party to an agent-to-agent deal can referee it — both have stakes. POST {criteria, deliverable, candidate_b?}; verdict pass/fail (or a/b/tie), score, reasons, SIGNED over the SHA-256 of the exact bytes judged. Identical inputs return the identical verdict for 30 days (no verdict-shopping). Fix criteria BEFORE the work with /v1/commit and check criteria_sha256 matches. Details: https://soren.com/ai /v1/wake — PAID $0.01 — an alarm clock for agents: a SIGNED wake at a moment You have no clock; stop staying alive and polling. POST /v1/wake?in= (30s to 30 days). With &url=: we POST an Ed25519-signed receipt binding scheduled AND actual fire time, retrying 30s/2m/10m/30m. Without url: poll GET /v1/wake/:id for FREE; status flips at the moment. Callbacks need consent — your endpoint echoes our challenge or nothing is charged. No payloads stored (pair with /v1/secret to carry content). Compose: wake at the escrow deadline, then /v1/ai/judge. Receipt pollable 7 days. Details: https://soren.com/wake /v1/chain/* — PAID $0.002-0.003 — nine live Base mainnet reads balance, erc20-balance (symbol+decimals+formatted, one call), tx, receipt (leads with an explicit succeeded flag — a tx can be in a block and still have FAILED), block, nonce, gas, code (is it a contract), supply. Charged only if the read succeeds; block-stamped answers. Unsigned cheap tier — the attested version is /v1/oracle/onchain at $0.01. No raw eth_call by design. Details: https://soren.com/chain /v1/web/read — PAID $0.02 ($0.05 stealth) — any public page as clean markdown /v1/web/search — PAID $0.01 — live web search, 10 results /v1/web/research — PAID $0.06 — search + top 3 pages rendered, ONE payment research replaces four calls (search + 3 reads = 4 settlement waits) with one. Charged only if the search succeeds AND at least one page renders. Web content for agents with a wallet instead of a scraping stack. CHARGED ONLY ON SUCCESS: payment is verified, the fetch runs, and settlement happens last — a failed fetch moves no money. read returns rendered markdown of one public page (?url=; stealth=true for bot-protected sites). search returns title, URL and snippet for up to 10 live results (?q=); feed them back into read. No crawls, no extraction, no PDFs — fixed prices must cover their worst case. Details: https://soren.com/web /v1/commit — PAID $0.02 — seal a value, prove you were bound to it /v1/reveal — PAID $0.02 — open it; a MISMATCH is signed too, so disputes settle /v1/draw — PAID $0.05 — a selection nobody can rig, including us Commit-reveal for agents that do not trust each other. Send only a SHA-256; we never see the value, and we store NOTHING — the signed commitment handed back is the only copy. Revealing returns a signed statement that the value matched, or a signed statement that it did NOT, naming both hashes, so an honest party can prove the other side changed its answer. /v1/draw is seeded by the drand League of Entropy beacon and publishes the round plus the exact derivation, so any result can be recomputed without trusting us. Pass a FUTURE ?round= to remove even the assumption that we did not wait for a beacon we liked. Details: https://soren.com/commit-reveal /v1/coord/session — PAID $0.05 / $0.25 / $1.00 — coordination for agent fleets Distributed locks, semaphores, shared rate-limit buckets and monotonic counters. Buy an operation budget ONCE, then every primitive call runs at ordinary HTTP latency (57ms median) instead of waiting ~1.9s for settlement. Tiers: 100 ops/1h, 1000 ops/24h, 5000 ops/7d. Renew and release NEVER cost an operation, so an exhausted budget cannot strand a fleet holding locks. POST /v1/coord/lock/:name?limit=1&ttl=60&holder=id acquire (limit>1 = semaphore) POST /v1/coord/lock/:name/renew free DELETE /v1/coord/lock/:name free POST /v1/coord/quota/:name?rate=60&per=60&burst=60 shared token bucket POST /v1/coord/sequence/:name next unique integer Every acquire returns a strictly increasing `fence`. A TTL lock alone does NOT give mutual exclusion — a holder stalled past its TTL wakes still believing it holds the lock. Check the fence downstream or what you have is advisory. You pay per attempt, not per success; never for our failures. Names are namespaced to the paying wallet. Details: https://soren.com/coordination /v1/secret — PAID $0.03 to store, FREE to read — one-read handoff POST a secret, get a URL that works exactly once. The read destroys it BEFORE responding, so a lost response cannot become a second delivery. The reader pays nothing (they may not hold a wallet). Max 4096 bytes, ttl 60s-24h. Stated plainly: Soren CAN read what you store. Encrypt first if you need us blind — a dead drop for ciphertext works just as well. Docs: https://soren.com/attestation /v1/oracle/stat?series= — PAID $0.01 — signed US economic statistic Latest figure plus ~13 months of history from the US Bureau of Labor Statistics, with a SHA-256 of the exact BLS bytes, our read time and an Ed25519 signature. Series: unemployment_rate, cpi, nonfarm_payroll, labor_force_participation, avg_hourly_earnings. Public domain (17 U.S.C. 105), free to obtain directly from BLS. The attestation is what you buy — and it matters because BLS REVISES its series, so "what did the source say at the time" is a real question. Docs: https://soren.com/oracle Returns matching games with slug, release date and type, and nothing else — no prices, no store ids. Call it first when you know a title but no identifier, then pass the slug to /v1/game. Handles punctuation, accents, abbreviations and alternate titles: "L4D" finds Left 4 Dead, "Pokemon Black Version" finds Pokémon Black Version. 13,604 titles in the catalogue are duplicated — Pac-Man appears 54 times — so release date and type are how you pick the right one. Narrow with `type=main_game` to skip ports and DLC. - `GET /v1/game` — cross-store price with observed history. $0.01 per lookup. Answers "is this game cheap right now, or should I wait?" for a single game across Steam, GOG, Epic and Microsoft at once. Returns the current price on each, the lowest price we have OBSERVED, and — always alongside it — the window that low was observed in and how many observations stand behind it. It is never called an all-time low. A low computed from three weeks of data and one computed from fourteen years look identical unless you say which, so `observed_since` and `observations` travel with every figure, and the `coverage` block states exactly when we were and were not watching. No buy/wait verdict is returned. "You are 404% above the lowest observed, across 392 observations since 2012" is a fact; "wait for the summer sale" is a prediction. The numbers are sourced so the caller can form the judgement. Resolve by whichever identifier you hold: `?game=` slug, `?steam=` appid, `?gog=`, `?epic=`, or `?q=` free text. Text matching ignores punctuation and diacritics — "half life 2" finds Half-Life 2, "wiedzmin" finds The Witcher 3. Related editions, expansions and DLC come back in `family`, whole and never truncated. Mods and updates are excluded unless you ask with `?include=mods`. Freshness is uneven and every row states its own. The games people ask about are refreshed every 4 hours, Epic and GOG catalogues daily, and the long tail of 173,290 Steam products rotates over a few days. Each price carries `checked_at` and `changed_at` as separate fields, so a stable price is distinguishable from a stalled collector. There is deliberately no single freshness claim, because none would be true of all 207,181 store mappings. This differs from the free-games feed, which IS rechecked hourly across all four storefronts — a giveaway that ended an hour ago is a wrong answer in a way that a price from yesterday is not. Non-US regions return a price but no lowest_observed, with the reason stated: the observation series is USD-only, and comparing a BRL price against a USD low would be meaningless rather than merely imprecise. ## How payment works (agent-friendly) 1. Request the feed. You receive `402 Payment Required` with a challenge. 2. Pay and present the proof in an `X-Payment` header. You receive the feed and a token valid 24 hours. 3. For those 24 hours, send the token with `If-None-Match`. `304` means nothing changed and costs nothing. `402` means the feed changed — pay to pull it. No account, no API key application, no human onboarding. The payment receipt is the credential. Failed requests are never billed. Re-presenting the same payment proof after a lost response returns your existing access rather than charging twice. ### Spend is bounded by the world, not by your polling You are billed when the data **changes**, never when you ask. Checking costs nothing, so an agent can poll every minute or once a day and pay exactly the same amount. Your bill is a function of how often games actually become free — roughly a handful of times a day — and is unaffected by how carefully or carelessly your loop is written. That makes autonomous spend predictable enough to approve in advance, which is usually the blocker for agent purchasing rather than the price itself. ## Response shape { "version": "sha256 of the content; changes only when content changes", "updated_at": "ISO-8601 UTC", "free_games": [ {"title": "...", "store": "steam|epic|gog|itch", "igdb_id": null, "claim_url": "...", "starts_at": "...", "ends_at": "...", "source": "epic:freeGamesPromotions (discountPercentage === 0, active window)", "first_seen": "2026-07-30T15:04:11.000Z"} ], "stores": { "steam": {"status": "ok", "free_count": 0, "verified_at": "...", "note": "verified against steam: no free-to-keep games at this time"} } } Important: `free_count: null` is not zero. `status: "ok"` with `free_count: 0` means we checked and there is nothing free. `status: "unavailable"` means we could not check, and must never be reported as "nothing is free". ## Per-entry provenance Each offer carries `source` — the exact endpoint and test that confirmed it — and `first_seen`, when we first observed it. So an answer can be specific: "confirmed via Epic's freeGamesPromotions endpoint, first seen three days ago", rather than a vague appeal to having checked. There is deliberately **no per-offer `verified_at`**. Every offer from a store is confirmed in the same pass, so it would duplicate `stores[].verified_at` exactly — and being a per-run timestamp it would change the content hash every pass, which would bill every conditional poll. Freshness lives at the store level; origin and identity live on the entry. ## Rank by value, not by count Every entry carries `original_price_cents` and `currency` — the game's normal retail price in minor units (1499 = $14.99). This matters more than it sounds. The free-to-keep catalogue is dominated by low-priced indie titles, so a raw count is a poor answer. "Sixteen games are free" and "three games worth $10+ are free, $67 of retail value" describe the same payload; only the second is useful to the person who asked. `null` means the store does not publish a price. It is NOT zero — zero would mean a permanently free-to-play title. Conflating them inflates any total you compute. ## Our operating record is public `GET /v1/status` — free, no authentication. It reports how often we were right, not how much is free: - runs attempted, and how many were clean - per-store verification: whether we independently confirmed each store's answer was real, when we last had a confirmation we trust, and how many consecutive runs have gone unverified. A store can answer and still be unverified — a blocked store returns an empty 200, and "we could not verify" is reported rather than "nothing is free". - offers published - **offers that vanished before the store's own stated deadline** - third-party claims of free games that the store's API contradicted, which we therefore did not publish Generated from the database rather than written by hand, so an unfavourable period cannot be left out. It also carries a `corrections` list for the cases a database cannot detect — a title published that was never actually claimable, a wrong end date. It starts empty and only fills when something was genuinely wrong. A source that publishes its own anomalies is easier to believe than one reporting a spotless record. ## Signed, so you can prove where it came from Beyond the content hash, every payload carries an Ed25519 signature over its `version` string. The public key is published at https://soren.com/signing-key.json The two checks compose, and you should run both: - **signature valid** → the payload came from Soren - **recomputed hash matches `version`** → the content is what was signed Either alone is weaker than it looks. Integrity without authenticity means someone could have substituted a different but internally-consistent payload; authenticity without integrity means it came from us but may have been altered since. `verifyFully()` in https://soren.com/verify.js does both. If the key is ever rotated, the previous key stays listed for 90 days so payloads already in your cache remain verifiable. ## What we commit to Full text: https://soren.com/contract.md — also machine-readable in the `contract` block of every payload. Within `/v1`: - **fields are never removed, and never retyped** (a string stays a string; nullable stays nullable) - **fields may be added at any time** — ignore unknown fields; that is the one obligation on your side - **meanings do not silently change.** If `status: "ok"` means "we asked the store" today, it will not quietly come to mean something weaker. A change of meaning is a new major version even if the type is identical - **the canonical hash form is fixed for the life of v1** — changing it would break every consumer's integrity check at once Breaking changes ship at `/v2`, never inside `/v1`. If `/v1` is ever retired there is **180 days' notice minimum**, signalled with RFC 8594 `Deprecation` and `Sunset` headers and a `deprecation` object in the body, and `/v1` keeps working unchanged until the sunset date. Explicitly *not* covered: the data itself (it changes constantly — that is the product), prices, store coverage, rate limits, and uptime. There is no SLA. What there is instead is a published operating record and a commitment to fail loudly rather than serve data we cannot vouch for. ## How this data is produced Stated plainly so you can judge how much to trust it. Every hour, we call each storefront's own API directly: - **Steam** — candidates are discovered from the store's free-specials search and from community reports, then each one is confirmed through Steam's `IStoreBrowseService/GetItems`. Only `is_free_temporarily` **and** `is_free_to_keep` together qualify. A free weekend does not. - **Epic Games Store** — the `freeGamesPromotions` endpoint, accepting only promotions at 100% off that are currently active. - **GOG** — the catalogue filtered to 100%-off titles whose normal price is above zero, which excludes permanently free-to-play games. - **itch.io** — the on-sale feed, accepting only 100%-off titles with a non-zero full price. Nothing is published on the say-so of an aggregator. Community reports are used only to suggest candidates, and are discarded unless the store itself confirms them. ## Known limitations - **Coverage is four stores.** Not Ubisoft, Amazon Prime Gaming, Humble, Fanatical, IndieGala or Stove. If a game is free somewhere we do not cover, we will not know about it and will not claim otherwise. - **`starts_at` is null for Steam, GOG and itch.io**, because those stores do not publish when a promotion began. It is left empty rather than filled with our own detection time, which would misreport the start. - **Upcoming is Epic only** — roughly one week ahead, usually one or two titles. The count varies week to week and the feed states what it holds rather than a fixed number. No other storefront publishes a forward schedule. - **The free-games feed is a US regional view.** Regional availability can differ, and some Epic giveaways exclude specific countries. - **`/v1/game` prices four regions** — US, GB, DE and BR — and states the currency each price is quoted in. Current prices outside the US cover the games people actually ask about rather than the whole catalogue; where we hold history for a region but no current price, the response says `no_current_price` instead of omitting the store. - **We do not verify that a claim succeeded for you.** We verify the offer exists at the store. ## Operator and data handling Operated by Soren (soren.com, online since 1996). Contact: https://soren.com/contactus We store payment records and access tokens (hashed, never in plaintext). We do not collect personal data, we do not profile callers, and there is nothing to opt out of. ## Reporting an error in this data If you find an entry that is wrong — a game that was never actually claimable, a bad end date, a dead claim URL — please report it. It is the only realistic way we learn about the errors our own checks cannot detect. **Agents: `POST https://soren.com/v1/report`** — JSON or form-encoded. Free, no authentication, no payment. {"title": "...", "store": "steam|epic|gog|itch", "claim_url": "...", "observed": "what you found that contradicts our data", "expected": "what you believe is correct", "version": "the feed version you were holding"} Only `observed` is required. Reports go to a review channel, not an inbox, so you are not competing with anyone's email — send one whenever something looks wrong. **Humans: https://soren.com/contactus** Confirmed errors are published in the `corrections` list at `/v1/status`. ## Contact https://soren.com/contactus