# BBB Business Scraper - Ratings, Accreditation, Complaints (`amitaviv/bbb-business-scraper`) Actor

Scrape Better Business Bureau (bbb.org) business profiles in the US and Canada by category and city, or from bbb.org URLs: BBB rating and reasons, accreditation date, years in business, complaint and review counts, phone, website, hours, licences. Optional review and complaint texts.

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

## Pricing

from $2.40 / 1,000 businesses

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

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 Business Scraper: Ratings, Accreditation, Complaints, Contacts

Business profiles from the Better Business Bureau (bbb.org), for the US and Canada, from a search term and a location or from pasted bbb.org URLs: BBB rating, accreditation and its date, years in business, complaint and review counts, phone, website, address, opening hours, licences. It works over plain HTTP (no browser), you pay only for businesses delivered, and a run that delivers nothing because of an error or a block ends as Failed, never as a green empty run.

### What you get

- Name, other names the business trades under, BBB profile URL, primary category and all categories, description
- BBB letter rating (A+ to F, `NR` when not rated) with BBB's reasons for it, accredited yes/no, accredited since, revocation date when there is one
- Business started, started locally, incorporated, BBB file opened, years in business, legal entity type, number of employees when listed
- Complaint counts: total (BBB counts the last 3 years), closed in the last 12 months, closed in the last 3 years
- Customer review average (1-5 stars) and number of reviews
- Phone and further phones, fax, website and other websites, social media links, opening hours per weekday
- Optional (off by default): role and company email addresses, see "Email addresses" below
- Street, city, state or province, ZIP or postal code, country, latitude and longitude, service area, headquarters address for a local branch
- Licences listed on the profile (number, issuing agency, expiry date), payment methods
- Management contacts as company roles ("Owner", "President", "Director of Customer Service"), without names by default
- Optional: the newest customer reviews (date, stars, text, and the replies of the business and the reviewer in order) and complaints (date, type, status, text, the business's and the consumer's responses), up to 500 each per business
- One record per business, as JSON, CSV or Excel, or through the API, plus a run summary (`OUTPUT_SUMMARY` in the run's key-value store): what was requested, delivered and charged, which inputs failed or were ignored, how many requests bbb.org refused and how they were retried, and why the run stopped.

What it does not return by default: email addresses, names of contact persons, reviewer names (each can be turned on, see "Legal and responsible use"). Never: anything behind a bbb.org login, BBB's own scam tracker and charity reports.

### How to use

1. Type what you are looking for into **Search terms** (`plumber`, `roofing contractors`, a business name) and where into **Locations** (`Denver, CO`, `Toronto, ON`, `80202`). Every term is searched in every location.
2. Or paste bbb.org URLs into **bbb.org URLs**: business profiles (any tab of a profile), search result pages, or category pages such as `www.bbb.org/us/co/denver/category/plumber`.
3. Set **Maximum businesses per search** and press Start.
4. Download the results from the Output tab, or read them from the API.

### Input

| Field | Type | Default | What it does |
|---|---|---|---|
| `searchTerms` | list of text | none | Categories, keywords or business names. |
| `locations` | list of text | empty | `City, ST`, `City, Province` (`Calgary, AB` or `Calgary, Alberta`) or a ZIP / postal code. State and province names are turned into their codes. Canadian locations are recognised by province or postal code. Empty: all of the country. bbb.org matches a place it does not know to some other place (`Denver, Colorado` would become Denver City, TX; `Xyzzy, CO` becomes Denver); when the place it chose does not match the city and state you wrote, that search fails with a message and nothing is charged for it. A city without a state or postal code is searched as bbb.org reads it, which may not be the city you meant: `Toronto` alone is Toronto, OH (with `country` `CAN` it is Toronto, ON), and `Austin` alone is Austin, IN, not Austin, TX. The run then logs a warning, the place used is in `resolvedLocation` in OUTPUT_SUMMARY and in each record's `searchLocation`, and nothing fails; add the state or province code (`Austin, TX`) to be sure. |
| `country` | `USA` or `CAN` | from the location | Which bbb.org country to search. Choose `CAN` to search Canadian cities by name alone (`Toronto` is then Toronto, ON); the province code is still the safer choice. |
| `startUrls` | list of text | empty | bbb.org profile, search or category URLs, with or without `https://`. Other entries are ignored and listed in the summary. |
| `maxItemsPerSearch` | number | 100 | Stop each search after this many businesses. Profile URLs are not limited by it. |
| `includeDetails` | yes/no | yes | Read each business's profile page. Off: only the search result fields (name, address, phone, rating, accredited, categories, coordinates rounded to about 1 km); faster, same price per business. |
| `accreditedOnly` | yes/no | no | Only BBB Accredited businesses. |
| `radiusMiles` | number | empty | Only businesses within this distance of each location. |
| `includeBranches` | yes/no | yes | bbb.org lists a company's local branches as separate results; each is its own record (`isLocalBranch: true`). Off: a branch result is replaced by the company's headquarters profile, so each company is delivered and charged once. |
| `splitLargeSearches` | yes/no | yes | Get past bbb.org's 225 results per search, see "Limits". |
| `includeReviews`, `includeComplaints` | yes/no | no | Add review and/or complaint texts. Texts are as the consumer wrote them: bbb.org masks most names, but a reviewer can still name themselves or staff of the business. |
| `maxTextsPerBusiness` | number | 20 | Newest reviews, and newest complaints, per business (1-500). |
| `includeEmails` | yes/no | no | Role and company email addresses, see "Email addresses". |
| `includeContactNames` | yes/no | no | Contact person names (and, with `includeEmails`, email addresses that may belong to a person). See "Legal and responsible use". |
| `includeReviewerNames` | yes/no | no | Reviewer names on reviews. See "Legal and responsible use". |
| `onlyNew`, `monitorKey` | yes/no, text | no, empty | Monitoring, see below. |
| `proxyConfiguration` | proxy | off | Not needed, see the FAQ. |

Runs use 512 MB by default; more is not needed.

This input was run as `P5ovecDNt6wqpk0bD` on 2026-10-24 (300 businesses, all 300 with their profile read, 86 s):

```json
{ "searchTerms": ["dentist"], "locations": ["Chicago, IL"], "maxItemsPerSearch": 300 }
```

### Example result

Taken from run `37DnMjRou3YYjZfhA` on 2026-10-24 (the prefilled input: "plumber" in "Denver, CO", 5 businesses, no proxy), unedited:

```json
{
  "id": "1296_90254831_892624",
  "name": "Repipe Specialists of Colorado",
  "alternateNames": [
    "Repipe Specialists Inc."
  ],
  "profileUrl": "https://www.bbb.org/us/co/denver/profile/plumber/repipe-specialists-of-colorado-1296-90254831/addressId/892624",
  "bbbRating": "A+",
  "ratingReasons": [],
  "accredited": true,
  "accreditedSince": "2018-10-31",
  "accreditationRevoked": null,
  "primaryCategory": "General Contractor",
  "categories": [
    "General Contractor",
    "Plumber",
    "Building Contractors",
    "Pipe Line Contractors",
    "Piping Contractors",
    "Repiping Specialist"
  ],
  "description": "General and Plumbing Contractors",
  "phone": "(720) 605-7799",
  "additionalPhones": [
    "(800) 737-4746",
    "(303) 854-6410"
  ],
  "fax": null,
  "email": null,
  "additionalEmails": [],
  "emailsWithheld": 2,
  "website": "https://colorado.repipe.com/",
  "otherWebsites": [],
  "socialMedia": {
    "facebook": "https://www.facebook.com/TheRepipeSpecialists",
    "instagram": "https://www.instagram.com/repipespecialists/",
    "youtube": "https://www.youtube.com/@repipespecialists",
    "reddit": "https://www.reddit.com/user/RepipeSpecialists/?solution"
  },
  "street": "191 University Blvd Ste 145",
  "city": "Denver",
  "state": "CO",
  "postalCode": "80206-4613",
  "country": "US",
  "latitude": 39.71999,
  "longitude": -104.959869,
  "serviceArea": "Repipe Specialists serves Denver, CO and surrounding areas.",
  "openingHours": null,
  "businessStarted": "1991-11-12",
  "businessStartedLocally": "2018-01-01",
  "yearsInBusiness": 34,
  "incorporated": "2021-12-06",
  "bbbFileOpened": "2018-03-09",
  "entityType": "Limited Liability Company (LLC)",
  "numberOfEmployees": null,
  "licenses": [
    {
      "number": "MP.00600677",
      "agency": "Department of Regulatory Agencies (DORA)",
      "expires": "2027-02-28"
    }
  ],
  "paymentMethods": [],
  "complaintsTotal": 37,
  "complaintsClosedLast12Months": 12,
  "complaintsClosedLast3Years": 37,
  "reviewsAverage": 4.03,
  "reviewsCount": 79,
  "contacts": [
    {
      "role": "Director of Customer Service",
      "isPrincipal": false
    },
    {
      "role": "CEO",
      "isPrincipal": false
    },
    {
      "role": "Regional Manager",
      "isPrincipal": false
    },
    {
      "role": "Service Manager",
      "isPrincipal": false
    }
  ],
  "headquarters": {
    "name": "Repipe Specialists",
    "street": null,
    "city": "North Hollywood",
    "state": "CA",
    "postalCode": "91602"
  },
  "isLocalBranch": true,
  "outOfBusiness": false,
  "logoUrl": "https://m.bbb.org/prod/ProfileImages/2025/58e46cfe-6139-43a5-ad0b-f7e8a722fdc1.png",
  "localBbb": "BBB Great West + Pacific",
  "bbbId": "1296",
  "businessId": "90254831",
  "searchTerm": "plumber",
  "searchLocation": "Denver, CO",
  "detailsFetched": true,
  "reviews": null,
  "complaints": null,
  "textsFetched": null,
  "scrapedAt": "2026-10-24T12:05:45.244Z"
}
```

| Field | Meaning |
|---|---|
| `id` | `bbbId_businessId`, plus `_addressId` for a local branch, taken from the profile URL. The same business found by several searches, or also pasted as a profile URL, has the same id and is delivered once per run. A business with branches appears once for the headquarters profile and once per branch (`isLocalBranch: true`, with its `headquarters`), unless `includeBranches` is off. |
| `bbbRating`, `ratingReasons` | BBB's letter grade and the reasons BBB gives for it. |
| `accredited`, `accreditedSince` | Accreditation now, and since when. A formerly accredited business has `accredited: false` and no date. |
| `complaintsTotal` | Complaints BBB shows for the business; BBB counts the last 3 years. |
| `reviewsAverage`, `reviewsCount` | Average star rating of customer reviews (null when there are none) and their number. |
| `email`, `additionalEmails`, `emailsWithheld` | Only with `includeEmails`: role or company addresses. `emailsWithheld` counts the addresses in the profile's data that were not returned (all of them when `includeEmails` is off). |
| `contacts` | Management contacts as `{ role, isPrincipal }`; `name` only with `includeContactNames`. |
| `detailsFetched` | `true` when the profile page was read. `false` when profile reading was off, or bbb.org refused the profile page after all retries (not seen in testing): the record then has the search fields only, and is charged like any business. |
| `reviews`, `complaints`, `textsFetched` | Only with the text options; otherwise `null`. A review has `date`, `rating`, `text`, `businessResponse`, `businessResponseDate` and `responses` (every reply in order, `from` business, consumer or bbb); a complaint has `date`, `type`, `status`, `text` and `responses`. |
| `searchTerm`, `searchLocation` | The search that found the business (location as bbb.org understood it). |

Fields bbb.org does not show for a business are `null` (or an empty list), never missing.

### Pricing

Pay per event. You pay for what is delivered, not for time or memory.

| Event | Free | Bronze | Silver | Gold and above |
|---|---|---|---|---|
| Actor start (once per run at the default 512 MB) | $0.003 | $0.003 | $0.003 | $0.003 |
| Business delivered (search fields and, by default, the profile details) | $0.0036 | $0.003 | $0.0027 | $0.0024 |
| Review and complaint texts, per business that has at least one | $0.0012 | $0.001 | $0.0009 | $0.0008 |

Worked examples on Bronze:

- 100 plumbers in Denver with profile details: $0.003 + 100 x $0.003 = **$0.303**.

- 1,000 businesses from a split search: $0.003 + 1,000 x $0.003 = **$3.003**.

- 100 with details and review texts, of which 60 have reviews: $0.303 + 60 x $0.001 = **$0.363**.

- A daily monitoring run that finds 2 new businesses: $0.003 + 2 x $0.003 = **$0.009**; a run with nothing new costs the start event, $0.003.

- A business is delivered and charged once per run, however many searches or URLs return it. The texts fee is charged only for businesses with at least one review or complaint added. A profile URL that has no business page on bbb.org is reported and not charged.

- The maximum total charge you set for a run is respected: the actor stops before it would go over, keeps what was delivered, and says so (run `l1bQAndBjYsFBylu9`: limit $0.012, 3 businesses delivered, $0.012 charged).

### Reliability and failure behaviour

| What happened | Run status | Status message |
|---|---|---|
| Businesses delivered | Succeeded | `Scraped 300 businesses from 1 query` (`P5ovecDNt6wqpk0bD`) |
| bbb.org has nothing for the search | Succeeded | `0 results: bbb.org has no businesses for this query` (`InHg51Ec3Wpm3Q6Se`) |
| bbb.org does not know the location | **Failed** (that search) | `… bbb.org does not recognise the location "Nowhereville, ZZ"; use "City, ST" …` (`E1JFvi37dzdbaHQL0`) |
| bbb.org matched the location to another place | **Failed** (that search), nothing charged for it | `… bbb.org matched the location "Xyzzyqq, CO" to "Denver, CO", not the place asked for, so this search was not run; …` (`m0ctOtg660hQ6XMq6`) |
| A pasted profile URL has no business page | Succeeded (the others are delivered) | `… — 1 of the given URLs has no business page on bbb.org (removed, or not that kind of page; nothing charged for it)` (`hItBGJNiR9anmrIHO`) |
| Some profiles refused after all retries | Succeeded | `… — details could not be fetched for N businesses (detailsFetched: false)` |
| Nothing delivered because of errors or blocking | **Failed** | `No businesses delivered: … No businesses were charged (the start fee applies).` |
| Input the actor cannot use | **Failed**, within seconds | `Nothing to do: the input has no "searchTerms" and no "startUrls". …` (`fO5Ov28eJVMXV6r2R`, 1 s) |
| Maximum charge reached | Succeeded | `… — stopped at the maximum charge set for this run` (`l1bQAndBjYsFBylu9`) |
| Run timeout close | Succeeded | `… — stopped just before the run timeout; raise the timeout to get the rest` (`dqpvo7AFEbadFAeyf`: 20 s timeout, 53 delivered) |

- bbb.org's profile pages sit behind Cloudflare, which can refuse requests (on 2026-10-20, 5% to 40% of direct requests from Apify were refused; on 2026-10-24, none of 321 in run `P5ovecDNt6wqpk0bD`). A refused request is retried at once with a new browser fingerprint, and after two refusals through Apify Proxy (datacenter, then US residential), up to 8 attempts. The search and review/complaint data come from bbb.org's own JSON endpoints, which were not refused in testing.
- In every test run so far, every delivered business had its profile read; refusals were all recovered by a retry or by the datacenter proxy. The residential step has not been needed yet; it is tested offline only.
- Results are saved page by page. A run that is aborted, times out or is resurrected keeps what was delivered and never delivers or charges the same business twice. A graceful abort (the default in the Console) finishes the page in progress and writes the run summary (run `ASVCF4N8mM4lI9tHq`: summary, dataset and charged events all 107); a forced abort ends the run at once without a summary.

### Limits

- bbb.org shows at most 225 businesses (15 pages) per search, and its total for a city can be in the thousands ("plumber" in Denver: 1,943, many of them based elsewhere but serving Denver). With **Split large searches** on and a maximum above 225, the actor also reads the search in other orders (A-Z, Z-A, distance, rating) and narrowed by rating group (A to F), accreditation, state and category, each up to 225; each business is delivered and charged once. Run `9gzkXH8Y47cFSz3Ng`: "plumber" in "Denver, CO" with a maximum of 2,000 delivered **1,206 of 1,943** businesses (62%), all with profile details, from 623 search pages. That is not every business: for more, also search ZIP codes or nearby cities with a radius. The run summary gives bbb.org's total per search (`totalResults`) and whether the search was read completely (`complete`).
- bbb.org's result pages sometimes repeat a business and skip another. A search of 225 or fewer that is read to its end and found fewer distinct businesses than bbb.org's total is read once more in A-Z order for the ones skipped (run `AzUVFqLgDVKej6t7O`: "plumber" within 5 miles of Denver, 118 of 118; an earlier build delivered 117). Repeats are not delivered or charged twice.
- Local branches: bbb.org lists a company's branches as separate results. In test searches 3-5% of records were branches of a company that was also delivered (9 of 186 dentists in Houston, 12 of 340 roofers in Dallas). Turn off **Include local branch listings** for one record per company (run `CfIhj7SdiRixReQ4A`: 225 roofing results in Dallas became 215 companies).
- Speed with profile details: 300 businesses in 86 s (`P5ovecDNt6wqpk0bD`); a split search spends extra time on search pages that mostly repeat businesses already delivered (those are not charged). Search fields only (`includeDetails` off) is faster: one request returns 15 businesses.
- A local branch's reviews and complaints are those of its headquarters, as on bbb.org.
- Coordinates from search results only (`includeDetails` off) are rounded by bbb.org to 2 decimals (about 1 km).
- US and Canada.

### Monitoring: only new businesses

1. Turn on **Only new businesses** and save the input as a task.
2. Schedule the task (for example daily or weekly).
3. The first run delivers what it finds (up to the maximum). Every later run delivers and charges only businesses it has not delivered before for the same search; a run with nothing new ends as Succeeded with `0 new businesses…` and charges only the start event. Tested on "locksmith" in "Boise, ID" (49 businesses): runs `I86i5XZPjOVghjW1o` and `ehEPBdqU1TG6MQEp9` with a maximum of 10 delivered 10, then the next 10; `6KFhMyZffajaQZrtl` with a maximum of 100 delivered the remaining 29; `BuijIodooNA3JI20I` then delivered 0 new.

The memory is a key-value store named `bbb-business-scraper-monitor` in your own account (one record per search and monitor name, at most 10,000 ids each). Delete the store or change **Monitor name** to start over. A search with more businesses than the maximum is delivered over several runs: each run delivers the next ones it has not delivered yet. bbb.org lists by relevance, not by date, so "new" means "not delivered by this monitor before", which includes businesses newly listed on bbb.org.

### Email addresses

bbb.org does not display email addresses on its profile pages: visitors get an "Email this Business" form. Many profiles still carry an address in the page's data, in disguised form. With **Include email addresses** (`includeEmails`) on, the actor decodes it and returns it when it is a role or company address. It is off by default.

- Returned: addresses whose name part is made only of role words (info@, office@, customerservice@) and words of the business's own name, category or city (movemastersdenver@, mikesplumbing@), with at least one word that is not a person's name.
- Withheld and counted in `emailsWithheld`: anything else, including a first name alone or with an initial (mike@ for "Mike's Plumbing", jsmith@, lcooper@), the names in a practice named after a person (jsmithdds@ for "John Smith DDS"), and words that come only from another name the business trades under. This is a heuristic: a family-named business can still have an address that names a person (longdds@ for "Long Family & Cosmetic Dentistry" was returned in run `49Y3Bx6hdjy3uxUbg`).
- With **Include names of contact persons** also on, all addresses are returned.
- Run `49Y3Bx6hdjy3uxUbg` (200 dentists in Houston, `includeEmails` on): 57 records with an address, 77 addresses withheld.

Turn it on only if you have a lawful basis to process these addresses (GDPR, CCPA, CASL in Canada). Do not use them for unsolicited commercial email: US CAN-SPAM treats addresses collected automatically from websites strictly, and bbb.org's terms may restrict their use. You are responsible for how you use them.

### FAQ

**Do I need a proxy?** No. The actor sends requests directly and moves a refused request to Apify Proxy by itself. Set a proxy only if you want every request to go through it.

**Why are there no email addresses?** Email addresses are off by default; see "Email addresses".

**Why fewer results than bbb.org says?** bbb.org shows 225 per search; see "Limits".

**Can I run it from code?** Yes: the API, the Apify clients for JavaScript and Python, Make, Zapier, n8n. The Output tab shows the URLs.

**What happens when bbb.org changes?** Runs fail loudly instead of returning empty data. Report the run id under Issues.

### Legal and responsible use

This actor collects business information that bbb.org shows publicly without a login. By default it does not return personal data of individuals: management contacts come as company roles without names, no email addresses are returned, and reviews come without reviewer names. bbb.org masks most consumer names in complaint and review texts; the actor also masks names after Mr/Mrs/Ms and in the form "the customer, Name," (run `l9E1r0x2tK2bRZI3n` masked one that bbb.org had left), but a name in other wording can remain in free text.

Three options can return personal data: **Include email addresses** (`includeEmails`, see "Email addresses"), **Include names of contact persons** (`includeContactNames`: names of owners and managers, and with `includeEmails` the addresses that may belong to a person) and **Include reviewer names** (`includeReviewerNames`). Turn them on only if you have a lawful basis to process that data, for example GDPR Art. 6(1)(f) legitimate interest after a balancing test, or a purpose permitted under the CCPA and other US state laws, and only if you will honour the rights of the people concerned (information, objection, deletion). You are the controller of the data you collect.

You are responsible for how you use the output: respect bbb.org's terms of use, BBB trademarks, copyright and database rights, anti-spam and telemarketing rules (CAN-SPAM, TCPA, CASL in Canada), and data-protection law. Do not present BBB ratings as your own endorsement, and do not use the output for unsolicited marketing to individuals. If unsure, ask a lawyer before you build on it.

This actor is not affiliated with or endorsed by the Better Business Bureau or the International Association of Better Business Bureaus.

### Changelog

- 2026-10-24 (0.1.21): a city name without a state, province or postal code (`Toronto`, `Austin`) no longer fails with "log is not defined"; it is searched as bbb.org reads it, with a warning. The review texts option now says that free text can name the reviewer or staff.
- 2026-10-24: first version. Email addresses opt-in; locations checked against the place bbb.org chose; state and province names understood; `country` and `includeBranches` options; review reply threads; consumer names left in texts masked where recognisable.

### Related actors

- [Europages Company Scraper](https://apify.com/amitaviv/europages-company-scraper): B2B company listings for Europe (manufacturers, wholesalers, service providers).
- [Yandex Maps Scraper](https://apify.com/amitaviv/yandex-maps-scraper): business listings with phones, websites, hours and ratings from Yandex Maps.
- [Company Hiring Tracker](https://apify.com/amitaviv/company-hiring-tracker): which companies are hiring and what changed since the last run, from public job boards.

### Support

Open an issue on the actor's **Issues** tab with the run id and what you expected.

# Actor input Schema

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

What to search for on bbb.org: a category or keyword ("plumber", "roofing contractors", "dentist") or a business name. Each term is searched in each location below.

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

Where to search: "City, ST" ("Denver, CO"), "City, Province" ("Calgary, AB" or "Calgary, Alberta") or a ZIP / postal code. State and province names are turned into their codes. Canadian locations are recognised by the province or postal code (or choose Canada under "Country"). Leave empty to search all of the country. bbb.org matches a place it does not know to another one ("Denver, Colorado" would become Denver City, TX); when the place it chose does not match the city and state you wrote, that search fails with a message and nothing is charged for it. A city name alone ("Austin") is searched as bbb.org reads it, which can be another state (Austin, IN), with a warning in the log; add the state code ("Austin, TX") to be sure.

## `country` (type: `string`):

Optional. Which bbb.org country to search. Leave empty to decide from each location (a Canadian province code, province name or postal code means Canada, anything else the US). Choose Canada to search Canadian cities by name alone ("Toronto").

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

Optional. Business profile URLs (www.bbb.org/us/…/profile/…; any tab of a profile works), search result URLs (www.bbb.org/search?find_text=…) or category pages (www.bbb.org/us/co/denver/category/plumber). Profiles are read in full; searches and category pages are read like search terms.

## `maxItemsPerSearch` (type: `integer`):

Stop each search (term x location, or search URL) after this many businesses. bbb.org shows at most 225 businesses per search; see "Split large searches" to go further. Profile URLs are not limited by this.

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

On (default): each business is completed from its bbb.org profile: rating reasons, accreditation date, years in business, start date, complaint counts, review average and count, website, opening hours, licences, social links, contact roles. Off: only what the search results show (name, address, phone, rating, accredited, categories); faster, same price per business.

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

Return only BBB Accredited businesses.

## `radiusMiles` (type: `integer`):

Optional. Only businesses within this distance of each location. A ZIP code with a small radius is the way to get complete coverage of a large city.

## `includeBranches` (type: `boolean`):

On (default): bbb.org lists a company's local branches as results of their own, and each is delivered as its own record (isLocalBranch: true, with its own address and its headquarters). In test searches 3-5% of records were branches of a company that was also delivered. Off: a branch result is replaced by its company's headquarters profile, so each company is delivered and charged once.

## `splitLargeSearches` (type: `boolean`):

bbb.org shows at most 225 businesses per search. When a search has more and your maximum is above 225, the actor also reads it in other orders (A-Z, Z-A, distance, rating) and narrowed by rating group, accreditation, state and category. On "plumber" in "Denver, CO" (1,943 results) this delivered 1,206 distinct businesses instead of 225. Each business is delivered and charged once. For complete coverage of a big area, also search ZIP codes with a radius.

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

Add the newest customer reviews of each business (date, star rating, text, and the replies of the business and the reviewer in order). Reviewer names are not included unless you also turn on "Include reviewer names". Review texts are as written by the reviewer and can mention names (the reviewer's own, or staff of the business); bbb.org does not mask them all. Charged as one "review and complaint texts" event per business.

## `includeComplaints` (type: `boolean`):

Add the newest complaints of each business (date, type, status, complaint text, the business's and the consumer's responses). bbb.org masks most consumer names in these texts; the actor also masks names after Mr/Mrs/Ms and in "the customer, Name,", but a name in other wording can remain. Charged as one "review and complaint texts" event per business.

## `maxTextsPerBusiness` (type: `integer`):

How many of the newest reviews, and how many complaints, to add per business.

## `includeContactNames` (type: `boolean`):

Off (default): management contacts are returned as roles only ("Owner", "President"). On: contacts also get their names, and with "Include email addresses" on, addresses that may belong to a person are returned too. Turn this on only if you have a lawful basis to process personal data of the people named (for example GDPR Art. 6(1)(f) legitimate interest with a balancing test, or a CCPA-compliant purpose) and you will honour their rights. You are the data controller for what you collect.

## `includeEmails` (type: `boolean`):

Off (default): no email addresses. bbb.org does not display email addresses on its profile pages (visitors get an "Email this Business" form); the addresses are in the page's data in disguised form, and this option decodes them. On: role and company addresses (info@, office@, an address made of the business's own name) are returned, and addresses that may name a person are withheld (counted in emailsWithheld) unless "Include names of contact persons" is also on. Turn this on only if you have a lawful basis to process these addresses (GDPR, CCPA, CASL), and do not use them for unsolicited commercial email: US CAN-SPAM treats addresses collected automatically from websites strictly. You are responsible for how you use them.

## `includeReviewerNames` (type: `boolean`):

Off (default): reviews come without the reviewer's name. Turn this on only if you confirm a lawful basis to process these consumers' personal data (for example GDPR Art. 6(1)(f) legitimate interest with a balancing test) and you will honour their rights.

## `onlyNew` (type: `boolean`):

Deliver and charge only businesses that earlier runs of this actor in your account have not delivered for the same search. Useful on a schedule: each run returns the businesses that newly appear in the search. The memory is a key-value store named bbb-business-scraper-monitor in your account; delete it to start over.

## `monitorKey` (type: `string`):

Only with "Only new businesses". Runs with the same search and monitor name share one memory; give two schedules different names to keep their memories apart.

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

Leave off (default). The actor sends requests directly; when bbb.org's Cloudflare protection refuses a request, that request is retried through Apify Proxy on its own (datacenter first, then US residential). Set a proxy here only to send every request through it.

## Actor input object example

```json
{
  "searchTerms": [
    "plumber"
  ],
  "locations": [
    "Denver, CO"
  ],
  "maxItemsPerSearch": 5,
  "includeDetails": true,
  "accreditedOnly": false,
  "includeBranches": true,
  "splitLargeSearches": true,
  "includeReviews": false,
  "includeComplaints": false,
  "maxTextsPerBusiness": 20,
  "includeContactNames": false,
  "includeEmails": false,
  "includeReviewerNames": false,
  "onlyNew": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

All delivered businesses as JSON.

## `resultsCsv` (type: `string`):

All delivered businesses as a CSV file.

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

What was requested, delivered and charged, which inputs failed or were ignored, and why the run stopped.

# 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": [
        "Denver, CO"
    ],
    "maxItemsPerSearch": 5,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("amitaviv/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 = {
    "searchTerms": ["plumber"],
    "locations": ["Denver, CO"],
    "maxItemsPerSearch": 5,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("amitaviv/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 '{
  "searchTerms": [
    "plumber"
  ],
  "locations": [
    "Denver, CO"
  ],
  "maxItemsPerSearch": 5,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call amitaviv/bbb-business-scraper --silent --output-dataset

```

## MCP server setup

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