# Contact Page & Form Finder: Contact URLs from Websites (`sandy_yclept/contact-page-finder`) Actor

Find each company's contact page and whether it has a contact form, from a list of websites: the contact URL, the form's page and kind (HTML, HubSpot, Typeform, Google Forms...). For outreach through forms. Pay only per website with a contact page found.

- **URL**: https://apify.com/sandy_yclept/contact-page-finder.md
- **Developed by:** [Rafal Sav](https://apify.com/sandy_yclept) (community)
- **Categories:** Lead generation, Automation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 contact pages

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

### What does Contact Page & Form Finder do?

**Contact Page & Form Finder** reads a list of company websites and returns each one's **contact page** and whether it has a **contact form** — and if so, what kind: written into the page, or embedded from HubSpot, Typeform, Google Forms, JotForm, Tally and other form services.

It is for outreach through contact forms, and for checking which companies take enquiries online. A contact page is the page the site itself names as one (`/contact`, `/kontakt`, `/contacto`, `/nous-contacter`...), or the page holding its contact form. You pay **only for a website whose contact page was found**.

Run it from the Apify Console, through the API, on a schedule, or from an AI agent through Apify's MCP server.

### Why use it?

- **Contact-form outreach**: a list of form URLs to send through, by hand or with an automation tool.
- **Lead qualification**: a business that takes enquiries through a form, and through which service.
- **Agencies**: find the sites still without a form — a sales argument.

### How to use it

1. Open the **Input** tab and paste the websites (domains or URLs), upload a text or CSV file, or link a Google Sheet.
2. Click **Start**.
3. Download the results as JSON, CSV, Excel or HTML from the **Output** tab.

### Input

| Field | What it does |
|---|---|
| Websites | Company websites, one row per website. A URL listed twice is read once. |
| Max contact and about subpages | Pages read on top of the homepage (default 3): the contact page first, then about pages. |

### Output

sunsethillvet.com, as read on October 10, 2026:

```json
{
  "domain": "https://sunsethillvet.com/",
  "website": "https://sunsethillvet.com/",
  "contact_page": "https://sunsethillvet.com/contact/",
  "contact_form": true,
  "contact_form_kind": "html",
  "contact_form_page": "https://sunsethillvet.com/contact/",
  "status": "found",
  "pages_crawled": 4,
  "billed": true
}
```

| Field | Meaning |
|---|---|
| `contact_page` | The site's contact page; the page with its form when no page is named as one. |
| `contact_form` | Whether a contact form was found on the pages read. |
| `contact_form_kind` | `html` for a form in the page, or the service it is embedded from: `hubspot`, `typeform`, `google-forms`, `jotform`, `tally`, `microsoft-forms`, `formstack`, `wufoo`, `cognito`, `zoho`. |
| `contact_form_page` | The page the form was found on. |
| `status` | `found`, `not_found`, or `unreachable`. |

A contact form is one that asks for a message: a text area, or an email field beside a field named "message". A search box, a newsletter's single email field and a blog's comment form are not counted.

### How often it finds one

Measured on three samples picked before anything was run on them:

| Sample | Websites read | Contact page | Contact form |
|---|---|---|---|
| Startups (Y Combinator) | 294 | 104 (35%) | 83 (28%) |
| German websites (`.de`) | 115 | 75 (65%) | 43 (37%) |
| Local businesses (Google Maps) | 120 | 108 (90%) | 80 (67%) |

Most forms are written into the page (195 of 206); the rest are HubSpot, Typeform, Google Forms, Tally and JotForm. A form a site draws with scripts after the page loads is not seen.

### How much does it cost?

**$0.001 per website with a contact page found**, on the Free and Starter plans; 10% less on Scale, 20% less from Business up. A website without one costs nothing. 1,000 contact pages cost $1.

### Related Actors

- [Website Contact & Email Scraper](https://apify.com/sandy_yclept/website-contact-email-scraper) — the emails and phone numbers on the same websites, priced per contact.
- [Social Media Profile Finder](https://apify.com/sandy_yclept/social-media-profile-finder) — each company's social profiles from its website.
- [Google Maps Email Extractor](https://apify.com/sandy_yclept/google-maps-email-extractor) — local businesses from a Google Maps search, each with the email from its own website.

### Disclaimer

This Actor finds forms; it does not fill them in. You are responsible for complying with the law on unsolicited messages where you send them, the sites' Terms of Service, and `robots.txt`, which it respects.

### Support

A form that was missed or mistaken? Open an issue on the **Issues** tab with the website.

# Actor input Schema

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

Company websites (domains or URLs) to find the contact page of. One row per website. Paste them, upload or link a text or CSV file of them, or link a Google Sheet shared with "anyone with the link". A URL listed twice is read once.

## `maxContactPages` (type: `integer`):

How many subpages to read on each website on top of its homepage: the contact page first, then about pages, where a form sometimes sits.

## `phoneRegion` (type: `string`):

ISO 3166-1 alpha-2 country code (e.g. PL, US, GB, DE) used to parse phone numbers written without an international prefix. Leave empty to auto-detect from each site's TLD; numbers written with a leading + are always parsed.

## `proxyConfiguration` (type: `object`):

Optional proxy for fetching pages. Apify Proxy's datacenter IPs are included in every Apify plan, the Free plan too; residential proxies are billed separately. Left disabled, the Actor connects directly, and retries sites that refuse it through Apify Proxy (see below).

## `proxyFallback` (type: `boolean`):

Every cloud run has a datacenter IP address, and some sites turn those away. A site that refuses or drops the direct connection is retried once through Apify Proxy's datacenter IPs (included in every Apify plan, the Free plan too), then once more directly. Sites that answer directly never go through the proxy, and robots.txt is respected either way. Has no effect when a proxy is configured above.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://sunsethillvet.com/"
    },
    {
      "url": "https://www.barefootphysiotherapy.com.au/"
    },
    {
      "url": "https://apify.com/"
    }
  ],
  "maxContactPages": 3,
  "phoneRegion": "PL",
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "proxyFallback": true
}
```

# Actor output Schema

## `results` (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": [
        {
            "url": "https://sunsethillvet.com/"
        },
        {
            "url": "https://www.barefootphysiotherapy.com.au/"
        },
        {
            "url": "https://apify.com/"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("sandy_yclept/contact-page-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 = { "startUrls": [
        { "url": "https://sunsethillvet.com/" },
        { "url": "https://www.barefootphysiotherapy.com.au/" },
        { "url": "https://apify.com/" },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("sandy_yclept/contact-page-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 '{
  "startUrls": [
    {
      "url": "https://sunsethillvet.com/"
    },
    {
      "url": "https://www.barefootphysiotherapy.com.au/"
    },
    {
      "url": "https://apify.com/"
    }
  ]
}' |
apify call sandy_yclept/contact-page-finder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,sandy_yclept/contact-page-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/sTqXsNqgUkFbBKyVa/builds/pmxUlBYrgVsPCjHVq/openapi.json
