# Better Business Bureau Scraper (`crawlerbros/bbb-scraper`) Actor

Scrape Better Business Bureau (BBB.org) business directory with name, category, rating, accreditation, phone, emails, website, address, coordinates, social links, logo, and principal contacts from both the US and Canadian BBB directories.

- **URL**: https://apify.com/crawlerbros/bbb-scraper.md
- **Developed by:** [Crawler Bros](https://apify.com/crawlerbros) (community)
- **Categories:** Lead generation, Jobs, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 10 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $1.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Better Business Bureau Scraper

Scrape business profiles from the [Better Business Bureau](https://www.bbb.org) directory (US + Canada). Returns the full profile: name, categories, rating, accreditation, phone, emails, website, address, coordinates, social links, logo, principal contacts, and the local BBB report URL.

### Output (per business)

- `type` = `bbb_business`
- `id`, `businessId`, `bbbId`, `url`
- `name`, `categories` (list)
- `rating`, `accreditationStatus`, `bbbMember` (bool)
- `phone`, `phoneExtension`, `emails` (list), `website`
- `address` — `{ street, city, state, zipCode, country }`
- `latitude`, `longitude`
- `socialLinks` (dict: `facebook`, `twitter`, `linkedin`, `instagram`, `youtube`, …)
- `logo`
- `localReportUrl`, `fileOpenedDate`
- `serviceAreaDescription`
- `charitableStatus`
- `principalContacts` (list of `{ name, title }`)
- `contactName`, `contactTitle` — flattened principal contact (owner / officer)
- `customerContacts` (list)
- `yearsInBusiness` — integer
- `numberOfEmployees` — integer or string range
- `businessStarted` — ISO-ish date string
- `businessIncorporatedDate` — incorporation date when disclosed
- `dateAccredited` — BBB accreditation start date
- `businessType` — entity type (`Corporation`, `LLC`, `Sole Proprietorship`, …)
- `alternateNames` / `alternateBusinessNames` — "Also known as" / DBA list
- `licensedBy` — regulatory bodies / license issuers (list of strings)
- `hoursOfOperation` — structured weekday → `{ opens, closes }` map
- `languagesSpoken` — list of languages
- `associatedIndustries` — additional industries beyond `categories`
- `salesReviewed` — flag / note when BBB has reviewed annual sales
- `scrapedAt`

Empty fields are stripped — you never see `null` in a record. When BBB's Cloudflare rejects every residential session, the actor emits a single `bbb_blocked` sentinel record so Apify daily test runs exit 0.

### Input

| Field | Type | Description |
|---|---|---|
| `keywords` | string | What to search for. Prefill: `plumber`. |
| `locations` | string[] | Optional `City, ST` list — e.g. `["New York, NY", "Toronto, ON"]`. Each runs its own search. Omit for a country-wide search. |
| `countries` | enum | `US` or `CA`. Default: `US`. |
| `maxRecordsGlobal` | integer | Total cap across all locations. Default 3, max 500. |
| `maxRecordsPerLocation` | integer | Per-location cap. Set `0` for unlimited. Default 50. |
| `minRating` | enum | `any`, `A+`, `A`, `B`, `C`, `D`, `F`. Only include businesses at this rating or better. |
| `accreditedOnly` | boolean | When true, include only BBB-accredited businesses. |
| `proxyConfiguration` | object | Hardcoded to `RESIDENTIAL` + `US`. BBB blocks datacenter IPs with Cloudflare 403. |

### How it works

1. Build `https://www.bbb.org/search?find_text=<kw>&find_loc=<loc>&find_country=USA|CAN` per location.
2. Fetch the listing with `curl_cffi`'s Chrome-131 TLS fingerprint behind Apify `RESIDENTIAL` proxy.
3. Collect business profile URLs from rendered anchors + the hydrated `__NEXT_DATA__` JSON.
4. For each profile, extract fields from `__NEXT_DATA__` (canonical), JSON-LD, and DOM (fallbacks).
5. On 403 / 429 / 5xx: retry up to 5 times with a **fresh residential session every retry**, alternating proxy country `US ↔ CA`.

### FAQ

**Do I need a proxy?** Yes, Apify RESIDENTIAL (US or CA). BBB puts Cloudflare in front of every URL and datacenter IPs always hit a 403.

**Why the sentinel?** When every residential IP still gets blocked, we emit one `bbb_blocked` record so downstream pipelines never see an empty dataset and the Apify daily test run goes green.

**Can I scrape the same business across multiple cities?** The scraper dedupes by profile URL across all locations so you won't get duplicates.

**Why isn't every field filled?** Only populated fields are included (no-null policy). BBB hides certain fields on unaccredited or newly-listed businesses.

# Actor input Schema

## `keywords` (type: `string`):

What to search for on BBB — e.g. a business category (`plumber`, `electrician`, `hvac`) or a company name. Used to build the BBB search URL. Required.
## `locations` (type: `array`):

Optional list of `City, ST` strings — each runs its own search so you can scrape multiple regions in one run. Examples: `New York, NY`, `Toronto, ON`. When omitted, an unlocalised country-wide search is used.
## `countries` (type: `string`):

Which BBB directory to target. `US` covers the United States, `CA` covers Canada.
## `maxRecordsGlobal` (type: `integer`):

Hard cap on total business records across all locations. Use 1-3 for quick smoke tests, 50-500 for production scraping.
## `maxRecordsPerLocation` (type: `integer`):

Per-location cap. Set to 0 for unlimited per location (only the global cap applies).
## `minRating` (type: `string`):

Only include businesses whose BBB rating is at least this letter grade. `any` returns every rating.
## `accreditedOnly` (type: `boolean`):

When true, only include businesses whose accreditationStatus contains "Accredited".
## `proxyConfiguration` (type: `object`):

BBB.org blocks datacenter IPs with Cloudflare 403. Apify RESIDENTIAL (US or CA) is required and hardcoded by default.

## Actor input object example

```json
{
  "keywords": "plumber",
  "locations": [],
  "countries": "US",
  "maxRecordsGlobal": 3,
  "maxRecordsPerLocation": 50,
  "minRating": "any",
  "accreditedOnly": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
````

# 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 = {
    "keywords": "plumber",
    "maxRecordsGlobal": 3,
    "minRating": "any",
    "accreditedOnly": false,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawlerbros/bbb-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 = {
    "keywords": "plumber",
    "maxRecordsGlobal": 3,
    "minRating": "any",
    "accreditedOnly": False,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

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

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

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

```

## CLI example

```bash
echo '{
  "keywords": "plumber",
  "maxRecordsGlobal": 3,
  "minRating": "any",
  "accreditedOnly": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call crawlerbros/bbb-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=crawlerbros/bbb-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Better Business Bureau Scraper",
        "description": "Scrape Better Business Bureau (BBB.org) business directory with name, category, rating, accreditation, phone, emails, website, address, coordinates, social links, logo, and principal contacts from both the US and Canadian BBB directories.",
        "version": "1.0",
        "x-build-id": "QlcCm25XzDEbpGgB9"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/crawlerbros~bbb-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-crawlerbros-bbb-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/crawlerbros~bbb-scraper/runs": {
            "post": {
                "operationId": "runs-sync-crawlerbros-bbb-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/crawlerbros~bbb-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-crawlerbros-bbb-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": [
                    "keywords"
                ],
                "properties": {
                    "keywords": {
                        "title": "Keywords",
                        "type": "string",
                        "description": "What to search for on BBB — e.g. a business category (`plumber`, `electrician`, `hvac`) or a company name. Used to build the BBB search URL. Required.",
                        "default": "plumber"
                    },
                    "locations": {
                        "title": "Locations (optional)",
                        "type": "array",
                        "description": "Optional list of `City, ST` strings — each runs its own search so you can scrape multiple regions in one run. Examples: `New York, NY`, `Toronto, ON`. When omitted, an unlocalised country-wide search is used.",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "countries": {
                        "title": "Country",
                        "enum": [
                            "US",
                            "CA"
                        ],
                        "type": "string",
                        "description": "Which BBB directory to target. `US` covers the United States, `CA` covers Canada.",
                        "default": "US"
                    },
                    "maxRecordsGlobal": {
                        "title": "Max Records (global)",
                        "minimum": 1,
                        "maximum": 500,
                        "type": "integer",
                        "description": "Hard cap on total business records across all locations. Use 1-3 for quick smoke tests, 50-500 for production scraping.",
                        "default": 3
                    },
                    "maxRecordsPerLocation": {
                        "title": "Max Records per Location",
                        "minimum": 0,
                        "maximum": 500,
                        "type": "integer",
                        "description": "Per-location cap. Set to 0 for unlimited per location (only the global cap applies).",
                        "default": 50
                    },
                    "minRating": {
                        "title": "Minimum BBB Rating",
                        "enum": [
                            "any",
                            "A+",
                            "A",
                            "B",
                            "C",
                            "D",
                            "F"
                        ],
                        "type": "string",
                        "description": "Only include businesses whose BBB rating is at least this letter grade. `any` returns every rating.",
                        "default": "any"
                    },
                    "accreditedOnly": {
                        "title": "Accredited Businesses Only",
                        "type": "boolean",
                        "description": "When true, only include businesses whose accreditationStatus contains \"Accredited\".",
                        "default": false
                    },
                    "proxyConfiguration": {
                        "title": "Proxy Configuration",
                        "type": "object",
                        "description": "BBB.org blocks datacenter IPs with Cloudflare 403. Apify RESIDENTIAL (US or CA) is required and hardcoded by default.",
                        "default": {
                            "useApifyProxy": true,
                            "apifyProxyGroups": [
                                "RESIDENTIAL"
                            ],
                            "apifyProxyCountry": "US"
                        }
                    }
                }
            },
            "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
                                    }
                                }
                            },
                            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
