Product Labels through REST API and MCP
Product Labels write Google Merchant Center custom labels from rules over each product's Google Ads metrics. Through the API and MCP you can read the rule sets, see which label every product carries, try a draft rule set without saving it, follow the label history and check the supplemental feed. Every surface returns the same numbers as the Product Labels page of the admin panel, from the same calculation.
Access
Every endpoint and tool on this page needs the reports:read scope and is read-only. Rule sets are created, changed and switched on in the admin panel, or with the opt-in settings:write scope through the write operations described in Settings API and MCP: save_product_label_rules, apply_product_label_template and refresh_product_labels. Authentication, envelopes, errors and rate limits follow the REST API v1 reference; full schemas are in the OpenAPI document.
How labels are chosen
- Slots: slot 0 to 4 is
custom_label_0tocustom_label_4in Merchant Center. Each slot has at most one rule set. - First match wins: the rules of a set are evaluated in order; a product gets the label of the first rule it matches, otherwise the default label, or no label when there is none.
- Anti-flap: a new label must keep matching for
anti_flap_daysbefore it replaces the published one. Until then it is thecandidate_label.
Operations
| REST | MCP tool | What it answers |
|---|---|---|
GET .../product-labels/rule-sets | list_product_label_rule_sets | The stored rule sets by slot: enabled, default_label, anti_flap_days, the ordered rules and updated_at, plus label_summary: per slot, each published label and how many products carry it as of the latest refresh. |
POST .../product-labels/preview | preview_product_label_rules | Evaluates a draft rule set against today's catalog and stores nothing. |
GET .../product-labels/assignments | list_product_label_assignments | One page of the products of a slot with their 30-day metrics, label and candidate label. |
GET .../product-labels/history | get_product_label_history | The label history of one product or one slot, per day. |
GET .../product-labels/feed | get_product_label_feed | The supplemental feed state, its masked URL and the last sync and refresh. |
Every REST path starts with /api/v1/workspaces/{workspace_id}.
Rule sets
curl -H "Authorization: Bearer $TOKEN" \
"https://axitrace.com/api/v1/workspaces/$WORKSPACE/product-labels/rule-sets"{
"data": {
"rule_sets": [
{
"slot": 0,
"enabled": true,
"default_label": "standard",
"anti_flap_days": 3,
"rules": [{"label": "top_seller", "conditions": [{"field": "roas", "window": "30d", "operator": "gte", "value": 4}]}],
"updated_at": "2026-10-20T08:15:00+00:00"
}
],
"label_summary": [
{
"slot": 0,
"snapshot_date": "2026-10-20",
"total": 992,
"unlabelled": 0,
"labels": [{"label": "standard", "products": 950}, {"label": "top_seller", "products": 42}]
}
]
}
}label_summary lists every slot that has published labels: each label with the number of products that carry it, unlabelled for the products that got no label, as of the slot's latest daily refresh (snapshot_date).
Preview a draft rule set
Send the draft rule set as the JSON body, in the same shape the admin panel saves. Nothing is stored. truncated: true means the scan stopped at its time or product cap after scanned_products of the catalog_products in the catalog, so the counts cover only the products read. An invalid draft returns 422 validation_failed with the paths to fix.
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"slot": 0, "default_label": "standard", "anti_flap_days": 3, "rules": [...]}' \
"https://axitrace.com/api/v1/workspaces/$WORKSPACE/product-labels/preview"{
"data": {
"rule_counts": [{"label": "top_seller", "index": 0, "count": 42}],
"default_count": 950,
"no_label_count": 0,
"total": 992,
"truncated": false,
"scanned_products": 992,
"catalog_products": 992
}
}Assignments
slot: 0 to 4 (default 0).label: only products with this published label, case-insensitive, at most 100 characters.search: only products whose item id or title contains this text, at most 255 characters.labelled_only:truekeeps only products that carry a published label in the slot, or a label waiting out the anti-flap hold (candidate_label). Defaultfalse: every product of the catalog, unlabelled ones with an emptylabel.page(default 1) andper_page(1 to 100, default 50; MCP at most 50). Followlinks.next(MCP:next_page).
truncated: true means the scan stopped at its time or product cap after scanned_products of the catalog_products in the catalog, so total and the order cover only the products read.
curl -H "Authorization: Bearer $TOKEN" \
"https://axitrace.com/api/v1/workspaces/$WORKSPACE/product-labels/assignments?slot=0&label=top_seller"{
"data": {
"rows": [
{"item_id": "SKU-1", "title": "Oak chair", "brand": "Acme", "price": 199.0, "currency": "EUR", "availability": "in_stock",
"impressions": 5400.0, "clicks": 210.0, "cost": 80.0, "conversions": 9.0, "conversion_value": 1791.0, "roas": 22.39,
"label": "top_seller", "candidate_label": null, "candidate_since": null}
],
"total": 42,
"page": 1,
"per_page": 50,
"truncated": false,
"scanned_products": 5416,
"catalog_products": 5416
},
"links": {"next": null}
}History
Pass exactly one of item_id (one product, every slot) or slot (one slot, every product). from and to are UTC days (YYYY-MM-DD), both included, at most 90 days apart; the default is the 30 days ending today. Each day lists label_counts and the products whose label changed (at most 100, changes_truncated beyond that).
curl -H "Authorization: Bearer $TOKEN" \
"https://axitrace.com/api/v1/workspaces/$WORKSPACE/product-labels/history?slot=0&from=2026-10-01&to=2026-10-20"{
"data": {
"request": {"item_id": null, "slot": 0, "from": "2026-10-01", "to": "2026-10-20"},
"days": [
{"date": "2026-10-20", "label_counts": {"top_seller": 42, "standard": 950},
"changes": [{"slot": 0, "item_id": "SKU-1", "from": "standard", "to": "top_seller"}], "changes_truncated": false}
],
"rows_truncated": false
}
}Feed
The feed answer carries masked_url: the feed URL with its secret token shortened to its first and last four characters. The full URL is shown only in the admin panel, to users who may edit Product Labels; the API and MCP never return it.
curl -H "Authorization: Bearer $TOKEN" \
"https://axitrace.com/api/v1/workspaces/$WORKSPACE/product-labels/feed"{
"data": {
"state": "live",
"masked_url": "https://axitrace.com/feeds/product-labels/abcd...6789.xml",
"catalog_source": "xml_url",
"xml_host": "shop.example.com",
"last_catalog_sync_at": "2026-10-20T03:00:00+00:00",
"last_catalog_sync_status": "success",
"last_refresh_at": "2026-10-20T04:00:00+00:00",
"last_refresh_status": "success",
"latest_snapshot_date": "2026-10-20",
"item_count": 992
}
}state is not_configured, exact_ids_required, no_snapshot or live.
Errors
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | A parameter is outside the ranges above, or a date is not YYYY-MM-DD; the message names it. |
| 403 | insufficient_scope | The token has no reports:read. |
| 403 | plan_required | Product Labels are not included in the plan of the workspace. |
| 404 | workspace_not_found | The workspace does not exist or the token may not read it. |
| 422 | validation_failed | The draft rule set of a preview is invalid. |
| 429 | rate_limited | Too many calls; retry after the time in Retry-After. |
| 503 | temporarily_unavailable | The metrics could not be read right now; retry in a minute. |