Data Outputs
Coolpie.ai exposes its competitive pricing data for external consumption via a JSON export file and Google BigQuery. This page documents both formats in detail.
JSON Export
Each website has a dedicated JSON file that is regenerated on every price monitoring cycle. The file contains competitive pricing data for all matched products in your catalogue β grouped by product, with all competitor and aggregator matches nested inside each entry.
Finding the JSON URL
- Go to Websites in the left sidebar.
- Click the name of your website to open the Organization Website Detail page.
- In the detail table, find the Data row and click the JSON link.
URL Structure
| Segment | Description |
|---|---|
{website_id} | Unique ID of the website β visible in the URL when you open the Website Detail page. |
{api_key} | Your website's API key, shown as API Key on the Website Detail page. The key is the only access control β keep it confidential. |
File Characteristics
| Property | Value |
|---|---|
| Format | JSON, UTF-8 |
| Root type | Array β one element per matched product (identified by GTIN) |
| Update cadence | Regenerated on each successful price monitoring run (frequency is configured per competitor) |
| Scope | All products in your feed that have at least one active competitor or aggregator match |
| Empty state | Returns [] when no matched data exists yet |
| Access | HTTPS GET, no additional headers required |
JSON Structure Overview
The file is a flat JSON array. Each element represents one of your products, identified by its GTIN. All competitor and aggregator matches for that product are grouped inside a nested products array within that element.
The key structural point is that there is no type field at the top level. The distinction between a direct competitor and an aggregator is determined by looking at the offers field inside each products entry:
Value of offers | Record type |
|---|---|
null | Direct competitor β single retailer, one price in price.amount |
[ β¦ ] (array) | Aggregator β comparison site or marketplace; multiple sellers listed in the array |
Top-level Product Entry
One entry in the root array, representing a single product from your catalogue.
{
"id": "9788086686059", // Your product's GTIN (EAN-13 / UPC-12)
"rank": 2, // Your price rank (1 = cheapest among all matched competitors)
"suggestions": {
"price": null // Recommended price from your active Price Suggestion rule,
// or null if no rule applies to this product
},
"products": [ β¦ ] // Array of matched competitor / aggregator entries (see below)
}
Top-level field reference
idGTIN of your product as supplied in the product feed (EAN-13, UPC-12, or equivalent). String.rankYour current price rank across all matched direct competitors for this product.1means you have the lowest price. Integer.nullor absent when no competitor prices are available.suggestions.priceThe recommended price calculated by the active Price Suggestion rule for this product. Number (float), ornullif no rule applies or the rule produced no output.productsArray of matched entries. Each element is either a direct competitor entry or an aggregator entry β distinguished by whetheroffersisnullor an array. May be an empty array[]if no matches have been confirmed yet.
products[] entry β Direct Competitor
A direct competitor is a single retailer with one price for this product. The offers field is null. The price lives in price.amount.
{
"title": "Example Product Alpha 60 caps", // Product title on the competitor's site
"website": {
"domain": "example-shop.com" // Competitor domain
},
"price": {
"amount": 49.90, // Competitor's current price (numeric, in local currency)
"beforeDiscount": null, // Original price before any discount, if available
"withShipping": null, // Price including shipping, if available
"custom": null, // Custom price field (platform-specific use)
"currency": "", // Currency code β may be empty if inherited from website config
"lastUpdatedAt": "2026-01-15T08:00:00.000" // ISO 8601 timestamp of last price update (no timezone = UTC)
},
"offers": null, // null β this is a direct competitor, not an aggregator
"seller": null // Reserved; currently always null for direct competitors
}
Field reference β direct competitor entry
titleThe product's title as it appears on the competitor's website. String.website.domainDomain of the competitor site (e.g."example-shop.com"). String.price.amountThe competitor's current selling price. Number (integer or float).nullonly for aggregator entries where pricing is per-offer.price.beforeDiscountThe original price before a promotional discount, if the competitor displays one. Number ornull.price.withShippingTotal price including shipping cost, if available from the competitor's site. Number ornull.price.customAn additional custom price field available for platform-specific use cases. Number ornull.price.currencyISO 4217 currency code for this price. String. May be an empty string""when the currency is inherited from the website's global configuration.price.lastUpdatedAtTimestamp of the most recent price update for this competitor entry. ISO 8601 string without timezone suffix β treat as UTC. String.offersnullfor direct competitors. An array for aggregator entries β see the next section.sellerReserved field. Currently alwaysnull.
products[] entry β Aggregator
An aggregator is a price-comparison site or marketplace (such as Heureka, Google Shopping, or Amazon) that lists multiple sellers for the same product. For aggregator entries, price.amount is null and the per-seller pricing data is in the offers array. On marketplace platforms, this also covers the Buy Box β the featured seller displayed to shoppers.
{
"title": "Example Product Beta 10 ml",
"website": {
"domain": "example-aggregator.com"
},
"price": {
"amount": null, // Always null for aggregators β use offers[].priceAmount
"beforeDiscount": null,
"withShipping": null,
"custom": null,
"currency": null,
"lastUpdatedAt": "2026-01-15T12:00:00.000"
},
"offers": [ // One entry per seller listed on the aggregator
{
"name": "", // Offer label (often empty β seller name is in "seller")
"seller": "Seller A", // Seller display name on the aggregator
"priceAmount": 29.90, // Seller price (convenience copy of price.amount)
"priceCurrency": "EUR", // Currency (convenience copy of price.currency)
"priceBeforeDiscount": null,
"priceWithShipping": null,
"priceCustom": null,
"price": { // Full price object (mirrors top-level price structure)
"amount": 29.90,
"currency": "EUR",
"beforeDiscount": null,
"withShipping": null,
"custom": null
}
},
{
"name": "",
"seller": "Seller B",
"priceAmount": 24.50,
"priceCurrency": "EUR",
"priceBeforeDiscount": null,
"priceWithShipping": null,
"priceCustom": null,
"price": {
"amount": 24.50,
"currency": "EUR",
"beforeDiscount": null,
"withShipping": null,
"custom": null
}
}
// β¦ further sellers omitted for brevity
],
"seller": null
}
Field reference β aggregator entry
The title, website, price, and seller fields follow the same rules as in a direct competitor entry, with the following differences:
price.amountAlwaysnullfor aggregator entries. Per-seller prices are inoffers[].priceAmountinstead.price.currencyAlwaysnullat this level for aggregators. Currency is provided per offer inoffers[].priceCurrency.offersArray of seller offer objects. One element per seller listed on the aggregator for this product. Sorted by ascending price where available. Never empty β if an aggregator page has no sellers, the entry is omitted from the file.offers[].nameOffer label as shown by the aggregator. Often an empty string β the seller's identity is inoffers[].seller. String.offers[].sellerDisplay name of the seller as shown on the aggregator (e.g."Seller A","Seller B"). String.offers[].priceAmountThe seller's price as a plain number β convenience copy ofoffers[].price.amount. Number.offers[].priceCurrencyCurrency code β convenience copy ofoffers[].price.currency. String.offers[].priceBeforeDiscountOriginal price before discount, if provided. Number ornull.offers[].priceWithShippingPrice including shipping, if provided. Number ornull.offers[].priceCustomCustom price field. Number ornull.offers[].priceFull price object mirroring the top-levelpricestructure β containsamount,currency,beforeDiscount,withShipping,custom. The flatpriceAmount/priceCurrencyfields are identical convenience copies of the values inside this object.
Identifying record type in code
Check the offers field of each entry inside products to determine whether it is a direct competitor or an aggregator:
// JavaScript
const records = await fetch('https://storage.googleapis.com/β¦/{api_key}.json').then(r => r.json());
for (const product of records) {
const gtin = product.id;
const rank = product.rank;
const suggestedPrice = product.suggestions.price;
for (const match of product.products) {
if (match.offers === null) {
// Direct competitor
console.log(match.website.domain, match.price.amount);
} else {
// Aggregator β iterate sellers
for (const offer of match.offers) {
console.log(match.website.domain, offer.seller, offer.priceAmount);
}
}
}
}
# Python
import requests
records = requests.get('https://storage.googleapis.com/β¦/{api_key}.json').json()
for product in records:
gtin = product['id']
rank = product['rank']
suggested_price = product['suggestions']['price']
for match in product['products']:
if match['offers'] is None:
# Direct competitor
print(match['website']['domain'], match['price']['amount'])
else:
# Aggregator β iterate sellers
for offer in match['offers']:
print(match['website']['domain'], offer['seller'], offer['priceAmount'])
Google BigQuery
For organisations that need historical trend analysis, large-scale data querying, or integration with BI tools (Looker Studio, Power BI, Tableau), Coolpie.ai offers a Google BigQuery integration. When enabled, competitive pricing data is continuously streamed into a BigQuery dataset, creating a queryable, time-series record of all competitor and aggregator prices.
What BigQuery adds over the JSON export
The JSON export reflects only the current state of prices. BigQuery retains the full historical record, enabling you to:
- Query price changes over time for any product, competitor, or aggregator.
- Build trend dashboards in Looker Studio, Power BI, or Tableau.
- Run SQL aggregations across millions of price points without custom infrastructure.
- Join competitive pricing data with your own sales or margin data for profitability analysis.
Dataset structure
Data is written to a single dataset named by your website ID inside your Google Cloud project. The dataset contains several tables split by type and retention policy.
{website_id} is a placeholder for that value, and {your_gcp_project} is your Google Cloud project ID.
Tables come in two variants:
- _ACTUAL β current state only. Truncated and re-written on every monitoring run. Use for dashboards that always show present prices.
- _SNAPSHOT β append-only historical record. Every monitoring run adds new rows. Use for trend analysis and time-series queries.
Table reference
COMPETITOR_PRODUCT_FULL_ACTUAL / COMPETITOR_PRODUCT_FULL_SNAPSHOT
One row per matched productβcompetitor pair. _ACTUAL always reflects the latest monitoring run; _SNAPSHOT accumulates all historical records.
| Column | BQ Type | Description |
|---|---|---|
timestamp | TIMESTAMP | Time of the monitoring run (UTC) |
your_id | STRING | Your product's internal Coolpie ID |
your_brand | STRING | Brand from your product feed. Nullable. |
your_category | STRING | Category from your product feed. Nullable. |
your_price | DECIMAL | Your product's price at the time of this snapshot. Nullable. |
your_rank | INTEGER | Price rank among all matched direct competitors. Nullable. |
competitor_id | STRING | Internal competitor ID. Join with COMPETITORS_V1 to get the domain name. |
competitor_price | DECIMAL | Competitor's price for this product at the time of this snapshot. |
COMPETITOR_PRODUCTS_LIGHT_ACTUAL / COMPETITOR_PRODUCTS_LIGHT_SNAPSHOT
One row per product β aggregated market statistics without the per-competitor breakdown. Lighter to query for product-level dashboards.
| Column | BQ Type | Description |
|---|---|---|
timestamp | TIMESTAMP | Time of the monitoring run (UTC) |
your_product_id | STRING | Your product's internal Coolpie ID |
your_product_name | STRING | Product name from your feed |
your_price | DECIMAL | Your current price |
your_price_rank | INTEGER | Price rank among all matched competitors. Nullable. |
your_brand | STRING | Brand from your feed. Nullable. |
your_category | STRING | Category from your feed. Nullable. |
recommended_price | DECIMAL | Price Suggestion output for this product. Nullable. |
competitor_lowest_price | DECIMAL | Lowest price found across all matched competitors. Nullable. |
competitor_median_price | DECIMAL | Median competitor price. Nullable. |
competitor_median_difference | DECIMAL | Your price minus the median competitor price. Nullable. |
competitor_average_price | DECIMAL | Average competitor price. Nullable. |
AGGREGATOR_PRODUCT_ACTUAL / AGGREGATOR_PRODUCT_SNAPSHOT
One row per aggregatorβproductβseller combination. Use this table to track aggregator pricing over time.
| Column | BQ Type | Description |
|---|---|---|
timestamp | TIMESTAMP | Time of the monitoring run (UTC) |
aggregator_id | STRING | Internal aggregator ID. Join with AGGREGATORS_V1 to get the domain name. |
your_product_id | STRING | Your product's internal Coolpie ID |
seller_id | STRING | Internal seller identifier as tracked on the aggregator |
seller_price | DECIMAL | Seller's price on this aggregator at the time of the snapshot |
PRODUCTS_V1
One row per product per monitoring run β a summary of your catalogue with aggregated market pricing. Useful as a base table for portfolio-level reports.
| Column | BQ Type | Description |
|---|---|---|
timestamp | TIMESTAMP | Time of the monitoring run (UTC) |
id | STRING | Your product's internal Coolpie ID |
name | STRING | Product name from your feed |
brand | STRING | Brand. Nullable. |
category | STRING | Category. Nullable. |
rank | INTEGER | Price rank among direct competitors. Nullable. |
price | DECIMAL | Your current price. Nullable. |
lowest_price | DECIMAL | Lowest price across matched competitors. Nullable. |
median_price | DECIMAL | Median price across matched competitors. Nullable. |
COMPETITORS_V1 / AGGREGATORS_V1
Lookup tables mapping internal IDs to human-readable names. Join these with the product tables to resolve competitor and aggregator domain names in your queries.
| Column | BQ Type | Description |
|---|---|---|
timestamp | TIMESTAMP | Time this record was written |
competitor_id / aggregator_id | STRING | Internal ID β matches the same field in the product tables |
competitor_name / aggregator_name | STRING | Human-readable domain name |
Example SQL Queries
Products where you are not the cheapest (current state)
SELECT
your_product_id,
your_product_name,
your_brand,
your_price,
competitor_lowest_price,
your_price_rank
FROM `{your_gcp_project}.{website_id}.COMPETITOR_PRODUCTS_LIGHT_ACTUAL`
WHERE your_price_rank > 1
AND competitor_lowest_price IS NOT NULL
ORDER BY (your_price - competitor_lowest_price) DESC;
Price trend for one product over 30 days (direct competitors)
SELECT
DATE(f.timestamp) AS date,
c.competitor_name,
f.competitor_price,
f.your_price,
f.your_rank
FROM `{your_gcp_project}.{website_id}.COMPETITOR_PRODUCT_FULL_SNAPSHOT` f
JOIN (
SELECT competitor_id, competitor_name
FROM `{your_gcp_project}.{website_id}.COMPETITORS_V1`
QUALIFY ROW_NUMBER() OVER (PARTITION BY competitor_id ORDER BY timestamp DESC) = 1
) c ON c.competitor_id = f.competitor_id
WHERE f.your_id = 'your_internal_product_id'
AND f.timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
ORDER BY date, c.competitor_name;
Cheapest aggregator seller per product (current state)
SELECT
a.aggregator_name,
ap.your_product_id,
ap.seller_id,
MIN(ap.seller_price) AS lowest_price
FROM `{your_gcp_project}.{website_id}.AGGREGATOR_PRODUCT_ACTUAL` ap
JOIN (
SELECT aggregator_id, aggregator_name
FROM `{your_gcp_project}.{website_id}.AGGREGATORS_V1`
QUALIFY ROW_NUMBER() OVER (PARTITION BY aggregator_id ORDER BY timestamp DESC) = 1
) a ON a.aggregator_id = ap.aggregator_id
GROUP BY a.aggregator_name, ap.your_product_id, ap.seller_id
ORDER BY lowest_price ASC;