BBB Scraper - Business Ratings, Reviews, Complaints & B2B Leads avatar

BBB Scraper - Business Ratings, Reviews, Complaints & B2B Leads

Pricing

from $1.20 / 1,000 business results

Go to Apify Store
BBB Scraper - Business Ratings, Reviews, Complaints & B2B Leads

BBB Scraper - Business Ratings, Reviews, Complaints & B2B Leads

Scrape bbb.org (Better Business Bureau, USA & Canada) business profiles: BBB rating, accreditation, contact and lead-gen data (decoded email, phone, website, socials, owner), address + coordinates, business type, years in business, licenses, hours, plus opt-in customer reviews and complaints..

Pricing

from $1.20 / 1,000 business results

Rating

0.0

(0)

Developer

Abot API

Abot API

Maintained by Community

Actor stats

1

Bookmarked

19

Total users

12

Monthly active users

0.57 hours

Issues response

11 hours ago

Last modified

Share

BBB.org Scraper: Business Profiles, Ratings, Reviews and Complaints

BBB.org Scraper turns Better Business Bureau (BBB) into structured lead and reputation data for the USA and Canada. Get the BBB rating, accreditation status, decoded contact details, address with coordinates, licenses, hours, category tree, and years in business for every profile, plus each business's own opt-in customer reviews and complaints. Search by business term and location or paste BBB links, then export to JSON, CSV or Excel, or pull results straight into your app through the API.

Why This Scraper?

  • Two ways to find businesses. Search by business term plus location, or paste BBB search-result and business-profile links, mixed in one run.
  • Decoded contact details. BBB scrambles published email addresses against harvesters; this actor returns the plain address, and leaves it empty when the business truly has not published one.
  • Full profile depth. Rating, accreditation, owner, address with coordinates, business type, years in business, licenses, operating hours, service areas and the full category tree.
  • Optional reviews and complaints. Turn on a per-business add-on to also pull customer reviews and complaints, each paginated up to a per-business page cap you set (0 walks every page).
  • Resume or monitor on a schedule. Continue one interrupted crawl by run id, or turn on Incremental mode so recurring runs return only new and changed businesses.
  • Built to fail loudly, not quietly. A run that could not read BBB at all stops with a clear error instead of reporting an empty result as if nothing was found.

Use Cases

  • Lead generation: pull business name, decoded email and phone, website, owner and address for every plumber, roofer or HVAC company in a city.
  • Reputation monitoring: track a business's BBB rating, accreditation, complaint volume and resolution rate over time.
  • Compliance and licensing checks: collect license numbers, issuing agency and expiration dates alongside years in business and entity type.
  • Customer feedback analysis: read customer reviews and complaints together with the business's own responses.
  • Recurring portfolio monitoring: schedule the same search or URL list and use Incremental mode to see only what changed since the last run.

Data You Get

Sample shape: values are illustrative placeholders, not from a live record.

