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

  1. Go to Websites in the left sidebar.
  2. Click the name of your website to open the Organization Website Detail page.
  3. In the detail table, find the Data row and click the JSON link.

URL Structure

https://storage.googleapis.com/force-spike-prod.appspot.com/organization-websites/{website_id}/competitive-data/{api_key}.json
SegmentDescription
{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.
Security: The API key is embedded in the URL. Anyone who knows the URL can read the file. Do not share it publicly or commit it to version control. Contact support to rotate a compromised key.

File Characteristics

PropertyValue
FormatJSON, UTF-8
Root typeArray β€” one element per matched product (identified by GTIN)
Update cadenceRegenerated on each successful price monitoring run (frequency is configured per competitor)
ScopeAll products in your feed that have at least one active competitor or aggregator match
Empty stateReturns [] when no matched data exists yet
AccessHTTPS 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.

[  // Root array β€” one element per product (GTIN)
{
"id": string  // GTIN of your product
"rank": integer  // Your price rank across all matched competitors
"suggestions": { "price": number|null } // Price Suggestion output
"products": [ // All competitor & aggregator matches
{  // ← Direct competitor entry  (offers: null)
{  // ← Aggregator entry        (offers: [...])
]
},
{ … }
]

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 offersRecord type
nullDirect 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

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

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:

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.

BigQuery is an optional add-on β€” it must be enabled per organisation by the Coolpie.ai team. The BigQuery column on the Organizations page shows whether it is currently active. It also powers the Data Insights reports inside the platform.

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:

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.

Dataset naming: The dataset ID matches your Coolpie website ID β€” visible in the URL when you open the Website Detail page in the Admin section. In the SQL examples below, {website_id} is a placeholder for that value, and {your_gcp_project} is your Google Cloud project ID.

Tables come in two variants:

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.

ColumnBQ TypeDescription
timestampTIMESTAMPTime of the monitoring run (UTC)
your_idSTRINGYour product's internal Coolpie ID
your_brandSTRINGBrand from your product feed. Nullable.
your_categorySTRINGCategory from your product feed. Nullable.
your_priceDECIMALYour product's price at the time of this snapshot. Nullable.
your_rankINTEGERPrice rank among all matched direct competitors. Nullable.
competitor_idSTRINGInternal competitor ID. Join with COMPETITORS_V1 to get the domain name.
competitor_priceDECIMALCompetitor'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.

ColumnBQ TypeDescription
timestampTIMESTAMPTime of the monitoring run (UTC)
your_product_idSTRINGYour product's internal Coolpie ID
your_product_nameSTRINGProduct name from your feed
your_priceDECIMALYour current price
your_price_rankINTEGERPrice rank among all matched competitors. Nullable.
your_brandSTRINGBrand from your feed. Nullable.
your_categorySTRINGCategory from your feed. Nullable.
recommended_priceDECIMALPrice Suggestion output for this product. Nullable.
competitor_lowest_priceDECIMALLowest price found across all matched competitors. Nullable.
competitor_median_priceDECIMALMedian competitor price. Nullable.
competitor_median_differenceDECIMALYour price minus the median competitor price. Nullable.
competitor_average_priceDECIMALAverage 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.

ColumnBQ TypeDescription
timestampTIMESTAMPTime of the monitoring run (UTC)
aggregator_idSTRINGInternal aggregator ID. Join with AGGREGATORS_V1 to get the domain name.
your_product_idSTRINGYour product's internal Coolpie ID
seller_idSTRINGInternal seller identifier as tracked on the aggregator
seller_priceDECIMALSeller'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.

ColumnBQ TypeDescription
timestampTIMESTAMPTime of the monitoring run (UTC)
idSTRINGYour product's internal Coolpie ID
nameSTRINGProduct name from your feed
brandSTRINGBrand. Nullable.
categorySTRINGCategory. Nullable.
rankINTEGERPrice rank among direct competitors. Nullable.
priceDECIMALYour current price. Nullable.
lowest_priceDECIMALLowest price across matched competitors. Nullable.
median_priceDECIMALMedian 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.

ColumnBQ TypeDescription
timestampTIMESTAMPTime this record was written
competitor_id / aggregator_idSTRINGInternal ID β€” matches the same field in the product tables
competitor_name / aggregator_nameSTRINGHuman-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;
Getting access: To enable BigQuery for your organisation, contact the Coolpie.ai support team. You will need to grant the Coolpie.ai service account write access to a dataset in your Google Cloud project.