# Contact Details Scraper – Emails, Phones, Socials from Websites (`smoldesk/contact-details-scraper`) Actor

Get emails, phone numbers, social profiles (Facebook, Instagram, LinkedIn, X, TikTok, YouTube, WhatsApp) and contact forms from any list of websites. Checks homepage + contact/about pages, flags emails whose domain takes no mail. Pay only for websites where contacts are found.

- **URL**: https://apify.com/smoldesk/contact-details-scraper.md
- **Developed by:** [Smoldesk Studios](https://apify.com/smoldesk) (community)
- **Categories:** Lead generation, AI
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 website with contacts

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

## Contact Details Scraper – Emails, Phones, Socials from Websites

![Contact Details Scraper: emails, phones, social profiles and contact forms from any list of websites](https://raw.githubusercontent.com/smoldesk/apify-scrapers/main/assets/contact-details/banner.webp)

Give it a list of websites and get back each one's **emails, phone numbers, social profiles** (Facebook, Instagram, LinkedIn, X, TikTok, YouTube, Pinterest, WhatsApp, Telegram) and **contact form**, one clean row per website. The Actor reads the **homepage plus the contact, about, team and legal pages**, where businesses actually put their details. Every email comes with **the page it was found on**, whether it's on **the website's own domain**, and a free **mail-server check** of its domain. **You pay only for websites where contacts were found.** Websites with nothing, unreachable ones and duplicates are free.

Works great with a list from [Google Maps Scraper](https://apify.com/smoldesk/google-maps-scraper): pick its dataset as the input and your places come back with emails and socials.

### What you can use it for

- **Lead generation**: turn a list of company websites into a sheet of emails, phones and social profiles.
- **Enrich a Google Maps or directory export**: add emails and socials to the places you already have, joined back by name or place ID.
- **Agencies and freelancers**: find the right contact (and the contact form) for every prospect before you pitch.
- **CRM clean-up**: check which of your accounts still list an email, and whether that email's domain still takes mail.
- **Social media research**: collect the Instagram, TikTok and LinkedIn accounts behind a list of brands.

### How to scrape contact details from websites

1. Create a free [Apify account](https://console.apify.com/sign-up) and open this Actor.
2. Paste your websites into **Websites**, one per line. Full URLs or bare domains both work (`barleyswine.com`).
3. Or pick a dataset in **…or websites from a dataset**, for example the results of a Google Maps Scraper run. The `website` field is read by default.
4. Keep **Pages per website** at 4 (homepage + 3 contact-like pages). That finds almost everything.
5. Click **Start**. About 700 websites take 6 minutes. Download the results as CSV, Excel or JSON, or read them through the API.

![Step 1: paste websites or pick a dataset](https://raw.githubusercontent.com/smoldesk/apify-scrapers/main/assets/contact-details/how-to-1.webp)

![Step 2: click Start](https://raw.githubusercontent.com/smoldesk/apify-scrapers/main/assets/contact-details/how-to-2.webp)

![Step 3: export the contacts](https://raw.githubusercontent.com/smoldesk/apify-scrapers/main/assets/contact-details/how-to-3.webp)

### What we measured

We ran it on **690 restaurant websites in Austin, TX** (the websites from a Google Maps search):

| Result | Websites |
|---|---|
| Contacts found (charged) | **506 (73%)** |
| …with at least one email | 360 |
| …with a social profile | 423 |
| …with a phone number | 219 |
| …with a contact form | 172 |
| No contacts on the website (free) | 84 |
| Protected by a bot check, not scraped (free) | 53 |
| Unreachable, 403/404 or not a web page (free) | 33 |
| A social profile given instead of a website (free) | 14 |

The run took 6 minutes. On the Free plan it would cost **$1.52** (506 × $0.003).

### Output

One row per website. The **Contacts** view shows the flat columns; **Email details** and **Run report** show the rest.

```json
{
  "website": "http://fabrikatx.com/",
  "url": "https://www.fabrikatx.com/",
  "domain": "fabrikatx.com",
  "name": "Fabrik Austin",
  "status": "contacts found",
  "redirectedTo": null,
  "primaryEmail": "contact@fabrikatx.com",
  "emails": ["contact@fabrikatx.com"],
  "phones": ["+1 512-222-9816"],
  "facebook": null,
  "instagram": "http://instagram.com/fabrikatx",
  "linkedin": null,
  "twitter": null,
  "tiktok": null,
  "youtube": null,
  "whatsapp": null,
  "telegram": null,
  "pinterest": null,
  "contactFormUrl": "https://www.fabrikatx.com/",
  "emailDetails": [
    {
      "email": "contact@fabrikatx.com",
      "ownDomain": true,
      "foundOn": "https://www.fabrikatx.com/",
      "foundIn": "schema.org",
      "domainAcceptsMail": true
    }
  ],
  "phoneDetails": [
    {
      "number": "+1-512-222-9816",
      "international": "+1 512-222-9816",
      "e164": "+15122229816",
      "country": "US",
      "how": "schema.org",
      "foundOn": "https://www.fabrikatx.com/contact"
    }
  ],
  "socialProfiles": { "instagram": ["http://instagram.com/fabrikatx"] },
  "pagesChecked": [
    "https://www.fabrikatx.com/",
    "https://www.fabrikatx.com/contact",
    "https://www.fabrikatx.com/about-fabrik"
  ],
  "sourceData": {
    "name": "Fabrik Austin",
    "placeId": "ChIJv-uDeMq1RIYRvxi1IOQLRj4",
    "phone": "(512) 222-9816",
    "address": "3108 Windsor Rd, Austin, TX 78703",
    "category": "Vegan restaurant"
  },
  "scrapedAt": "2026-11-07T05:56:30+00:00"
}
```

| Field | What it is |
|---|---|
| `primaryEmail` | The best email: on the website's own domain and from structured data or a `mailto:` link first, emails whose domain takes no mail last |
| `emails` / `phones` | All emails and phone numbers as plain lists (phones in international format) |
| `facebook`, `instagram`, `linkedin`… | The first profile of each network, ready for a spreadsheet; `socialProfiles` has all of them |
| `contactFormUrl` | A page with a contact form (a message box or an email + name form, not a newsletter box) |
| `emailDetails[].foundIn` | `schema.org` (the site's structured data), `mailto link`, `page text` or `page source` |
| `emailDetails[].ownDomain` | `true` when the email is on the website's domain (`info@theirsite.com`) |
| `emailDetails[].domainAcceptsMail` | Result of the DNS check: `false` means the domain has no mail server (a typo like `gmail.co`, or a dead domain) |
| `status` | `contacts found`, `no contacts found`, `protected by a bot check (not scraped)`, `unreachable: …`, `blocked or missing (HTTP 4xx)` |
| `sourceData` | Fields copied from your dataset row, so you can join the results back (`null` for websites typed into the list) |

Every row has the same fields, including websites with nothing found (empty lists and `null`), so CSV columns stay the same from run to run.

### Clean emails, not junk

Contact scrapers often return emails that aren't anyone's real contact. This one removes or flags them:

- **Removed**: `noreply@`, placeholders (`you@example.com`, `name@yourdomain.com`), image file names (`logo@2x.png`), error-tracking keys (`…@o123.ingest.sentry.io`), template emails from website builders and font licences.
- **Social links that aren't the business are dropped**: the website builder's own accounts in the footer (`facebook.com/wix`, `instagram.com/squarespace`), share buttons and the old `facebook.com/2008/fbml` code that sits in many page headers.
- **Emails glued to the next word are repaired**: `info@shop.comsocial` → `info@shop.com`, but only when the repaired domain really takes mail.
- **Every email's domain is checked in DNS** (MX record). Typos and dead domains are marked `domainAcceptsMail: false` and moved to the end, or removed with **Drop emails whose domain takes no mail**.
- **Emails written as `name [at] domain [dot] com`** are read too.

Gmail addresses and a parent company's domain are kept, because many small businesses use them. Switch on **Only emails on the website's own domain** if you want just `@theirdomain` emails.

### Input examples

A list of websites:

```json
{
  "websites": ["barleyswine.com", "https://www.oddduckaustin.com", "https://www.roaringfork.com"]
}
```

Enrich a Google Maps Scraper run (pick its dataset in the input, or paste the ID):

```json
{
  "datasetId": "<dataset ID of your Google Maps Scraper run>",
  "datasetUrlField": "website",
  "datasetFieldsToKeep": ["name", "placeId", "phone", "address", "category"]
}
```

Strict B2B list: own-domain emails only, dead domains removed, UK numbers:

```json
{
  "websites": ["example-agency.co.uk", "another-firm.co.uk"],
  "onlyOwnDomainEmails": true,
  "dropEmailsWithoutMailServer": true,
  "defaultCountry": "GB"
}
```

### Pricing

You pay once per website **where at least one email, phone number or social profile was found**, with a lower price on bigger Apify plans.

| Event | Free plan | Starter | Scale | Business |
|---|---|---|---|---|
| Website with contacts | $3.00 / 1,000 | $2.50 / 1,000 | $2.00 / 1,000 | $1.50 / 1,000 |
| Actor start | $0.00005 | $0.00005 | $0.00005 | $0.00005 |

**Free**: websites with no contacts, unreachable or bot-protected websites, duplicates and social-profile URLs. The price is the same whether the Actor reads 1 page or 10, so you never pay for pages crawled. Proxy and compute are included.

Example: 1,000 websites where 7 in 10 have contacts (what we measured) = 700 × $0.003 = **$2.10** on the Free plan, $1.05 on Business. Set **Maximum cost per run** in the run options and the Actor stops before going over it.

### Use with AI agents (Claude, ChatGPT, Cursor, MCP)

Add the Actor as a tool through the Apify MCP server: `https://mcp.apify.com?tools=smoldesk/contact-details-scraper`. In Claude Desktop, Claude Code, Cursor or VS Code, add this to your MCP config (you sign in to Apify in the browser on first use, or put your API token in a `headers` → `Authorization: Bearer <APIFY_TOKEN>` field instead):

```json
{
  "mcpServers": {
    "contact-details": {
      "url": "https://mcp.apify.com?tools=smoldesk/contact-details-scraper"
    }
  }
}
```

Or let the Apify CLI write it for you: `apify mcp install cursor --tools smoldesk/contact-details-scraper` (also `claude-code`, `vscode`, `codex`). Then paste this into your agent's system prompt or tool notes:

```text
Tool: smoldesk/contact-details-scraper (Contact Details Scraper, Apify)
Use it when: the user has websites or domains and wants their emails, phone numbers, social profiles (Facebook, Instagram, LinkedIn, X, TikTok, YouTube, WhatsApp) or contact form. Also to enrich a dataset of places/companies that has a website field.
Don't use it when: the user wants a specific person's email from a name only, emails behind a login, or data from social networks themselves.
How to call: run the Actor with JSON input and read the dataset items. Each website with contacts costs money, so pass only the websites the user asked about.
Example input: {"websites": ["barleyswine.com", "roaringfork.com"]}
```

Questions an agent can answer with it:

- Find the contact email and Instagram of these 20 agencies.
- Which of the companies in this sheet have a contact form?
- Add emails to the places in my last Google Maps Scraper run.

### Use with AI (LangChain / LlamaIndex / CrewAI)

With LangChain (`pip install langchain-apify`):

```python
from langchain_apify import ApifyWrapper
from langchain_core.documents import Document

apify = ApifyWrapper()  # reads APIFY_API_TOKEN
loader = apify.call_actor(
    actor_id="smoldesk/contact-details-scraper",
    run_input={"websites": ["barleyswine.com", "roaringfork.com"]},
    dataset_mapping_function=lambda v: Document(
        page_content=f"{v.get('name') or v['domain']}: emails {v.get('emails')}, phones {v.get('phones')}, "
                     f"socials {v.get('socialProfiles')}, contact form {v.get('contactFormUrl')}",
        metadata={"domain": v["domain"], "status": v["status"]},
    ),
)
docs = loader.load()
```

Already have a run? Use `ApifyDatasetLoader(dataset_id="<run's dataset ID>", dataset_mapping_function=...)`. **LlamaIndex**: `ApifyActor` from `llama-index-readers-apify`. **CrewAI**: use the Apify MCP server above.

### Limitations

- **Websites behind a bot check are skipped, not scraped.** About 8% of the restaurant websites we tested show a Cloudflare "Just a moment…" page to scripts. The Actor doesn't try to get around these checks. It marks the row `protected by a bot check (not scraped)` and doesn't charge for it.
- **No browser.** Contacts that appear only after JavaScript runs (some single-page apps, or emails loaded by a widget) are missed. Most business websites (WordPress, Squarespace, Wix, Shopify, Webflow) show their contacts in the HTML and work fine.
- **Emails protected by Cloudflare's email obfuscation, or shown only as an image, are not read.** The website owner hid them on purpose.
- **Emails are not verified by sending mail.** The DNS check tells you the domain accepts mail, not that the mailbox exists.
- **Phones**: numbers in `tel:` links and structured data are always read. Numbers written in plain text are read when the country is known, from **Country for local phone numbers** or the website's country domain (`.co.uk`, `.de`, `.com.my`). On `.com` sites set the country to pick them up.
- **It reads only the website you give it**, not LinkedIn, Facebook or search engines, and doesn't guess emails from names.

### FAQ

**How is this priced compared with crawler-based contact scrapers?** Many charge for every page they crawl, so a website with nothing on it still costs you. Here a website costs nothing unless it gave at least one email, phone or social profile, and the price doesn't change with the number of pages read.

**Which pages does it read?** The homepage, then the best contact-like links on it: *contact* first, then *about*, *team* and *legal / impressum / privacy*, in many languages (kontakt, contacto, hubungi, 联系, お問い合わせ…). If no contact link is found it tries `/contact`.

**Can I use the results of Google Maps Scraper?** Yes. Pick that run's dataset in **…or websites from a dataset**. The `website` field is read and `name`, `placeId`, `phone`, `address` and `category` are copied into `sourceData`.

**Do I need proxies?** No. If a website refuses the run's IP, the Actor retries it once through an Apify datacenter proxy on its own. Residential proxies are never used.

**Is it legal to scrape contact details?** The Actor reads only pages that are public on the business's own website. You are responsible for how you use the data, especially for contacting people (GDPR, CAN-SPAM and similar rules).

### Feedback

Found a bug or a website where contacts were missed? Open an issue in the **Issues** tab with the URL. If the Actor saved you time, a short review on this page helps other people find it.

Code examples and a quick reference are also on GitHub: [smoldesk/contact-details-scraper](https://github.com/smoldesk/contact-details-scraper).

### More Actors from Smoldesk Studios

- [Google Maps Scraper](https://apify.com/smoldesk/google-maps-scraper): places with phones, websites, ratings and hours from any search, plus emails and social profiles from each business website, lead filters and new-place monitoring
- [Career Site Jobs Scraper](https://apify.com/smoldesk/career-site-jobs-scraper): live jobs from 12,000+ company career sites (Greenhouse, Lever, Workday, Ashby) with salary and seniority
- [Shopify Store Analyzer](https://apify.com/smoldesk/shopify-store-analyzer): apps, contacts and products of any Shopify store
- [Google Ads Transparency Scraper](https://apify.com/smoldesk/google-ads-transparency-scraper): every ad a competitor runs on Google Search, YouTube and Shopping — creatives, landing pages, countries, EU impressions and new-ad alerts
- [Google Hotels Scraper](https://apify.com/smoldesk/google-hotels-scraper): hotel prices for your dates in any currency, ratings, review aspects and booking-site prices, worldwide
- [Google Hotels Price Calendar](https://apify.com/smoldesk/google-hotel-price-calendar): the cheapest nightly rate for every check-in date up to a year ahead, for any hotel, with taxes and booking-site prices
- [Google Trends Scraper](https://apify.com/smoldesk/google-trends-scraper): interest over time, by region and city, related queries and Trending now — no API key
- [YouTube Scraper](https://apify.com/smoldesk/youtube-scraper): videos, Shorts, channels and comments from search, channels and playlists — exact views, likes, publish dates and durations, no API key
- [YouTube Comments Scraper](https://apify.com/smoldesk/youtube-comments-scraper): comments and replies from videos, channels, playlists or keyword searches, with likes, pinned/hearted flags and authors
- [YouTube Channel Scraper](https://apify.com/smoldesk/youtube-channel-scraper): subscribers, exact total views, join date, country and social links of any channel, plus its latest videos
- [Tennis Scraper](https://apify.com/smoldesk/tennis-scraper): results, fixtures and betting odds for ATP, WTA, Challenger and ITF, with H2H and rankings
- [SEEK, JobStreet & JobsDB Scraper](https://apify.com/smoldesk/seek-jobs-scraper): jobs and salaries across Australia and Southeast Asia
- [Telegram Channel Scraper](https://apify.com/smoldesk/telegram-channel-scraper): public channel posts, views, reactions and subscribers
- [Substack Scraper](https://apify.com/smoldesk/substack-scraper): newsletters, subscriber counts and posts
- [Weibo Scraper](https://apify.com/smoldesk/weibo-scraper): keyword and hashtag search, posts, comments, profiles and the hot-search board from China's biggest microblog, no login
- [WeChat Articles Scraper](https://apify.com/smoldesk/wechat-articles-scraper): WeChat Official Account (公众号) articles from keyword search, full text as Markdown, images, account IDs and publish dates, no login
- [Toutiao Scraper](https://apify.com/smoldesk/toutiao-scraper): Toutiao (今日头条) news search with date ranges, reads and likes, comments and the hot board (热榜), no login
- [Bilibili Scraper](https://apify.com/smoldesk/bilibili-scraper): videos, stats, danmaku, comments and creators from China's video site, no login

# Actor input Schema

## `websites` (type: `array`):

Website URLs or bare domains, one per line (e.g. <code>barleyswine.com</code>). Each website is checked once: its homepage plus its contact, about, team and legal pages.

## `datasetId` (type: `string`):

Pick a dataset to read website URLs from — for example the output of <a href='https://apify.com/smoldesk/google-maps-scraper'>Google Maps Scraper</a> (or paste its ID). Combined with the list above.

## `datasetUrlField` (type: `string`):

Which field of each dataset row holds the website URL.

## `datasetFieldsToKeep` (type: `array`):

Fields copied from each dataset row into <code>sourceData</code>, so you can join the contacts back to your list.

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

Homepage plus the best contact-like pages (contact first, then about, team, legal/impressum). 4 finds almost everything; more pages = slower runs, same price.

## `maxWebsites` (type: `integer`):

Stop after this many websites (0 = all). You can also set a maximum cost per run in the run options.

## `defaultCountry` (type: `string`):

Two-letter country code (US, GB, MY, DE…) used to read phone numbers written without a + prefix. Leave empty to use the website's country domain (.co.uk → GB, .com.my → MY); on .com sites only numbers with a + prefix or a tel: link are then kept.

## `checkEmailDomains` (type: `boolean`):

Look up each email's domain in DNS and mark <code>domainAcceptsMail</code> true/false. Typos and dead domains (e.g. <code>name@home.come</code>) are moved to the end. Free and fast; it does not send any email.

## `dropEmailsWithoutMailServer` (type: `boolean`):

Remove emails whose domain has no mail server at all (needs the check above).

## `onlyOwnDomainEmails` (type: `boolean`):

Keep only emails like <code>info@theirdomain.com</code>. Off by default: many small businesses use Gmail or a parent company's domain, and those are real contacts too.

## `saveWebsitesWithoutContacts` (type: `boolean`):

Save a row for every website, with a <code>status</code> such as <i>no contacts found</i> or <i>protected by a bot check</i>. These rows are never charged.

## `maxConcurrency` (type: `integer`):

How many websites to check at the same time.

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

Not needed: if a website refuses the run's IP, the Actor retries it once through an Apify datacenter proxy by itself. Residential proxy is not used.

## Actor input object example

```json
{
  "websites": [
    "https://www.barleyswine.com",
    "https://www.oddduckaustin.com",
    "https://www.roaringfork.com"
  ],
  "datasetUrlField": "website",
  "datasetFieldsToKeep": [
    "name",
    "placeId",
    "phone",
    "address",
    "category"
  ],
  "maxPagesPerSite": 4,
  "maxWebsites": 0,
  "checkEmailDomains": true,
  "dropEmailsWithoutMailServer": false,
  "onlyOwnDomainEmails": false,
  "saveWebsitesWithoutContacts": true,
  "maxConcurrency": 16
}
```

# Actor output Schema

## `contacts` (type: `string`):

One row per website: best email, all emails, phones, social profiles and contact form.

## `emails` (type: `string`):

Each email with the page it was found on, own-domain flag and mail-server check.

## `status` (type: `string`):

Every website with its status (contacts found, no contacts, unreachable, bot check).

# 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 = {
    "websites": [
        "https://www.barleyswine.com",
        "https://www.oddduckaustin.com",
        "https://www.roaringfork.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("smoldesk/contact-details-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 = { "websites": [
        "https://www.barleyswine.com",
        "https://www.oddduckaustin.com",
        "https://www.roaringfork.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("smoldesk/contact-details-scraper").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 '{
  "websites": [
    "https://www.barleyswine.com",
    "https://www.oddduckaustin.com",
    "https://www.roaringfork.com"
  ]
}' |
apify call smoldesk/contact-details-scraper --silent --output-dataset

```

## MCP server setup

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

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/uPYRr7xkqG97uMBYt/builds/inRq7SCyxiyLPZshq/openapi.json
