BusinessesForSale.com Scraper at a glance
BusinessesForSale.com Scraper is a ready-to-run Apify actor from abotapi for collecting automation data. Its documented output includes id, listing id, change type (incremental mode), url and title, which you can export as JSON, CSV or Excel or read through the Apify API. It costs $3.00 per 1,000 results on the Apify Free plan and from $1.50 on paid plans, with smaller per-run or optional feature charges listed on Apify.
- Runs on
- Apify cloud: console, API, schedules and integrations
- Data type
- Automation, Lead Generation, Real Estate
- Pricing
- $3.00 per 1,000 results (Free plan), from $1.50 on paid plans
- Output
- Documented below · JSON, CSV, Excel
- Inputs
- 41 options
- Run success
- 100.0% of completed public runs in the last 30 days
From $1.5/1K. Extract businesses and franchises listed for sale on BusinessesForSale.com. Search by keywords, country, price, revenue, profit, sort, or use search/listing URLs. Returns title, asking price, revenue, cash flow, location, premises size, employees, photos, categories, and description.
What BusinessesForSale.com Scraper can do
- Two ways to start. Build a search from filters (keywords, country, price/revenue/cash-flow ranges, sort order) or paste any search-result URL, both of which walk as many result pages as you ask for, or paste an individual listing URL for a single record.
- Rich detail on demand. Turn on detail enrichment for the full description, every gallery photo, premises size, employee count, year established, trading hours and lease/freehold tenure, on top of the headline financials.
- Server-side filters that actually narrow the catalogue. Keywords, global coverage or one of seven markets, asking price/revenue/cash-flow ranges, disclosed-only toggles, and eight sort orders.
- Global coverage. Search the worldwide catalogue or pin to a single market: United States, United Kingdom, Australia, Canada, Ireland, New Zealand or South Africa.
- Cost control built in. A results-only search run skips the detail-enrichment surcharge, and Max pages / Max listings cap any run before it grows (Max listings is clamped to 1,000,000 per run).
- Built for monitoring. Incremental mode returns only new and changed listings on repeat runs against the same search, and Resume continues one interrupted run without re-scraping what you already collected.
Choose what to collect
These inputs let you configure BusinessesForSale.com Scraper - Businesses, Prices & Financials. Open the actor on Apify to enter your values and review the current options.
| Input | What it controls |
|---|---|
modestring · required | Pick how to start. 'search' builds a query from the filters below. 'url' scrapes the listing/search URLs you paste. |
keywordsstring | Free-text search, for example 'restaurant', 'car wash', 'ecommerce'. Leave blank to list everything. |
countrystring | Limit results to one market, or 'Global' for every country. |
minPriceinteger | Minimum asking price (in the market's local currency). |
maxPriceinteger | Maximum asking price. |
minRevenueinteger | Minimum yearly sales revenue. |
maxRevenueinteger | Maximum yearly sales revenue. |
Show 34 more inputs
| Input | What it controls |
|---|---|
minProfitinteger | Minimum yearly cash flow / net profit. |
maxProfitinteger | Maximum yearly cash flow / net profit. |
priceDisclosedOnlyboolean | Hide listings whose asking price is 'on request'. |
revenueDisclosedOnlyboolean | Only listings that disclose revenue. |
profitDisclosedOnlyboolean | Only listings that disclose cash flow / profit. |
sortBystring | Result ordering. |
queryKeywordsarray | Run one search per keyword (each with the same filters). Overrides the single Keywords field; switches to search mode. |
querySortstring | Free-text sort: Newest, PriceAsc, PriceDesc, RevenueAsc, RevenueDesc, ProfitAsc, ProfitDesc (case-insensitive). Used only when Sort by is Default; unknown text is ignored. |
queryStateOrRegionstring | State or region name, added as a location word to the keyword search. |
queryCityOrAreastring | City or area name, added as a location word to the keyword search. |
queryIndustrySlugstring | Industry slug from the site, for example restaurants or car-washes. Searches /search/-for-sale. |
querySearchPathstring | Advanced: a custom path under the market host, must start with /search, for example /search/restaurants-for-sale. Filters above still apply. |
queryMinAskingPricestring | Short money string such as 10K or 1.5M (plain digits also work). Used only when the matching numeric filter above is empty. |
queryMaxAskingPricestring | Short money string such as 10K or 1.5M (plain digits also work). Used only when the matching numeric filter above is empty. |
queryMinGrossIncomestring | Short money string such as 10K or 1.5M (plain digits also work). Used only when the matching numeric filter above is empty. |
queryMaxGrossIncomestring | Short money string such as 10K or 1.5M (plain digits also work). Used only when the matching numeric filter above is empty. |
queryMinCashFlowstring | Short money string such as 10K or 1.5M (plain digits also work). Used only when the matching numeric filter above is empty. |
queryMaxCashFlowstring | Short money string such as 10K or 1.5M (plain digits also work). Used only when the matching numeric filter above is empty. |
urlsarray | Paste BusinessesForSale.com search-result URLs (keyword, category, filtered, sorted, any market) or individual listing (.aspx) URLs. Other pages (franchises landing, contact, RSS) are skipped with a warning. Left untouched, one example of each supported shape is scraped. Filter fields above are ignored. Results continue from the page number in each URL across the page limit below. |
startUrlsarray | Alias of the URLs field. When filled, the run switches to URL mode and scrapes these links. |
fetchDetailsboolean | Include the full per-listing record: full description, all photos, size, employees, trading hours, year established, categories, and lease/freehold tenure. Turn off for a faster, cheaper results-only run. |
pageSizestring | Listings fetched per request (50 or 100). Higher means fewer requests. |
maxPagesinteger | Optional limit on how many result pages each search / URL returns. Leave empty (or 0) for every available page, bounded by Max listings. |
maxListingsinteger | Stop after this many listings total. 0 = unlimited (bounded then only by Max pages). |
maxItemsinteger | Alias of Max listings; used only when Max listings is empty. 0 = unlimited. |
resumeFromRunIdstring | Paste a previous run ID or dataset ID to continue a full-catalogue walk across separate runs. Listings already collected there (matched by listing id) are skipped, so this run only saves the new ones. Leave empty for a normal fresh run. This continues ONE specific interrupted run, for recurring monitoring of the same search, use Incremental mode below instead. |
incrementalModeboolean | Turn this on for daily/weekly or other recurring monitoring of the SAME search. The first run returns every matching listing as NEW. Later runs normally return only NEW, UPDATED, and REAPPEARED listings. UPDATED means a listing's own data changed: title, labels, location, asking price, revenue, cash flow, categories, images, description or seller details. The scrape timestamp is never compared, and the image host (CDN domain) is ignored, so only a different image path counts. Changed fields are listed in changedFields. Use "Ignore fields" to exclude more. Turn on "Emit unchanged" or "Emit expired" only when you also want those returned (and billed). State is kept separately per mode/keywords/country/filters/fetchDetails setup; use State key to name or deliberately share a monitoring campaign. Defaults off, so existing schedules are unaffected. To continue one specific interrupted run instead, use Resume from a previous run above. |
stateKeystring | Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently-configured runs. Leave empty to let the actor derive a key automatically from mode/keywords/country/filters/fetchDetails, different searches then never mix state with each other. |
emitUnchangedboolean | Off by default. Turn on to also return listings that have not changed since the last run, marked UNCHANGED. This returns, and bills, extra rows you already have, so leave it off unless you specifically want the full snapshot every run. |
emitExpiredboolean | Off by default. Turn on to also return listings that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked search, not when Max pages/Max listings capped it or when Resume was used. This returns, and bills, extra synthetic rows, so leave it off unless you need expiry tracking. |
ignoreFieldsForChangesarray | Optional. Field names that must NOT count as a change in incremental mode, e.g. images or labels. Use dot paths for nested fields (seller.name). The fields stay in the output; they are only left out of the comparison and changedFields. Changing this list does not make unchanged listings show up as UPDATED. |
mcpConnectorsarray | Optionally send the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify → Settings → Integrations, then select it here. The connector receives a condensed, human-readable summary per item (title + key fields), not the full JSON, the complete record stays in the dataset. Leave empty to skip. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com). |
notionParentPageUrlstring | URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors. |
maxNotifyListingsinteger | Cap on items written to each connector per run. Does not affect the dataset. |
Fields in the output
The documented dataset includes the fields below. Availability can depend on the source page and selected mode.
idIDlistingIdListing IDchangeTypeChange type (incremental mode)urlURLtitleTitlelistingClassTypelabelsLabelssearchCountryMarketlocationTextLocationlocationLocationlocationDetailLocation detailscountryCountryShow 34 more fields
regionRegionlocalityLocalityaskingPriceDisplayAsking priceaskingPriceValueAsking (num)priceCurrencyCurrencybusinessFunctionSale typerevenueDisplayRevenuerevenueValueRevenue (num)cashFlowDisplayCash flowcashFlowValueCash flow (num)tenureTenure (lease/freehold)sizeSqFtSize (sq ft)tradingHoursTrading hoursemployeesEmployeesyearsEstablishedEstablisheddescriptionDescriptiondetailsAll disclosed facts (verbatim) (object)categoriesCategoriesstatusStatusebitdaEBITDAffneFF&EinventoryValueInventory valuetotalPhotosTotal photoshasVideoHas videohasVirtualTourHas virtual tourmetadataPage metadata (object)isVaultTeaserVault teaserthumbnailCard thumbnailimageCountImagesimagesPhoto galleryscrapedAtScrapedchangedFieldsChanged fields (incremental mode)firstSeenAtFirst seen at (incremental mode)lastSeenAtLast seen at (incremental mode)Example output
An example from this actor’s documentation. Values are illustrative; this is not a fresh live result.
{
"id": "us:sample-coffee-shop",
"url": "https://us.businessesforsale.com/us/sample-coffee-shop.aspx",
"title": "Sample Coffee Shop For Sale",
"listingClass": "business",
"labels": [
"New",
"High Value"
],
"searchCountry": "us",
"locationText": "Sample City, Sample State, US",
"locationDetail": "Sample City ST 00000",
"country": "US",
"region": "Sample State",
"locality": "Sample City",
"askingPriceDisplay": "$500,000",
"askingPriceValue": 500000,
"priceCurrency": "USD",
"businessFunction": "Sell",
"revenueDisplay": "$300,000",
"revenueValue": 300000,
"cashFlowDisplay": "$120,000",
"cashFlowValue": 120000,
"tenure": "Lease",
"sizeSqFt": "1,200",
"tradingHours": "7 a.m. to 6 p.m.",
"employees": "5",
"yearsEstablished": "2015",
"description": "Full listing description text appears here when Fetch listing details is on.",
"categories": [
{
"name": "Cafes & Coffee Shops",
"url": "https://us.businessesforsale.com/us/search/cafes-coffee-shops"
}
],
"financials": {
"Asking Price": "$500,000",
"Revenue": "$300,000",
"Cash Flow": "$120,000"
},
"details": {
"Location": "Sample City ST 00000",
"Size in square feet": "1,200",
"Trading hours": "7 a.m. to 6 p.m."
},
"thumbnail": "https://www.businessesforsale.com/uploadsstatic/00000000mediumthumbnail.jpg",
"imageCount": 4,
"images": [
"https://us.businessesforsale.com/uploads/00000000.jpg"
],
"scrapedAt": "2026-01-01T00:00:00.000Z"
}Ways to use this data
- Business brokers and M&A advisors: build comparable-listing sets by market, price band and cash flow to benchmark a new mandate.
- Buy-side searchers and search funds: monitor a keyword or category for new listings and get notified only when something changes.
- Franchise researchers: pull franchise fee, investment and financial figures across markets to compare opportunities.
- Market analysts: track listing counts and price/revenue bands in a country over time.
- Lead generation teams: build outreach lists of business sellers in a given industry or region from public listing data.
Before you run
Start with a small input and check the resulting records against your expected fields. Source content and actor options can change; the current Apify listing is the reference for availability and pricing.
Questions about this actor
What data does BusinessesForSale.com Scraper return?
The documented output for BusinessesForSale.com Scraper includes id, listing id, change type (incremental mode), url, title, type, labels and market. Availability of each field depends on the source page and the selected mode.
How much does BusinessesForSale.com Scraper cost?
It costs $3.00 per 1,000 results on the Apify Free plan and from $1.50 on paid plans, with smaller per-run or optional feature charges listed on Apify. The Apify listing shows the current rate for every plan.
Can I get only new or changed listings on a schedule?
Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns NEW and UPDATED listings; UNCHANGED ones are suppressed (and not billed) unless you turn "Emit unchanged" on. EXPIRED and REAPPEARED are rarer: EXPIRED only appears after a run that scans the whole tracked search with no page/listing cap and no Resume in play, and only with "Emit expired" on; REAPPEARED only appears for a listing that was previously marked EXPIRED this way and is later found again.
What's the difference between Resume and Incremental mode?
Resume (resumeFromRunId) continues ONE specific interrupted run from a pasted run or dataset ID, so a large walk that stopped partway can pick up where it left off. Incremental mode is for running the SAME search again and again, typically on a schedule, to get only what changed since the last run. They solve different problems and are not meant to be combined: starting Incremental mode with resumeFromRunId set while that state key already has saved state fails the run with a clear message instead of mixing the two.
Why did my run fail instead of returning an empty dataset?
The run fails with a clear message only when there's no usable connection to start with, when every pasted individual listing URL comes back unreadable, when a pasted resumeFromRunId can't be read, or when Incremental mode is combined with Resume on a search that already has saved state. A search that gets blocked partway through ends normally instead, with zero or partial listings and warnings in the run log, so check the log and re-run for a fresh connection if that happens.
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.
