# Business Website Booking & Contact Finder (`natstandridge/business-website-action-links`) Actor

Find booking, reservation, ordering, quote and contact links on public business websites. Get provider names, public emails and phones, source URLs and page coverage.

- **URL**: https://apify.com/natstandridge/business-website-action-links.md
- **Developed by:** [Nathaniel Standridge](https://apify.com/natstandridge) (community)
- **Categories:** Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 inspected business websites

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

## Business Website Booking & Contact Finder

Turn a list of business websites into an inventory of the actions a visitor can take: book an appointment, reserve a table, order online, request a quote, or contact the business. Each observed link includes its source page and label. Public email links, telephone links and social profiles are included alongside the action links.

Use the website column from a Google Maps export, a local directory, or your own business list. This Actor reads the supplied websites; it does not call a Maps scraper or require a paid upstream Actor.

### Price

**$1 per 1,000 inspected websites ($0.001 each). No startup fee. Platform usage is included.**

One result is charged when the supplied page is successfully fetched and inspected and its website record is delivered to the dataset. The record includes up to two additional selected pages at the same price. A readable site with no observed action links is still an inspected website. If a secondary page fails, the website record is charged and clearly marks partial coverage. Failed or blocked supplied pages, exact normalized duplicate inputs and unprocessed websites do not generate a result charge.

Set Apify's maximum run cost to control the number of results. For example, $0.01 allows up to 10 inspected websites. There are no separate page, email, phone or booking-link charges.

### Input

```json
{
  "urls": ["https://www.delfinasf.com/"],
  "maxPagesPerSite": 3,
  "maxRunSeconds": 180
}
```

| Field | Meaning |
| --- | --- |
| `urls` | 1–50 public HTTPS URLs. Include a path when you want a particular location page. Credentials, IP literals, private hosts and nonstandard ports are rejected. |
| `maxPagesPerSite` | 1–3, default 3. Inspect the supplied page and at most two same-host contact, about or location pages linked from it; contact pages are prioritized, then locations. |
| `maxRunSeconds` | 30–180, default 180. Work budget; the run reports partial processing when exhausted. Keep the platform timeout at least 60 seconds longer. |

Fragments and the explicit HTTPS port are normalized before deduplicating inputs. Different paths or query strings remain different inputs and can produce separate billed results. Redirect aliases are not automatically merged. For one result per business, submit one URL per business.

### Output

Each dataset row includes:

- `inputUrl`, `finalUrl`, `title`, `checkedAt` and whether the supplied URL redirected to a different host.
- `actionLinks`: observed `booking`, `reservation`, `ordering`, `quote` and `contact` links with URL, label, provider when recognized, classification reason and `sourceUrl`.
- `emails` and `phones`: explicit public `mailto:` and `tel:` links with source URLs.
- `socialLinks`: recognized profile links, excluding recognized sharing URLs.
- `pages`: the pages actually inspected.
- `coverage`: selected-page status, failed secondary pages, page limit and truncation indicators. `absenceIsConclusive` is always `false`.

For example, a page containing an OpenTable reservation link can produce this action entry:

```json
{
  "kind": "reservation",
  "url": "https://www.opentable.com/r/example-restaurant",
  "provider": "OpenTable",
  "label": "Reserve a table",
  "sourceUrl": "https://restaurant.example/",
  "classification": "provider_host_and_english_label"
}
```

This is an illustrative link, not a live restaurant result. Download dataset rows as JSON, CSV or Excel; JSON preserves the nested evidence best. The `OUTPUT` key-value record reports website counts, failures, duplicate inputs, processing completeness and any spending or time limit that stopped the run.

### What the check establishes

The Actor finds links present in the static HTML it receives. It recognizes selected booking and ordering provider domains and uses conservative English link-text patterns for other actions. Provider recognition describes a link's destination; it does not verify an account, availability, prices, business ownership or whether a checkout works.

It never opens external booking or ordering links, submits forms, sends messages, verifies email delivery, logs in or circumvents access controls. The supplied page may redirect to another public HTTPS host; additional pages must stay on the resulting hostname. Robots exclusions, DNS safety checks, bounded redirects, timeouts and response-size limits apply.

JavaScript-only widgets, iframe contents, image-only controls without a usable text, accessible label or image description, plain-text phone numbers and obfuscated emails may be missed. Non-English action labels are not comprehensively classified. Some social/profile or provider URLs may be unrecognized. A generic contact link does not prove that a working contact form exists. No observed booking link does **not** mean the business lacks online booking.

Coverage is marked partial when discovered follow pages exceed the selected page limit, extraction is truncated, or a selected follow page fails. A run can finish processing all input websites while some website records have partial page coverage. The crawl covers the selected pages, not the whole website. Each site has a 35-second inspection budget, including a 15-second supplied-page limit and up to 12 seconds per additional page. HTML bodies are limited to 2 MB. Evidence arrays share a 750 KB output budget; unusually large results are marked truncated with partial coverage. Slow or blocked sources can reduce coverage. Failed supplied-page fetches are reported in `OUTPUT` and are not charged; if every supplied page fails, the run fails with no result charge.

### Repeat runs and recovery

Each new run is a fresh check and can be charged again. Within a resumed run, confirmed dataset rows are reconciled before more work starts. A dataset append with an unknown outcome is not blindly retried: the run stops with an explicit delivery error so it cannot silently duplicate a charge. Keep the original run's storage intact during recovery.

### Support

Report a public URL, the expected visible link, and the relevant run ID through this Actor's Issues tab. Avoid sharing login credentials, signed URLs or private customer data. Site layouts and provider destinations can change; the observed evidence and coverage fields make results auditable.

# Actor input Schema

## `urls` (type: `array`):

1–50 public HTTPS website URLs. Paste website URLs from a Maps export or your own list. Exact normalized duplicates are inspected once. Do not supply signed URLs, credentials or private hosts.

## `maxPagesPerSite` (type: `integer`):

The supplied page plus up to two contact, about or location links discovered on that page, on the same final hostname.

## `maxRunSeconds` (type: `integer`):

Stops starting new website checks after this time. A partial run reports remaining work; use a platform timeout at least 60 seconds longer.

## Actor input object example

```json
{
  "urls": [
    "https://www.delfinasf.com/"
  ],
  "maxPagesPerSite": 3,
  "maxRunSeconds": 180
}
```

# Actor output Schema

## `websites` (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 = {
    "urls": [
        "https://www.delfinasf.com/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("natstandridge/business-website-action-links").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 = { "urls": ["https://www.delfinasf.com/"] }

# Run the Actor and wait for it to finish
run = client.actor("natstandridge/business-website-action-links").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 '{
  "urls": [
    "https://www.delfinasf.com/"
  ]
}' |
apify call natstandridge/business-website-action-links --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,natstandridge/business-website-action-links"
        }
    }
}
```

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/aTPb9dvT4oY9UZ1wA/builds/cySOr2xbPv3cGXBug/openapi.json
