1. 2scraper
  2. Business data & reviews
  3. Better Business Bureau
Open source · MITFree to run · paid add-ons optional

Better Business Bureau Scraper

BBB search and category listings with no key or proxy, plus full business profiles with grades and complaints.

  • Playwright
  • Selenium
  • Puppeteer
  • Scraping Browser API
  • JSON
  • CSV
bbb-scraper
# Keyword search
$ python playwright_scraper.py --text restaurants --location "New York, NY" --pages 3
✓ 10 sample rows · 53 columns in sample_output.json
3modes: search, category, profile
53columns in the sample output
0keys or proxies needed for listings
225rows BBB serves per query (15 pages of 15)
Available Better Business Bureau scrapers

What the Better Business Bureau scraper collects

Scrapes business listings and business profiles from bbb.org: name, address, phone numbers, BBB letter grade, accreditation, categories and coordinates, and on a profile the accreditation date, years in business and complaint counts. Listings are read from the JSON endpoint BBB's own front end uses; profiles are read from the rendered page. Output is JSON or CSV with a run sidecar.

Keyword search

Input: A search term and a location

One row per business location matching the query, with grade, accreditation, address, phones and coordinates. Defaults to A-Z ordering rather than BBB's accredited-first Best Match.

Category listing

Input: A bbb.org category URL

Every business BBB lists in a category, read from the same JSON endpoint as search, with the same columns.

Business profile

Input: A bbb.org business profile URL

One fully populated row: accreditation date, years in business, entity type, review and complaint counts and rating reasons. Shares the listing sku, so a profile run joins a listing run.

Quick start

From zero to Better Business Bureau data in 3 steps

  1. Install

    Clone the repository and install one browser engine. Python 3.9+.

    $ git clone https://github.com/2scraper/bbb-scraper.git
    $ cd bbb-scraper
    $ pip install -r requirements.txt -r requirements-playwright.txt
    $ playwright install chromium
  2. Run a scraper

    Pick what you need and run it. Every flag is documented in the README.

    $ python playwright_scraper.py --text restaurants --location "New York, NY" --pages 3
  3. Get structured data

    Rows land in JSON or CSV (--format), with a .meta.json sidecar recording status, pages fetched and why the run stopped.

Output

Better Business Bureau data fields

53 columns per row in a predictable schema. The most useful ones:

FieldWhat it holds
titleBusiness name
skuBusiness at a location: {bbbId}_{businessId}_{addressId}
rating_gradeBBB letter grade (A+ to F); null when BBB has not graded the business
rating_scoreThe same rating as BBB's numeric score; null when ungraded
is_accreditedWhether the business is BBB Accredited
categoryPrimary BBB category
addressStreet address
cityCity
stateState or province
phoneList of phone numbers
latitudeLatitude of the location
longitudeLongitude of the location
accredited_sinceAccreditation date (profile mode)
years_in_businessYears in business (profile mode)
complaints_totalTotal complaints (profile mode)
sortOrdering the run used; it decides which businesses are in the file
[
  {
    "source": "bbb.org",
    "scraped_at": "2026-09-16T12:10:03.339781+00:00",
    "url": "https://www.bbb.org/us/ny/new-york-/profile/restaurants/1849-bar-and-grill-0121-100402",
    "sku": "0121_100402_93156",
    "title": "'1849' Bar and Grill",
    "business_id": "100402",
    "address_id": "93156",
    "bbb_id": "0121",
    "bbb_office": "BBB Serving Metropolitan New York",
    "rating_grade": null,
    "rating_score": null,
    "is_accredited": false,
    "category": "Restaurants",
    "category_id": "50544-000",
    "categories": [
      "Restaurants"
    ],
    "address": "183 Bleecker Street",
    "city": "New York",
    "state": "NY",
    "postal_code": "10012-1406",
    "country": "USA",
    "latitude": 40.7290153503418,
    "longitude": -74.0008773803711,
    "service_areas": null,
    "has_service_area": false,
    "phone": [
      "(212) 505-3200"
    ],
    "logo_url": null,
    "local_profile_url": null,
    "leave_review_url": "https://www.bbb.org/us/ny/new-york-/profile/restaurants/1849-bar-and-grill-0121-100402/…",
    "request_quote_url": null,
    "is_charity": false,
    "charity_seal": false,
    "accredited_charity": false,
    "out_of_business": null,
    "website": null,
    "description": null,
    "fax": null,
    "accredited_since": null,
    "bbb_file_opened": null,
    "business_started": null,
    "years_in_business": null
  },
  {
    "source": "bbb.org",
    "scraped_at": "2026-09-16T12:10:03.339804+00:00",
    "url": "https://www.bbb.org/us/ny/hampton-bays/profile/restaurants/1-north-steakhouse-0121-184362",
    "sku": "0121_184362_223774",
    "title": "1 North Steakhouse",
    "business_id": "184362",
    "address_id": "223774",
    "bbb_id": "0121",
    "bbb_office": "BBB Serving Metropolitan New York",
    "rating_grade": "A+",
    "rating_score": 100.0,
    "is_accredited": false,
    "category": "Restaurants",
    "category_id": "50544-000",
    "categories": [
      "Restaurants"
    ],
    "address": "322 West Montauk Highway",
    "city": "Hampton Bays",
    "state": "NY",
    "postal_code": "11946-3525",
    "country": "USA",
    "latitude": 40.87,
    "longitude": -72.52,
    "service_areas": null,
    "has_service_area": false,
    "phone": [
      "(631) 594-3419"
    ],
    "logo_url": null,
    "local_profile_url": null,
    "leave_review_url": "https://www.bbb.org/us/ny/hampton-bays/profile/restaurants/1-north-steakhouse-0121-1843…",
    "request_quote_url": null,
    "is_charity": false,
    "charity_seal": false,
    "accredited_charity": false,
    "out_of_business": null,
    "website": null,
    "description": null,
    "fax": null,
    "accredited_since": null,
    "bbb_file_opened": null,
    "business_started": null,
    "years_in_business": null
  }
]

