# No Booking Finder — Local Business Opportunities (`segm/no-booking-finder`) Actor

Rank appointment-driven businesses by public demand, apparent online booking gaps and nearby competitor evidence.

- **URL**: https://apify.com/segm/no-booking-finder.md
- **Developed by:** [Segm Labs](https://apify.com/segm) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 results

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/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## OpportunityFilter — Local Business Opportunity Finder

### What it does

OpportunityFilter ranks appointment-driven businesses whose public websites show demand but no detected online-booking mechanism. It combines supplied source records, bounded static-HTML analysis, comparable businesses already in the same input, and deterministic evidence. It is a prospect-qualification aid for agencies, consultants and operators evaluating digital booking work; it does not contact businesses or claim that a booking feature is absent.

### Quick start

The simplest first run is the default Google Maps route: enter a search term, location and maximum number of records. Google Maps sourcing is performed by the configured upstream Actor and has separate provider charges. For a no-upstream-cost test, choose `source=direct` and provide normalized businesses, or choose `source=dataset` to reuse an existing Dataset.

Start with 20–100 source records. `maxResults` is a source-record budget; duplicates, malformed records, filters and missing fields can reduce the number of final rows. The default settings use bounded HTTP only, at most two pages per website, one retry and ten concurrent business tasks.

### What you provide

- Search route: `searchTerms`, `location`, `maxResults`, and optional filters.
- Dataset route: `datasetId` and `datasetAdapter` (`google_maps` or `normalized`).
- Direct route: an array of businesses; `name` is required and category, geography, demand fields and `websiteUrl` improve the evidence.

The complete input form is generated in `input_schema.json`. It rejects unknown fields, contradictory source selections and values outside the documented limits with a named diagnostic.

### What you receive

The default Dataset contains stable `schemaVersion=1.0` rows with a deterministic `resultId`, rank, normalized business data, website status, booking state, evidence, competitor comparison, score components, confidence and bounded errors. The `output_schema.json` links the ranked Dataset as JSON and the run summary in `OUTPUT`.

Example result shape (real fields, abbreviated):

```json
{
  "schemaVersion": "1.0",
  "resultId": "cbfe976bf2d5a5b35783f98e25f54421",
  "product": "NO_BOOKING_FINDER",
  "rank": 1,
  "status": "COMPLETE",
  "business": { "name": "Synthetic poor", "city": "Lyon", "category": "dentist" },
  "website": { "status": "AVAILABLE", "booking": { "state": "NOT_DETECTED" } },
  "opportunityScore": 59,
  "confidence": 0.65,
  "evidence": [],
  "errors": []
}
```

`NOT_DETECTED` is an observation, not proof of absence. Static HTML, consent walls, client rendering and inaccessible pages can hide a mechanism. Version 1 caps apparent gaps at 59/100 and preserves uncertain rows for manual review.

### API example

Replace `<ACTOR_ID>` with the account's secure value. Keep the token in the environment; do not put it in source control, a URL or a shell history.

```powershell
$input = Get-Content examples/input-direct.json -Raw
Invoke-RestMethod `
  -Method Post `
  -Uri "https://api.apify.com/v2/acts/<ACTOR_ID>/runs" `
  -Headers @{ Authorization = "Bearer $env:APIFY_TOKEN" } `
  -ContentType 'application/json' `
  -Body $input
```

Fetch the default Dataset through the run's `defaultDatasetId`, or use the output link rendered by `output_schema.json`.

### Cost and PPE status

The configured price is **$0.003 per result** written to the default Dataset, using the single `apify-default-dataset-item` event. A resumed run appends only missing rows; a new, independent run is a new processing request. Invalid input that produces no Dataset item produces no result event. Google Maps sourcing has separate upstream provider costs; check the current Apify pricing panel before running a search.

Remote qualification has verified one result/one event, zero results/zero events, ten results/ten events and a same-run restart without a duplicate event. These tests used a Free account and do not establish paid-customer revenue or payouts. The configured price is distinct from measured platform usage and developer earnings.

### Limitations and privacy

- No browser automation; JavaScript-only booking widgets may remain `UNKNOWN`.
- At most three same-origin pages, bounded response bytes, deadlines and retries.
- Competitor rates describe the supplied comparable sample, not the whole market.
- No authentication, CAPTCHA bypass, private data access, outreach or contact harvesting.
- Raw HTML is not persisted; Dataset and checkpoint retention is controlled by the Apify account owner.

### Support and version

For a failed run, inspect the structured `run_start`, `source_initialization`, `analysis_complete`, `run_summary`, `run_end` or `run_failed` records, then inspect `OUTPUT` and the default Dataset. `OUTPUT_CONFLICT` requires a fresh storage/run after checking the existing Dataset; an uncertain upstream launch must be verified before retrying.

This documentation describes OpportunityFilter package `1.0.4` and Actor version `1.3`. See the repository changelog and the production qualification for the exact release and remaining external gates.

# Actor input Schema

## `searchTerms` (type: `array`):

Appointment-driven businesses to search, for example dentist or barber.

## `location` (type: `string`):

One city and country, for example Lyon, France.

## `maxResults` (type: `integer`):

Maximum source records read. Upstream limits are split across search terms. Duplicates or malformed records can reduce final results.

## `source` (type: `string`):

Search Google Maps (paid upstream Actor), reuse a Dataset, or supply businesses directly.

## `businesses` (type: `array`):

Normalized records for source=direct. Name is required; supply category, geography, reviews and a website for useful scoring.

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

Cloud Dataset to reuse with source=dataset. No new Google Maps run is started.

## `datasetAdapter` (type: `string`):

Choose the Google Maps format or normalized OpportunityFilter business inputs.

## `minimumRating` (type: `number`):

0 disables the filter. Missing ratings fail a positive minimum.

## `minimumReviews` (type: `integer`):

0 disables the filter. Missing review counts fail a positive minimum.

## `analyzeWebsites` (type: `boolean`):

Inspect bounded static HTML and useful internal pages.

## `analyzeCompetitors` (type: `boolean`):

Reuse only the businesses already collected. No extra searches.

## `includeLowConfidenceResults` (type: `boolean`):

Keep partial, unscorable and low-confidence results for review.

## `minimumConfidence` (type: `number`):

Applied only when uncertain results are excluded. Confidence measures evidence completeness, not a calibrated probability.

## `competitorRadiusKm` (type: `number`):

Great-circle radius when both businesses have coordinates; otherwise same nonempty city and country.

## `minimumCompetitorSample` (type: `integer`):

Smaller samples reduce the competitor weight and confidence.

## `websiteConcurrency` (type: `integer`):

Maximum parallel business tasks. Transport additionally serializes requests to the same host.

## `requestTimeoutMs` (type: `integer`):

Bounded deadline for one request attempt, including redirects.

## `maxResponseBytes` (type: `integer`):

Maximum decompressed HTML response size.

## `maxPagesPerWebsite` (type: `integer`):

Homepage plus at most two same-origin pages; stop early when a booking mechanism is detected.

## `maxRetries` (type: `integer`):

Retry transient network errors, HTTP 429 and selected 5xx responses only.

## `upstreamActorId` (type: `string`):

Identifier of a compatible Actor. The adapter expects the documented compass schema.

## `upstreamBuild` (type: `string`):

Pinned tested input-schema build. Adjust when choosing another compatible Actor.

## `upstreamMaxChargeUsd` (type: `number`):

Apify pay-per-event limit passed to the upstream Actor. Platform compute/storage charges may be separate.

## `upstreamTimeoutSecs` (type: `integer`):

Maximum duration passed to the Google Maps Actor.

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

Nonnegative weights are normalized by their sum. Default: demand .35, booking gap .35, competitor gap .20, digital gap .10.

## Actor input object example

```json
{
  "searchTerms": [
    "dentist"
  ],
  "location": "Lyon, France",
  "maxResults": 100,
  "source": "google_maps",
  "datasetAdapter": "google_maps",
  "minimumRating": 0,
  "minimumReviews": 0,
  "analyzeWebsites": true,
  "analyzeCompetitors": true,
  "includeLowConfidenceResults": true,
  "minimumConfidence": 0.65,
  "competitorRadiusKm": 10,
  "minimumCompetitorSample": 3,
  "websiteConcurrency": 10,
  "requestTimeoutMs": 15000,
  "maxResponseBytes": 1000000,
  "maxPagesPerWebsite": 2,
  "maxRetries": 1,
  "upstreamActorId": "compass/crawler-google-places",
  "upstreamBuild": "0.14.750",
  "upstreamMaxChargeUsd": 2,
  "upstreamTimeoutSecs": 600,
  "scoringWeights": {
    "demand": 0.35,
    "bookingGap": 0.35,
    "competitorGap": 0.2,
    "digitalGap": 0.1
  }
}
```

# Actor output Schema

## `opportunities` (type: `string`):

No description

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

No description

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("segm/no-booking-finder").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("segm/no-booking-finder").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 '{}' |
apify call segm/no-booking-finder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,segm/no-booking-finder"
        }
    }
}
```

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/LvJjv7asgnDPnEOow/builds/qaqkOm4t3yzOuq79T/openapi.json
