Modelo de datos
Tres objetos explican casi todo:
- Producto: el artículo, igual en todas las tiendas (
prd_). Tiene marca, categoría, tamaño y códigos de barras (GTIN). - Oferta: ese producto en una tienda (
ofr_), con su URL y su título original. - Observación: lo que vimos en una oferta en un momento: precio, condiciones, stock y de dónde salió. Las observaciones no se modifican; el historial es la serie de observaciones.
Precio: qué significa cada campo#
price es lo que paga cualquier persona sin condiciones: el precio de oferta si existe, si no el de lista. Lo condicionado va aparte, para que nadie lo presente como "el precio".
| Campo | Qué es |
|---|---|
list_price | Precio normal publicado |
sale_price | Precio rebajado sin condiciones, o null |
price | El menor de los dos: lo que paga cualquiera |
conditional_prices[] | Precios que exigen algo distinto de la cantidad: medio de pago, nivel de club, app o cupón |
card_price, card_label | Atajo: el menor precio con tarjeta o membresía (por ejemplo "Tarjeta Cencosud") |
promotions[] | Exigen cantidad o combinación: "2 x $2.590", la segunda unidad, porcentaje |
unit_price | Precio por unidad base (kg, l, unidad, m o m2), para comparar tamaños |
installments | Cuotas sin interés de la oferta |
availability | Stock: in_stock, out_of_stock, limited o unknown |
Procedencia#
Cada precio dice de dónde salió, para que cualquiera lo pueda comprobar:
| Campo | Qué es |
|---|---|
url | La ficha del producto en la tienda |
observed_at | Fecha y hora de la captura, en UTC |
observed_local_date | Fecha de la captura en la zona de la tienda |
capture_method | public_page (la página pública), public_api (un endpoint abierto del sitio), retailer_feed (entregado por la tienda) u open_data (un dataset público) |
meta.attribution.text | Una línea lista para citar: marca, tienda, hora e id |
Mismo producto entre tiendas#
Cada oferta trae match: cómo se unió al producto y con qué confianza.
| Campo | Qué es |
|---|---|
match.method | Cómo se unió: por GTIN, por reglas o por un modelo |
match.confidence | Entre 0 y 1 |
match.kind | identical (el mismo artículo), pack (N unidades del producto, ver pack_ratio) o equivalent (sustituto de otra marca) |
match.pack_ratio | Cuántas unidades trae el pack |
Por defecto los precios y las comparaciones agrupan solo ofertas identical con confianza de 0,85 o más. min_confidence y match_kinds (por ejemplo identical,pack) amplían el grupo, y meta.warnings avisa cuando quedaron ofertas fuera. En un pack, compara por unit_price.
Calidad#
Los precios se entregan tal como los publica la tienda, nunca corregidos. Si una validación ve algo raro, el precio llega con quality_status: "warning" y la razón en quality_flags[]: discount_over_85 (descuento sobre 85 % del precio normal) o round_price_low (precio redondo de $1.000 o menos). Si una observación se enmendó después de capturada, corrected es true y el original se guarda.
Moneda, impuestos e idioma#
- Montos en la moneda de la tienda; CLP no tiene decimales.
currency=USDagregaconvertedcon la tasa, su fecha y su fuente. tax_includeddice si el precio publicado incluye impuestos (en Chile, sí).tax_rateyprice_ex_taxse derivan de la regla del país (tax_rule_id), o vienen ennullsi no hay regla.- Nombres y categorías en español o inglés según
lang;namestrae todos los idiomas ytitle_original, el título de la tienda sin traducir.
Taxonomía#
Las categorías son un árbol con ids legibles (food.dairy.milk): filtrar por food.dairy incluye sus subcategorías. El árbol completo está en GET /v1/categories.
Campos#
Precio en una tienda (RetailerPrice)#
| Campo | Tipo | Descripción |
|---|---|---|
currency requerido | string | |
price requerido | number | |
list_price requerido | number | |
sale_price | number | null | |
card_price | number | null | Convenience: the lowest price in conditional_prices that requires a payment method or membership. |
card_label | string | null | |
conditional_prices | ConditionalPrice[] | Every price that requires a condition other than quantity (payment method, loyalty tier, app, coupon), as published. |
conditional_prices[].kind requerido | string | Valores / values: payment_method, membership, app_only, coupon, other. |
conditional_prices[].label requerido | string | As shown by the retailer. |
conditional_prices[].price requerido | number | |
conditional_prices[].payment_method | string | null | |
conditional_prices[].membership_tier | string | null | |
conditional_prices[].installments_count | integer | null | |
conditional_prices[].interest_free | boolean | null | |
unit_price | object | null | |
unit_price.amount requerido | number | |
unit_price.per requerido | string | Valores / values: kg, l, unit, m, m2. |
promotions | Promotion[] | |
promotions[].type requerido | string | Valores / values: multi_buy, nth_unit, percent_off, amount_off, bundle, gift, other. |
promotions[].label requerido | string | As shown by the retailer, e.g. "2 x $2.990". |
promotions[].min_quantity | integer | null | |
promotions[].effective_unit_price | number | null | Price per item when the promotion applies. |
promotions[].requires_card | string | null | Payment method required, if any. |
promotions[].valid_until | string (date) | null | |
availability requerido | string | Valores / values: in_stock, out_of_stock, limited, unknown. |
tax_included | boolean | As shown by the retailer (observed). |
tax_rate | number | null | Derived. Main VAT rate for this country and category, null when not known. |
price_ex_tax | number | null | Derived from price and tax_rate. Null when tax_rate is null. |
tax_rule_id | string | null | Versioned rule used to derive tax_rate. |
other_taxes_included | boolean | null | True when other taxes remain in price_ex_tax (e.g. Chile IABA on sweetened drinks). |
installments | object | |
observed_at requerido | string (date-time) | |
observed_local_date | string (date) | Capture date in the retailer time zone. Daily series use this date. |
capture_method requerido | string | How the price was obtained, part of the provenance with url and observed_at. public_page: read from the retailer's public web page (HTML, JSON-LD or the page payload). public_api: an open, unauthenticated JSON endpoint of the retailer's own site. retailer_feed: delivered by the retailer under an agreement. open_data: a public dataset published by a government or third party. Valores / values: public_page, public_api, retailer_feed, open_data. |
quality_status | string | warning when a validation flagged the observation (price jump, zero, unusual unit). Quarantined observations are never served. Valores / values: ok, warning. |
quality_flags | string[] | Why the price was flagged. The price is delivered as published, never corrected. discount_over_85: more than 85% off the list price. round_price_low: a round price of CLP 1,000 or less. Both often mean a placeholder price or a store error. |
corrected | boolean | True when the observation was amended after capture (the original is kept). |
converted | Converted | |
converted.currency requerido | string | |
converted.price requerido | number | |
converted.list_price | number | null | |
converted.card_price | number | null | |
converted.unit_price | number | null | |
converted.fx_rate requerido | number | Units of target currency per unit of source currency. |
converted.fx_date requerido | string (date) | Publication date of the rate used (last published on or before the observation date). |
converted.fx_source | string | |
converted.fx_method | string | Valores / values: direct, cross. |
retailer requerido | RetailerRef | |
retailer.id requerido | string | |
retailer.name requerido | string | |
offer_id requerido | string | |
store_id | string | null | Null when the price is national/online. |
url requerido | string (uri) | |
title_original requerido | string | |
seller | Seller | Who sells the offer. On marketplaces it is a third party; name is null when the seller is a natural person (no personal data). |
seller.id requerido | string | |
seller.name requerido | string | null | |
seller.is_retailer_itself requerido | boolean | |
match requerido | Match | How the offer was linked to the canonical product. |
match.method requerido | string | singleton: the offer matched nothing else and is its own product. Valores / values: gtin, rules, model, manual, singleton. |
match.confidence requerido | number | |
match.kind requerido | string | identical: the same item. pack: the offer is N units of the product (pack_ratio), comparable by unit price. equivalent: a declared substitute of another brand or lab, not the same item. Valores / values: identical, pack, equivalent. |
match.pack_ratio | number | null | Units of the product in the offer, for kind pack. |
Producto (Product)#
| Campo | Tipo | Descripción |
|---|---|---|
id requerido | string | |
name requerido | string | |
brand requerido | BrandRef | |
brand.id requerido | string | |
brand.name requerido | string | |
category requerido | CategoryRef | |
category.id requerido | string | |
category.label requerido | string | |
category.path | string[] | |
size | object | |
gtin | string[] | |
image_url | string | null | |
price_summary requerido | PriceSummary | |
price_summary.currency requerido | string | |
price_summary.min requerido | number | null | |
price_summary.max requerido | number | null | |
price_summary.median | number | null | |
price_summary.offers_count requerido | integer | |
price_summary.retailers_count requerido | integer | |
price_summary.best | object | null | |
price_summary.observed_at | string (date-time) | null | |
price_summary.converted | object | |
score | number | Relevance score, search only. |
names | object | Name in every available language. |
names_meta | object | Provenance of each name, keyed by language. |
private_label | boolean | Store brand of one retailer. |
relations | object[] | Links to other canonical products. |
relations[].kind requerido | string | Valores / values: pack_of, size_of, variant_of, equivalent, replaced_by. |
relations[].product_id requerido | string | |
relations[].quantity_ratio requerido | number | null | For pack_of and size_of, how many units of product_id this product contains. |
identifiers | object | |
identifiers.gtin | string[] | |
identifiers.mpn | string | null | |
attributes | object | |
countries | string[] | |
first_seen | string (date-time) |
Precio condicional (ConditionalPrice)#
| Campo | Tipo | Descripción |
|---|---|---|
kind requerido | string | Valores / values: payment_method, membership, app_only, coupon, other. |
label requerido | string | As shown by the retailer. |
price requerido | number | |
payment_method | string | null | |
membership_tier | string | null | |
installments_count | integer | null | |
interest_free | boolean | null |
Promoción (Promotion)#
| Campo | Tipo | Descripción |
|---|---|---|
type requerido | string | Valores / values: multi_buy, nth_unit, percent_off, amount_off, bundle, gift, other. |
label requerido | string | As shown by the retailer, e.g. "2 x $2.990". |
min_quantity | integer | null | |
effective_unit_price | number | null | Price per item when the promotion applies. |
requires_card | string | null | Payment method required, if any. |
valid_until | string (date) | null |
Atribución (Attribution)#
| Campo | Tipo | Descripción |
|---|---|---|
text requerido | string | |
source requerido | string | Brand name. |
as_of requerido | string (date-time) | |
resource_id requerido | string | null | Product or offer the response is about |
url requerido | string (uri) | null | Public page for the product |
capture_method | string | null | Set when the response is about one offer. Valores / values: public_page, public_api, retailer_feed, open_data. |
source_url | string (uri) | null | Retailer URL of the offer, when the response is about one offer. |
Conversión (Converted)#
| Campo | Tipo | Descripción |
|---|---|---|
currency requerido | string | |
price requerido | number | |
list_price | number | null | |
card_price | number | null | |
unit_price | number | null | |
fx_rate requerido | number | Units of target currency per unit of source currency. |
fx_date requerido | string (date) | Publication date of the rate used (last published on or before the observation date). |
fx_source | string | |
fx_method | string | Valores / values: direct, cross. |
Punto del historial (HistoryPoint)#
| Campo | Tipo | Descripción |
|---|---|---|
t requerido | string | Date (day, week, month start) or timestamp (raw). |
price requerido | number | |
list_price requerido | number | |
sale_price | number | null | |
card_price | number | null | |
availability requerido | string | Valores / values: in_stock, out_of_stock, limited, unknown. |
converted | object | Present when currency is set. Converted with the rate of the point date (or fx_date). |
converted.price | number | |
converted.list_price | number | |
converted.fx_rate | number | |
converted.fx_date | string (date) |
Data model
Three objects explain almost everything:
- Product: the item, the same at every store (
prd_). It has a brand, a category, a size and barcodes (GTIN). - Offer: that product at one store (
ofr_), with its URL and original title. - Observation: what we saw on an offer at one moment: price, conditions, stock and where it came from. Observations are never modified; history is the series of observations.
Price: what each field means#
price is what anyone pays with no conditions: the sale price if there is one, otherwise the list price. Conditional prices come separately, so nobody presents them as "the price".
| Field | What it is |
|---|---|
list_price | Regular published price |
sale_price | Unconditional sale price, or null |
price | The lower of the two: what anyone pays |
conditional_prices[] | Prices that require something other than quantity: payment method, loyalty tier, app or coupon |
card_price, card_label | Shortcut: the lowest price with a card or membership (for example "Tarjeta Cencosud") |
promotions[] | Require a quantity or combination: "2 x CLP 2,590", second unit, percentage |
unit_price | Price per base unit (kg, l, unit, m or m2), to compare sizes |
installments | Interest-free installments on the offer |
availability | Stock: in_stock, out_of_stock, limited or unknown |
Provenance#
Every price says where it came from, so anyone can check it:
| Field | What it is |
|---|---|
url | The product page at the store |
observed_at | Capture date and time, UTC |
observed_local_date | Capture date in the store's time zone |
capture_method | public_page (the public page), public_api (an open endpoint on the site), retailer_feed (provided by the store) or open_data (a public dataset) |
meta.attribution.text | A line ready to quote: brand, store, time and id |
Same product across stores#
Every offer carries match: how it was joined to the product and with what confidence.
| Field | What it is |
|---|---|
match.method | How it was joined: by GTIN, by rules or by a model |
match.confidence | Between 0 and 1 |
match.kind | identical (the same item), pack (N units of the product, see pack_ratio) or equivalent (a substitute from another brand) |
match.pack_ratio | How many units the pack holds |
By default prices and comparisons group only identical offers with confidence of 0.85 or more. min_confidence and match_kinds (for example identical,pack) widen the group, and meta.warnings notes when offers were left out. For a pack, compare by unit_price.
Quality#
Prices are delivered as the store publishes them, never corrected. If a validation sees something odd, the price comes with quality_status: "warning" and the reason in quality_flags[]: discount_over_85 (a discount above 85% of the regular price) or round_price_low (a round price of CLP 1,000 or less). If an observation was amended after capture, corrected is true and the original is kept.
Currency, taxes and language#
- Amounts in the store's currency; CLP has no decimals.
currency=USDaddsconvertedwith the rate, its date and its source. tax_includedsays whether the published price includes taxes (in Chile, it does).tax_rateandprice_ex_taxare derived from the country rule (tax_rule_id), or come asnullif there is no rule.- Names and categories in Spanish or English depending on
lang;namescarries every language andtitle_originalthe store's untranslated title.
Taxonomy#
Categories are a tree with readable ids (food.dairy.milk): filtering by food.dairy includes its subcategories. The full tree is at GET /v1/categories.
Fields#
Price at a store (RetailerPrice)#
| Field | Type | Description |
|---|---|---|
currency required | string | |
price required | number | |
list_price required | number | |
sale_price | number | null | |
card_price | number | null | Convenience: the lowest price in conditional_prices that requires a payment method or membership. |
card_label | string | null | |
conditional_prices | ConditionalPrice[] | Every price that requires a condition other than quantity (payment method, loyalty tier, app, coupon), as published. |
conditional_prices[].kind required | string | Valores / values: payment_method, membership, app_only, coupon, other. |
conditional_prices[].label required | string | As shown by the retailer. |
conditional_prices[].price required | number | |
conditional_prices[].payment_method | string | null | |
conditional_prices[].membership_tier | string | null | |
conditional_prices[].installments_count | integer | null | |
conditional_prices[].interest_free | boolean | null | |
unit_price | object | null | |
unit_price.amount required | number | |
unit_price.per required | string | Valores / values: kg, l, unit, m, m2. |
promotions | Promotion[] | |
promotions[].type required | string | Valores / values: multi_buy, nth_unit, percent_off, amount_off, bundle, gift, other. |
promotions[].label required | string | As shown by the retailer, e.g. "2 x $2.990". |
promotions[].min_quantity | integer | null | |
promotions[].effective_unit_price | number | null | Price per item when the promotion applies. |
promotions[].requires_card | string | null | Payment method required, if any. |
promotions[].valid_until | string (date) | null | |
availability required | string | Valores / values: in_stock, out_of_stock, limited, unknown. |
tax_included | boolean | As shown by the retailer (observed). |
tax_rate | number | null | Derived. Main VAT rate for this country and category, null when not known. |
price_ex_tax | number | null | Derived from price and tax_rate. Null when tax_rate is null. |
tax_rule_id | string | null | Versioned rule used to derive tax_rate. |
other_taxes_included | boolean | null | True when other taxes remain in price_ex_tax (e.g. Chile IABA on sweetened drinks). |
installments | object | |
observed_at required | string (date-time) | |
observed_local_date | string (date) | Capture date in the retailer time zone. Daily series use this date. |
capture_method required | string | How the price was obtained, part of the provenance with url and observed_at. public_page: read from the retailer's public web page (HTML, JSON-LD or the page payload). public_api: an open, unauthenticated JSON endpoint of the retailer's own site. retailer_feed: delivered by the retailer under an agreement. open_data: a public dataset published by a government or third party. Valores / values: public_page, public_api, retailer_feed, open_data. |
quality_status | string | warning when a validation flagged the observation (price jump, zero, unusual unit). Quarantined observations are never served. Valores / values: ok, warning. |
quality_flags | string[] | Why the price was flagged. The price is delivered as published, never corrected. discount_over_85: more than 85% off the list price. round_price_low: a round price of CLP 1,000 or less. Both often mean a placeholder price or a store error. |
corrected | boolean | True when the observation was amended after capture (the original is kept). |
converted | Converted | |
converted.currency required | string | |
converted.price required | number | |
converted.list_price | number | null | |
converted.card_price | number | null | |
converted.unit_price | number | null | |
converted.fx_rate required | number | Units of target currency per unit of source currency. |
converted.fx_date required | string (date) | Publication date of the rate used (last published on or before the observation date). |
converted.fx_source | string | |
converted.fx_method | string | Valores / values: direct, cross. |
retailer required | RetailerRef | |
retailer.id required | string | |
retailer.name required | string | |
offer_id required | string | |
store_id | string | null | Null when the price is national/online. |
url required | string (uri) | |
title_original required | string | |
seller | Seller | Who sells the offer. On marketplaces it is a third party; name is null when the seller is a natural person (no personal data). |
seller.id required | string | |
seller.name required | string | null | |
seller.is_retailer_itself required | boolean | |
match required | Match | How the offer was linked to the canonical product. |
match.method required | string | singleton: the offer matched nothing else and is its own product. Valores / values: gtin, rules, model, manual, singleton. |
match.confidence required | number | |
match.kind required | string | identical: the same item. pack: the offer is N units of the product (pack_ratio), comparable by unit price. equivalent: a declared substitute of another brand or lab, not the same item. Valores / values: identical, pack, equivalent. |
match.pack_ratio | number | null | Units of the product in the offer, for kind pack. |
Product (Product)#
| Field | Type | Description |
|---|---|---|
id required | string | |
name required | string | |
brand required | BrandRef | |
brand.id required | string | |
brand.name required | string | |
category required | CategoryRef | |
category.id required | string | |
category.label required | string | |
category.path | string[] | |
size | object | |
gtin | string[] | |
image_url | string | null | |
price_summary required | PriceSummary | |
price_summary.currency required | string | |
price_summary.min required | number | null | |
price_summary.max required | number | null | |
price_summary.median | number | null | |
price_summary.offers_count required | integer | |
price_summary.retailers_count required | integer | |
price_summary.best | object | null | |
price_summary.observed_at | string (date-time) | null | |
price_summary.converted | object | |
score | number | Relevance score, search only. |
names | object | Name in every available language. |
names_meta | object | Provenance of each name, keyed by language. |
private_label | boolean | Store brand of one retailer. |
relations | object[] | Links to other canonical products. |
relations[].kind required | string | Valores / values: pack_of, size_of, variant_of, equivalent, replaced_by. |
relations[].product_id required | string | |
relations[].quantity_ratio required | number | null | For pack_of and size_of, how many units of product_id this product contains. |
identifiers | object | |
identifiers.gtin | string[] | |
identifiers.mpn | string | null | |
attributes | object | |
countries | string[] | |
first_seen | string (date-time) |
Conditional price (ConditionalPrice)#
| Field | Type | Description |
|---|---|---|
kind required | string | Valores / values: payment_method, membership, app_only, coupon, other. |
label required | string | As shown by the retailer. |
price required | number | |
payment_method | string | null | |
membership_tier | string | null | |
installments_count | integer | null | |
interest_free | boolean | null |
Promotion (Promotion)#
| Field | Type | Description |
|---|---|---|
type required | string | Valores / values: multi_buy, nth_unit, percent_off, amount_off, bundle, gift, other. |
label required | string | As shown by the retailer, e.g. "2 x $2.990". |
min_quantity | integer | null | |
effective_unit_price | number | null | Price per item when the promotion applies. |
requires_card | string | null | Payment method required, if any. |
valid_until | string (date) | null |
Attribution (Attribution)#
| Field | Type | Description |
|---|---|---|
text required | string | |
source required | string | Brand name. |
as_of required | string (date-time) | |
resource_id required | string | null | Product or offer the response is about |
url required | string (uri) | null | Public page for the product |
capture_method | string | null | Set when the response is about one offer. Valores / values: public_page, public_api, retailer_feed, open_data. |
source_url | string (uri) | null | Retailer URL of the offer, when the response is about one offer. |
Conversion (Converted)#
| Field | Type | Description |
|---|---|---|
currency required | string | |
price required | number | |
list_price | number | null | |
card_price | number | null | |
unit_price | number | null | |
fx_rate required | number | Units of target currency per unit of source currency. |
fx_date required | string (date) | Publication date of the rate used (last published on or before the observation date). |
fx_source | string | |
fx_method | string | Valores / values: direct, cross. |
History point (HistoryPoint)#
| Field | Type | Description |
|---|---|---|
t required | string | Date (day, week, month start) or timestamp (raw). |
price required | number | |
list_price required | number | |
sale_price | number | null | |
card_price | number | null | |
availability required | string | Valores / values: in_stock, out_of_stock, limited, unknown. |
converted | object | Present when currency is set. Converted with the rate of the point date (or fx_date). |
converted.price | number | |
converted.list_price | number | |
converted.fx_rate | number | |
converted.fx_date | string (date) |