# BBB Scraper — Ratings, Reviews, Complaints & Business Leads (`tortuga/bbb-scraper`) Actor

Scrape BBB.org business listings for any keyword and US or Canada location: name, phone, address, website, BBB rating, accreditation status, years in business, review and complaint counts. Build local B2B lead lists.

- **URL**: https://apify.com/tortuga/bbb-scraper.md
- **Developed by:** [Trevor Ortega](https://apify.com/tortuga) (community)
- **Categories:** Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 businesses

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

Scrape BBB.org (Better Business Bureau) business listings for any keyword and US or Canada location: name, phone, address, website, BBB rating, accreditation status, years in business, review and complaint counts. Build local B2B lead lists.

Search BBB the way you would on the website (e.g. "plumber" in "Chicago, IL"), or paste BBB search, category or business profile URLs. Get a clean dataset you can download as JSON, CSV or Excel, or pull through the API into your CRM, Google Sheets or an LLM pipeline. Optionally open each profile for full details and collect customer reviews. You pay only for the results you get.

### What data does BBB Scraper extract?

From the search results (every business):

| Field | Description |
|---|---|
| `id` | BBB business id (`<bbbId>-<businessId>`, e.g. `0654-90028307`) |
| `name` | Business name |
| `url` | BBB business profile URL |
| `localProfileUrl` | Profile URL of the local branch, when the card is a branch of a multi-location business |
| `phone`, `phones` | Main business phone and all phones on the card |
| `address` | `{street, city, state, postalCode, country}`; `addressText` is the one-line form |
| `latitude`, `longitude` | Coordinates of the listed location |
| `primaryCategory`, `categories` | BBB categories (type of business) |
| `bbbRating` | BBB letter grade: `A+` ... `F`, or `NR` (not rated) |
| `bbbRatingScore` | BBB's numeric rating score (0-100) |
| `isAccredited` | BBB Accredited Business |
| `isOutOfBusiness` | BBB marks the business as out of business |
| `serviceAreas` | Service area summary (cities or ZIP codes) |
| `logoUrl`, `localBbb`, `requestQuoteUrl` | Logo, the local BBB that manages the file, BBB quote-request link |
| `isSponsored`, `isAd` | Always `false`: BBB search has no paid placements inside the results |
| `rank`, `searchTerm`, `searchLocation` | Position in the results and the search that found it |
| `scrapedAt` | Timestamp (UTC) |

With **Open each business profile** (`includeDetails`), each business also gets:

| Field | Description |
|---|---|
| `website`, `additionalWebsites`, `socialMedia` | Website, other websites, Facebook / Instagram / LinkedIn / YouTube / ... |
| `phones`, `faxNumbers`, `email` | All business phone numbers, fax numbers, business email when BBB publishes one |
| `contacts` | Contact persons the profile publishes, as BBB prints them: `[{name, title, firstName, lastName, isPrincipal, isManagement, isCustomerContact}]`, e.g. `"Mr. Benjamin Vance"`, `"Owner"`. The flags say whether BBB lists the person under Principal Contacts, Business Management and/or Customer Contacts. `[]` when BBB lists nobody |
| `principalContact` | The main principal as one string, `"Mr. Benjamin Vance, Owner"` (BBB's primary principal first); `null` when none |
| `address`, `latitude`, `longitude`, `headquartersAddress` | Full profile address (for branch cards the branch address is kept and the head-office address is in `headquartersAddress`) |
| `hours` | Opening hours by weekday (`"Monday": "09:00-17:00"`, `"Closed"`, `"Open 24 Hours"`) |
| `accreditedSince`, `accreditationRevoked` | Accreditation date (ISO) |
| `businessStarted`, `incorporated`, `bbbFileOpened`, `newOwnershipDate` | Key dates (ISO) |
| `yearsInBusiness`, `typeOfEntity`, `numberOfEmployees` | e.g. `7`, `"Corporation"`, `12` |
| `categories`, `serviceAreas`, `serviceAreaDescription` | Full category list and service area |
| `licenses`, `licensingInfo` | License numbers, expiry dates and agencies; BBB licensing text |
| `ratingReasons`, `notRatedReason` | Why BBB gave this rating / why it is not rated |
| `complaintCount`, `complaintsClosedLast3Years`, `complaintsClosedLast12Months` | Complaint totals |
| `reviewRating`, `reviewCount` | Customer review star average (1-5) and number of reviews |
| `alternateNames`, `description`, `productsAndServices`, `paymentMethods`, `isClaimed`, `isHeadquarters`, `alerts` | More profile info |

With **Include customer reviews** (`includeReviews`), reviews are saved as separate items with `type: "review"`:

| Field | Description |
|---|---|
| `businessId`, `businessName`, `businessUrl` | The reviewed business |
| `reviewId` | BBB review id |
| `reviewerName` | Reviewer's display name as BBB shows it above the review (usually first name and last initial, e.g. `"Leanna T"`); `null` if none is shown |
| `rating` | Stars (1-5) |
| `text`, `date` | Review text and date (`YYYY-MM-DD`) |
| `businessResponse`, `businessResponseDate` | The business's reply, if any |
| `customerFollowUp`, `customerFollowUpDate` | The customer's follow-up comment, if any |

Names of people are collected only where BBB shows them publicly: the contact persons on a business profile (with **Open each business profile**) and reviewer display names on reviews. No photos or avatars, and no personal email addresses or phone numbers beyond the business contact details BBB publishes.

### How to scrape BBB business listings

1. Enter **Search terms** (e.g. `plumber`, `roofing contractor`, `dentist`) and **Locations** (`Chicago, IL`, `Toronto, ON`, a ZIP code). Every term is combined with every location.
2. Or paste BBB **Start URLs**: search pages, category pages (`https://www.bbb.org/us/il/chicago/category/plumber`) or business profiles.
3. Optionally turn on **BBB Accredited businesses only**, **Open each business profile** and **Include customer reviews**.
4. Set **Max businesses** and click **Start**. Results appear in the **Dataset** tab.

### Input example

```json
{
  "searchTerms": ["plumber", "roofing contractor"],
  "locations": ["Chicago, IL", "Austin, TX"],
  "maxItems": 200,
  "maxPagesPerSearch": 10,
  "accreditedOnly": false,
  "includeDetails": true,
  "includeReviews": false,
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "US" }
}
```

### Output example

```json
{
  "type": "business",
  "id": "0654-90028307",
  "name": "Rescue Plumbing, Inc.",
  "url": "https://www.bbb.org/us/il/chicago/profile/plumber/rescue-plumbing-inc-0654-90028307",
  "phone": "(773) 799-8848",
  "website": "https://www.myrescueplumbing.com/",
  "address": { "street": "1137 W Webster Ave Unit B", "city": "Chicago", "state": "IL", "postalCode": "60614-9251", "country": "US" },
  "categories": ["Plumber", "Plumbing Renovation", "Sewer Cleaning", "Sewer Contractors"],
  "bbbRating": "A+",
  "isAccredited": true,
  "accreditedSince": "2019-09-16",
  "yearsInBusiness": 7,
  "businessStarted": "2018-12-15",
  "typeOfEntity": "Corporation",
  "contacts": [
    { "name": "Mr. Benjamin Vance", "title": "Owner", "firstName": "Benjamin", "lastName": "Vance", "isPrincipal": true, "isManagement": true, "isCustomerContact": true }
  ],
  "principalContact": "Mr. Benjamin Vance, Owner",
  "hours": { "Monday": "Open 24 Hours", "Tuesday": "Open 24 Hours" },
  "reviewRating": 1,
  "reviewCount": 2,
  "complaintCount": 0,
  "complaintsClosedLast12Months": 0,
  "rank": 3,
  "searchTerm": "plumber",
  "searchLocation": "Chicago, IL",
  "detailsScraped": true,
  "scrapedAt": "2026-10-05T17:45:00Z"
}
```

A review item:

```json
{
  "type": "review",
  "businessId": "0292-1922",
  "businessName": "Roto-Rooter Plumbing & Water Cleanup",
  "reviewId": "0292_1922_163307",
  "reviewerName": "Leanna T",
  "rating": 1,
  "text": "We had a toilet issue that our local RotoRooter successfully fixed. ...",
  "date": "2026-10-02",
  "businessResponse": "Thank you for sharing your feedback. ...",
  "businessResponseDate": "2026-10-04"
}
```

### How much does it cost to scrape BBB?

Pay per event, no monthly fee and no start fee:

- **$0.005 per business** from the search results (`result`)
- **+$0.008 per business profile opened** with "Open each business profile" (`detail`); business profile start URLs always include details
- **$0.006 per review** (`review`)

1,000 businesses with basic data cost $5; with full profile details $13. Apify platform usage (compute, proxy) is billed separately by Apify.

### How many results can I get per search?

BBB shows 15 businesses per page and at most 15 pages (225 businesses) for one search, even when it reports thousands of matches. To get more, split the area into several cities or ZIP codes, add related search terms, or use different sort orders. Duplicates (multi-location businesses appear several times) are removed automatically and not charged.

### How to scrape BBB reviews and complaints

Turn on **Include customer reviews** and set **Max reviews per business** (newest first, 10 per request). Complaint totals (all, closed in the last 3 years, closed in the last 12 months) come with **Open each business profile**. The texts of individual complaints are not scraped.

### Does BBB block scrapers?

BBB.org is behind Cloudflare. In our tests plain requests went through without challenges, but the actor detects challenge pages and retries them on a fresh proxy IP. For larger runs keep the default residential proxy.

### Integrations and API

Use the run in Zapier, Make, n8n, Google Sheets, or call it from Python/Node with the Apify client. See the **API** tab for ready-made snippets.

### Is it legal to scrape BBB?

This actor collects only information BBB.org shows publicly to logged-out visitors: business names, business phone numbers, addresses, websites, ratings and review texts, the names and job titles of the contact persons a business profile lists (owners, principals, managers, customer contacts), and reviewers' display names as printed on the reviews (usually first name and last initial). It does not log in and does not collect photos, avatars or anything BBB hides or masks. Contact-person and reviewer names are personal data under GDPR and similar laws: make sure you have a lawful basis for processing them, and use them for business outreach only in line with applicable law. You are responsible for how you use the data and for complying with BBB's terms and applicable law (e.g. GDPR, CAN-SPAM, TCPA for outreach).

### See also

See also: [Yellow Pages Scraper](https://apify.com/tortuga/yellowpages-scraper) for more US business leads with hours, email and categories.

### Support

Found a bug or need a field added? Open an issue in the **Issues** tab; it is usually answered within a day.

# Actor input Schema

## `searchTerms` (type: `array`):

What to search for on BBB.org, e.g. plumber, roofing contractor, dentist, moving company, or a business name. Every search term is combined with every location.

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

US or Canadian cities ("Chicago, IL", "Toronto, ON"), states/provinces or ZIP/postal codes. Required when you use search terms.

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

Optional BBB URLs instead of (or in addition to) search terms: search pages (https://www.bbb.org/search?find_text=plumber\&find_loc=Chicago%2C%20IL), category pages (https://www.bbb.org/us/il/chicago/category/plumber) or business profile pages (https://www.bbb.org/us/il/chicago/profile/plumber/rescue-plumbing-inc-0654-90028307). Profile URLs always include full details.

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

Stop after this many businesses in total (across all searches). Reviews are not counted here; they are capped by 'Max reviews per business'.

## `maxPagesPerSearch` (type: `integer`):

Result pages to read per search (15 businesses per page). BBB itself shows at most 15 pages (225 businesses) for one search; split big areas into several cities or ZIP codes, or use several related search terms, to get more.

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

Only return BBB Accredited Businesses (the same as BBB's 'Accredited' filter).

## `sortBy` (type: `string`):

Order of search results, same as the sort menu on BBB. Applied to search terms; start URLs keep their own sort.

## `includeDetails` (type: `boolean`):

Also open every business's BBB profile for the full address, all phone numbers, website, social links, opening hours, business started/incorporated dates, entity type, employee count, all categories, service area, licensing info, accreditation date, BBB rating reasons, complaint totals (all, closed in 3 years, closed in 12 months), the customer review average and count, and the contact persons the profile lists (name and title). One extra request per business, charged as a `detail` event.

## `includeReviews` (type: `boolean`):

Also save the business's BBB customer reviews as separate items (`type: "review"`: star rating, text, date, business response and date). Includes the reviewer's display name as BBB shows it (e.g. "Leanna T"). Each review is charged as a `review` event.

## `maxReviewsPerBusiness` (type: `integer`):

Newest reviews to save per business when 'Include customer reviews' is on (10 per request).

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

BBB.org is behind Cloudflare. Apify residential proxy (US) keeps larger runs reliable.

## Actor input object example

```json
{
  "searchTerms": [
    "plumber"
  ],
  "locations": [
    "Chicago, IL"
  ],
  "maxItems": 100,
  "maxPagesPerSearch": 10,
  "accreditedOnly": false,
  "sortBy": "Relevance",
  "includeDetails": false,
  "includeReviews": false,
  "maxReviewsPerBusiness": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# 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 = {
    "searchTerms": [
        "plumber"
    ],
    "locations": [
        "Chicago, IL"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("tortuga/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 = {
    "searchTerms": ["plumber"],
    "locations": ["Chicago, IL"],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("tortuga/bbb-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 '{
  "searchTerms": [
    "plumber"
  ],
  "locations": [
    "Chicago, IL"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call tortuga/bbb-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,tortuga/bbb-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/rxy9wFseoA4pQaeMk/builds/DLbl4PoZfieP9c6zL/openapi.json
