BBB Scraper - Business Ratings, Reviews, Complaints & B2B Leads
Pricing
from $1.20 / 1,000 business results
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
Maintained by CommunityActor stats
1
Bookmarked
19
Total users
12
Monthly active users
0.57 hours
Issues response
11 hours ago
Last modified
Categories
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.
| Field | Example |
|---|---|
businessId | "0825-1000000000" |
name / alternateNames | "Example Plumbing Co." / ["Example Plumbing Co., LLC"] |
profileUrl | BBB 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 / accreditedDate | true / "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 / longitude | 30.27 / -97.74 |
serviceAreas | ["Travis County, TX"] |
primaryCategory / categories | "Plumber" / ["Plumber", "Plumbing Renovation"] |
businessType / yearsInBusiness | "Limited Liability Company (LLC)" / 3 |
licenses | number, expiration date and issuing agency (when on file) |
hours | opening and closing time per day (when published) |
reviewsTotal / averageReviewRating | 1 / 1.0 |
complaintsTotal / complaintResolutionRate | 0 / null |
socialProfiles | the business's own linked social accounts (when published) |
logoUrl | link to the business's logo image |
alerts / governmentActions | BBB 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 / leaveReviewUrl | true / 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
- Pick a mode:
search(business term plus location) orurl(paste BBB links). - Fill in the fields for that mode, and add filters such as accredited-only or a search radius.
- Optionally turn on Scrape customer reviews and/or Scrape complaints, and set their page caps.
- 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 ApifyClientclient = 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 by plain names, or paste any BBB link
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 shape | Example | Supported? |
|---|---|---|
| USA profile | https://www.bbb.org/us/ca/los-angeles/profile/plumber/fords-plumbing-1216-100081178 | โ |
| Canada profile | https://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=N | https://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 pages | https://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 profiles | https://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 classifiedNEW,UPDATED(withchangedFields),UNCHANGED(suppressed and not billed unlessemitUnchangedis on), orEXPIRED.EXPIREDis only produced once a run has scanned the full tracked scope withemitExpiredon and neither Max businesses nor Max pages capped it, and never on a run that used Resume.REAPPEAREDonly appears for a business a previous complete run already markedEXPIRED, if that same business turns up again on a later run; it is not something an ordinary rerun produces on its own.stateKeynames 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.
- Authorize a connector once under Apify, Settings, API & Integrations (Notion, Linear, Airtable, or Apify).
- Select it under Pipe results into your apps (
mcpConnectors) in this actor's input. - 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. - 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
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | search | search (business term plus location) or url (paste BBB links). |
searchTerms | array | none (prefill: ["plumber"]) | Business terms to search. Search mode only. |
location | string | none (prefill: "Los Angeles, CA") | City plus state/province, or a ZIP/postal code. Empty searches nationwide. |
country | string | USA | USA or CAN. |
locations | array | none | Extra 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. |
sort | string | Relevance | Relevance, Rating, Distance, AToZ or ZToA, applied by BBB itself. |
distance | integer | none (BBB's own default radius applies) | Limit results to within this many miles of the location, using BBB's own distance filter. |
accreditedOnly | boolean | false | Only return BBB-accredited businesses from search results and pasted search-result links; a directly pasted profile link is always returned regardless of accreditation. |
minRating | string | none (every rating) | Minimum BBB letter grade, A+ to F; unrated (NR) businesses are skipped. Same scope as accreditedOnly. |
category | string | none | One BBB category, by name ("Commercial Plumber") or BBB category code ("10113-100"). Search mode only. |
stateFilter | string | none | 2-letter state/province code applied by BBB, most useful for a nationwide search. Search mode only. |
requestQuoteOnly | boolean | false | Only businesses that accept "Get a Quote" requests through BBB. Search mode only. |
servesMyAreaOnly | boolean | false | Only businesses BBB lists as serving the searched location. Search mode only. |
startUrls | array | none (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. |
maxItems | integer | 20 | Hard cap on businesses returned for the whole run. 0 = unlimited. |
maxPagesPerQuery | integer | none (empty = unlimited) | Result pages walked per search term or search URL (15 businesses per page). |
proxy | object | {"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. |
scrapeReviews | boolean | false | Also collect each business's customer reviews. Adds a per-business charge. |
scrapeComplaints | boolean | false | Also collect each business's complaints. Adds a per-business charge. |
maxReviewPages | integer | 5 | Cap on review pages per business. 0 = unlimited, stops at BBB's last reported page. |
maxComplaintPages | integer | 5 | Cap on complaint pages per business. 0 = unlimited, stops once every complaint BBB counts has been collected. |
maxReviewsPerBusiness | integer | 20 | Cap on reviews per business, on top of maxReviewPages; whichever is hit first stops. 0 = unlimited. |
maxComplaintsPerBusiness | integer | 0 | Optional cap on complaints per business, on top of maxComplaintPages. 0 = unlimited. |
resumeFromRunId | string | none | Continue one specific interrupted run by its run or dataset id. |
incrementalMode | boolean | false | Turn on for recurring monitoring; returns only new and changed businesses on later runs. |
stateKey | string | none (auto-derived from the search/URL and detail settings) | Name or share one monitoring campaign's state. |
emitUnchanged | boolean | false | Also return (and bill) businesses unchanged since the last run, marked UNCHANGED. |
emitExpired | boolean | false | Also return (and bill) businesses no longer found, marked EXPIRED. Only produced on a complete, uncapped, non-resumed scan. |
ignoreFieldsForChanges | array | none | Output fields (or dotted nested paths like complaints.respondedAt) that should not count as a change in Incremental mode. searchKeyword/searchLocation never count. |
mcpConnectors | array | none (skip) | Optional: send a summary of each business to apps authorized under Integrations. |
notionParentPageUrl | string | none | Notion connector only: page under which business pages are created. |
maxNotifyListings | integer | 50 | Cap 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.
Is it legal to scrape BBB.org?
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.