{
  "openapi": "3.0.1",
  "info": {
    "title": "Sam's Club Scraper - Products, Prices, Stock & Reviews",
    "description": "Scrape Sam's Club (samsclub.com) products by keyword, catalogue, category or URL. Extract prices, original prices, unit prices, ratings, stock availability, variants, images, specifications and customer reviews. Supports scheduled price change tracking.",
    "version": "0.1",
    "x-build-id": "CidyyjbJG8E3QmXPQ"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/abotapi~samsclub-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-abotapi-samsclub-scraper",
        "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/abotapi~samsclub-scraper/runs": {
      "post": {
        "operationId": "runs-sync-abotapi-samsclub-scraper",
        "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/abotapi~samsclub-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-abotapi-samsclub-scraper",
        "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",
        "required": [
          "mode"
        ],
        "properties": {
          "mode": {
            "title": "Mode",
            "enum": [
              "search",
              "catalog",
              "category",
              "url"
            ],
            "type": "string",
            "description": "Choose 'search' to search by keyword, 'catalog' to walk the store's product list, 'category' to pick category pages by name, or 'url' to scrape pasted links. Each mode reads only its own fields below.",
            "default": "search"
          },
          "queries": {
            "title": "Search keywords",
            "type": "array",
            "description": "Only used when mode = search, ignored in the other modes. One entry per search, for example 'coffee' or 'paper towels'. Products found by several keywords are returned once. A keyword that names a whole category (for example 'ipad') is answered by the store with that category's page, which is read instead. Words the store cannot match still return its closest products. Up to 50 different keywords per run; Max products is shared between them.",
            "items": {
              "type": "string"
            }
          },
          "slugKeywords": {
            "title": "Words in the product name",
            "type": "array",
            "description": "Only used when mode = catalog, ignored in the other modes. Each entry is a word or phrase, for example 'paper towels' or 'tire'. A product matches when its name holds every word of at least one entry. Leave empty to take the products in the order the store lists them. Some listed product pages no longer exist; those are skipped and never charged.",
            "items": {
              "type": "string"
            }
          },
          "categoryKeywords": {
            "title": "Words in the category name",
            "type": "array",
            "description": "Only used when mode = category, ignored in the other modes. Each entry is a word or phrase, for example 'tires', 'coffee' or 'paper towels'. A category matches when its name holds every word of at least one entry. Category pages list the products with price, was-price, rating and stock; tick 'Read each product's page' for full detail.",
            "items": {
              "type": "string"
            }
          },
          "maxCategories": {
            "title": "Max categories",
            "minimum": 1,
            "maximum": 200,
            "type": "integer",
            "description": "Only used when mode = category. How many matching categories are walked, broadest names first (a shorter name is a broader category), then in the order the store lists them. Max products is shared between them.",
            "default": 5
          },
          "urls": {
            "title": "Sam's Club links",
            "type": "array",
            "description": "Only used when mode = url, ignored in the other modes. Accepted: product links (/ip/...) and bare product numbers, category pages (/browse/..., a filter written into the link such as ?facet=Brand%3AQuaker is kept), key-phrase pages (/c/kp/...), category hubs (/cp/...) and the store's sitemaps. A search page (/search?q=...) is read as that keyword. Account, cart and checkout pages are skipped with a warning. A link carrying page=N starts at that page. Tracking parameters are ignored.",
            "default": [
              "https://www.samsclub.com/ip/Bounty-Select-A-Size-2-Ply-Paper-Towels-12-Rolls-110-sheets-roll/18040450426",
              "https://www.samsclub.com/browse/paper-towels/1110179",
              "https://www.samsclub.com/browse/gluten-free/710125?facet=Brand%3AQuaker",
              "https://www.samsclub.com/c/kp/tires-for-suvs",
              "https://www.samsclub.com/cp/housewares/1373",
              "https://www.samsclub.com/sitemap_products_1.xml"
            ],
            "items": {
              "type": "string"
            }
          },
          "sortBy": {
            "title": "Sort by",
            "enum": [
              "relevance",
              "priceAsc",
              "priceDesc"
            ],
            "type": "string",
            "description": "Order of search and category result pages. A sort order written into a pasted link wins over this setting. Catalogue mode and product links have no order to set.",
            "default": "relevance"
          },
          "minPrice": {
            "title": "Minimum price",
            "minimum": 0,
            "type": "number",
            "description": "Only return products priced at or above this value, in US dollars. Applied to the returned rows."
          },
          "maxPrice": {
            "title": "Maximum price",
            "minimum": 0,
            "type": "number",
            "description": "Only return products priced at or below this value, in US dollars. Applied to the returned rows."
          },
          "onSaleOnly": {
            "title": "Only discounted products",
            "type": "boolean",
            "description": "Only return products that currently show a was-price above the current price.",
            "default": false
          },
          "minRating": {
            "title": "Minimum rating",
            "minimum": 0,
            "maximum": 5,
            "type": "number",
            "description": "Only return products whose average customer rating is at least this value (0 to 5)."
          },
          "skipSponsored": {
            "title": "Skip sponsored products",
            "type": "boolean",
            "description": "Category pages mix paid placements in with the organic products. Turn on to leave the sponsored ones out; each category row carries an isSponsored flag either way.",
            "default": false
          },
          "fetchDetails": {
            "title": "Read each product's page",
            "type": "boolean",
            "description": "Search, category and key-phrase pages only: read every product's own page for the full description, specifications, options, rating breakdown and image gallery. Off by default because it costs one extra page per product. Catalogue mode and pasted product links always read the product page (there is no result card behind them), so those rows are always billed one 'Product page detail' event whatever this toggle says.",
            "default": false
          },
          "fetchReviews": {
            "title": "Fetch customer reviews",
            "type": "boolean",
            "description": "Also collect customer reviews for each product: rating, title, text, date, first name, helpful votes, verified-purchase flag. Also reads the product page, so it carries the same per-product surcharge as the toggle above, plus one 'Customer review' event per review returned. The average rating, review count and rating breakdown do not need this.",
            "default": false
          },
          "maxReviewsPerProduct": {
            "title": "Max reviews per product",
            "minimum": 1,
            "maximum": 1000,
            "type": "integer",
            "description": "Cap on how many individual reviews are collected for each product when 'Fetch customer reviews' is on. The first ten come with the product page; further ones are read ten at a time. Each review returned is billed, so keep this tight.",
            "default": 20
          },
          "reviewSort": {
            "title": "Review order",
            "enum": [
              "relevance",
              "newest",
              "oldest",
              "highest",
              "lowest",
              "helpful"
            ],
            "type": "string",
            "description": "Which reviews come first when a product has more than the cap.",
            "default": "relevance"
          },
          "includeReviewImages": {
            "title": "Include review photos",
            "type": "boolean",
            "description": "Add the links of the photos reviewers attached to each review.",
            "default": false
          },
          "fixedClub": {
            "title": "Read every page as one fixed club",
            "type": "boolean",
            "description": "The store ties stock, pickup and delivery answers, and which products exist at all, to a club that it assigns by the connection's location, so two rows of one run can come from different clubs (each product row names its club). Turn this on to read every page as the store's own default club instead: the answers are then consistent, but products that club does not sell (for example some tyres) are reported as not available. Prices were identical across clubs in every check. Off by default.",
            "default": false
          },
          "maxItems": {
            "title": "Max products",
            "minimum": 0,
            "maximum": 100000,
            "type": "integer",
            "description": "Maximum number of products to return across the whole run, EXPIRED rows from Incremental mode included. This is the run's cap. Use 0 for the maximum of 100000 products per run.",
            "default": 20
          },
          "maxPages": {
            "title": "Max result pages per keyword, category or link",
            "minimum": 0,
            "type": "integer",
            "description": "Maximum number of result pages to take per keyword, category or link. Each page carries about 50 products. 0 = no page limit set; the run then stops at Max products, when there are no more results, or at a safety limit of 4000 pages per keyword or category (far above what Max products can reach). When a limit you set here stops a keyword or category that still had results, the log says so. Catalogue mode and product links have no result pages.",
            "default": 0
          },
          "proxy": {
            "title": "Proxy configuration",
            "type": "object",
            "description": "Connection used for every request. Leave the default; the run escalates on its own when the store refuses a page. Choose residential for the most reliable results.",
            "default": {
              "useApifyProxy": true
            }
          },
          "resumeFromRunId": {
            "title": "Resume from a previous run",
            "type": "string",
            "description": "Paste a previous run ID or dataset ID to continue a large pull without returning or charging for products already collected there. Use this after an interrupted run. For recurring monitoring of the same scope, use Incremental mode below instead."
          },
          "incrementalMode": {
            "title": "Incremental changes for scheduled runs",
            "type": "boolean",
            "description": "Turn this on for daily or recurring monitoring. The first run returns every matching product as NEW. Later runs normally return only NEW, UPDATED and REAPPEARED products, which makes it a cheap way to watch price drops. What is compared: price, was-price, discount, on-sale flag, unit price, name, brand, description, specifications, options with their prices and the category path. What is excluded by default (still returned in the row, never a change): scrape timestamps, the product's rank and page, the sponsored flag, delivery and pickup texts, popularity badges, the club the page was read for, image and video links, the store's generated summary, and ratings and reviews (average rating, review count, rating breakdown, topics, summary and the review sample). Stock and availability are also excluded unless you turn on 'Count stock changes' because the store's stock answer differs between two reads of an unchanged product. Option lists are compared regardless of the order the store returns them. 'Changes to watch' narrows this to price (and stock when counted) only. Turn on 'Emit unchanged' or 'Emit expired' only when you also want those rows returned (and billed). State is kept separately for each scope and filter setup; use State key to name or deliberately share a monitoring campaign. Which products a category lists, and in what order, can follow the club the connection resolves to, so for scheduled monitoring of a capped or very large category turn on 'Read every page as one fixed club'. To continue one specific interrupted run instead, use Resume from a previous run above.",
            "default": false
          },
          "stateKey": {
            "title": "State key (incremental mode only)",
            "type": "string",
            "description": "Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently configured runs. Leave empty to derive a key automatically from the scope and filter settings, so two different scopes never mix state."
          },
          "emitUnchanged": {
            "title": "Emit unchanged products (incremental mode only)",
            "type": "boolean",
            "description": "Off by default. Turn on to also return products that have not changed since the last run, marked UNCHANGED. This returns, and bills, extra rows you already have, so leave it off unless you want a full snapshot every run.",
            "default": false
          },
          "emitExpired": {
            "title": "Emit expired products (incremental mode only)",
            "type": "boolean",
            "description": "Off by default. Turn on to also return products that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked scope without a page that could not be read, never when Max products capped it or Resume was used. A product page the store reports as not available also counts as missing, and one club's range can differ from another's, so consider 'Expire after missed scans' above 1. EXPIRED rows count against Max products: a run returns at most the room left under it, and the rest are returned on a later complete run. This returns, and bills, extra rows, so leave it off unless you need expiry tracking.",
            "default": false
          },
          "changeScope": {
            "title": "Changes to watch (incremental mode only)",
            "enum": [
              "all",
              "priceAndStock"
            ],
            "type": "string",
            "description": "All fields (default): any change to the compared product data marks a product UPDATED. Price only (and stock when counted): just price, was-price, discount, on-sale flag and per-option price, so name, description or specification edits never produce a billable UPDATED row. Changing this never starts a new baseline: the existing saved state is kept (under an automatic or a named State key) and each stored product is simply compared under the new setting.",
            "default": "all"
          },
          "watchStock": {
            "title": "Count stock changes (incremental mode only)",
            "type": "boolean",
            "description": "Off by default. The store's stock, pickup and delivery answers differ between two reads of the same unchanged product (the same product, same club, two reads seconds apart, in stock then out of stock), so counting them marks products UPDATED, and bills them, without a real change. Turn on only if you accept that: stock status, availability texts, order limits and per-option stock then count as changes.",
            "default": false
          },
          "compactUpdates": {
            "title": "Compact UPDATED rows (incremental mode only)",
            "type": "boolean",
            "description": "Off by default. When on, UPDATED and EXPIRED rows are slim: ids, link, title, current price and stock, plus priceChanges (old and new value of each field that moved) and variantChanges (per-option ADDED, REMOVED or CHANGED). NEW rows stay full. A compact row does not carry product-page detail fields or reviews, so no detail or review charge applies to it.",
            "default": false
          },
          "expireAfterMissedScans": {
            "title": "Expire after missed scans (incremental mode only)",
            "minimum": 1,
            "maximum": 30,
            "type": "integer",
            "description": "Used with 'Emit expired'. A product is marked EXPIRED only after it was missing from this many CONSECUTIVE complete scans (default 1). EXPIRED means the product dropped out of the scanned results or its page is gone; one club's range can differ from another's, so a value of 2 or more avoids false expiries.",
            "default": 1
          },
          "ignoreFieldsForChanges": {
            "title": "Ignore fields when detecting changes (incremental mode only)",
            "type": "array",
            "description": "Field names that must not count as a change, for example name or highlights. Use a dotted path to ignore a key inside every element of a list, for example variants.url. Ignored fields are still returned in the row. Rank, sponsored flag, delivery text, popularity badges, club, media links and ratings and reviews are already ignored by default. Changing this list never starts a new baseline: the saved state is kept and each stored product is re-compared under the new list, so no product comes back as NEW.",
            "items": {
              "type": "string"
            }
          },
          "mcpConnectors": {
            "title": "Pipe results into your apps (optional)",
            "type": "array",
            "description": "Optionally send results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize one under Apify, Settings, API & Integrations, then select it here. Notion gets a page per product; other connectors get a best-effort write or digest. Each connector receives a condensed summary per product, not the full record; the complete record always stays in the dataset. Leave empty to skip; this never changes the dataset output. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com)."
          },
          "notionParentPageUrl": {
            "title": "Notion parent page (Notion connector only)",
            "type": "string",
            "description": "URL or id of the Notion page under which product pages are created. Required to enable the Notion export; ignored by other connectors."
          },
          "maxNotifyListings": {
            "title": "Max items to export per connector",
            "minimum": 1,
            "maximum": 1000,
            "type": "integer",
            "description": "Cap on products written to each connector per run. Does not affect the dataset.",
            "default": 50
          }
        }
      },
      "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}