{
  "openapi": "3.0.1",
  "info": {
    "title": "BBB Scraper - Business Ratings, Reviews, Complaints & B2B Leads",
    "description": "Scrape bbb.org (Better Business Bureau, USA & Canada) business profiles: BBB rating, accreditation, contact and lead-gen data (decoded email, phone, website, socials, owner), address + coordinates, business type, years in business, licenses, hours, plus opt-in customer reviews and complaints..",
    "version": "1.0",
    "x-build-id": "UQl0cGDzLbL3A5eWa"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/abotapi~bbb-org-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-abotapi-bbb-org-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~bbb-org-scraper/runs": {
      "post": {
        "operationId": "runs-sync-abotapi-bbb-org-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~bbb-org-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-abotapi-bbb-org-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",
              "url"
            ],
            "type": "string",
            "description": "Choose 'search' to run business-term + location searches with filters, or 'url' to scrape pasted BBB search-result pages and business-profile URLs (both are auto-detected and can be mixed).",
            "default": "search"
          },
          "searchTerms": {
            "title": "Business terms",
            "type": "array",
            "description": "Only used when mode = search — ignored in url mode. Business category or keyword terms to search on BBB, e.g. \"plumber\", \"roofing\", \"hvac\". Each term is combined with the location below and searched separately.",
            "items": {
              "type": "string"
            }
          },
          "location": {
            "title": "Location",
            "type": "string",
            "description": "City + state (US) or city + province (Canada) to search within, e.g. \"Los Angeles, CA\" or \"Toronto, ON\". A ZIP / postal code also works. Leave empty for a nationwide search of the selected country. To search several places in one run, add them under \"More locations\" below."
          },
          "locations": {
            "title": "More locations",
            "type": "array",
            "description": "Optional. Extra cities, states/provinces or ZIP/postal codes to search in the same run, e.g. \"San Diego, CA\", \"90210\". Every business term is searched in the Location above AND in each of these; a business found by more than one search is returned (and billed) once. Search mode only.",
            "items": {
              "type": "string"
            }
          },
          "country": {
            "title": "Country",
            "enum": [
              "USA",
              "CAN"
            ],
            "type": "string",
            "description": "Country to search within. Covers the USA and Canada.",
            "default": "USA"
          },
          "sort": {
            "title": "Sort order",
            "enum": [
              "Relevance",
              "Rating",
              "Distance",
              "AToZ",
              "ZToA"
            ],
            "type": "string",
            "description": "Order of search results, applied by BBB itself.",
            "default": "Relevance"
          },
          "distance": {
            "title": "Search radius (miles)",
            "minimum": 1,
            "maximum": 500,
            "type": "integer",
            "description": "Limit results to businesses within this many miles of the location (BBB's own distance filter; needs a Location). Leave empty for BBB's default radius."
          },
          "accreditedOnly": {
            "title": "Accredited businesses only",
            "type": "boolean",
            "description": "Only return BBB-accredited businesses; non-accredited results are skipped and not billed. Applies to keyword searches (BBB filters those itself) and pasted search-result URLs; a directly pasted profile URL is always returned.",
            "default": false
          },
          "minRating": {
            "title": "Minimum BBB rating",
            "enum": [
              "A+",
              "A",
              "A-",
              "B+",
              "B",
              "B-",
              "C+",
              "C",
              "C-",
              "D+",
              "D",
              "D-",
              "F"
            ],
            "type": "string",
            "description": "Only return businesses rated at least this BBB letter grade (e.g. \"B\" keeps A+ through B, and drops B- and below). Businesses BBB has not rated (NR) are skipped. Leave empty for every rating. Applies to keyword searches and pasted search-result URLs; skipped businesses are not billed."
          },
          "category": {
            "title": "Category filter",
            "type": "string",
            "description": "Optional. Narrow each search to one BBB business category, by name (e.g. \"Commercial Plumber\", \"Drain Cleaning\") or BBB category code (e.g. \"10113-100\"). A name is matched to BBB's own category filter for that search; if BBB doesn't offer it there, each business's own categories are matched instead. Search mode only."
          },
          "stateFilter": {
            "title": "State / province filter",
            "pattern": "^$|^[A-Za-z]{2}$",
            "type": "string",
            "description": "Optional 2-letter state or province code (e.g. \"TX\", \"ON\") applied by BBB itself. Most useful with an empty Location (a nationwide search). Search mode only."
          },
          "requestQuoteOnly": {
            "title": "Only businesses that take quote requests",
            "type": "boolean",
            "description": "Only return businesses that accept \"Get a Quote\" requests through BBB (BBB's own filter). Search mode only.",
            "default": false
          },
          "servesMyAreaOnly": {
            "title": "Only businesses serving the location",
            "type": "boolean",
            "description": "Only return businesses BBB lists as serving the searched location, including ones based elsewhere (BBB's \"Serving my Area\" filter). Needs a Location. Search mode only.",
            "default": false
          },
          "startUrls": {
            "title": "BBB URLs",
            "type": "array",
            "description": "Only used when mode = url — ignored in search mode. Paste BBB search-result pages (https://www.bbb.org/search?... , filters and page= all work) or direct business-profile URLs — USA (https://www.bbb.org/us/ca/los-angeles/profile/plumber/fords-plumbing-1216-100081178 ) or Canada (https://www.bbb.org/ca/on/toronto/profile/house-cleaning/custom-maids-incorporated-0107-1151951 ), with or without a trailing slash, a /customer-reviews or /complaints suffix, or tracking params. Search and profile URLs are auto-detected and can be mixed in one run.",
            "items": {
              "type": "string"
            }
          },
          "maxItems": {
            "title": "Max businesses",
            "minimum": 0,
            "type": "integer",
            "description": "Hard cap on the number of business profiles returned across the whole run. This is the run's cap. Use 0 for unlimited.",
            "default": 20
          },
          "maxPagesPerQuery": {
            "title": "Max pages per search / URL",
            "minimum": 0,
            "type": "integer",
            "description": "Maximum result pages walked per search term / pasted search URL (15 businesses per page). Leave empty for unlimited — the run then stops at Max businesses or when a page repeats no new businesses."
          },
          "proxy": {
            "title": "Proxy configuration",
            "type": "object",
            "description": "Apify Proxy is used by default on a Residential connection, since bbb.org's bot protection is unreliable on plain datacenter IPs. If Residential is refused for this account (e.g. a free plan), the run automatically continues on a backup connection — no action needed.",
            "default": {
              "useApifyProxy": true,
              "apifyProxyGroups": [
                "RESIDENTIAL"
              ]
            }
          },
          "scrapeReviews": {
            "title": "Scrape customer reviews",
            "type": "boolean",
            "description": "Also collect each business's customer reviews (rating, author, text, date, business response), paginated up to Max review pages. Adds the reviews & complaints surcharge per business. 💲 Turning this on charges the **detail-enrichment** event (billed as \"Reviews & complaints\") once per business returned — including businesses that turn out to have none.",
            "default": false
          },
          "scrapeComplaints": {
            "title": "Scrape complaints",
            "type": "boolean",
            "description": "Also collect each business's complaints (type, status, date, text, response count), paginated up to Max complaint pages. Adds the reviews & complaints surcharge per business. 💲 Turning this on charges the **detail-enrichment** event (billed as \"Reviews & complaints\") once per business returned — including businesses that turn out to have none.",
            "default": false
          },
          "maxReviewPages": {
            "title": "Max review pages per business",
            "minimum": 0,
            "type": "integer",
            "description": "Cap on review pages pulled per business (~10 reviews per page). Only used when \"Scrape customer reviews\" is on. Use 0 for unlimited, the walk then stops on its own at the last review page BBB reports.",
            "default": 5
          },
          "maxComplaintPages": {
            "title": "Max complaint pages per business",
            "minimum": 0,
            "type": "integer",
            "description": "Cap on complaint pages pulled per business (~10 complaints per page). Only used when \"Scrape complaints\" is on. Use 0 for unlimited, the walk then stops on its own once every complaint BBB counts for the business has been collected.",
            "default": 5
          },
          "maxReviewsPerBusiness": {
            "title": "Max reviews per business",
            "minimum": 0,
            "type": "integer",
            "description": "Hard cap on reviews collected per business, on top of \"Max review pages per business\" — whichever limit is hit first stops the walk. Only used when \"Scrape customer reviews\" is on. Use 0 for unlimited (page cap still applies).",
            "default": 20
          },
          "maxComplaintsPerBusiness": {
            "title": "Max complaints per business",
            "minimum": 0,
            "type": "integer",
            "description": "Optional hard cap on complaints collected per business, on top of \"Max complaint pages per business\" — whichever limit is hit first stops the walk. Only used when \"Scrape complaints\" is on. Use 0 for unlimited (page cap still applies).",
            "default": 0
          },
          "resumeFromRunId": {
            "title": "Resume from a previous run",
            "type": "string",
            "description": "Paste a previous run ID or dataset ID to continue a large crawl of businesses without returning or charging for businesses already collected there. Use this after an interrupted run, or when continuing a business list pull in another run. For recurring daily monitoring of the same search, 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 all matching businesses as NEW. Later runs normally return only NEW, UPDATED, and REAPPEARED businesses. Turn on \"Emit unchanged\" or \"Emit expired\" only when you also want those businesses returned (and billed). State is kept separately for each search/URL and reviews/complaints setup; use State key when you want to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from a previous run above.",
            "default": false
          },
          "stateKey": {
            "title": "State key (optional, 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 let the actor derive a key automatically from the search/URL and detail settings — different searches then never mix state with each other."
          },
          "emitUnchanged": {
            "title": "Emit unchanged businesses (incremental mode only)",
            "type": "boolean",
            "description": "Off by default. Turn on to also return businesses 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 specifically want the full snapshot every run.",
            "default": false
          },
          "emitExpired": {
            "title": "Emit expired businesses (incremental mode only)",
            "type": "boolean",
            "description": "Off by default. Turn on to also return businesses that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked search — not when Max businesses capped it, not when Max result pages capped it, and not when Resume was used. Leave BOTH \"Max businesses\" and \"Max result pages\" empty for the run that should detect expiries. This returns — and bills — extra synthetic rows, so leave it off unless you need expiry tracking.",
            "default": false
          },
          "ignoreFieldsForChanges": {
            "title": "Ignore fields when detecting changes (incremental mode only)",
            "type": "array",
            "description": "Output field names that should NOT make a business count as UPDATED (incremental mode only). Use a plain field name (e.g. \"logoUrl\") to ignore a whole top-level field, or a dotted path (e.g. \"complaints.respondedAt\") to ignore just that nested field inside every item of a list like reviews/complaints/categoryTree. Ignored fields are still returned in the output and never removed from it — this only controls what counts as a change. searchKeyword and searchLocation (which of your searches found the business) never count as a change, so a business reached by a different term or location is not reported as UPDATED.",
            "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 rich page-per-item export; other connectors get a best-effort write/digest. Leave empty to skip; never changes the dataset output."
          },
          "notionParentPageUrl": {
            "title": "Notion parent page (Notion connector only)",
            "type": "string",
            "description": "URL or id of the Notion page under which item 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 items 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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}