API

Barato, sí. Also in JSON

barato.si is a JSON file rebuilt every three hours, with a website on top. The API is free, needs no key and is made of static files: cache them and do not ask more often than that.

Endpoints

PathWhat it returns
https://barato.si/api/deals.jsonToday's offers (up to 400) that passed the interest filter, in editorial order: price-error candidates first.
https://barato.si/api/errors.json{ date, generatedAt, deals, recent?, nearest? }: today's offers with an errorScore of 40 or more. It can be empty, and that is a result.
https://barato.si/api/deals/YYYY-MM-DD.jsonOne specific day. The last 30 days (UTC) are kept.
https://barato.si/api/index.json{ latest, days }: the most recent date and every available one, newest first.
https://barato.si/api/raw/YYYY-MM-DD.jsonEach day's original syndicated content (itemXml included), for attribution only.
https://barato.si/feed.xmlRSS 2.0 with the top 30, carrying the source's original content.
https://barato.si/errors.xmlRSS 2.0 with the day's possible price errors.
https://barato.si/llms.txtA plain-text description for agents and models.

Updates: every three hours (GitHub may delay a run for a while). The generatedAt field says when each file was generated.

Examples

curl + jq
curl -s https://barato.si/api/errors.json \
  | jq '.deals[] | {errorScore, title, price, reference, link: (.storeUrl // .url)}'
fetch (JavaScript)
const res = await fetch('https://barato.si/api/deals.json')
const { generatedAt, deals } = await res.json()

// The store's own page when known; otherwise the source's link.
const link = d => d.storeUrl ?? d.url
// Probable errors backed by an observed price, not just the seller's list price.
const errors = deals.filter(d => (d.errorScore ?? 0) >= 70 && d.reference?.kind === 'ledger')
console.log(generatedAt, errors.length, errors[0] && link(errors[0]))

Schema

Each file is a DealsFile: { version, date, generatedAt, count, sources[], classifier?, gate?, deals[] }. version is 3 since November 2026, 2 since October 6, 2026, and missing in earlier files. sources says which sources answered and which did not, and direct marks the ones that are the store itself, so a dead source never goes unnoticed. classifier says whether the model ran and how many offers it classified, so a model failure is never mistaken for "there were no candidates". gate (v3) is { considered, published, rejected, byReason }: how many offers were looked at, how many were published and why the rest were rejected. Text fields written by barato.si (reasons, errorSignals, reference.note) are in Spanish. Each element of deals is a Deal:

FieldTypeNote
idstringStable across days for the same offer.
titlestringThe source's original title, untranslated.
urlstringThe link the source gives, as is: the store, or the link the aggregator requires.
storeUrlstring | nullThe store's own page, when known and linkable. The site prefers it over url.
storestringStore: "Amazon", "Macy's"…
storeDomainstring | nullStore domain, e.g. "amazon.com".
directboolean?v3. true only when the main link goes straight to the product page at the store.
productKeystring?v3. Stable product identity across sources: asin:B0…, bestbuy:123, shopify:domain:handle, steam:440, url:<id>.
brandstring | null ?v3. Canonical brand when recognized. null is not a judgment: it just earns no brand points.
source / sourceUrlstringWhere it came from, and a link to that source.
currencystringISO 4217.
pricenumberOffer price, in currency units. 0 = free.
listPricenumber | nullList price, if the source gives one. It is what the seller says; it is not verified.
discountPctnumber | null0–100 against listPrice. null without a list price.
referenceobject?v3. { price, kind, observations?, asOf?, note }: the best evidence of what it usually costs. kind, strongest first: ledger (median of observed days), cross-store, curated, source-previous, list. Absent when there is none. note is in Spanish.
referenceDiscountPctnumber | null ?v3. 0–100 against reference. This, not discountPct, is what the error score uses.
imagestring | nullProduct image from the source (https). null if there is none.
categorystring | nullOur own category (videojuegos, informatica, hogar…, Spanish slugs); in old files, the source's.
publishedAt / foundAtISO 8601Published at the source / first seen.
scorenumber0–100. barato.si's score: how good the discount is.
errorScorenumber0–100. How likely it is a price error rather than an intended sale. Heuristic: in v3 it weighs depth against reference by what its evidence is worth. Independent of score. 0 in v1 files.
errorSignalsstring[]The signals behind errorScore, in Spanish, strongest first.
interest / interestSignalsnumber? / string[]?v3. 0–100: whether it deserves a reader's attention, and why, in Spanish.
seenInstring[]?v3. Sources that carried this product in this run. Two or more is corroboration.
staffPickboolean?v3. DealNews editors' pick.
flag"error-probable" | "chollo" | "normal"barato.si's label; "error-probable" when errorScore ≥ 70. On the web: "Possible price error", "Dirt cheap" or nothing.
reasonsstring[]Why, in Spanish. Our own analysis.
historyobject?{ days, minPrice, maxPrice, firstSeen }: what the archive saw of this offer on earlier days. Absent if it is new today.
aiobject?{ product, category, verdict, confidence, note, model }: optional classification by a small model, candidates only. Absent if it did not run.
syndicationobject?The source's attribution. The original itemXml no longer travels here: it is in /api/raw/YYYY-MM-DD.json.

Change in v2: syndication.itemXml (each feed item's original XML) no longer travels in deals.json. It is in /api/raw/YYYY-MM-DD.json, one file per day, for anyone who needs full attribution. The new fields (storeUrl, errorScore, history, ai…) are optional or have a default: code that read v1 keeps working.

Change in v3: reference replaces listPrice as the measure of what a product usually costs, and says where it comes from. With kind: "list" the reference is only what the seller says: treat it as the weakest evidence. Earlier files do not carry these fields; the site reads them as a list price when there is one. url, storeUrl and listPrice keep their meaning.

Attribution and fair use

Credit barato.si and each offer's original source (the source field). Keep the links and the attribution as they are. Do not rewrite the source's titles or present them as your own.

The score, the label and the reasons are barato.si's independent analysis, not the source's text. "Possible price error" is an automatic signal, not a confirmation that the store made a mistake.

Do not resell the sources' content or use it to train models without their owners' permission. Some sources set their own terms: DealNews content, for example, may not be used in public browser extensions.

Offers expire and may have conditions: coupons, a minimum order for shipping, membership. Check at the source before you buy.