# Web Data to Notion Dashboard (`grizzlygriff/web-data-to-notion-dashboard`) Actor

Scrape local businesses (restaurants, real estate, gyms, hotels, schools) or social content (YouTube, Instagram, TikTok) and get a polished Notion dashboard with table, gallery, and map views — one Actor run. Powered by Apify MCP Connectors: we never see your Notion token.

- **URL**: https://apify.com/grizzlygriff/web-data-to-notion-dashboard.md
- **Developed by:** [Griffin Trent](https://apify.com/grizzlygriff) (community)
- **Categories:** AI, Agents, Automation
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, NaN bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.10 / 1,000 mcp calls

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 a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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

## Web Data to Notion Dashboard

**Scrape eight curated verticals — restaurants, real estate, gyms, hotels, schools, YouTube, Instagram, and TikTok — and get a polished Notion dashboard with table, gallery, and map views in one Actor run.** Powered by [Apify MCP Connectors](https://docs.apify.com/platform/integrations/mcp-connectors): **Apify never sees your Notion token. Your authorization is brokered by Apify's MCP proxy and stays on the proxy side.**

> **🟢 New here? Here's the whole thing in 3 steps:**
>
> **1. Connect Notion once** (~60 sec) — [click to authorize](https://console.apify.com/account/integrations/mcp-connectors/new?serverUrl=https%3A%2F%2Fmcp.notion.com%2Fmcp), log in, click **Allow**. *This is the "MCP Connector": a secure, one-time link so this Actor can build a dashboard in your Notion. Apify never sees your Notion password or token.*
> **2. Paste a Notion page URL** — any blank page works; the dashboard lands inside it. (No page yet? In Notion: New page → name it "Apify Dashboards" → copy the URL. You'll reuse it on every run.)
> **3. Pick a vertical + location, then click Run.** Want to spend **$0** first? Set **Data source → "Sample data — preview only"** (keep Vertical = Restaurants) and Run to see exactly what you'll get — steps 1 and 2 are still needed (that's how the dashboard reaches your Notion), but there's no scrape charge.
>
> **What it costs:** the sample preview is **$0**. A real run is typically **$0.40–$1.20** and never exceeds the **$3 cap you control** (per run). Connecting Notion is free.

---

### What does Web Data to Notion Dashboard do?

Pick a vertical, enter a location or search query, pick your Notion workspace — the Actor scrapes the data, builds a structured Notion database, and adds table, gallery, and map views where they make sense. The whole thing happens in one run, typically in 30–90 seconds.

**Supported verticals:**

| Vertical | Upstream source | What you enter | Notion columns |
|---|---|---|---|
| Restaurants | Google Maps | City + State + ZIP | Name, Address, Lat/Lng, Rating, Price tier, Photos, Source, Maps link |
| Real estate | Zillow | City + State + ZIP | Name, Address, Lat/Lng, Rating, Price tier, Photos, Source, Maps link |
| Gyms | Google Maps | City + State + ZIP | Name, Address, Lat/Lng, Rating, Price tier, Photos, Source, Maps link |
| Hotels | Google Maps | City + State + ZIP | Name, Address, Lat/Lng, Rating, Price tier, Photos, Source, Maps link |
| Schools | Google Maps | City + State + ZIP | Name, Address, Lat/Lng, Rating, Price tier, Photos, Source, Maps link |
| YouTube | streamers/youtube-scraper | Search query or channel URL | Title, Channel, Views, Likes, Duration, Uploaded, Description, Video link, Thumbnail |
| Instagram | apify/instagram-scraper | Profile URL or hashtag URL | Caption, Username, Likes, Comments, Type, Posted, Photos, Post link |
| TikTok | clockworks/tiktok-scraper | Search query or hashtag | Caption, Creator, Likes, Views, Comments, Posted, Photos, Post link |

All five local-business verticals share the same column set (fields a source doesn't provide are left empty — e.g. price tier for schools). Content verticals get their own columns, listed above.

**Why through MCP Connectors specifically?** You authorize Notion once in Apify's settings. The Actor then writes to your workspace via Apify's MCP proxy — your Notion token never reaches the Actor's code, never appears in logs, and never leaves the proxy. The proxy also enforces a strict allowlist of which Notion tools this Actor can call (`notion-create-database`, `notion-create-pages`, `notion-create-view`, `notion-fetch`). Even if the Actor wanted to read your other workspaces, the proxy would block it.

### Why use this?

- **Moving to a new city?** Scrape restaurants/gyms/real estate in the destination ZIP, get a Notion dashboard, share it with your partner or roommate.
- **Local-market research?** A real estate agent, marketer, or analyst wanting a shareable snapshot of one ZIP's businesses without exporting CSVs and rebuilding charts.
- **Demoing MCP Connectors?** This Actor is the canonical example of the "scrape + write back" pattern from the Apify MCP Connectors launch — read the source for a copyable template.

### How to use Web Data to Notion Dashboard

1. **Authorize Notion** in Apify Console → Settings → MCP Connectors → Notion. (~60 seconds via Notion's standard OAuth popup.)
2. **Create or pick a parent page in Notion** — any blank page in your workspace works. **Recommended:** before pasting the URL, open that page in Notion and click Share → Publish to web. Every dashboard the Actor creates under that page will automatically inherit public access.
3. **Open this Actor's Input form.** Pick a vertical, then enter a location or query: for local-business verticals use City + State + ZIP (try `Austin, TX 78704` or `Cambridge, MA 02139` — a bare ZIP can match foreign postal codes); for YouTube/Instagram/TikTok enter a search query, profile/hashtag URL, or hashtag. Pick your Notion connection and paste the parent page URL.
4. **Click Run.** The status log shows what's happening step by step. Typical runs take 30–90 seconds.
5. **Open your dashboard** from the Output tab. If the parent page was already shared, you'll see a public URL ready to send to anyone. Otherwise, click Share → Publish to web in Notion to make it public.

### Input

| Field | Type | Required | Description |
|---|---|---|---|
| `vertical` | enum | ✓ | What to scrape. See verticals table above. |
| `location` | string | ✓ | **Local-business:** City + State + ZIP. Bare ZIPs match foreign postal codes — `37604` alone matches Pohang, South Korea. Use `Johnson City, TN 37604`. **YouTube:** search query or channel URL. **Instagram:** profile or hashtag URL (`https://www.instagram.com/apify/`). **TikTok:** search query or hashtag (`web scraping` or `#datascience`). |
| `maxItems` | int | | Default 50, max 500. Map view shows top 100. |
| `notionConnector` | Notion account (connect once) | ✓ | Log in to Notion once and pick your workspace — ~60 seconds, no token to find. Apify never sees your password or token. |
| `parentPageUrl` | string | ✓ | URL of a Notion page in your workspace. Pre-share it for instant public dashboards. |
| `dataSource` | enum | | `yourOwn` (live scrape, default) or `demo` (sample Austin restaurants, no cost). |
| `siteTitle` | string | | Optional — will auto-generate if left blank, named from what the run actually finds (e.g. "Restaurants in Austin, TX" from the scraped addresses, "YouTube videos by VeriTones" from the channel). Ignored for sample data. |
| `includeMapView` | enum | | `auto` (default — on for restaurants, real estate + hotels; off for gyms + schools), `on`, or `off`. Always off for YouTube/Instagram/TikTok — no coordinates. |
| `includeGalleryView` | bool | | Default true. Auto-disabled if too few photos load. |
| `costCap` | number | | Worst-case ceiling, default `$3`. Typical run cost: `$0.40–$1.20`. |
| `bypassCache` | bool | | Default false. Set true to re-scrape and re-deploy fresh. |

### Output

The Actor writes a Notion database under your parent page. For local-business verticals the columns are:

| Column | Type | Notes |
|---|---|---|
| Name | title | Business name. |
| Address | rich_text | Full street address. Notion's Map view geocodes from this. |
| Lat / Lng | number | Used by the Google Maps fallback link below. |
| Rating | number | 0–5 scale where available. |
| Price tier | rich_text | `$` / `$$` / `$$$` / `$$$$` where available. |
| Photos | rich_text | Up to 8 photo URLs per row, used by the gallery view. (Note: external URLs may go stale — re-run for fresh.) |
| Source | url | The source page on Google Maps / Zillow / etc. |
| Google Maps | url | A constructed `maps.google.com/?q=lat,lng` link — works even when Notion's geocoder misses. |
| Vertical | select | The vertical you ran. |
| Scraped at | date | UTC timestamp. |

Content verticals (YouTube, Instagram, TikTok) get their own column sets — see the verticals table above.

The Output tab in Apify Console gives you the dashboard URL, a public URL (when applicable), counts of rows + geocoding hits, and a `cachedFromPreviousRun` flag.

### Pricing / Cost estimation

- **Typical run** (50 items, restaurants vertical): **$0.40–$1.20** in Apify charges.
- **Worst case** (500 items, high-cost vertical, retries): set your `costCap` accordingly.
- **`dataSource: demo`**: **$0** — uses bundled sample data, no upstream scrape runs.
- **Notion side**: no charge for normal API calls. Notion's free tier covers thousands of dashboard writes per month.

The `costCap` field is a **hard ceiling, not an estimate**. If the run hits it, scraping stops and whatever was scraped is still written to Notion.

### Tips and advanced options

- **Pre-share your parent page** in Notion before running. Every dashboard under it inherits public access — saves you one click per run.
- **Lower `costCap`** if you only want a partial scrape under a budget. The Actor will stop cleanly when the budget hits.
- **Leave the Dashboard title blank** — it auto-names from what the run actually finds (the majority city/state from the scraped addresses, or the channel name for content verticals).
- **Use `bypassCache: true`** to re-scrape — by default we cache identical runs for 14 days to save you cost.
- **Each run creates a new dashboard** under your parent page. Delete old ones in Notion to keep things tidy. (In-place updates are planned for v1.1.)
- **Iterate the dashboard design in Notion** — the Actor only sets up the schema and writes rows. You can rearrange columns, add Notion AI summaries, or change colors freely.

### How this works under the hood (for builders)

This Actor is built as a reference implementation for the [Apify MCP Connectors launch](https://docs.apify.com/platform/integrations/mcp-connectors). If you're building your own Connector Actor, here's the pattern:

#### Architecture

````

User → Apify Actor → Apify MCP Proxy (injects user's Notion token) → Notion MCP server
↓
Apify SDK (uses APIFY\_TOKEN for upstream scraper call)
↓
Curated upstream Actor (Google Maps Scraper, Zillow ZIP Search, etc.)

````

#### Declaring the Notion Connector

In `.actor/input_schema.json`:

```json
"notionConnector": {
  "type": "string",
  "resourceType": "mcpConnector",
  "mcpServers": [{
    "url": "https://mcp.notion.com/mcp",
    "tools": {
      "required": [
        "notion-create-database",
        "notion-create-pages",
        "notion-create-view",
        "notion-fetch"
      ]
    }
  }]
}
````

The `tools.required` array is both an **eligibility filter** (only Connectors exposing these tools appear in the picker) and a **runtime ceiling** (the proxy blocks calls to tools outside this list, even if Notion supports them).

#### Connecting at runtime

Standard `@modelcontextprotocol/sdk` — no Apify-specific client needed. See `src/notion-client.ts` for the connection pattern with fuzzy tool-name resolution (defensive against Notion-side renames).

#### Why we use lat/lng + Google Maps URL instead of Notion's place property

Notion's API has partial support for the `place` property type — you can read it but writes return `null`. Until Notion ships full write support, we store coordinates in separate `Lat` and `Lng` number columns, write the address into a `rich_text` column for Notion's built-in geocoder, and emit a constructed `Google Maps` URL per row so every row has a working map link regardless of Notion's geocoder accuracy.

#### Cost model breakdown

Per run: `(upstream scrape cost) + (this Actor's compute) + (Notion API: free)`.

- Upstream scrape: ~`maxItems * worstCasePerItemUsd` (varies by vertical, see `.actor/vertical_actors.json`).
- This Actor's compute: ~`$0.05/minute` while waiting on the upstream + writing to Notion.
- The `costCap` input enforces a ceiling on the upstream scrape via `maxTotalChargeUsd`.

### FAQ, disclaimers, and support

**Q: Does Apify see my Notion data?**
A: No. Apify operates the MCP proxy that forwards your requests to Notion using your Notion-side authorization. The Apify-side proxy logs request metadata (which tools were called) but does not log the content. The Actor code never touches your Notion token at all.

**Q: Why does Notion ask me to manually "Publish to web" after the run?**
A: That's intentional — Notion's API can't make a page public on your behalf, even if Apify wanted to. It's a built-in security boundary that protects you from any Actor that misbehaves. **Pre-share the parent page once** and every dashboard inside it is automatically public.

**Q: What locations work best?**
A: Metro areas return the richest data; rural areas may return very few or no results. Always use the full City + State + ZIP form (`Austin, TX 78704`, `Cambridge, MA 02139`, `San Francisco, CA 94110`) — a bare 5-digit ZIP can match a foreign postal code and return results from the wrong country.

**Q: Why is the map view sometimes missing pins?**
A: Notion's built-in geocoder runs on the address column you provide. Quality varies by region and address format. Every row also has a `Google Maps` URL (computed from lat/lng) that works regardless.

**Q: Can I run this on a schedule?**
A: Yes — set up an Apify Schedule pointing at this Actor. The idempotency cache returns the same dashboard URL for identical inputs within 14 days, so scheduled re-runs are free until inputs change. Use `bypassCache: true` to force fresh.

**Q: Why does the same city with different formatting create two dashboards?**
A: The cache key is computed from your inputs exactly as typed — `location`, vertical, max items, parent page, Notion connection, data source, and view options. `"Austin, TX 78704"` and `"austin, tx 78704"` are different keys, and so are `"Austin, TX"` and `"Austin, TX 78704"`. Be consistent with your location format if you want the idempotency cache to fire.

**Q: How do I get a webhook when a run finishes?**
A: Wire it in the Apify Console — open this Actor → Integrations tab → add a webhook for "Run finished" or "Run failed". Apify's platform handles retries with exponential backoff. The Actor itself also records terminal-state telemetry to a named KVS (`telemetry`) on each run, which you can read via the Apify API if you need run-completion metadata that's not in the dataset.

**Q: Where do I report bugs?**
A: Use the "Report issue" link on this Actor's Apify Store page, or post in the [Apify Discord](https://discord.com/invite/jyEM2PRvMU). Include the discovered Notion tool list from the run log if the issue is connection-related.

**Disclaimer:** This Actor scrapes data from third-party sources (Google Maps, Zillow, etc.). Respect each source's Terms of Service and applicable laws regarding data use. The Actor doesn't store or transmit personal data beyond what the upstream scrapers return.

# Actor input Schema

## `vertical` (type: `string`):

**Local business** (Restaurants, Real estate, Gyms, Hotels, Schools) → enter a location below.

**Content** (YouTube, Instagram, TikTok) → enter a search query, URL, or handle below.

More verticals (Google Search, Website Crawler, Facebook) are planned.

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

**Local business** — City + State + ZIP: `Austin, TX 78704` · `Johnson City, TN 37604` · `Brooklyn, NY 11215`. Bare ZIPs can match foreign postal codes.

**YouTube** — search query or channel URL: `Apify tutorials` or `https://www.youtube.com/@apify`

**Instagram** — profile or hashtag URL: `https://www.instagram.com/apify/` or `https://www.instagram.com/explore/tags/webscraping/`

**TikTok** — search term or hashtag: `web scraping` or `#datascience`

## `maxItems` (type: `integer`):

Cap on rows fetched from the upstream source. **Approximate costs** (upstream scraping only — Apify compute is extra): 5 items ≈ $0.04–0.06 · 50 items ≈ $0.20–0.60 (Google Maps / Zillow) · 50 YouTube videos ≈ $0.10. Notion's map view shows at most 100 items.

## `notionConnector` (type: `string`):

Pick your Notion workspace. **One-time setup** — authorize once and every future run uses the same connection automatically. No need to re-authorize between runs.

Don't see a connection? [Authorize Notion in 60 seconds](https://console.apify.com/account/integrations/mcp-connectors/new?serverUrl=https%3A%2F%2Fmcp.notion.com%2Fmcp) — click the link, log in to Notion, click 'Allow'. Apify never sees your Notion token. Your authorization is brokered by Apify's MCP proxy and is encrypted at rest.

## `parentPageUrl` (type: `string`):

Paste the URL of any Notion page in your workspace — the dashboard lands inside it as a child. **This is a one-time setup; future runs reuse the same page.** Don't have one? Open Notion → New page → name it 'Apify Dashboards' → copy that URL.

**Recommended:** publish this page first (Share → Publish to web) — Notion only lets *you* (not the Actor) make pages public, so this one click makes every dashboard inside automatically shareable.

## `dataSource` (type: `string`):

**Live scrape** runs the upstream Actor for your location or query. **Sample data** writes a bundled 5-row Austin restaurants dataset at zero cost — a quick way to see the pipeline without spending anything. Sample data only works with Vertical = Restaurants; picking any other vertical with Sample data selected will fail fast with a clear message.

## `siteTitle` (type: `string`):

**Will auto-generate, if left blank —**

Named from what the run actually finds, e.g. "Restaurants in Austin, TX" from the scraped addresses, "YouTube videos by VeriTones" from the channel, or "TikTok videos — #fitness" from your query. Ignored when Data source = Sample data.

## `includeMapView` (type: `string`):

Adds a map view. **Auto** enables for location-based verticals with reliable geocoding (Restaurants, Real estate, Hotels) and disables for Gyms and Schools. Never available for content verticals (YouTube, Instagram, TikTok) — no coordinates. Map shows up to 100 items.

## `includeGalleryView` (type: `boolean`):

Adds a gallery view with photo thumbnails (local-business verticals) or video/post thumbnails (YouTube, Instagram, TikTok). Auto-skipped if the scraped items return no images.

## `costCap` (type: `number`):

Worst-case ceiling for upstream scraping cost — not an estimate. Typical run: $0.40–$1.20 for 50 items (Google Maps), $0.40–$1.50 for 50 Zillow listings, ~$1.00 for 50 YouTube videos. Default $3 includes a buffer. Runs stop and write what they have if this is hit.

## `bypassCache` (type: `boolean`):

Bypass the idempotency cache and re-scrape even if you ran this same query recently.

## Actor input object example

```json
{
  "vertical": "restaurants",
  "location": "Austin, TX 78704",
  "maxItems": 50,
  "dataSource": "yourOwn",
  "includeMapView": "auto",
  "includeGalleryView": true,
  "costCap": 3,
  "bypassCache": false
}
```

# Actor output Schema

## `dashboard` (type: `string`):

Click to open the dashboard in Notion.

## `publicUrl` (type: `string`):

Set when your parent page was already shared. Otherwise, open the dashboard and click Share → Publish to web.

## `itemsWritten` (type: `string`):

No description

## `placedOnMap` (type: `string`):

How many rows the geocoder successfully pinned on the map view.

## `geocodingMisses` (type: `string`):

Rows that had no geocoordinates and were not placed on the map.

## `viewsCreated` (type: `string`):

No description

## `dashboardIsPublic` (type: `string`):

No description

## `cachedFromPreviousRun` (type: `string`):

True when this run reused a recent identical run's dashboard. costCap was not evaluated.

## `isSampleData` (type: `string`):

No description

## `costUsd` (type: `string`):

Estimated cost of this run. Real billing is reported on the Apify Billing page.

## `errorCategory` (type: `string`):

Set on failed runs. INVALID\_INPUT, NOTION\_AUTH\_FAILED, COST\_EXCEEDED, SCRAPE\_FAILED, NOTION\_WRITE\_FAILED, or INTERNAL\_ERROR. Use this to branch retry logic without parsing status\_message text.

## `errorRecoverable` (type: `string`):

True when a retry (possibly with adjusted inputs) may succeed. False for permanent failures like invalid input.

## `dataset` (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 = {
    "location": "Austin, TX 78704"
};

// Run the Actor and wait for it to finish
const run = await client.actor("grizzlygriff/web-data-to-notion-dashboard").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 = { "location": "Austin, TX 78704" }

# Run the Actor and wait for it to finish
run = client.actor("grizzlygriff/web-data-to-notion-dashboard").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "location": "Austin, TX 78704"
}' |
apify call grizzlygriff/web-data-to-notion-dashboard --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=grizzlygriff/web-data-to-notion-dashboard",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Web Data to Notion Dashboard",
        "description": "Scrape local businesses (restaurants, real estate, gyms, hotels, schools) or social content (YouTube, Instagram, TikTok) and get a polished Notion dashboard with table, gallery, and map views — one Actor run. Powered by Apify MCP Connectors: we never see your Notion token.",
        "version": "0.1",
        "x-build-id": "be1g7uig2LNSjeHf1"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/grizzlygriff~web-data-to-notion-dashboard/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-grizzlygriff-web-data-to-notion-dashboard",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/grizzlygriff~web-data-to-notion-dashboard/runs": {
            "post": {
                "operationId": "runs-sync-grizzlygriff-web-data-to-notion-dashboard",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/grizzlygriff~web-data-to-notion-dashboard/run-sync": {
            "post": {
                "operationId": "run-sync-grizzlygriff-web-data-to-notion-dashboard",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "vertical",
                    "location",
                    "notionConnector",
                    "parentPageUrl"
                ],
                "properties": {
                    "vertical": {
                        "title": "What to scrape",
                        "enum": [
                            "restaurants",
                            "real_estate",
                            "gyms",
                            "hotels",
                            "schools",
                            "youtube",
                            "instagram",
                            "tiktok"
                        ],
                        "type": "string",
                        "description": "**Local business** (Restaurants, Real estate, Gyms, Hotels, Schools) → enter a location below.\n\n**Content** (YouTube, Instagram, TikTok) → enter a search query, URL, or handle below.\n\nMore verticals (Google Search, Website Crawler, Facebook) are planned.",
                        "default": "restaurants"
                    },
                    "location": {
                        "title": "Location or search query",
                        "type": "string",
                        "description": "**Local business** — City + State + ZIP: `Austin, TX 78704` · `Johnson City, TN 37604` · `Brooklyn, NY 11215`. Bare ZIPs can match foreign postal codes.\n\n**YouTube** — search query or channel URL: `Apify tutorials` or `https://www.youtube.com/@apify`\n\n**Instagram** — profile or hashtag URL: `https://www.instagram.com/apify/` or `https://www.instagram.com/explore/tags/webscraping/`\n\n**TikTok** — search term or hashtag: `web scraping` or `#datascience`"
                    },
                    "maxItems": {
                        "title": "Max items to scrape",
                        "minimum": 1,
                        "maximum": 500,
                        "type": "integer",
                        "description": "Cap on rows fetched from the upstream source. **Approximate costs** (upstream scraping only — Apify compute is extra): 5 items ≈ $0.04–0.06 · 50 items ≈ $0.20–0.60 (Google Maps / Zillow) · 50 YouTube videos ≈ $0.10. Notion's map view shows at most 100 items.",
                        "default": 50
                    },
                    "notionConnector": {
                        "title": "Notion workspace",
                        "type": "string",
                        "description": "Pick your Notion workspace. **One-time setup** — authorize once and every future run uses the same connection automatically. No need to re-authorize between runs.\n\nDon't see a connection? [Authorize Notion in 60 seconds](https://console.apify.com/account/integrations/mcp-connectors/new?serverUrl=https%3A%2F%2Fmcp.notion.com%2Fmcp) — click the link, log in to Notion, click 'Allow'. Apify never sees your Notion token. Your authorization is brokered by Apify's MCP proxy and is encrypted at rest."
                    },
                    "parentPageUrl": {
                        "title": "Parent page in Notion",
                        "type": "string",
                        "description": "Paste the URL of any Notion page in your workspace — the dashboard lands inside it as a child. **This is a one-time setup; future runs reuse the same page.** Don't have one? Open Notion → New page → name it 'Apify Dashboards' → copy that URL.\n\n**Recommended:** publish this page first (Share → Publish to web) — Notion only lets *you* (not the Actor) make pages public, so this one click makes every dashboard inside automatically shareable."
                    },
                    "dataSource": {
                        "title": "Data source",
                        "enum": [
                            "yourOwn",
                            "demo"
                        ],
                        "type": "string",
                        "description": "**Live scrape** runs the upstream Actor for your location or query. **Sample data** writes a bundled 5-row Austin restaurants dataset at zero cost — a quick way to see the pipeline without spending anything. Sample data only works with Vertical = Restaurants; picking any other vertical with Sample data selected will fail fast with a clear message.",
                        "default": "yourOwn"
                    },
                    "siteTitle": {
                        "title": "Dashboard title",
                        "type": "string",
                        "description": "**Will auto-generate, if left blank —**\n\nNamed from what the run actually finds, e.g. \"Restaurants in Austin, TX\" from the scraped addresses, \"YouTube videos by VeriTones\" from the channel, or \"TikTok videos — #fitness\" from your query. Ignored when Data source = Sample data."
                    },
                    "includeMapView": {
                        "title": "Map view",
                        "enum": [
                            "auto",
                            "on",
                            "off"
                        ],
                        "type": "string",
                        "description": "Adds a map view. **Auto** enables for location-based verticals with reliable geocoding (Restaurants, Real estate, Hotels) and disables for Gyms and Schools. Never available for content verticals (YouTube, Instagram, TikTok) — no coordinates. Map shows up to 100 items.",
                        "default": "auto"
                    },
                    "includeGalleryView": {
                        "title": "Gallery view",
                        "type": "boolean",
                        "description": "Adds a gallery view with photo thumbnails (local-business verticals) or video/post thumbnails (YouTube, Instagram, TikTok). Auto-skipped if the scraped items return no images.",
                        "default": true
                    },
                    "costCap": {
                        "title": "Cost cap (USD)",
                        "minimum": 0.5,
                        "maximum": 50,
                        "type": "number",
                        "description": "Worst-case ceiling for upstream scraping cost — not an estimate. Typical run: $0.40–$1.20 for 50 items (Google Maps), $0.40–$1.50 for 50 Zillow listings, ~$1.00 for 50 YouTube videos. Default $3 includes a buffer. Runs stop and write what they have if this is hit.",
                        "default": 3
                    },
                    "bypassCache": {
                        "title": "Force fresh run",
                        "type": "boolean",
                        "description": "Bypass the idempotency cache and re-scrape even if you ran this same query recently.",
                        "default": false
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
