CopyHero

Get Cluster Products

GET
/api/v1/watchlist/universe/clusters/{cluster_key}/products

List the watchlist products in one active-layout cluster across ALL of the viewer's subscribed stores — ONE row per vendor (a product watched via N stores yields N rows), plus the cluster's cluster_label/cluster_color (None when cluster_key isn't in the active layout). No subscription / no active layout → both None and empty data (count 0), never a 404.

Authorization

HTTPBearer
headerAuthorizationBearer <token>

Path Parameters

cluster_key*Cluster Key

Query Parameters

skip?Skip
Range0 <= value
Default0
limit?Limit
Range1 <= value <= 200
Default100
sort_by?string|null
sort_order?Sort Order
Default"desc"
q?string|null
stores?array<string>|

Competitor storefront URLs to narrow to. Narrows within the subscription gate; never widens it.

window_days?Window Days

Days scaling_units_per_day looks back when the list has no date range. With published_after/published_before it compares the bestseller snapshots of that range instead, else those of created_after/created_before.

Range1 <= value
Default30
tags?array<string>|

Only products carrying any of these tags (head state).

vendor?array<string>|

Only products with any of these vendors (head state).

product_type?array<string>|

Only products with any of these types (head state).

published_after?|

Only products the competitor published at/after this time

published_before?|

Only products the competitor published at/before this time

created_after?|

Only products the competitor created at/after this time (Shopify created_at; products without it drop out)

created_before?|

Only products the competitor created at/before this time (Shopify created_at; products without it drop out)

sales_estimate?array<>|

Only products whose sales_estimate compares true, written <op>:<n>: op is eq, gt, gte, lt or lte, n a non-negative integer. gt:0 = sold above the tail; eq:0 = in the tail; also e.g. gte:10. The estimate is compared rounded to a whole number. A null sales_estimate never matches, under any operator. Lags real sales by days. Repeat the param to bound a range (gte:5, lte:10).

revenue_estimate?array<>|

Only products whose revenue_estimate compares true in revenue_currency, written <op>:<n>: op is eq, gt, gte, lt or lte, n a non-negative decimal. Revenue is converted from the store's currency at the latest ECB rate; a null revenue, or a store whose currency the ECB does not quote, never matches. Repeat the param to bound a range.

revenue_currency?EcbCurrency

Currency of the revenue_estimate filter and sort, an ECB-quoted ISO 4217 code. The list items keep their revenue in the store's currency.

Default"EUR"
Value in"EUR""USD""JPY""CZK""DKK""GBP""HUF""PLN""RON""SEK""CHF""ISK""NOK""TRY""AUD""BRL""CAD""CNY""HKD""IDR""ILS""INR""KRW""MXN""MYR""NZD""PHP""SGD""THB""ZAR"
bestseller_top_percent?|

Only products whose bestseller_top_percent compares true, written <op>:<n>: op is eq, gt, gte, lt or lte, n a non-negative integer, e.g. lte:5 = in the top 5 % of their store's sold products (tail_start - 1). A null bestseller_top_percent never matches, so a product in the list's last group matches no operator; fewer than 100 sold products have no top 1 %.

scaling_units_per_day?|

Only products whose scaling_units_per_day compares true, written <op>:<n>: op is eq, gt, gte, lt or lte, n a non-negative decimal, e.g. gt:0 = climbed at all, gte:0.5. A null scaling_units_per_day never matches.

scaling_overtake_percent?array<>|

Only products whose scaling_overtake_percent compares true, written <op>:<n>: op is eq, gt, gte, lt or lte, n a non-negative decimal, e.g. gte:10 = overtook at least 10 % of the store's sold products. A null scaling_overtake_percent never matches. With scaling_units_per_day both apply. Repeat the param to bound a range.

price?array<>|

Only products whose price (first variant, in the store's currency) compares true. A list of <op>:<n>, op eq, gt, gte, lt or lte; every value must hold (AND). A null price never matches.

category_group?array<>|

Only products in any of these category groups, written in:<group_label>.

category_label?array<>|

Category labels, written in:<label> (any of them) and/or not_in:<label> (none of them; unclassified products pass). With category_group a product matches either one.

imported_store_ids?array<>|

Imported into each of these stores (in:<store id>, a bare id means in) and into none of not_in:<store id>; every value must hold (AND).

first_sale_at?array<>|

The first sale of the product, written <op>:<ISO 8601>; every value must hold (AND). A naive time is UTC.

watched_store_ids?array<string>|

Watched stores, written in:<id> (only these; a bare id means in) or not_in:<id> (every subscribed store but these); one operator per request. Narrows within the subscription gate; never widens it.

store_tags?array<string>|

Stores the viewer tagged, written in:<tag> (any of them; no prefix means in) or not_in:<tag> (none of them); one operator per request. Named apart from tags, which means PRODUCT tags. Narrows within the subscription gate; never widens it.

store_product_count?array<>|

The store's product count. A list of <op>:<n>, op eq, gt, gte, lt or lte; every value must hold (AND).

bestseller_sold_count?array<>|

The store's bestseller_sold_count. A list of <op>:<n>, op eq, gt, gte, lt or lte; every value must hold (AND). A store without a bestseller snapshot never matches.

store_currency?array<>|

Only stores selling in any of these ISO 4217 currencies, written in:<code>.

store_market?array<>|

Only stores that ship to any of these ISO 3166 alpha-2 countries (their meta.json ships_to_countries), written in:<code>, e.g. in:DE. A store not scanned since the field exists never matches.

Response Body

Successful Response

application/json
  1. response

Products in one active-layout cluster (the cross-vendor drill-down of the GET /clusters/{cluster_key}/products endpoint), plus the cluster's own name/color looked up from LayoutCluster so the client no longer has to pass the label via a URL param. cluster_label/cluster_color are None when cluster_key isn't in the active layout (stale link / invalid key). No subscription / no active layout → both None and empty data (count 0), never a 404.

cluster_label*string|null
cluster_color*string|null
data*array<>
count*Count
Example Request
curl -X GET 'https://app.copyhero.app/api/v1/watchlist/universe/clusters/0/products'
Example Response
{  "cluster_label": "string",  "cluster_color": "string",  "data": [    {      "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",      "watched_store_id": "e604763d-50a8-4edb-b056-d359e3c6cc2f",      "store_name": "string",      "store_url": "string",      "title": "string",      "handle": "string",      "vendor": "string",      "product_type": "string",      "external_handle": "string",      "external_published_at": "2019-08-24T14:15:22Z",      "external_created_at": "2019-08-24T14:15:22Z",      "bestseller_rank": 0,      "bestseller_list_size": 0,      "bestseller_sold_count": 0,      "bestseller_top_percent": 0,      "sales_estimate": 0,      "revenue_estimate": 0,      "revenue_currency": "string",      "revenue_converted": 0,      "revenue_converted_currency": "string",      "scaling_units_per_day": 0,      "scaling_days": 0,      "scaling_method": "F3",      "scaling_overtake_percent": 0,      "is_new": true,      "tags": [        "string"      ],      "images": [        {          "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",          "src": "string",          "position": 0,          "alt_text": "string",          "thumbnail_url": "string",          "blur_data_url": "string"        }      ],      "source_url": "string",      "price": 0,      "price_min": 0,      "category_group": "string",      "category_label": "string",      "imported_store_ids": [        0      ],      "first_sale_at": "2019-08-24T14:15:22Z",      "store_product_count": 0,      "store_currency": "string",      "store_country": "string",      "store_language": "string"    }  ],  "count": 0}