# Business-for-Sale Deal Screener & Valuation Tool (`craigtechservicesllc/business-for-sale-deal-screener`) Actor

Turn BizBuySell, BizQuest, BusinessesForSale, and other business-for-sale datasets into ranked acquisition opportunities using valuation, DSCR, cash-on-cash return, buy-box scoring, deduplication, and price-change tracking.

- **URL**: https://apify.com/craigtechservicesllc/business-for-sale-deal-screener.md
- **Developed by:** [Daniel Craig](https://apify.com/craigtechservicesllc) (community)
- **Categories:** Automation, Developer tools, Other
- **Stats:** 2 total users, 1 monthly users, 83.3% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 scored listings

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Business-for-Sale Deal Screener & Valuation Analyzer

Turn mixed business-for-sale listing exports into a ranked acquisition shortlist—without scraping, contacting sellers, or sending deal data to an AI service.

The Actor accepts raw records through an `items` array or Apify `datasetId`, recognizes common marketplace fields, calculates acquisition and financing metrics, scores each unique listing from 0–100, and explains every pass or rejection. Add `previousDatasetId` to spot new listings, price changes, removals, and conservative relists.

### What you get

- Normalized asking price, revenue, cash flow, SDE, profit, industry, location, ID, title, URL, source, and currency.
- Price-to-SDE, price-to-revenue, cash-flow yield, estimated annual debt service, DSCR, equity investment, and cash-on-cash return.
- Configurable buy-box thresholds, scoring weights, financing assumptions, and scoring benchmarks.
- Deterministic score, A–F grade, pass/fail, strengths, rejection reasons, and data-quality warnings.
- Deduplication by source/listing ID, normalized URL, then conservative normalized title + location.
- Optional comparison statuses: `new`, `unchanged`, `price-drop`, `price-increase`, `removed`, and `relisted`.
- Original input preserved verbatim under `original`, with `aliasMatches` showing which source field supplied each normalized value.
- A separate `SUMMARY` record with totals, grades, pass/fail counts, rejection reasons, duplicates, warnings, sources, and changes.

### What this Actor does not do

It does not visit listing sites, bypass access controls, use a proxy, contact a seller, call an external AI API, or fetch exchange rates. It only processes records you supply. Currency conversion occurs only when you provide an exchange rate.

### Quick start

Paste records directly:

```json
{
  "items": [
    {
      "source": "BizBuySell",
      "listingNumber": "BBS-101",
      "title": "Commercial Cleaning Company",
      "category": "Cleaning",
      "location": "Chicago, Illinois",
      "askingPrice": "$650,000",
      "grossRevenue": "$1,250,000",
      "cashFlow": "$240,000",
      "sde": "$225,000",
      "url": "https://www.bizbuysell.com/example-listing"
    }
  ],
  "financing": {
    "downPaymentPercent": 20,
    "annualInterestRatePercent": 10,
    "loanTermYears": 10
  },
  "buyBox": {
    "minScore": 70,
    "maxAskingPrice": 750000,
    "minRevenue": 500000,
    "minSde": 150000,
    "maxPriceToSde": 3.5,
    "minDscr": 1.25
  }
}
```

Or analyze an existing Apify dataset:

```json
{
  "datasetId": "YOUR_CURRENT_DATASET_ID",
  "previousDatasetId": "YOUR_PREVIOUS_DATASET_ID",
  "buyBox": {
    "minScore": 75,
    "maxAskingPrice": 1500000,
    "maxPriceToSde": 3.25,
    "minDscr": 1.5,
    "allowedLocations": ["Illinois", "Wisconsin"],
    "excludedIndustries": ["Restaurant"]
  }
}
```

Exactly one of `items` and `datasetId` is required.

### Example result

```json
{
  "source": "BizBuySell",
  "listingId": "BBS-101",
  "title": "Commercial Cleaning Company",
  "financials": {
    "currency": "USD",
    "askingPrice": 650000,
    "revenue": 1250000,
    "cashFlow": 240000,
    "sde": 225000,
    "profit": null
  },
  "metrics": {
    "priceToSde": 2.8889,
    "priceToRevenue": 0.52,
    "cashFlowYield": 0.3692,
    "estimatedAnnualDebtService": 82462.06,
    "dscr": 2.7285,
    "equityInvestment": 130000,
    "cashOnCashReturn": 1.0964
  },
  "score": 96,
  "grade": "A",
  "passed": true,
  "change": { "status": "new" }
}
```

The actual result also contains reasons, warnings, score breakdown, normalized identity fields, alias matches, converted base-currency values, and the complete original record. See `examples/sample-output.json` in the source package.

### Marketplace compatibility

The Actor is field-shape compatible rather than tied to a particular scraper. It recognizes common exports from:

| Source | Common fields recognized |
| --- | --- |
| BizBuySell | `listingNumber`, `askingPrice`, `grossRevenue`, `cashFlow`, `sde`, `category`, `location` |
| BizQuest | `listing_id`, `list_price`, `annual_revenue`, `seller_cash_flow`, `business_category`, `business_location` |
| BusinessesForSale | `businessId`, `price`, `revenue`, `ownerBenefit`, `industry`, structured `location` |
| Flippa | `listing_id`, `asking_price`, `revenue_average`, `profit_average`, `property_type`, `country_name` |
| Empire Flippers | `listing_number`, `listing_price`, `monthly_revenue`, `monthly_net_profit`, `monetization`, `country` |

Monthly fields such as Flippa `revenue_average` / `profit_average` and Empire Flippers `monthly_revenue` / `monthly_net_profit` are annualized by multiplying by 12. Alias matching also works inside nested objects.

### Metrics and assumptions

`earningsBasis: "auto"` uses SDE first, then cash flow, then profit for DSCR and cash-on-cash calculations. Set it explicitly to `sde`, `cashFlow`, or `profit` if your acquisition model requires one definition.

Debt service uses a standard fully amortizing monthly-payment formula. Equity investment equals down payment + closing costs + working capital. Cash-on-cash return is `(selected annual earnings - annual debt service) / equity investment`.

These calculations are screening estimates, not lender underwriting or a certified valuation.

### Scoring

The six score components are:

| Component | Default weight |
| --- | ---: |
| Price / SDE | 25 |
| Price / revenue | 10 |
| Cash-flow yield | 15 |
| DSCR | 20 |
| Cash-on-cash return | 20 |
| Data completeness | 10 |

Weights can be any non-negative values and are normalized to 100. Scores are deterministic: the same record and configuration produce the same score. Grades are A ≥ 85, B ≥ 70, C ≥ 55, D ≥ 40, and F below 40.

Buy-box rules determine pass/fail. If a threshold is configured and its required data is missing, the listing fails by default. Set `failOnMissingThresholdData` to `false` to leave that threshold unevaluated instead.

### Currency handling

Source-currency ratios remain calculable without conversion. Price-based buy-box checks use `baseCurrency` and require a user-supplied rate when the source currency differs.

```json
{
  "baseCurrency": "USD",
  "defaultCurrency": "EUR",
  "exchangeRates": {
    "USD": 1,
    "EUR": 1.17,
    "GBP": 1.34
  }
}
```

Each rate means “units of base currency per one unit of source currency.” The Actor never retrieves or claims a live rate.

### Comparison rules

Current and previous records are matched in this order:

1. Same normalized source + listing ID.
2. Same normalized URL after removing query parameters, fragments, `www`, and trailing slashes.
3. Same conservative normalized title fingerprint + location.

A title/location-only match with a changed ID or URL is `relisted`. An unmatched previous record is emitted as `recordType: "removed"` when `includeRemovedRecords` is true. Removed records have no score and do not trigger the `scored-listing` event.

### Warnings

Warnings identify missing asking price, revenue, earnings, title, or location; assumed or unconvertible currency; invalid URLs; negative financial values; conflicting aliases; earnings exceeding revenue; and materially inconsistent SDE/cash-flow/profit figures.

Warnings do not silently rewrite the source data. Review them before relying on the screen.

### Output and integrations

- Default dataset: scored current listings plus optional removed records.
- Default key-value store: `SUMMARY` JSON.
- Dataset views: deal screen and normalized records.
- Strict runtime schemas: input, output record, and summary.

Outputs can be exported from Apify as JSON, CSV, Excel, XML, RSS, or JSONL and used with webhooks, schedules, Make, Zapier, n8n, or the Apify API.

### Pricing

- Actor start: **$0.00005** per run.
- Scored listing: **$0.01** per unique current listing.
- Duplicates and removed records are not scored-listing events.

To prevent double charging, the publication configuration removes the automatic `apify-default-dataset-item` event and uses the custom `scored-listing` event. A 100-listing run costs $1.00005 before any Apify Store plan discount.

### Privacy and security

No third-party API keys are requested. The Actor does not scrape, browse, contact sellers, or transmit listing content to outside services. Data remains in the Apify storages associated with the run and is subject to the retention and access settings on your Apify account.

### Limits

- Up to 100,000 supplied `items` per run under the strict input schema.
- Default memory: 512 MB; increase toward 1 GB for unusually large or deeply nested records.
- Objects and arrays inside `original` are preserved, which increases output size for very large source records.
- Source sites can change export field names. Unrecognized fields remain preserved and produce missing-data warnings rather than guessed values.

### Disclaimer

This Actor is a preliminary screening tool. It does not verify listing claims, replace financial/legal/tax due diligence, provide investment advice, or produce a certified business valuation. Confirm all figures and financing terms with qualified professionals before making an acquisition decision.

# Actor input Schema

## `items` (type: `array`):

Raw listing objects. Use this or datasetId, not both.

## `datasetId` (type: `string`):

Dataset containing raw listing objects. Use this or items, not both.

## `previousDatasetId` (type: `string`):

Optional earlier raw or Actor-output dataset used for change detection.

## `baseCurrency` (type: `string`):

Three-letter uppercase currency for monetary buy-box thresholds.

## `defaultCurrency` (type: `string`):

Currency assumed when a listing supplies no code or symbol.

## `exchangeRates` (type: `object`):

Map of uppercase currency code to units of baseCurrency per one source-currency unit.

## `financing` (type: `object`):

Loan and equity assumptions used for debt service, DSCR, and cash-on-cash return.

## `buyBox` (type: `object`):

Only supplied thresholds are enforced. Monetary thresholds use baseCurrency.

## `scoringWeights` (type: `object`):

Non-negative weights are normalized automatically and must total more than zero.

## `scoringBenchmarks` (type: `object`):

Reference points used when a matching buy-box threshold is absent.

## `deduplication` (type: `object`):

Controls exact source ID, URL, and conservative title/location deduplication.

## `includeRemovedRecords` (type: `boolean`):

Include unmatched previous listings as unscored, unbilled removed records.

## Actor input object example

```json
{
  "items": [
    {
      "source": "BizBuySell",
      "listingNumber": "SAMPLE-001",
      "title": "Commercial Cleaning Company",
      "category": "Cleaning Services",
      "location": "Chicago, Illinois",
      "currency": "USD",
      "askingPrice": 650000,
      "grossRevenue": 1250000,
      "cashFlow": 240000,
      "sde": 225000,
      "url": "https://example.com/business-for-sale/sample-001"
    }
  ],
  "baseCurrency": "USD",
  "defaultCurrency": "USD",
  "exchangeRates": {
    "USD": 1
  },
  "includeRemovedRecords": true
}
```

# Actor output Schema

## `records` (type: `string`):

Default dataset containing unique scored current listings and optional unbilled removed records.

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

Aggregate totals, grades, matches, rejection reasons, duplicates, warnings, sources, and changes.

# 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 = {
    "items": [
        {
            "source": "BizBuySell",
            "listingNumber": "SAMPLE-001",
            "title": "Commercial Cleaning Company",
            "category": "Cleaning Services",
            "location": "Chicago, Illinois",
            "currency": "USD",
            "askingPrice": 650000,
            "grossRevenue": 1250000,
            "cashFlow": 240000,
            "sde": 225000,
            "url": "https://example.com/business-for-sale/sample-001"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("craigtechservicesllc/business-for-sale-deal-screener").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 = { "items": [{
            "source": "BizBuySell",
            "listingNumber": "SAMPLE-001",
            "title": "Commercial Cleaning Company",
            "category": "Cleaning Services",
            "location": "Chicago, Illinois",
            "currency": "USD",
            "askingPrice": 650000,
            "grossRevenue": 1250000,
            "cashFlow": 240000,
            "sde": 225000,
            "url": "https://example.com/business-for-sale/sample-001",
        }] }

# Run the Actor and wait for it to finish
run = client.actor("craigtechservicesllc/business-for-sale-deal-screener").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 '{
  "items": [
    {
      "source": "BizBuySell",
      "listingNumber": "SAMPLE-001",
      "title": "Commercial Cleaning Company",
      "category": "Cleaning Services",
      "location": "Chicago, Illinois",
      "currency": "USD",
      "askingPrice": 650000,
      "grossRevenue": 1250000,
      "cashFlow": 240000,
      "sde": 225000,
      "url": "https://example.com/business-for-sale/sample-001"
    }
  ]
}' |
apify call craigtechservicesllc/business-for-sale-deal-screener --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,craigtechservicesllc/business-for-sale-deal-screener"
        }
    }
}

```

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/hFH3iRInjfKctNGor/builds/ylryjaiMMTMxkaWbG/openapi.json
