The JSON feeds

← back to the index

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: *.

FeedWhat it coversHow it is gathered
api-health.jsonfree, no-auth public data APIsmeasured — one read-only GET per API, every day
ai-deals.jsonfree, trial and discounted offers from AI providersdocumented — read off the provider's own page on a stated date

The distinction that matters most

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 measuredevidence 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.

How fresh each one is

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.

Top-level fields

api-health.json:

FieldMeaning
generated_atwhen the sweep that produced this file ran (UTC, ISO 8601)
countstotals by status for this run, plus total and measured
resultsone 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:

FieldMeaning
schemafeed format identifier
verticalsubject area of the catalog
generated_atwhen this file was generated (UTC, ISO 8601)
curated_atwhen the catalog was originally researched — older than generated_at, and deliberately so
transferred_at, transferred_bywhen the catalog was handed to this project, and by whom
provenancehow the rows were gathered
staleness_warningcomputed at build time from the rows; names its denominator
countstotals by offer status
itemsone 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.

api-health.json — result fields

FieldMeaning
namethe API's name
categoryDeveloper, Finance, Geo, Health, Knowledge, News, Weather
authwhat the endpoint requires — none for the no-auth set
check_urlthe exact URL probed; one small idempotent GET
statusOK, AUTH, HTTP_ERR, or DOWN
httpHTTP status code returned, if the request completed
latency_msround-trip time in milliseconds
content_typeContent-Type as returned
body_snippetfirst few hundred characters of the response, emails redacted

ai-deals.json — item fields

FieldMeaning
idstable identifier for the row
name, provider, categorywhat the offer is and who runs it
offer_statusavailable, 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_typefree, discount, or spend_unlock. Stated on every row of the feed, so absence never has to be interpreted
free_whatwhat is actually free, in the provider's terms. null on every negative, and on a discount row, where nothing is free
deal_whaton a discount row, what the discount gives you
findingon a negative row, what was checked and not found
accesswhat the call needs: api-key, oauth, phone-verification, credit-card
acquisitionwhat you need to get in: none, api-signup, web-form, oauth-only, phone-verify, payment-card, partner-program
acquisition_evidenceprobed if the signup page was fetched and read; documented if derived from access or stated on the row
acquisition_probed_atwhen it was probed, where it was
acquisition_sourcethe page the gate was read from
agent_usablecan a running agent consume this via API once a human has cleared the barrier
evidencedocumented throughout — see above
confidencehow firm the row is
source_urlthe provider page the claim comes from
verified_atthe date that claim was read. This is the field to compute freshness from
caveatsanything that qualifies the offer
observationsreal calls made by other workers, attributed, never first-party

Why access and acquisition are separate fields

They 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.

Provenance rules

Reuse

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.

Corrections

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.

In the feeds right now

Generated with the feeds themselves, so this section cannot drift from what is actually served.

api-health.jsonai-deals.json
Rows3560
Generated2026-08-26T01:40:03Z2026-08-26T01:40:03Z
Gathered bydaily measurementprovider documentation

Staleness of ai-deals.json36 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.