FieldExample
businessId"0825-1000000000"
name / alternateNames"Example Plumbing Co." / ["Example Plumbing Co., LLC"]
profileUrlBBB link to the business profile
bbbRating / ratingScore"A+" / 97.0 (BBB's 0-100 score behind the grade; search results only, null when unrated)
isAccredited / accreditedDatetrue / "2024-01-16"
phone / additionalPhones"(555) 010-0100" / null
email / additionalEmails"info@example.com" / null
website"https://www.example.com/"
ownerName / ownerTitle"Jane Doe" / "Owner/Manager"
formattedAddress"100 Example St, Austin, TX 78701"
latitude / longitude30.27 / -97.74
serviceAreas["Travis County, TX"]
primaryCategory / categories"Plumber" / ["Plumber", "Plumbing Renovation"]
businessType / yearsInBusiness"Limited Liability Company (LLC)" / 3
licensesnumber, expiration date and issuing agency (when on file)
hoursopening and closing time per day (when published)
reviewsTotal / averageReviewRating1 / 1.0
complaintsTotal / complaintResolutionRate0 / null
socialProfilesthe business's own linked social accounts (when published)
logoUrllink to the business's logo image
alerts / governmentActionsBBB alerts (e.g. {"title": "Licensing", "text": "...", "typeId": 8111, "severity": 1}) and government actions ({"title", "text"}) on the profile; null when none. ratingNotRatedReason explains an NR grade
acceptsQuoteRequests / hasServiceArea / leaveReviewUrltrue / true (search results only) / BBB link to leave a review
searchKeyword / searchLocation"plumber" / "Austin, TX": which of your searches found the business (null for a pasted profile link)
scrapedAt"2026-01-01T00:00:00Z"
changeType"NEW" (Incremental mode only, see below)

With Scrape customer reviews and/or Scrape complaints turned on, the same business record also carries a reviews array (one entry per review with id, author, rating, date, text, businessResponse, businessResponseDate, businessRebuttal and customerResponse) and/or a complaints array (one entry per complaint with id, type, status, date, text, responses and responseCount); both stay null when the corresponding option is off, and empty arrays turn into null too when a business genuinely has none on file.

How to Use

  1. Pick a mode: search (business term plus location) or url (paste BBB links).
  2. Fill in the fields for that mode, and add filters such as accredited-only or a search radius.
  3. Optionally turn on Scrape customer reviews and/or Scrape complaints, and set their page caps.
  4. Set Max businesses to control run size and cost, then click Start. Download the dataset as JSON, CSV or Excel, or read it through the API.

Search by business term and location:

{
"mode": "search",
"searchTerms": ["plumber"],
"location": "Los Angeles, CA",
"country": "USA",
"maxItems": 20
}

Paste a BBB profile link:

{
"mode": "url",
"startUrls": [
"https://www.bbb.org/us/ca/los-angeles/profile/plumber/fords-plumbing-1216-100081178"
],
"maxItems": 10
}

Search plus reviews and complaints:

{
"mode": "search",
"searchTerms": ["hvac"],
"location": "Toronto, ON",
"country": "CAN",
"scrapeReviews": true,
"scrapeComplaints": true,
"maxItems": 15
}

Recurring monitoring of one search:

{
"mode": "search",
"searchTerms": ["roofing"],
"location": "Denver, CO",
"incrementalMode": true,
"stateKey": "denver-roofing"
}

Run it from your code

Python:

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/bbb-org-scraper").call(run_input={
"mode": "search",
"searchTerms": ["plumber"],
"location": "Los Angeles, CA",
})
for business in client.dataset(run["defaultDatasetId"]).iterate_items():
print(business["name"], business["bbbRating"])

JavaScript:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/bbb-org-scraper').call({
mode: 'search',
searchTerms: ['plumber'],
location: 'Los Angeles, CA',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.

Search mode already takes plain text: a business term and a city/state ("plumber" / "Los Angeles, CA"), never a raw BBB id or slug. BBB's own free-text search resolves the name for you.

URL mode auto-detects every shape below from a pasted link; mix supported shapes freely in one startUrls list.

URL shapeExampleSupported?
USA profilehttps://www.bbb.org/us/ca/los-angeles/profile/plumber/fords-plumbing-1216-100081178โœ…
Canada profilehttps://www.bbb.org/ca/on/toronto/profile/house-cleaning/custom-maids-incorporated-0107-1151951โœ…
Profile, trailing slashโ€ฆ/fords-plumbing-1216-100081178/โœ…
Profile + /customer-reviews, /complaints or /detailsโ€ฆ/fords-plumbing-1216-100081178/customer-reviewsโœ… (scraped as the same business; reviews/complaints are still fetched correctly even from a suffixed link)
Profile + ?query / tracking params / #fragmentโ€ฆ/fords-plumbing-1216-100081178?utm_source=newsletterโœ… (query, tracking params and fragment are all ignored)
Bare bbb.org host (no www.)https://bbb.org/us/ca/los-angeles/profile/โ€ฆโœ…
Search-result page, with filters / page=Nhttps://www.bbb.org/search?find_text=plumber&find_loc=Los+Angeles%2C+CA&filter_category=plumber&page=2โœ… (walked across its result pages)
m.bbb.org (mobile host)https://m.bbb.org/us/ca/.../profile/...โŒ (BBB has no mobile subdomain; every path tested 404s. Logged as "unrecognized" and skipped.)
/addressId/<n> suffixโ€ฆ/fords-plumbing-1216-100081178/addressId/1โš ๏ธ accepted (id still resolves), but not confirmed as a real BBB page shape: probed live and 404'd on every business tried
Category / near-me listing pageshttps://www.bbb.org/us/ca/los-angeles/category/plumber, https://www.bbb.org/near-me/plumberโŒ (real pages, but a different, listing not profile, page shape this actor does not parse. Logged as "unrecognized" and skipped; use Search mode instead.)
give.org charity profileshttps://www.give.org/charity-reviews/โ€ฆโŒ (a different site, BBB Wise Giving Alliance, out of scope for this business-profile actor. Logged as "unrecognized" and skipped.)

