# GTM Tool Website Scraper (`rahul.truevibess/gtm-tool-website-scraper`) Actor

Per-URL enricher: renders each GTM/SaaS tool website with Playwright and returns tool\_name, a <500-word plain-text summary, and any demo video (YouTube/Vimeo/Wistia/Loom/mp4/link).

- **URL**: https://apify.com/rahul.truevibess/gtm-tool-website-scraper.md
- **Developed by:** [Rahul](https://apify.com/rahul.truevibess) (community)
- **Categories:** Agents, E-commerce, Lead generation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

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

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

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

# README

## GTM Tool Website Scraper

Build a **GTM (go-to-market) tool directory** at scale. Give this actor a list of tool website URLs and it returns, for **each** URL, a clean structured record:

- **`tool_name`** — the company/tool name
- **`summary`** — a plain-text description of what the tool does (deterministically kept **under 500 words** by default)
- **`demo_video_url`** — the first demo/product video found on the site
- **`demo_video_source`** — where that video lives (`youtube`, `vimeo`, `wistia`, `loom`, `html5`, `link`, or `none`)

It renders every site with a **real headless Chromium browser (Playwright)**, so modern client-side-rendered React / Next.js / Webflow marketing sites work just as well as server-rendered ones.

***

### Why this actor

Generic website-content crawlers hand you a wall of raw page text. This actor gives you **exactly the three fields a GTM tool directory needs** — name, a concise "what it does" summary, and the demo video — as clean structured columns, one row per URL. No LLM cost, fully deterministic and reproducible.

**Who it's for:** GTM / sales-ops teams building tool directories, product researchers, VCs mapping software categories, and content teams writing tool round-ups.

**Legal:** it only visits the tool's own **public marketing website** — no login, no gated data.

***

### Input

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `startUrls` | array of `{ url }` | ✅ | — | The GTM tool website URLs to scrape. One output item per URL. |
| `summaryMaxWords` | integer | — | `500` | Hard cap on summary length (min 20, max 2000). The summary is truncated on a word boundary to stay strictly under this number. |
| `maxRequestsPerCrawl` | integer | — | `100` | Safety cap on total requests to prevent runaway crawls. |

#### Example input

```json
{
  "startUrls": [
    { "url": "https://www.clay.com/" }
  ],
  "summaryMaxWords": 500,
  "maxRequestsPerCrawl": 100
}
```

***

### Output

One dataset item per input URL. Every value is a string.

| Field | Description |
|---|---|
| `url` | The URL that was scraped (resolved `loadedUrl`). |
| `tool_name` | Company/tool name. |
| `summary` | Plain-text "what it does" summary, under `summaryMaxWords` words. |
| `demo_video_url` | First demo/product video URL, or `""` if none. |
| `demo_video_source` | `youtube` | `vimeo` | `wistia` | `loom` | `html5` | `link` | `none`. |
| `scraped_at` | ISO-8601 UTC timestamp. |

#### Example output

```json
{
  "url": "https://www.clay.com/",
  "tool_name": "Clay",
  "summary": "Infrastructure to get any data, run agentic workflows, and launch GTM plays. Build systems to grow revenue ...",
  "demo_video_url": "",
  "demo_video_source": "none",
  "scraped_at": "2026-07-16T12:00:00.000Z"
}
```

> On `clay.com` the hero video is a decorative background loop (`autoplay loop muted`), which is intentionally **excluded** from demo detection, and `/demo` is a gated "book a demo" form with no embedded player — so `demo_video_source` is correctly `none`.

***

### How extraction works

#### Tool name (first non-empty wins)

1. `og:site_name`
2. `og:title` (split on `|`, `-`, `—`, `:` — keep the brand segment)
3. `<title>` (same cleaning)
4. header/nav logo `img[alt]`
5. capitalized registrable domain (e.g. `clay.com` → `Clay`)

#### Summary (deterministic extract-and-truncate — **no LLM**)

Candidate text is assembled in priority order — meta description → `og:description` → hero `<h1>` → hero subhead → first substantive `<main>` paragraphs → feature heading/description blocks — then whitespace-normalized, deduped, and truncated on a word boundary to strictly under `summaryMaxWords`. Nav, footer, cookie/consent and legal boilerplate are filtered out.

#### Demo video (first-match priority)

1. YouTube embeds / `youtu.be` / lite-youtube facades → `youtube`
2. Vimeo player iframes → `vimeo`
3. Wistia embeds → `wistia`
4. Loom embed/share links → `loom`
5. Genuine `<video>` mp4 — **decorative background loops excluded** (`autoplay` + `loop`/`muted`, or `video-bg`/`hero-video`/`bg-video` classes) → `html5`
6. Anchor last resort (`/demo`, `/watch`, `/video`, `/product-tour`, or "watch demo" / "see it in action" / "product tour" link text) → `link`
7. Nothing → `""` + `none`

All video URLs are resolved to absolute URLs.

***

### Edge cases handled

- Client-side SPAs (rendered with Playwright before extraction)
- Decorative background `<video>` loops excluded from demo detection
- Demos gated behind a "book a demo" form → `none`
- Missing meta tags → priority-chain fallbacks, domain fallback for the name
- Title padded with taglines/separators → brand segment kept
- Cookie banners / nav / footer legal text filtered out of the summary
- Very long pages → word-boundary truncation under the cap
- Redirects (http→https, www, trailing slash) → original input URL recorded, resolved page scraped
- Timeouts / 4xx-5xx → retried, then a best-effort row is still emitted (never lose a URL)
- Relative / protocol-relative video hrefs → resolved to absolute
- Duplicate input URLs → deduped to one item each

***

### Notes

- Uses Apify Proxy automatically to avoid IP blocks and geo-redirects across many domains.
- Memory: ~2 GB recommended (headless Chromium).
- Anti-detection/stealth is not needed — these are public marketing sites with no bot-hostile gating.

# Actor input Schema

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

List of GTM tool website URLs to scrape (Apify RequestList format: array of { url } objects). One output item is produced per URL.

## `summaryMaxWords` (type: `integer`):

Hard cap on the number of words in the summary field. The assembled candidate text is truncated to stay strictly under this limit.

## `maxRequestsPerCrawl` (type: `integer`):

Safety cap on total requests to prevent runaway crawls.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.clay.com/"
    }
  ],
  "summaryMaxWords": 500,
  "maxRequestsPerCrawl": 100
}
```

# Actor output Schema

## `url` (type: `string`):

The input URL that was scraped (the request's loadedUrl / original start URL).

## `tool_name` (type: `string`):

Company/tool name extracted from og:site\_name, og:title, <title>, logo alt, or domain fallback.

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

Concise plain-text description of what the tool does, deterministically truncated to under summaryMaxWords words.

## `demo_video_url` (type: `string`):

Best-effort URL of the first demo/product video found. Empty string if none detected.

## `demo_video_source` (type: `string`):

Origin of the demo video: 'youtube', 'vimeo', 'wistia', 'loom', 'html5', 'link', or 'none'.

## `slack_playable` (type: `string`):

True when demo\_video\_url plays inline in Slack (youtube/vimeo/wistia/loom/html5). False for a bare page link or when no video was found.

## `scraped_at` (type: `string`):

ISO-8601 UTC timestamp of when the page was scraped.

# 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": [
        {
            "url": "https://www.clay.com/"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("rahul.truevibess/gtm-tool-website-scraper").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": [{ "url": "https://www.clay.com/" }] }

# Run the Actor and wait for it to finish
run = client.actor("rahul.truevibess/gtm-tool-website-scraper").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 '{
  "startUrls": [
    {
      "url": "https://www.clay.com/"
    }
  ]
}' |
apify call rahul.truevibess/gtm-tool-website-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=rahul.truevibess/gtm-tool-website-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/gR9km9FSSHiF7WbHv/builds/6Lx4mEgwKlQu5JDUX/openapi.json
