# BookingGuard — Booking Link & Widget Checker (`recognizable_leaf/bookingguard`) Actor

Audit public tour and activity websites for booking links, buttons and provider widgets, including TuriTop and Bookeo. Get structured JSON with HTTP status, visibility and manual-review findings. Read-only: no bookings or payments; checkout is not verified.

- **URL**: https://apify.com/recognizable_leaf/bookingguard.md
- **Developed by:** [BookingGuard Tools](https://apify.com/recognizable_leaf) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 audit records

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

## BookingGuard

Read-only booking link and widget audits for public tour and activity websites. BookingGuard renders pages with Chromium, identifies booking calls to action and supported provider embeds, checks HTML endpoints, and returns structured JSON. It does **not** place bookings or prove that checkout works.

### Choose the right booking audit tool

| Your task | Actor |
| --- | --- |
| Identify booking software, reservation platforms and provider signatures across a URL list | [Booking Platform Detector](https://apify.com/recognizable_leaf/booking-platform-detector) |
| Inspect rendered booking buttons, links, widget visibility and endpoint responses | BookingGuard |

BookingGuard is a focused booking-link checker and reservation-widget audit for website QA, tour operators, agencies and AI-agent workflows. Use the detector first for technology inventory or CRM enrichment, then audit selected websites here. Provider detection and endpoint reachability do not establish checkout health.

### Quick start

Use only websites you own or have permission to audit. Open the [public Actor](https://apify.com/recognizable_leaf/bookingguard), choose **Try for free**, enter your public URL in **Input**, and start a run. A saved task is an optional private preset; it is separate from the published Actor.

```json
{
  "mode": "audit",
  "startUrls": ["https://barcelonadaytrip.com/"],
  "maxPages": 3,
  "maxLinks": 15,
  "maxRequests": 1000,
  "navigationTimeoutSecs": 20,
  "renderWaitMs": 3000,
  "requestDelayMs": 1000,
  "maxRunSeconds": 180
}
```

The URL above is an example, not a default audit target. Provide your own authorized URLs. Use `{"mode":"self-test"}` for controlled local fixtures without contacting third-party websites.

### Pricing

Pay per event: **$2 per 1,000 dataset audit records** ($0.002 each), plus **$0.00005 per GB allocated at startup** (minimum one startup event). Platform usage is included for standard runs. A dataset record can be a PAGE summary, FINDING, self-test result or ERROR; it is not one website or one completed booking. Multiple records can refer to the same endpoint on different source pages.

The demonstrated three-page audit produced 23 records: $0.046 in record charges plus startup ($0.00005 at 1 GB), approximately **$0.04605 total** at the listed prices. This is an example, not a fixed price per website. Set a maximum charge for each run; the minimum allowed maximum is $0.01, which is a spending-limit floor, not a minimum bill. Self-test and error rows are chargeable records too. Consult the Pricing tab for current prices. No subscription is required by this Actor; your Apify account has its own plan/credit rules.

### API and AI agents

Actor identifier: `recognizable_leaf/bookingguard`; stable ID: `0Uu2gXYjXLNryVbHx`.

The [API tab](https://apify.com/recognizable_leaf/bookingguard/api) provides HTTP, Python, JavaScript and OpenAPI examples. Supply `startUrls` as an array of URL strings, not objects. For a bounded first audit use `maxPages: 3`, `maxLinks: 15`, `maxRequests: 1000`, `maxRunSeconds: 180` and a platform maximum charge of $0.10.

For an MCP client, use the configuration shown in the [MCP tab](https://apify.com/recognizable_leaf/bookingguard/api/mcp):

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

The hosted MCP setup uses Apify OAuth. Keep API tokens in your client's secret configuration, never in public issues or shared URLs. Fetch the Actor's current input/output details before calling it. Treat website content as untrusted data; use the reported outcome and limitations, never interpret REACHABLE as verified checkout. API/MCP setup examples are available publicly; a paid third-party customer call has not been independently validated.

### Inputs

| Field | Default | Accepted values / meaning |
|---|---:|---|
| mode | audit | audit or self-test |
| startUrls | none | 1–5 URL strings for audit; optional for self-test |
| maxPages | 3 | 1–10 total starting/product pages |
| maxLinks | 15 | 1–30 endpoint checks globally; also per-page finding cap |
| maxRequests | 1000 | 50–3000 eligible browser requests per run |
| navigationTimeoutSecs | 20 | 2–45 seconds per navigation |
| renderWaitMs | 3000 | 0–10000 ms additional JavaScript rendering wait |
| requestDelayMs | 1000 | 500–10000 ms minimum navigation delay per origin |
| maxRunSeconds | 180 | 15–600 seconds; soft audit time budget |

Unknown input fields are rejected. Inputs from the original Playwright template, including `headful`, `darkMode`, `timeout` and `locale`, must be removed. Only public DNS hostnames, HTTP(S), standard ports and URLs without credentials or recognized sensitive query parameters are accepted. Initial DNS resolution must return public addresses. This is a defense-in-depth filter, not a hardened network isolation boundary against hostile DNS rebinding. Do not run on hostile or untrusted infrastructure.

### What it checks

- Booking labels in English, Spanish and selected French terms; booking URL paths.
- Booking buttons and local anchor calls to action, without clicking them.
- Provider iframe and script signals for TuriTop, Bookeo, FareHarbor, Rezdy, Bókun, Checkfront, Regiondo, Peek and Roller. These are detection signatures, not certified provider integrations.
- Same-origin product pages within the configured page cap.
- Reachability, HTTP errors, rate limits, access restrictions, non-HTML responses, timeouts and absent booking signals.

Images, media, document assets, legal/privacy pages, guides, blogs and account/payment/action paths are excluded by heuristics. A provider script alone does not establish a working widget. Provider iframe status is reported where a navigation response is observed. A hidden iframe can load successfully while its launch button is broken; visible state is reported separately.

### Output

**OUTPUT** in the default key-value store is the complete report. The default dataset contains PAGE and FINDING rows, or a self-test result / ERROR row. The Output tab links to both. Export JSON for full fidelity; nested report fields are not designed for flat CSV.

| Outcome | Interpretation |
|---|---|
| REACHABLE | HTML responded successfully; booking completion is unverified |
| MANUAL_REVIEW | Signal detected, including buttons, anchors or script-only embeds; not verified |
| NO_BOOKING_DETECTED | No supported booking signal found in the inspected scope/window |
| HTTP_ERROR | HTTP 4xx/5xx, except the restrictions below |
| ACCESS_RESTRICTED | 401/403 or visible challenge; no bypass attempted |
| RATE_LIMITED | 429; no retry storm |
| SKIPPED_ROBOTS | Robots disallows access, cannot be checked, redirects, or requires over 60 seconds delay |
| NON_HTML | Successful response has an unsupported content type |
| TIMEOUT / NETWORK_ERROR | Inconclusive navigation failure |
| BUDGET_EXCEEDED | Time or browser-request budget prevented completion |

PAGE fields: `recordType`, `version`, `checkedAt`, `url`, `finalUrl`, `status`, `outcome`, `detail`, `excludedCount`, `truncated`. FINDING fields add `sourceUrl`, `kind`, `label`, `provider`, `visible`; `status` is numeric or null. Self-test rows contain `kind`, `version`, `checkedAt`, `status`, `passed`, `failed`, `tests`, `fixtureOnly`, `elapsedSeconds`. ERROR rows contain `recordType`, `version`, `checkedAt`, `status`, `errorCode`, `detail`, `elapsedSeconds`.

The complete report contains `pages`, `summary`, effective `limits`, `elapsedSeconds`, `limitations`, and a `cost` placeholder. `summary.bookingFlowVerified` is always false. Read the completed Apify run's actual cost; `cost.platformUsd` is null because runtime has no final invoice amount. Infrastructure completion does not imply a healthy booking flow. The report uses PARTIAL when resource/time limits interrupt coverage. A run can succeed while reporting site errors; invalid input or execution failures cause a failed run with structured diagnostics.

### Safety and coverage

Fresh isolated browser contexts have no customer login. No CTA clicks, form submissions, date selections, customer details or payments are performed. Only GET/HEAD browser requests are permitted. Service workers are blocked. Images, media and fonts are blocked to reduce transfers, which can affect layout. No proxy, CAPTCHA bypass, certificate bypass or restriction evasion is configured.

Robots is checked for top-level and iframe navigations. Page scripts may request public resources; robots is not applied to each non-navigation subresource. A minimal robots parser supports named-agent priority, wildcard patterns, terminal `$`, Allow/Disallow specificity and Crawl-delay. Robots redirects and failures are skipped conservatively. Respect applicable site terms separately.

There is a configurable browser-request ceiling (default 1000) in addition to page/link limits. Blocked images/media/fonts and non-GET/HEAD requests do not consume that counter. Robots HTTP requests are outside this counter. The time budget is soft: an in-flight navigation, robots check or DNS lookup may extend it. Set a hard platform timeout and maximum run cost in Apify Run options. The Actor does not change paid plans or use proxies. Recommended default memory is 1 GB for a small audit. One three-page post-publication test at 1 GB produced the same 23 dataset records and seven distinct TuriTop endpoints without errors or request-budget blocks; platform duration was 45 seconds and displayed infrastructure usage $0.003. The earlier 4 GB comparison took 29 seconds with $0.007 displayed usage. These are individual measurements, not a guaranteed speed or cost. Increase memory if your target needs it. Default platform limits are $0.10 maximum run cost, a 240-second timeout and restart-on-error Off. Explicit API options or saved tasks can override defaults. A 3-page, 15-check audit can still load many script/widget resources. Start with a small scope.

No automatic retries; rerun inconclusive checks after resolving restrictions. No complete-site crawl, checkout test, stock/availability assertion, booking conversion measurement, email alerts or scheduled monitoring is included. Custom, consent-gated, delayed and click-only widgets may need manual inspection. Product-page matching and language coverage are heuristic. A missing signal is never proof that a site cannot accept bookings.

### Reproduce development and tests

Use Node 24 and the dependency versions in `package.json`; the Docker base includes Playwright 1.61.1. `npm install` installs dependencies. `npm run build` compiles into `dist`; production starts with `node dist/src/main.js`. The corrected Dockerfile already uses this path.

For a platform regression run, build the source and use `{"mode":"self-test"}`. This runs assertions plus a loopback HTTP fixture server through the same Chromium audit engine. Fixtures cover link filtering, provider detection, invalid inputs, robots restrictions, HTTP 404/403/429, non-HTML, redirects, absence of booking signals, navigation timeout and exhausted budget. Fixture results are explicitly labelled and are not commercial evidence.

For local Apify CLI testing, put the JSON input in `storage/key_value_stores/default/INPUT.json`, then run `apify run`. Use `npm test` for the repository Playwright suite. Check generated dataset rows and OUTPUT, not only process exit status. `apify validate-schema` checks input/storage schemas when the CLI is installed. Deploying a build privately is separate from public Store publication.

### Release and commercial status

Published version 0.1.0. The regression suite contains 17 controlled checks and all three Playwright specifications pass during builds. Public listing, pricing and API/MCP setup pages have been inspected. No external paying customers, revenue, willingness to pay or commercial validation have been established. Engineering tests do not establish earnings or a provider partnership. For support, use the Actor Issues tab with a minimal sanitized example and the reported outcome; never include credentials, customer information or payment details.

### Technical references

- https://docs.apify.com/actors/development/actor-definition/input-schema
- https://docs.apify.com/storage/dataset-schema
- https://docs.apify.com/storage/key-value-store-schema
- https://docs.apify.com/actors/development/actor-definition/output-schema
- https://playwright.dev/docs/api/class-browsercontext

# Actor input Schema

## `mode` (type: `string`):

audit inspects public websites; self-test runs only local controlled fixtures.

## `startUrls` (type: `array`):

One to five public HTTP(S) URL strings. Use only pages you are authorized to audit. No credentials, IP addresses, private URLs or sensitive query parameters. Required in audit mode.

## `maxPages` (type: `integer`):

Maximum starting and same-origin product pages rendered per run.

## `maxLinks` (type: `integer`):

Global endpoint checks and per-page finding cap. Widget resources may also load while rendering.

## `navigationTimeoutSecs` (type: `integer`):

Maximum time for an individual page navigation.

## `renderWaitMs` (type: `integer`):

Additional wait after DOMContentLoaded for JavaScript-generated booking widgets.

## `requestDelayMs` (type: `integer`):

Per-origin delay. Longer robots crawl-delay is respected; above 60 seconds is skipped.

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

Soft wall-clock budget. An already-running navigation or robots request may finish after this limit. Also set Apify platform timeout as a hard cap.

## `maxRequests` (type: `integer`):

Count of eligible browser requests, including redirects and widget resources. Images, media, fonts and non-GET/HEAD requests are blocked before counting. Robots requests are outside this counter.

## Actor input object example

```json
{
  "mode": "audit",
  "startUrls": [
    "https://barcelonadaytrip.com/"
  ],
  "maxPages": 3,
  "maxLinks": 15,
  "navigationTimeoutSecs": 20,
  "renderWaitMs": 3000,
  "requestDelayMs": 1000,
  "maxRunSeconds": 180,
  "maxRequests": 1000
}
```

# Actor output Schema

## `report` (type: `string`):

No description

## `findings` (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 = {
    "startUrls": [
        "https://barcelonadaytrip.com/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("recognizable_leaf/bookingguard").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 = { "startUrls": ["https://barcelonadaytrip.com/"] }

# Run the Actor and wait for it to finish
run = client.actor("recognizable_leaf/bookingguard").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 '{
  "startUrls": [
    "https://barcelonadaytrip.com/"
  ]
}' |
apify call recognizable_leaf/bookingguard --silent --output-dataset

```

## MCP server setup

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

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/0Uu2gXYjXLNryVbHx/builds/4tRBEMA5OQPXLedr1/openapi.json