An unsupported link is never a silent empty result: it is logged as Skipping unrecognized URL so you know to drop it or switch to Search mode, and every other URL in the same run is still processed.

Resume and recurring updates

  • Resume (resumeFromRunId) continues one interrupted run: paste its run or dataset id and businesses already collected there are skipped and not billed again.
  • Incremental mode (incrementalMode) is for scheduled runs over the same search or URL scope. Each business is classified NEW, UPDATED (with changedFields), UNCHANGED (suppressed and not billed unless emitUnchanged is on), or EXPIRED. EXPIRED is only produced once a run has scanned the full tracked scope with emitExpired on and neither Max businesses nor Max pages capped it, and never on a run that used Resume. REAPPEARED only appears for a business a previous complete run already marked EXPIRED, if that same business turns up again on a later run; it is not something an ordinary rerun produces on its own. stateKey names or shares one monitoring campaign's state. With Incremental mode off, output is exactly as before.

Send results into your apps (MCP connectors)

Businesses can be pushed straight into the tools you already use, over Model Context Protocol (MCP) connectors. This is an extra delivery step after the crawl: the Apify dataset and your bill are never changed by it.

  1. Authorize a connector once under Apify, Settings, API & Integrations (Notion, Linear, Airtable, or Apify).
  2. Select it under Pipe results into your apps (mcpConnectors) in this actor's input.
  3. For Notion, also set Notion parent page (notionParentPageUrl) to the page under which business pages should be created; the Notion export stays off until this is filled in. Other connectors ignore it.
  4. Use Max items to export per connector (maxNotifyListings) to cap how many businesses are written to each connector per run.

Notion receives one rich page per business; other connectors get a best-effort write, or a plain-text digest if no write tool is available. Leave mcpConnectors empty to skip this step entirely.

Input Parameters

