Every free-to-keep PC game, confirmed against the store’s own API before it ships. One call instead of six, one shape instead of four — and a negative you can trust: when we say nothing is free on Steam right now, it’s because we asked Steam this hour.
Confirmed against the store's own API before publication. Not aggregated, not inferred from a listicle.
Per-store status, so an agent can say "nothing is free there" as fact instead of guessing.
Pay per pull. Between pulls, conditional checks are free for 24 hours — poll as often as you like.
No signup, no API-key application, no human in the loop. The payment receipt is the credential — an agent can discover, pay and consume this entirely on its own.
4xx and 5xx are always free, and a lost response never double-charges: re-present the same payment proof and get your access back.
This is an N2N service: designed for autonomous agents, MCP servers and machine-to-machine pipelines rather than people clicking buttons. Discovery is machine-readable (llms.txt, OpenAPI 3.1), payment is HTTP 402 with no onboarding, and the response carries provenance — which store confirmed each entry and when — so a model can cite it instead of guessing.
While building this, a widely used free-games aggregator listed a title as an active Steam giveaway — with an end date. Steam’s own API reported the same game at $3.99, with no free-to-keep flag set.
Anything built on scraped aggregation publishes that game as free. Here it was discarded, because the store itself disagreed. That is the entire product: “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.
You are billed when the data changes, never when you ask. Checking costs nothing, so an agent polling every minute and one polling once a day pay exactly the same. Your bill is a function of how often games actually become free — a handful of times a day — not of how carefully your loop is written.
That makes autonomous spend predictable enough to approve in advance, which is usually the real blocker for agent purchasing rather than the price.
# 1. Ask. You'll get a 402 with a payment challenge. curl -i https://soren.com/v1/free-games # 2. Pay, and present the proof. You get the feed and a 24h token. curl -i https://soren.com/v1/free-games \ -H "X-Payment: <proof>" # 3. For the next 24h, check for changes for free. # 304 = nothing new. 402 = something changed, pay to pull it. curl -i https://soren.com/v1/free-games \ -H "Authorization: Bearer <token>" \ -H 'If-None-Match: "<version>"'
{
"version": "733d91c2…", // changes only when content does
"updated_at": "2026-07-31T15:17:52Z",
"free_games": [
{ "title": "OTXO", "store": "epic", "igdb_id": null,
"claim_url": "https://store.epicgames.com/en-US/p/otxo-396b8b",
"starts_at": "2026-07-30T15:00:00Z",
"ends_at": "2026-08-06T15:00:00Z",
"original_price_cents": 1499, "currency": "USD" } // rank by value
],
"stores": {
"steam": { "status": "ok", "free_count": 0,
"note": "verified against steam: no free-to-keep games at this time" },
"gog": { "status": "unavailable", "free_count": null,
"note": "could not be reached — NOT a statement that nothing is free" }
}
}
ok with
0 means we asked and there is nothing. unavailable
means we could not check. Rendering the second as "nothing is free" is the
exact false negative this feed exists to prevent.
Every entry carries original_price_cents and currency,
so an agent can answer the question that was actually asked. “Sixteen games
are free” is nearly useless when thirteen of them are $2 indie titles.
“Three games worth $10 or more are free, $67 of retail value in total”
is an answer.
null means the store
does not publish a price. 0 would mean a permanently free-to-play
title. Treating the first as the second silently inflates every total you
compute from this feed.
GET /v1/upcoming returns giveaways that have been
announced but have not started — the exact date each becomes
claimable, and what it normally costs. This is forward-looking information no
training data can contain and no cached answer can fake. A model asked
“what is free next week?” cannot know without asking something that
watches.
Epic Games Store only at present, because no other storefront publishes its schedule in advance. That is a limit of the world rather than of the feed, and /v1/status says so plainly instead of papering over it. Priced at $0.01, half the live feed, because it moves on a weekly clock rather than an hourly one. Full detail and sample response →
| Endpoint | What | Price |
|---|---|---|
GET /v1/free-games | Currently claimable, all stores | $0.02 / pull |
GET /v1/upcoming | Announced but not started yet | $0.01 / pull |
POST /mcp | MCP server — same data, same billing | see below |
GET /v1/sample | Frozen fixture — build against it before you pay | free |
GET /v1/status | Service health | free |
GET /openapi.json | OpenAPI 3.1 spec | free |
GET /llms.txt | What this is, for agents | free |
Each game carries the exact endpoint and test that confirmed it, plus when we first saw it — so an answer can be specific rather than a general appeal to having checked.
{
"title": "OTXO",
"claim_url": "https://store.epicgames.com/en-US/p/otxo-396b8b",
"source": "epic:freeGamesPromotions (discountPercentage === 0, active window)",
"first_seen": "2026-07-31T00:20:01.987Z"
}
There is deliberately no per-game verified_at: every
entry from a store is confirmed in the same pass, so it would duplicate the
store-level timestamp — and being a per-run value inside the hashed
content, it would change the version every pass and bill every check.
Freshness lives at the store level; origin lives on the entry.
Every response carries a version that is a SHA-256 of
its own content. Recompute it and a match proves the payload
reached you exactly as published — unaltered by us, by a cache, or by
anything in between.
// dependency-free, works in Node, Deno, Bun and browsers import { verifyFeed } from 'https://soren.com/verify.js'; const feed = await (await fetch('https://soren.com/v1/free-games', { headers })).json(); console.log(await verifyFeed(feed)); // { valid: true, ... }
Timestamps are excluded from the hash, so a refresh that changes nothing leaves the version untouched. That is the same property your billing rests on: an unchanged version is genuinely unchanged data.
Test all of that for free first.
GET /v1/sample returns a frozen fixture with the exact shape of
the paid feed, really signed with the production key — so verification
code written against it works unchanged against paid data. Build your
parser, your signature check and your error handling before you spend
anything.
sample: true, carries a not_current warning and
sends an X-Soren-Sample header. Never present its contents as
games that are currently free.
Point your agent at https://soren.com/mcp — streamable
HTTP, JSON-RPC 2.0, no separate signup. Four tools:
get_free_games and get_upcoming (paid),
check_for_changes and get_status (free).
Calling a paid tool without a payment proof returns payment instructions, not an error — so an agent can discover the price and pay without a human ever being involved.
| Code | Meaning | Billed |
|---|---|---|
200 | Feed returned, fresh 24h token issued | yes |
304 | Nothing changed since your version | no |
402 | Payment needed, or the feed changed | no |
429 | Rate limited — wait Retry-After seconds | no |
503 | Feed stale or unpublished — we fail rather than serve it | no |
Every payload carries an Ed25519 signature over its version
string, with the public key at
/signing-key.json. The signature proves it
came from us; recomputing the hash proves the content is what was signed.
verifyFully() does both.
And the shape holds still. Within /v1: fields are
never removed or retyped, meanings never silently change,
and the canonical hash form is fixed. Additions can appear without notice
— ignore unknown fields. Breaking changes ship at /v2
with 180 days’ notice.
Full contract.
/v1/status is free and unauthenticated, and reports how often we were right — not how much is free. Runs attempted and how many were clean, per-store reachability, offers published, and offers that vanished before the store’s own stated deadline.
It is generated from the database, not written by hand, so a bad week cannot be quietly left out. A source that publishes its own anomalies is easier to believe than one reporting a spotless record.
Steam · Epic · GOG · itch.io — checked hourly. Free-to-keep only: no demos, no free-to-play, no free weekends, no raffles, no key drops you have to earn.