First 2 of 10 rows from a real run — sample_output.json. Long values shortened here.

Built for Better Business Bureau

Handles what breaks naive Better Business Bureau scrapers

Reads BBB's own JSON, not its gated HTML

Search and category pages are built from a JSON endpoint that is not behind Cloudflare, so listings come back from a datacenter address that gets HTTP 403 on every HTML page (measured 2026-09-16).

Sidesteps the accredited-first default

BBB's Best Match returned 15 of 15 accredited businesses and no restaurants for a restaurant search. The scraper defaults to --sort a-z, records the ordering as a column, and refuses to diff runs taken under different sorts.

Complete is told apart from exhaustive

BBB caps every query at 15 pages of 15. The sidecar records total_results, pages_available and capped_by_site, and the scraper never asks for page 16, which BBB answers with HTTP 500.

Ungraded is null, not zero

BBB returns a 0.0 score for businesses it has not graded. Both rating columns go null so an unrated business does not drag every average down.

Everything built in

Scraping infrastructure included

The same tested core runs every 2scraper repository, so the hard parts work the same way on every site.

CAPTCHA handling

Detects the challenge a page really serves and, with a 2Captcha key, solves it only when it blocks the data — a capped number of solves per page.

Proxy rotation

Single proxy or a pool from a file, rotated on blocks with a fresh browser per exit. Credentials stay out of argv and logs.

Fingerprints

Optional complete device identity from the 2Captcha Fingerprint API — user agent, client hints, locale and timezone that agree with each other.

Scraping Browser API

Point --cdp-endpoint at a managed Chromium with its own exit country — no browser infrastructure to run yourself.

Honest run metadata

Every run writes a .meta.json sidecar and distinct exit codes: complete, blocked, empty and partial are never confused, and a failed run never overwrites good data.

Diff two runs

diff_runs.py compares two complete Better Business Bureau snapshots by id — new, removed and changed rows for monitoring.

Use cases

What teams do with Better Business Bureau data

Lead generation

Build lists of local businesses by category and location with addresses, phone numbers, websites and coordinates.

Vendor due diligence

Check a supplier's BBB grade, accreditation date, years in business and complaint counts before signing.

Reputation monitoring

Track grade changes and complaint counts for your own business or competitors with scheduled profile runs and run diffs.

Market mapping

Slice a category by state to see how many businesses BBB lists and what share are accredited.

Access & pricing

Free to run. Pay only for what the site requires.

Free to run · paid add-ons optional

Search and category listings need no account, key or proxy: the README measured a complete run from a datacenter VPS that gets 403 on every HTML page. Business profiles are Cloudflare-gated and need a residential exit or the Scraping Browser API, which is where every live profile measurement was taken. On that path the Scraping Browser's built-in captcha auto-solve clears the Managed Challenge; this repo does not implement Turnstile solving through the local solver.

Optional 2Captcha services — captcha solving, the Scraping Browser API, residential proxies and fingerprints — work behind one key.

Good to know

  • BBB caps every query at 225 results (15 pages of 15).
  • Profile mode needs a residential exit or the Scraping Browser API; Selenium cannot authenticate either.
  • position describes one fetch: two locations of one business can swap order between runs.

2scraper vs. hosted scraper APIs

2scraperTypical hosted API
CodeOpen source, MIT — read and patch itClosed
CostFree; optional services billed only when usedSubscription or per request / per result
Runs onYour machine, server or CIThe vendor's infrastructure
OutputJSON/CSV files + run metadataAPI responses
When the site changesFix it yourself or open an issue / PRWait for the vendor
FAQ

Better Business Bureau scraper FAQ

Do I need a proxy or a 2Captcha key?

Not for search or category listings, which read an ungated JSON endpoint. Profile mode needs a residential proxy or a Scraping Browser API session, because profile pages are behind Cloudflare.

Why doesn't it use BBB's default sort?

BBB's Best Match returned only accredited businesses, often outside the searched category. The scraper defaults to A-Z, which is also the only ordering stable between runs; pass --sort best-match to see what a visitor sees.

How many results can I get per query?

BBB serves at most 15 pages of 15 per query, whatever the total. Narrow the query, for example with --state NY, and each slice gets its own 15 pages.

Why does one business appear twice?

Each row is a business at a location. One business with two addresses yields two real rows with the same businessId and different sku values; dedupe on sku.

What output formats are there?

JSON and CSV with the same columns, plus a .meta.json sidecar recording status, sort, totals and whether the site capped the result set. A run that finds nothing writes nothing.

Is scraping BBB legal?

It reads public pages a visitor sees and deliberately skips the named officers BBB lists. Follow the site's terms and the law in your jurisdiction.