# BBB Scraper & API - Better Business Bureau Leads & Ratings (`rel8ble/bbb-business-scraper`) Actor

BBB scraper for Better Business Bureau (bbb.org) leads, US and Canada. Input: keywords, cities or ZIPs, filters (accredited, rating grade, distance) or BBB URLs. One result = one business: name, category, BBB rating, accreditation, phone, address, coordinates, profile URL. $1.00 per 1,000 results.

- **URL**: https://apify.com/rel8ble/bbb-business-scraper.md
- **Developed by:** [Giovanni Rich](https://apify.com/rel8ble) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## BBB Scraper & API - Better Business Bureau Leads & Ratings

This **BBB scraper** is an unofficial **BBB API** for **Better Business Bureau** search data in the US and Canada. Give it keywords or trades (`plumber`, `roofing contractors`, `dentist`) and cities, ZIP or postal codes, and get every matching business on bbb.org as clean JSON, CSV or Excel: business name, BBB category, **BBB rating** (A+ to F), **BBB Accredited** yes / no, **phone number**, street address, city, state / province, ZIP, latitude / longitude, the BBB profile URL, logo and service area - ready-made **B2B leads** and **contractor lists**.

It reads BBB's own search data over plain HTTP: no browser, no login, no API key. BBB shows at most 225 businesses for any one search; this scraper's **deep search splits big searches** by rating grade, state, category, distance, sort order and nearby towns and merges them without duplicates. In testing, `plumber` near Denver (1,954 listed by BBB) returned 1,524 unique business locations in 4 minutes, and `roofing contractors` near Houston (4,706 listed) returned 3,406 in under 6 minutes.

### What you get

One dataset item per business location:

- **Business**: `name`, `category` (BBB's main category), `categories`, `categoryId`, `logoUrl`.
- **BBB standing**: `rating` (A+ to F, `null` = not rated), `ratingScore`, `accredited` (true / false), `isCharity`, `charitySeal`, `outOfBusiness`.
- **Contact**: `phone` (main number) and `phones` (all listed numbers).
- **Where**: `address`, `city`, `state`, `postalCode`, `country` (US / CA), `latitude`, `longitude`, `serviceArea`, `serviceAreas`.
- **Links**: `profileUrl` (BBB profile), `locationProfileUrl` (this branch), `leaveReviewUrl`, `requestQuoteUrl`.
- **BBB office**: `localBbb`, `localBbbId`.
- **Ids**: `id` (one per business location, stable between runs), `businessId` (one per business; a company with several branches has several rows).
- **Where it came from**: `sourceQuery`, `sourceLocation`, `position`, `searchUrl`, `scrapedAt`.

### Sample output

From a real BBB search: `plumber` near `Denver, CO`.

| name | category | rating | accredited | phone | city |
|---|---|---|---|---|---|
| Ennis Plumbing LLC | Plumber | A+ | true | (720) 660-9812 | Denver, CO |
| Repipe Specialists of Colorado | General Contractor | A+ | true | (800) 493-1631 | Denver, CO |
| Speed Plumbing Services Inc. | Plumber | A+ | true | (720) 670-6361 | Denver, CO |

### Use cases

- **Local business leads**: every accredited roofer in Houston with a phone number, ready for a CRM or dialer.
- **Contractor directories** and comparison sites by trade and city.
- **Reputation and risk checks**: which HVAC companies near you are rated D or F (`ratings: ["D", "F"]`).
- **Sales territory planning** across US states and Canadian provinces.
- **Market research**: how many BBB-listed businesses a trade has in a region, and how many are accredited.
- **Weekly monitoring**: schedule a run and deduplicate on `id` to get only new businesses.

### Pricing

$1.00 per 1,000 results (pay per result; failed requests are never charged). No start fee; duplicates, out-of-business listings and unrecognised places are not charged either. Most BBB scrapers on the Store charge $2.00-$20.00 per 1,000.

### FAQ

**Is scraping BBB legal?**
The actor collects publicly visible business listings, not personal data, and does not bypass any access control. You are responsible for how you use the data and for following BBB's terms and the laws that apply to you (for example on cold calling). This is not legal advice.

**Does it need a login, API key or proxy?**
No login and no API key. BBB's search answers direct requests, so the default runs without a proxy; switch on Apify Proxy only if the log shows repeated blocks. (There is no public self-serve BBB search API - BBB sells data products to partners - so this actor reads the public search on bbb.org.)

**How fast is it and how many results can I get?**
A 100-business search takes a few seconds. BBB caps one search at 225 businesses; with `deepSearch` on (default) big searches reach thousands (1,524 plumbers near Denver in 4 minutes). Set `maxItems: 0` for everything the search can reach.

**Does it get BBB complaints and reviews?**
No. Those are on the profile pages, which are behind a bot check. You get the rating grade and accreditation, and `profileUrl` links to the full profile.

**Can I schedule it or use it via API?**
Yes: Apify Schedules, the Apify API and Python/JavaScript clients, Make, Zapier, n8n, webhooks, or the Apify MCP server for AI agents.

**Why do I get two rows for one company?**
BBB lists each branch address separately. `id` is per location, `businessId` per company: deduplicate on `businessId` if you want one row per company.

**Does it cover Canada?**
Yes: Canadian cities ("Toronto, ON"), provinces ("BC") and postal codes ("M5V 2T6") all work.

### How to use

1. Type one or more **Keywords or categories** (`plumber`, `auto repair`, `roofing contractors`, or a business name) and **Locations** (`Denver, CO`, `80212`, `Toronto, ON`, `M5V 2T6`, `TX`). Every keyword is searched in every location. No location = all of the US and Canada.
2. Optionally filter: **BBB Accredited only**, **Rating grades** (A, B, C, D, F), **Max distance** (5-100 miles), **States / provinces**, **exact BBB categories**, **Only businesses serving the area**, and choose a **Sort order**.
3. Set **Max businesses per search** (0 = everything the search can reach).
4. Click **Start**. Download the results as JSON, CSV, Excel or HTML from the **Output** tab, or pull them through the Apify API, Make, Zapier, n8n or an MCP client.

Already have links? Paste bbb.org **search URLs** or **category pages** (`https://www.bbb.org/us/co/denver/category/plumber`) into *BBB URLs*; the keyword, place, sort and filters are read from the URL.

### Input example

| Field | Default | Description |
|---|---|---|
| `searchQueries` | - | Keywords, trades or business names, one search each |
| `locations` | all US + Canada | "City, ST", ZIP / postal code or state / province code |
| `accreditedOnly` | false | Only BBB Accredited businesses |
| `ratings` | all | Any of `A`, `B`, `C`, `D`, `F` (A = A+, A, A-) |
| `maxDistanceMiles` | 0 (BBB default) | 5, 10, 25, 50 or 100 |
| `states` | all | Two-letter state / province codes |
| `categories` | - | Exact BBB category names (advanced) |
| `servingAreaOnly` | false | BBB's "Serving my Area" filter |
| `skipOutOfBusiness` | true | Leave out businesses marked out of business |
| `sort` | `relevance` | `distance`, `rating`, `atoz`, `ztoa` |
| `maxItems` | 100 | Max businesses per search; 0 = all |
| `deepSearch` | true | Go past BBB's 225-per-search limit |
| `startUrls` | - | bbb.org search or category URLs |

Accredited, A-rated roofers within 25 miles of Austin, best rated first:

```json
{
    "searchQueries": ["roofing"],
    "locations": ["Austin, TX"],
    "accreditedOnly": true,
    "ratings": ["A"],
    "maxDistanceMiles": 25,
    "sort": "rating",
    "maxItems": 60
}
```

Dentists and plumbers in Vancouver and downtown Toronto:

```json
{
    "searchQueries": ["dentist", "plumber"],
    "locations": ["Vancouver, BC", "M5V 2T6"],
    "maxItems": 30
}
```

Every plumber BBB lists around Denver, Colorado only:

```json
{
    "searchQueries": ["plumber"],
    "locations": ["Denver, CO"],
    "states": ["CO"],
    "maxItems": 0
}
```

From links you copied off the site:

```json
{
    "startUrls": [
        "https://www.bbb.org/search?find_country=USA&find_text=hvac&find_loc=Phoenix%2C%20AZ&filter_ratings=A&sort=AToZ",
        "https://www.bbb.org/us/co/denver/category/electrician"
    ],
    "maxItems": 20
}
```

### Output example

From a real run (`plumber` near `Denver, CO`):

```json
{
    "id": "1296_90156205_690834",
    "businessId": "90156205",
    "name": "Ennis Plumbing LLC",
    "category": "Plumber",
    "categories": ["Plumber", "Commercial Plumber"],
    "rating": "A+",
    "ratingScore": 100,
    "accredited": true,
    "phone": "(720) 660-9812",
    "phones": ["(720) 660-9812"],
    "address": "4865 Golden Ct",
    "city": "Denver",
    "state": "CO",
    "postalCode": "80212",
    "country": "US",
    "latitude": 39.74,
    "longitude": -104.97,
    "profileUrl": "https://www.bbb.org/us/co/denver/profile/plumber/ennis-plumbing-llc-1296-90156205",
    "locationProfileUrl": null,
    "leaveReviewUrl": "https://www.bbb.org/us/co/denver/profile/plumber/ennis-plumbing-llc-1296-90156205/leave-a-review",
    "requestQuoteUrl": "https://www.bbb.org/greatwestpacific/quote/request-ennis-plumbing-llc-90156205",
    "logoUrl": null,
    "serviceArea": "Ennis Plumbing services Thornton, CO.",
    "serviceAreas": ["Thornton, CO"],
    "isCharity": false,
    "charitySeal": false,
    "outOfBusiness": false,
    "outOfBusinessCode": null,
    "distanceMiles": null,
    "localBbb": "BBB Great West + Pacific",
    "localBbbId": "1296",
    "categoryId": "10113-000",
    "sourceQuery": "plumber",
    "sourceLocation": "Denver, CO",
    "position": 1,
    "searchUrl": "https://www.bbb.org/search?find_loc=Denver%2C%20CO&find_text=plumber",
    "scrapedAt": "2026-10-17T17:19:38.441Z"
}
```

A Canadian row from the same kind of run (`plumber` near `Vancouver, BC`, shortened): `"name": "Elafon Mechanical Ltd."`, `"rating": "A+"`, `"accredited": true`, `"phone": "(604) 688-0001"`, `"address": "239 970 Burrard St"`, `"postalCode": "V6Z 2R4"`, `"country": "CA"`, `"localBbb": "BBB, British Columbia & Yukon"`.

Fill rates on a 1,524-business test run (`plumber` near Denver, everything): name, category, city, state, ZIP, coordinates, profile URL 100%; phone 97%; street address 82% (service-area businesses hide it); BBB letter rating 75% (the rest are not rated); accredited 21%; service area 36%; logo 14%. A field BBB does not publish for a business is `null` or empty, never guessed.

### Integrations

- **Apify API**: run the actor and fetch results over REST, or with the official [Python](https://docs.apify.com/api/client/python) and [JavaScript / Node.js](https://docs.apify.com/api/client/js) clients.
- **MCP for AI agents**: through the Apify MCP server (https://mcp.apify.com), Claude, ChatGPT, Cursor and other agents can call this BBB search API directly. Every input field is described for agents ("find A-rated accredited electricians within 10 miles of 80212" maps straight onto the input).
- **Make, Zapier and n8n**: send new leads to HubSpot, Pipedrive, Salesforce, Google Sheets, Airtable or Slack.
- **Webhooks and schedules**: run weekly and deduplicate on `id` to get only new businesses.

Python example:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("rel8ble/bbb-business-scraper").call(run_input={
    "searchQueries": ["roofing"],
    "locations": ["Austin, TX"],
    "accreditedOnly": True,
    "maxItems": 100,
})
for b in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(b["name"], "|", b["rating"], "|", b["phone"], "|", b["city"], "|", b["profileUrl"])
```

### Limits (read before large runs)

- **225 businesses per single BBB search** is BBB's own limit (15 pages of 15). Deep search goes past it by running narrower searches; it reaches most but not all of BBB's count: 78% for `plumber` near Denver (1,524 of 1,954) in 4 minutes, 72% for `roofing contractors` near Houston (3,406 of 4,706) in 5.5 minutes. BBB's count also includes loose keyword matches and branches, so the true number of real matches is lower than it says. `RUN_SUMMARY` shows the count, what was reached and the coverage percent for every search.
- **Search results only.** BBB's business profile pages (complaints, reviews, years in business, website, owner names) sit behind a bot check, so this actor does not open them and does not try to get around it. Everything in the output comes from BBB's public search results.
- **Wide area.** BBB searches far around a city (Denver also returns Wyoming and Nebraska). Use `maxDistanceMiles` or `states` to keep it tight.
- **Unknown places** (a typo, an unknown town) make BBB search everything; the actor detects that, skips the search and says so in the log, without charging.
- **Speed**: requests are spaced politely (at most 5 a second). A 100-business search takes a few seconds; deep searches of a few thousand businesses take a few minutes.
- **No personal data.** Only business listing data is collected. Contact names are not collected, and e-mail addresses that look personal in free-text fields are replaced with `[email removed]`. Some sole traders trade under their own name; that is the business name BBB lists.

# Actor input Schema

## `searchQueries` (type: `array`):

Required unless startUrls is given. What to search for, as typed into the BBB search box, one per line: a trade or category ("plumber", "roofing contractors", "dentist", "auto repair") or a business name ("Mr. Rooter"). Each keyword is searched in each location and returns up to maxItems businesses. Typical: 1-10 keywords.

## `locations` (type: `array`):

Optional. Where to search, one per line: "City, ST" ("Denver, CO", "Toronto, ON"), a US ZIP code ("80212"), a Canadian postal code ("M5V 2T6") or a state / province code ("TX"). Every keyword is searched in every location (2 keywords x 3 locations = 6 searches). BBB searches a wide area around the place (often 50-100 miles); use maxDistanceMiles to tighten it. Empty = all of the US and Canada. A place BBB does not recognise is skipped and reported, never charged.

## `accreditedOnly` (type: `boolean`):

Optional. true = only businesses that are BBB Accredited (BBB's own filter). Default false = all businesses. Every row has an "accredited" true / false field either way.

## `ratings` (type: `array`):

Optional. Keep only businesses with these BBB letter grades: any of "A", "B", "C", "D", "F" ("A" includes A+, A and A-). Several values = any of them. Empty = all, including businesses BBB has not rated. Use \["D", "F"] to find poorly rated businesses, \["A"] for the best-rated.

## `maxDistanceMiles` (type: `integer`):

Optional. Only businesses within this many miles of each location. BBB offers 5, 10, 25, 50 and 100; other numbers are rounded to the nearest of those. 0 or empty = BBB's default wide area. Ignored when locations is empty.

## `states` (type: `array`):

Optional. Keep only businesses in these states or provinces, two-letter codes, one per line: "CO", "TX", "ON". Useful because a city search can reach across state lines (Denver also returns Wyoming and Nebraska). Empty = all.

## `categories` (type: `array`):

Optional, advanced. Narrow each search to exact BBB categories by name, one per line, e.g. "Water Heater Repair", "Commercial Plumber". Names BBB offers as a filter for that search use BBB's own category filter; other names keep only businesses that list that category. Most users leave this empty and put the trade in searchQueries instead.

## `servingAreaOnly` (type: `boolean`):

Optional. true = BBB's "Serving my Area" filter: only businesses that say they serve the searched location (some are based elsewhere). Default false.

## `skipOutOfBusiness` (type: `boolean`):

Optional. true (default) = leave out businesses BBB marks as out of business; they are not saved and not charged. false = keep them, with outOfBusiness: true on the row.

## `sort` (type: `string`):

Optional. Order of results: "relevance" (default, BBB's best match), "distance" (nearest first), "rating" (best rated first), "atoz" or "ztoa" (by name). Matters most when maxItems is small; with deep search the whole set is collected anyway.

## `maxItems` (type: `integer`):

Optional. Maximum businesses saved per search (one keyword in one location), integer >= 0. Default 100; 0 = every business the search can reach. You pay per business saved ($0.001 each), so 100 businesses = $0.10. Use 10-20 for a quick answer.

## `deepSearch` (type: `boolean`):

Optional. BBB shows at most 225 businesses (15 pages) for any one search. With deepSearch on (default) and maxItems above 225, the actor splits the search by rating grade, state, category, distance, sort order and nearby towns and merges the results without duplicates (tested: 78% of BBB's count for plumber near Denver, 1,954 listed; 72% for roofing contractors near Houston, 4,706 listed). Off = at most 225 per search.

## `startUrls` (type: `array`):

Optional. URLs copied from bbb.org, one per line. A search URL ("https://www.bbb.org/search?find_text=plumber\&find_loc=Denver%2C%20CO\&filter_ratings=A") is scraped with the keyword, place, sort and filters in the URL (the filter fields above are not applied to it). A category page ("https://www.bbb.org/us/co/denver/category/plumber") becomes that keyword near that city. Business profile URLs are not supported (BBB puts profile pages behind a bot check) and are skipped with a warning.

## `maxConcurrency` (type: `integer`):

Optional, advanced. Number of parallel requests, integer 1-10. Default 4. Requests are also spaced at most 5 a second whatever this is set to. Leave unset.

## `maxRequestRetries` (type: `integer`):

Optional, advanced. How many times a failed request is retried on a new session, integer 0-20. Default 8. Leave unset.

## `proxyConfiguration` (type: `object`):

Optional, advanced. Apify Proxy settings object. Default {"useApifyProxy": false}: BBB's search answers direct requests, so no proxy is needed. Switch on datacenter proxy only if the run log shows repeated blocks. Leave unset.

## Actor input object example

```json
{
  "searchQueries": [
    "plumber"
  ],
  "locations": [
    "Denver, CO"
  ],
  "accreditedOnly": false,
  "maxDistanceMiles": 0,
  "servingAreaOnly": false,
  "skipOutOfBusiness": true,
  "sort": "relevance",
  "maxItems": 20,
  "deepSearch": true,
  "maxConcurrency": 4,
  "maxRequestRetries": 8,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `businesses` (type: `string`):

All businesses found: name, BBB category and categories, BBB rating and score, accreditation, phone numbers, street address, city, state / province, ZIP / postal code, country, latitude / longitude, BBB profile URL, review and quote links, logo, service area and the local BBB.

## `summary` (type: `string`):

Per-search counts, the total BBB reported, the place BBB searched around, how many narrower searches deep search ran, coverage percent, stop reasons and anything that failed.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchQueries": [
        "plumber"
    ],
    "locations": [
        "Denver, CO"
    ],
    "maxItems": 20,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("rel8ble/bbb-business-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searchQueries": ["plumber"],
    "locations": ["Denver, CO"],
    "maxItems": 20,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("rel8ble/bbb-business-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchQueries": [
    "plumber"
  ],
  "locations": [
    "Denver, CO"
  ],
  "maxItems": 20,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call rel8ble/bbb-business-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,rel8ble/bbb-business-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/6vNnCV70P6RjybiZN/builds/DQYc9feb4t6rnEfhA/openapi.json
