{
  "openapi": "3.0.1",
  "info": {
    "title": "Catalog Matcher: Competitor Product Matching & Price Gaps",
    "description": "Match your product catalogue against competitors' by barcode (GTIN/UPC/EAN), MPN, SKU, model number or title, with match method and confidence per pair, price gap %, and the products only one side sells. Reads Shopify, any-store and Amazon datasets or your own rows.",
    "version": "0.1",
    "x-build-id": "DiSzxO8n6JZF5xZhq"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/cleanpull~catalog-matcher/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-cleanpull-catalog-matcher",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/cleanpull~catalog-matcher/runs": {
      "post": {
        "operationId": "runs-sync-cleanpull-catalog-matcher",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/cleanpull~catalog-matcher/run-sync": {
      "post": {
        "operationId": "run-sync-cleanpull-catalog-matcher",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "properties": {
          "yourDatasetId": {
            "title": "Your products (dataset)",
            "type": "string",
            "description": "A dataset of your products: a run of Shopify Store Scraper, Any Store Product Monitor or Amazon Product Scraper, or any dataset with title, brand, price and (best) barcode, MPN or SKU fields. Pick it here so this actor is given read access to it; it never sees your other storages."
          },
          "yourItems": {
            "title": "Your products (rows)",
            "type": "array",
            "description": "Or give your products as rows: title, brand, price, currency, url, and where you have them gtin (UPC/EAN), mpn and sku. Used together with Your products (dataset) if both are given."
          },
          "yourStore": {
            "title": "Your store",
            "type": "string",
            "description": "Optional. Your store's domain, for example mystore.com. With Competitor stores and no other catalogue of yours, your store is read with Shopify Store Scraper in this run too. When your products are in a competitor dataset (one Shopify Store Scraper run over your store and theirs), it tells yours apart. Without any catalogue of yours, the first store in the first competitor dataset counts as yours."
          },
          "competitorStores": {
            "title": "Competitor stores (Shopify)",
            "maxItems": 10,
            "type": "array",
            "description": "Optional. Up to 10 Shopify stores by domain or URL, for example rival.com. Each is read in this run by Shopify Store Scraper, started in your account and billed at its own prices ($0.80 per 1,000 products on the Free tier), then compared with your catalogue. With a cost limit, each read gets its share of it. A store that cannot be read in full is reported and left out; the others still compare.",
            "items": {
              "type": "string"
            }
          },
          "competitorDatasetIds": {
            "title": "Competitor products (datasets)",
            "type": "array",
            "description": "One or more datasets of competitor products from the same actors. Each store in them is matched as its own catalogue (a dataset of three stores gives three comparisons)."
          },
          "competitorItems": {
            "title": "Competitor products (rows)",
            "type": "array",
            "description": "Or competitor products as rows, with the same fields plus store (rows of one store are one catalogue; without it, the URL's domain)."
          },
          "titleMatching": {
            "title": "Match by title",
            "type": "boolean",
            "description": "After barcode, part number, SKU and model number: match the remaining products by title within the same brand (token-set ratio). Off: only code matches.",
            "default": true
          },
          "minTitleScore": {
            "title": "Title score threshold",
            "minimum": 50,
            "maximum": 100,
            "type": "integer",
            "description": "Lowest token-set ratio (0–100) for a title match. 90 kept false matches at 2.7% of 146 hand-checked pairs, all below 0.85 confidence; lower finds more and risks more.",
            "default": 90
          },
          "titleWithoutBrand": {
            "title": "Title match without a brand",
            "type": "boolean",
            "description": "Also match by title when one side gives no brand (needs a score of 95 or more; confidence at most 0.75).",
            "default": false
          },
          "minConfidence": {
            "title": "Lowest confidence",
            "minimum": 0,
            "maximum": 1,
            "type": "number",
            "description": "Pairs below this confidence (0–1) are not matches. Barcodes are 1, part numbers 0.95, SKUs 0.9, model numbers 0.8–0.95, titles up to 0.9.",
            "default": 0.6
          },
          "gaps": {
            "title": "Gaps",
            "enum": [
              "both",
              "they-sell",
              "you-sell",
              "none"
            ],
            "type": "string",
            "description": "Which unmatched products to return as gap rows (each one billed).",
            "default": "both"
          },
          "gapsInYourBrandsOnly": {
            "title": "Gaps in your brands only",
            "type": "boolean",
            "description": "Only return products they sell and you don't from brands you already carry: the ranges you could add first, and far fewer rows than every product of a large store. Off: every product they sell that you don't.",
            "default": true
          },
          "monitorName": {
            "title": "Watchlist name",
            "type": "string",
            "description": "Optional. Runs with the same name compare with each other: each row says what changed since the last run (new match, price moves, new gap), and pairs or gaps gone for two runs in a row are reported once."
          },
          "changesOnly": {
            "title": "Changes only",
            "type": "boolean",
            "description": "With a watchlist name: return only rows that changed since the last run. Unchanged pairs and gaps are billed as the small checked event.",
            "default": false
          },
          "minPriceChangePercent": {
            "title": "Smallest price change (%)",
            "minimum": 0,
            "maximum": 100,
            "type": "number",
            "description": "Ignore price moves smaller than this percentage.",
            "default": 0
          },
          "alertUndercutPercent": {
            "title": "Undercut alert (%)",
            "minimum": 0,
            "maximum": 100,
            "type": "number",
            "description": "Optional. Mark pairs where the competitor is at least this much cheaper than you (0: any amount) with undercut: true. With a watchlist name, a pair that newly crosses it gets an undercut change (also with Changes only), and each summary counts undercuts and new undercuts. Off by default."
          },
          "maxItemsPerCatalogue": {
            "title": "Max products per catalogue",
            "minimum": 1,
            "maximum": 200000,
            "type": "integer",
            "description": "At most this many products read from your catalogue and from each competitor's (default 50,000, which runs in the default 256 MB). For up to 200,000 a side, give 1 GB. A catalogue cut short gives no gap rows, since products not read would show as gaps.",
            "default": 50000
          },
          "maxRunCostUsd": {
            "title": "Max cost per run (USD)",
            "minimum": 0.01,
            "maximum": 1000,
            "type": "number",
            "description": "Optional. The most one run may charge in all. Use it for scheduled runs, which may have no spending limit of their own. With a run spending limit too, the lower one applies. With Competitor stores it covers their Shopify Store Scraper runs too. Your store is read first, with all but this actor's reserve; the rivals then share the rest equally. For 250-product stores, about $2 for one rival, $3 for two and $4 for three covers the first (dearest) run."
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}