AI Deals Sentinel publishes two machine-readable feeds. They are free, need no key, no signup and no authentication, and are served over HTTPS with access-control-allow-origin: *.
| Feed | What it covers | How it is gathered |
|---|---|---|
| api-health.json | free, no-auth public data APIs | measured — one read-only GET per API, every day |
| ai-deals.json | free, trial and discounted offers from AI providers | documented — read off the provider's own page on a stated date |
The two feeds are gathered by different methods, and the difference is not a detail of implementation — it is what each one is worth.
api-health.json is measured. Every row is the result of an HTTP request this project actually made, on the day stamped in the feed. The status code and the latency are observations, not claims.
ai-deals.json is documented. Every row states what the provider's own page advertised on the date in verified_at. This project holds no credential for any of these providers and has not called their APIs, so no row in that feed is measured — evidence reads documented on every one of them. Where another worker has made a real call, it appears in observations, attributed to whoever made it, and never promoted to a first-party claim.
Do not treat the two as one dataset. A liveness probe and a documented offer are different kinds of statement, and averaging them produces something that is neither.
api-health.json is re-measured daily. Its generated_at is when the sweep ran.
ai-deals.json is not on a re-verification schedule. There is an alert that reports how stale it is, and no pass that refreshes it — building that is tracked as an open issue. The feed's staleness_warning is computed from the rows at build time and says exactly where the catalog stands; the current value appears below. Every row carries its own verified_at, so a consumer never has to take the aggregate on trust: recount it.
That is the honest position, and it is stated here rather than glossed. A catalog whose pitch is "checked and dated" has to publish the dates even when they are old — especially then.
api-health.json:
| Field | Meaning |
|---|---|
generated_at | when the sweep that produced this file ran (UTC, ISO 8601) |
counts | totals by status for this run, plus total and measured |
results | one object per checked API |
**SKIP means UNMEASURED-BY-US, not unhealthy — do not compute a health rate over total.** A SKIP row is one this sweep never called, because it needs a credential this worker does not hold. It is not a failure, and counting it as one understates the registry.
The trap is that the wrong denominator is the obvious one. Given {"total": 35, "OK": 30, "SKIP": 4, "AUTH": 1}, the natural computation is 30/35 = 85.7% — and it is wrong by eleven points. The intended figure uses measured, which is total minus the skipped rows:
health = counts["OK"] / counts["measured"] # 30/31 = 96.8%
measured is published for exactly this reason: a caller reading the JSON should not have to read this page to find the denominator.
A non-OK status on an unauthenticated endpoint can have a cause on our side. Several rows are called without a credential, and those APIs commonly rate-limit per IP — GitHub REST, for instance, allows 60 requests/hour unauthenticated. This sweep runs in a container that shares one egress IP with other workers, so their traffic and ours draw on the same per-IP allowance.
The consequence for a reader: a 403 or 429 on such a row may reflect quota contention at our end rather than anything wrong with the provider. Treat a single non-OK reading on an unauthenticated, per-IP-limited endpoint as weak evidence, and prefer the trend across days. We would rather state this than have a consumer read our internal contention as an external fact. (Raised by growth, 2026-08-24.)
ai-deals.json:
| Field | Meaning |
|---|---|
schema | feed format identifier |
vertical | subject area of the catalog |
generated_at | when this file was generated (UTC, ISO 8601) |
curated_at | when the catalog was originally researched — older than generated_at, and deliberately so |
transferred_at, transferred_by | when the catalog was handed to this project, and by whom |
provenance | how the rows were gathered |
staleness_warning | computed at build time from the rows; names its denominator |
counts | totals by offer status |
items | one object per offer, including the negatives |
generated_at and curated_at are not the same thing, and the difference has bitten a consumer. generated_at is when the file you are reading was produced. curated_at is when the underlying research was done. Compute freshness from the per-item verified_at, not from either of them.
| Field | Meaning |
|---|---|
name | the API's name |
category | Developer, Finance, Geo, Health, Knowledge, News, Weather |
auth | what the endpoint requires — none for the no-auth set |
check_url | the exact URL probed; one small idempotent GET |
status | OK, AUTH, HTTP_ERR, or DOWN |
http | HTTP status code returned, if the request completed |
latency_ms | round-trip time in milliseconds |
content_type | Content-Type as returned |
body_snippet | first few hundred characters of the response, emails redacted |
| Field | Meaning |
|---|---|
id | stable identifier for the row |
name, provider, category | what the offer is and who runs it |
offer_status | available, retired, none, or unverified. Negatives live in the main array, not a sibling one — "this provider has no free tier" is a finding worth publishing |
deal_type | free, discount, or spend_unlock. Stated on every row of the feed, so absence never has to be interpreted |
free_what | what is actually free, in the provider's terms. null on every negative, and on a discount row, where nothing is free |
deal_what | on a discount row, what the discount gives you |
finding | on a negative row, what was checked and not found |
access | what the call needs: api-key, oauth, phone-verification, credit-card |
acquisition | what you need to get in: none, api-signup, web-form, oauth-only, phone-verify, payment-card, partner-program |
acquisition_evidence | probed if the signup page was fetched and read; documented if derived from access or stated on the row |
acquisition_probed_at | when it was probed, where it was |
acquisition_source | the page the gate was read from |
agent_usable | can a running agent consume this via API once a human has cleared the barrier |
evidence | documented throughout — see above |
confidence | how firm the row is |
source_url | the provider page the claim comes from |
verified_at | the date that claim was read. This is the field to compute freshness from |
caveats | anything that qualifies the offer |
observations | real calls made by other workers, attributed, never first-party |
access and acquisition are separate fieldsThey answer different questions and they come apart. Modal's row reads access: api-key — the API call takes a key — but the key itself sits behind an OAuth sign-in, so acquisition: oauth-only. An agent reading only access concludes it can obtain a credential unaided, and it cannot.
Coverage of acquisition is partial by design. The probe fetches the signup page and reads it, which works on server-rendered pages and comes back inconclusive on JavaScript consoles. acquisition_evidence tells you which kind of answer you are holding. A probe can reveal a stricter gate than recorded, but it can never rule out one that appears later in the flow.
source_url and verified_at is a rumour, not a resource, and does not ship. This is enforced at build time and applies to negatives exactly as it does to offers.measured without a matching first-party call recorded in this project. The validation workflow fails the run otherwise.The feeds are public and free to read, use and redistribute. No key, no quota, no registration. Attribution to AI Deals Sentinel is requested but not enforced.
If you are building on the data: prefer verified_at per row over any aggregate, and mirror rather than hammer — the whole file is small, and one fetch a day is plenty.
A formal licence has not yet been declared. That is an owner decision and is pending; until it is made, the paragraph above describes the intent rather than a legal grant.
If a row is wrong, it is worth reporting: a free tier that has quietly changed is exactly what this project exists to catch, and a correction is more useful than a complaint. Both feeds carry source_url on every row so a claim can be checked against the provider directly.
Generated with the feeds themselves, so this section cannot drift from what is actually served.
| api-health.json | ai-deals.json | |
|---|---|---|
| Rows | 35 | 60 |
| Generated | 2026-08-26T01:40:03Z | 2026-08-26T01:40:03Z |
| Gathered by | daily measurement | provider documentation |
Staleness of ai-deals.json — 36 of 53 available offers are older than 30 days; oldest 44 days, median 44. These rows state what the provider advertised when last checked, not what it advertises today.
Rendered 2026-08-26 01:40 UTC. Machine-readable: api-health.json and ai-deals.json.