Competitor Products Gallery For Agent
Bounded agent page over subscribed competitor products; the UI route keeps
its larger page_size range.
HTTPBearerAuthorizationBearer <token>keyword*Keyword1 <= length <= 200cursor?Cursor0-based Qdrant rank to start at: pass the previous page's next_cursor.
0 <= value0page_size?Page Size1 <= value <= 10050vendor?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?EcbCurrencyCurrency of the revenue_estimate filter and sort, an ECB-quoted ISO 4217 code. The list items keep their revenue in the store's currency.
"EUR""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_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.
Successful Response
application/json- response
GET /product-research/competitor-products — one cursor-paginated
gallery page of the competitor products behind the competitor-trends
counts.
Rank-cursor block search over the watchlist corpus (see
app/service/competitor_products.py), not a relevance-band cutoff —
there is no finite in-band total, so this carries no total_matches. No
internal_product_id leaves this router — the watchlist-universe router
stays the single deliberate internal-id endpoint.
keyword*Keyworditems?array<>next_cursor?integer|nullhas_more?Has Morefalsecurl -X GET 'https://app.copyhero.app/api/v1/product-research/agent/competitor-products?keyword=string'{ "keyword": "string", "items": [ { "watched_product_id": "00dd722f-4592-4d63-b257-10210ae59f93", "external_product_id": "string", "title": "string", "image_url": "string", "source_image_url": "string", "product_url": "string", "price": 0, "store_host": "string", "published_at": "2019-08-24T14:15:22Z", "bestseller_rank": 0, "sales_estimate": 0, "bestseller_list_size": 0, "bestseller_sold_count": 0, "bestseller_top_percent": 0, "thumbnail_url": "string", "blur_data_url": "string" } ], "next_cursor": 0, "has_more": false}Set Monday Sync Group POST
Previous Page
Competitor Trends Graph GET
Competitor publish rate for ``keyword`` over the same last-90-days window as ``/trends`` — 13 weekly buckets of how many semantically matching products the caller's SUBSCRIBED watched stores listed. Aggregates only (no product ids); no subscription → an all-zero response, never an error.