ParameterTypeDefaultDescription
modestringsearchsearch (business term plus location) or url (paste BBB links).
searchTermsarraynone (prefill: ["plumber"])Business terms to search. Search mode only.
locationstringnone (prefill: "Los Angeles, CA")City plus state/province, or a ZIP/postal code. Empty searches nationwide.
countrystringUSAUSA or CAN.
locationsarraynoneExtra locations; every business term is searched in location and in each of these. A business found more than once is returned (and billed) once. Search mode only.
sortstringRelevanceRelevance, Rating, Distance, AToZ or ZToA, applied by BBB itself.
distanceintegernone (BBB's own default radius applies)Limit results to within this many miles of the location, using BBB's own distance filter.
accreditedOnlybooleanfalseOnly return BBB-accredited businesses from search results and pasted search-result links; a directly pasted profile link is always returned regardless of accreditation.
minRatingstringnone (every rating)Minimum BBB letter grade, A+ to F; unrated (NR) businesses are skipped. Same scope as accreditedOnly.
categorystringnoneOne BBB category, by name ("Commercial Plumber") or BBB category code ("10113-100"). Search mode only.
stateFilterstringnone2-letter state/province code applied by BBB, most useful for a nationwide search. Search mode only.
requestQuoteOnlybooleanfalseOnly businesses that accept "Get a Quote" requests through BBB. Search mode only.
servesMyAreaOnlybooleanfalseOnly businesses BBB lists as serving the searched location. Search mode only.
startUrlsarraynone (prefill: 4 sample links, one per supported shape)BBB search-result and/or business-profile links (USA or Canada), mixed. See Search by plain names, or paste any BBB link for every supported/unsupported shape. URL mode only.
maxItemsinteger20Hard cap on businesses returned for the whole run. 0 = unlimited.
maxPagesPerQueryintegernone (empty = unlimited)Result pages walked per search term or search URL (15 businesses per page).
proxyobject{"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}Apify Proxy configuration. Residential is the default, since it is needed for reliability against bbb.org's bot protection; if Residential is refused for your account, the run automatically continues on a backup connection.
scrapeReviewsbooleanfalseAlso collect each business's customer reviews. Adds a per-business charge.
scrapeComplaintsbooleanfalseAlso collect each business's complaints. Adds a per-business charge.
maxReviewPagesinteger5Cap on review pages per business. 0 = unlimited, stops at BBB's last reported page.
maxComplaintPagesinteger5Cap on complaint pages per business. 0 = unlimited, stops once every complaint BBB counts has been collected.
maxReviewsPerBusinessinteger20Cap on reviews per business, on top of maxReviewPages; whichever is hit first stops. 0 = unlimited.
maxComplaintsPerBusinessinteger0Optional cap on complaints per business, on top of maxComplaintPages. 0 = unlimited.
resumeFromRunIdstringnoneContinue one specific interrupted run by its run or dataset id.
incrementalModebooleanfalseTurn on for recurring monitoring; returns only new and changed businesses on later runs.
stateKeystringnone (auto-derived from the search/URL and detail settings)Name or share one monitoring campaign's state.
emitUnchangedbooleanfalseAlso return (and bill) businesses unchanged since the last run, marked UNCHANGED.
emitExpiredbooleanfalseAlso return (and bill) businesses no longer found, marked EXPIRED. Only produced on a complete, uncapped, non-resumed scan.
ignoreFieldsForChangesarraynoneOutput fields (or dotted nested paths like complaints.respondedAt) that should not count as a change in Incremental mode. searchKeyword/searchLocation never count.
mcpConnectorsarraynone (skip)Optional: send a summary of each business to apps authorized under Integrations.
notionParentPageUrlstringnoneNotion connector only: page under which business pages are created.
maxNotifyListingsinteger50Cap on businesses written to each connector per run.

Output Example

Sample shape: values are illustrative placeholders, not from a live record.

{
"name": "Example Plumbing Co.",
"alternateNames": ["Example Plumbing Co., LLC"],
"businessId": "0825-1000000000",
"bbbId": "0825",
"bbbOfficeName": "BBB Heart of Texas",
"profileUrl": "https://www.bbb.org/us/tx/austin/profile/plumber/example-plumbing-co-0825-1000000000",
"bbbRating": "A+",
"isAccredited": true,
"accreditedDate": "2024-01-16",
"phone": "(555) 010-0100",
"email": "info@example.com",
"website": "https://www.example.com/",
"ownerName": "Jane Doe",
"ownerTitle": "Owner/Manager",
"streetAddress": "100 Example St",
"city": "Austin",
"stateCode": "TX",
"zipCode": "78701",
"formattedAddress": "100 Example St, Austin, TX 78701",
"countryCode": "US",
"latitude": 30.27,
"longitude": -97.74,
"serviceAreas": ["Travis County, TX", "Williamson County, TX"],
"primaryCategory": "Plumber",
"categories": ["Plumber", "Plumbing Renovation", "Commercial Plumber"],
"businessType": "Limited Liability Company (LLC)",
"yearsInBusiness": 3,
"licenses": [
{
"licenseNumber": "44648",
"expirationDate": "2026-11-30",
"agencyName": "Texas State Board of Plumbing Examiners",
"agencyPhone": "800-8456584"
}
],
"hours": [{"day": "M", "hours": [{"opens": "07:00", "closes": "16:00"}], "note": null}],
"reviewsTotal": 1,
"averageReviewRating": 1.0,
"complaintsTotal": 0,
"complaintResolutionRate": null,
"socialProfiles": null,
"reviews": null,
"complaints": null,
"logoUrl": "https://m.bbb.org/prod/ProfileImages/2024/1f7a8bd4-fdd2-45ca-8963-5c246e1d3b85.png",
"scrapedAt": "2026-01-01T00:00:00Z",
"changeType": "NEW",
"changedFields": [],
"firstSeenAt": "2026-01-01T00:00:00Z",
"lastSeenAt": "2026-01-01T00:00:00Z"
}

Plan Requirement

This actor uses pay-per-event pricing: one charge when the run starts, one charge per business profile returned, and one extra charge per business only when reviews and/or complaints scraping is turned on. Businesses suppressed by Incremental mode, or whose detail pages could not be read, are never billed the extra charge. The default proxy setting (Residential) works out of the box; if your Apify plan does not include Residential, the actor automatically continues on a backup connection, with no action needed.

FAQ

How much does it cost?

You pay per business profile returned, plus an extra charge per business only when Scrape customer reviews or Scrape complaints is switched on. The Pricing tab shows the current rates for your plan. Use Max businesses to cap the cost of any run.

This actor collects only publicly available business profile data that BBB itself publishes. You are responsible for how you use it: follow BBB's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution, particularly of customer reviews or complaint text.

Can I get only new or changed businesses on a schedule?

Yes. Schedule the actor and turn on Incremental changes for scheduled runs. The first run returns everything as NEW; later runs return only NEW and UPDATED businesses by default. Turn on Emit unchanged or Emit expired only if you also want unchanged or disappeared businesses returned (and billed). EXPIRED needs a complete, uncapped scan; REAPPEARED only shows up for a business an earlier complete run had already marked EXPIRED.

What is the difference between Resume and Incremental mode?

Resume (resumeFromRunId) continues one specific interrupted run so you do not pay twice for businesses it already collected. Incremental mode is for running the same search or URL list again and again on a schedule and getting only what changed. You can combine them, but only to bootstrap a brand-new monitoring campaign that has no saved state yet: the resumed businesses are seeded into the campaign's baseline as already present. Once a campaign has saved state, a run with Resume set fails with an error telling you to remove it or pick a different State key.

Why did my run fail instead of returning an empty dataset?

If BBB did not return any readable page for the entire run, even after the actor automatically tried every available connection, it stops with a clear error rather than reporting "no businesses found", so a real block is never confused with a genuinely empty search. Try again in a few minutes. If only some pages could not be read, the run still returns what it found, with a warning that the results may be incomplete.

Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.

๐Ÿ”— Want more leads data?

Pair this actor with these related scrapers from the same team:

๐Ÿ“‡ Yellow Pages AU
From $1/1K. Scrape business listings from Yellow Pages Australia by type and location...
๐Ÿ“‡ Yellow Pages NZ
From $0.8/1K. Scrapes business listings from Yellow.co.nz (New Zealand Yellow Pages)...
๐Ÿ“‡ PagesJaunes FR
From $0.8/1K. Scrapes business listings from PagesJaunes.fr (French Yellow Pages)...
๐Ÿ“‡ TrueLocal AU Directory Listings & Reviews Scraper
Scrape TrueLocal.com.au business listings by keyword, location, or URL. Extract names...
๐Ÿ“‡ HiPages Scraper
Scrape business listings from HiPages Australia by category, location, or URL. Extract...
๐Ÿ“‡ GoWork FR & DE Company Reviews and Profile Scraper
Extract company profiles from GoWork France and Germany, including contact details...

๐Ÿ‘‰ Browse all abotapi scrapers

๐Ÿ’ฌ Support & custom scrapers

  • ๐Ÿž Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
  • ๐Ÿ› ๏ธ Need another site, extra fields or a private build? Email contact@abotapi.com or message Telegram @abotapi.
  • โญ Enjoying it? A quick review on the actor page helps other users find it.