# Instagram Public Contact Finder (`data-slayer/instagram-email-phone-scraper`) Actor

Find publicly listed email, phone, and WhatsApp contact details from Instagram profiles and up to two linked pages. Every deduplicated contact includes its source URL, extraction method, verification status, and page-scan outcome.

- **URL**: https://apify.com/data-slayer/instagram-email-phone-scraper.md
- **Developed by:** [Data Slayer](https://apify.com/data-slayer) (community)
- **Categories:** Social media, Marketing
- **Stats:** 4 total users, 3 monthly users, 96.2% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.20 / 1,000 completed profile reports

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

## Instagram Public Contact Finder

Turn Instagram profiles into evidence-backed public contact reports. Find publicly listed email, phone, and WhatsApp details, optionally scan up to two linked pages, and keep the exact source behind every result.

### Watch the demo

YouTube video coming soon.

### What you get

Each input profile produces one report with:

- public email and phone fields exposed on the Instagram profile;
- email, labeled phone, and WhatsApp details found in the biography;
- optional contacts found on the profile's bio destination and one relevant child page;
- deduplicated contacts with every source URL retained;
- an extraction method for each piece of evidence;
- linked-page scan statuses, byte counts, and final URLs;
- a clear `contacts_found`, `no_public_contacts_found`, or failed-profile outcome.

This Actor finds publicly listed contact evidence. It does **not** test mailbox deliverability, guarantee that an address is current, or describe a discovered email as verified.

### Common uses

- add contact evidence to a curated creator or brand shortlist;
- prepare partnership, PR, recruiting, or sales research;
- separate profiles with public contact paths from profiles with no discoverable contact;
- audit where an existing lead's email or phone number was publicly listed;
- export source-backed contact reports to a CRM or spreadsheet.

### Input

| Key | Type | Required | Default | Allowed range | Purpose |
| --- | --- | --- | --- | --- | --- |
| `profiles` | `string[]` | Yes | None; the form prefill is `patagonia` | 1–50 items on paid plans; Free-plan cap: 5. Each item is a username, `@username`, or `instagram.com/<username>` profile URL. | Profiles to turn into contact reports. Duplicates are processed once. |
| `scanLinkedPages` | `boolean` | No | `true` | `true` or `false` | When enabled, scan the public bio destination and at most one relevant contact/about child page. |

Validation is atomic: an empty list, more than 50 items, an invalid username, a non-profile Instagram URL, a non-Instagram URL, URL credentials, or an unknown input key fails before profile processing begins. Usernames must be 1–30 letters, numbers, periods, or underscores after normalization. Matching inputs are deduplicated case-insensitively while preserving the first position.

Example:

```json
{
  "profiles": [
    "patagonia",
    "https://www.instagram.com/example_creator/"
  ],
  "scanLinkedPages": true
}
```

### Output

The primary output is one dataset row per normalized unique profile, in input order. A row with `status: completed` is one billable completed profile report. A row with `status: failed` documents an unavailable profile and is not billed as a completed report.

#### Profile report fields

| Key | Type | Nullable | Meaning / unit |
| --- | --- | --- | --- |
| `profileInput` | string | No | First submitted value that normalized to this profile. |
| `username` | string | No | Normalized Instagram username without `@`. |
| `profileUrl` | string | No | Canonical Instagram profile URL. |
| `displayName` | string | Yes | Public display name when available. |
| `biography` | string | Yes | Public biography when available. |
| `isPrivate` | boolean | Yes | Public privacy state when available. |
| `isVerified` | boolean | Yes | Instagram badge state when available; this is not email verification. |
| `externalUrl` | string | Yes | Public bio destination selected for bounded scanning. |
| `status` | string enum | No | `completed` or `failed`. |
| `outcome` | string enum | No | `contacts_found`, `no_public_contacts_found`, `profile_not_found`, or `profile_temporarily_unavailable`. |
| `contacts` | object\[] | No | Deduplicated public contacts with evidence sources. |
| `contactCount` | integer | No | Number of deduplicated contacts. |
| `emailCount` | integer | No | Number of deduplicated email contacts. |
| `phoneCount` | integer | No | Number of deduplicated phone contacts. |
| `whatsappCount` | integer | No | Number of deduplicated WhatsApp contacts. |
| `emails` | string\[] | No | Normalized email values for CSV and automation use. |
| `phones` | string\[] | No | Normalized phone values; international `+` format is preserved when evidence supports it. |
| `whatsapp` | string\[] | No | Normalized public WhatsApp links or numbers. |
| `scanRequested` | boolean | No | Whether linked-page scanning was requested. |
| `pagesAttempted` | integer | No | Number of public linked pages attempted, from 0 to 2. |
| `pagesFetched` | integer | No | Number of attempted pages successfully read as HTML or plain text. |
| `scanBytesRead` | integer | No | Total linked-page response bytes read, capped at 2 MiB per profile. |
| `pageScans` | object\[] | No | Evidence and status for each attempted linked page. |
| `errorCode` | string | Yes | Stable functional code for failed profiles; never a raw response or exception. |
| `errorMessage` | string | Yes | Customer-safe failed-profile explanation. |
| `errorRetryable` | boolean | Yes | Whether retrying later may succeed. |
| `processedAt` | string | No | ISO-8601 UTC time when this row was finalized. |

#### Contact fields

| Key | Type | Nullable | Meaning |
| --- | --- | --- | --- |
| `type` | string enum | No | `email`, `phone`, or `whatsapp`. |
| `value` | string | No | Customer-facing representative value. |
| `normalizedValue` | string | No | Deterministic deduplication/export value. |
| `isPubliclyListed` | boolean | No | Always `true` for returned contacts. |
| `verificationStatus` | string enum | No | `not_deliverability_verified` for email; `not_applicable` otherwise. |
| `sources` | object\[] | No | One or more public evidence locations. |

Each `sources` item contains `url` and `extractionMethod`. Allowed methods are `instagram_public_email_field`, `instagram_public_phone_field`, `instagram_biography_text`, `linked_page_mailto`, `linked_page_tel`, `linked_page_whatsapp`, and `linked_page_text`.

#### Linked-page scan fields

| Key | Type | Nullable | Meaning / unit |
| --- | --- | --- | --- |
| `requestedUrl` | string | No | Public destination selected for the attempt. |
| `finalUrl` | string | Yes | Final public URL after allowed redirects. |
| `status` | string enum | No | `fetched`, `blocked_by_policy`, `timeout`, `too_large`, `unsupported_content`, or `http_error`. |
| `httpStatus` | integer | Yes | Public destination HTTP status when a response was received. |
| `contentType` | string | Yes | Public destination content type when available. |
| `bytesRead` | integer | No | Response bytes read for this page, capped at 1 MiB. |
| `contactsFound` | integer | No | Deduplicated report contacts with evidence on this page. |

#### Illustrative JSON

```json
{
  "profileInput": "example_creator",
  "username": "example_creator",
  "profileUrl": "https://www.instagram.com/example_creator/",
  "displayName": "Example Creator",
  "biography": "Partnership inquiries and new work.",
  "isPrivate": false,
  "isVerified": false,
  "externalUrl": "https://example.com/",
  "status": "completed",
  "outcome": "contacts_found",
  "contacts": [
    {
      "type": "email",
      "value": "hello@example.com",
      "normalizedValue": "hello@example.com",
      "isPubliclyListed": true,
      "verificationStatus": "not_deliverability_verified",
      "sources": [
        {
          "url": "https://www.instagram.com/example_creator/",
          "extractionMethod": "instagram_public_email_field"
        },
        {
          "url": "https://example.com/contact",
          "extractionMethod": "linked_page_mailto"
        }
      ]
    },
    {
      "type": "phone",
      "value": "+1 202 555 0147",
      "normalizedValue": "+12025550147",
      "isPubliclyListed": true,
      "verificationStatus": "not_applicable",
      "sources": [
        {
          "url": "https://example.com/contact",
          "extractionMethod": "linked_page_tel"
        }
      ]
    }
  ],
  "contactCount": 2,
  "emailCount": 1,
  "phoneCount": 1,
  "whatsappCount": 0,
  "emails": ["hello@example.com"],
  "phones": ["+12025550147"],
  "whatsapp": [],
  "scanRequested": true,
  "pagesAttempted": 2,
  "pagesFetched": 2,
  "scanBytesRead": 27648,
  "pageScans": [
    {
      "requestedUrl": "https://example.com/",
      "finalUrl": "https://example.com/",
      "status": "fetched",
      "httpStatus": 200,
      "contentType": "text/html",
      "bytesRead": 18432,
      "contactsFound": 0
    },
    {
      "requestedUrl": "https://example.com/contact",
      "finalUrl": "https://example.com/contact",
      "status": "fetched",
      "httpStatus": 200,
      "contentType": "text/html",
      "bytesRead": 9216,
      "contactsFound": 2
    }
  ],
  "errorCode": null,
  "errorMessage": null,
  "errorRetryable": null,
  "processedAt": "2026-09-20T12:00:00Z"
}
```

The example above is illustrative and validates the implemented schema; it is not presented as an observed Store run.

#### Overview CSV

The overview view keeps one row per profile and flattens the most useful automation fields. Detailed contact sources and page attempts remain available in JSON.

```csv
profileInput,username,profileUrl,displayName,status,outcome,contactCount,emailCount,phoneCount,whatsappCount,emails,phones,whatsapp,scanRequested,pagesAttempted,pagesFetched,scanBytesRead,errorCode,processedAt
example_creator,example_creator,https://www.instagram.com/example_creator/,Example Creator,completed,contacts_found,2,1,1,0,"[""hello@example.com""]","[""+12025550147""]",[],true,2,2,27648,,2026-09-20T12:00:00Z
```

#### Run summary

The default key-value `OUTPUT` record contains `status` (`succeeded`, `partial`, or `failed`), `inputProfiles`, `uniqueProfiles`, `completedReports`, `failedReports`, `chargeableReports`, `profilesWithContacts`, `profilesWithoutContacts`, `contactsFound`, `pagesAttempted`, `pagesFetched`, `bytesRead`, `startedAt`, and `finishedAt`. No secondary dataset or binary download is included in the first release.

For a completed no-contact profile, `status` is `completed`, `outcome` is `no_public_contacts_found`, contact arrays are empty, counts are zero, page evidence is retained, and the report is billable. A profile that cannot be resolved returns a `failed` row with `profile_not_found` or `profile_temporarily_unavailable`, empty contacts, a stable safe error, and no completed-report charge. A linked-page failure does not fail an otherwise retrieved profile: the page status records the limitation and any profile-level contacts remain in the completed report. Mixed batches produce a `partial` run summary.

### How linked-page scanning is bounded

The first release scans no more than two pages per profile:

1. the public destination linked from the profile;
2. at most one relevant contact/about child page selected from that destination.

Each page is limited to public HTTP/HTTPS HTML or plain text, 1 MiB, three redirects, a five-second connection timeout, and an eight-second read timeout. Loopback, private, link-local, multicast, reserved, unspecified, and credential-bearing destinations are blocked before each request and redirect. The Actor does not log in, submit forms, retain cookies, download binary files, or bypass access controls.

If a page is blocked, slow, oversized, unavailable, or an unsupported content type, the report keeps any Instagram-level contacts and records the page outcome instead of inventing missing data.

### Contact interpretation

- `isPubliclyListed: true` means the value appeared on the public profile or an allowed public linked page.
- `verificationStatus: not_deliverability_verified` means the Actor did not test whether an email mailbox can currently receive messages.
- Multiple observations of the same normalized value are merged into one contact with multiple sources.
- Phone extraction is limited to explicit `tel:` links, WhatsApp links, labeled phone text, and clear international formats to reduce false positives.

### Pricing

The Actor charges once when a run starts and once per completed profile report. Linked-page scanning is included. A successfully completed report with zero discovered contacts is still charged because the profile and requested pages were examined and the no-contact outcome is delivered. A profile that cannot be retrieved after bounded retry is not charged as a completed report.

| Tier | Per completed report | Per 1,000 completed reports | One-time Actor start |
| --- | ---: | ---: | ---: |
| Free | $0.00500 | $5.00 | $0.002 |
| Bronze | $0.00400 | $4.00 | $0.002 |
| Silver | $0.00360 | $3.60 | $0.002 |
| Gold | $0.00320 | $3.20 | $0.002 |
| Platinum | $0.00320 | $3.20 | $0.002 |
| Diamond | $0.00320 | $3.20 | $0.002 |

Free-plan runs are capped at five profiles per run. Paid-plan runs accept up to 50 profiles.

### Limitations and responsible use

- Only public profile fields and publicly accessible linked pages are examined.
- Private, restricted, removed, or temporarily unavailable profiles can return a failed report.
- Websites can block automated requests, require JavaScript, or omit contact details from their initial HTML.
- A public address may be stale, role-based, or unsuitable for unsolicited messages.
- Results are not legal advice or permission to contact someone. Follow applicable privacy, marketing, and platform rules.
- Coverage and contact-bearing yield vary by account type, niche, geography, and how profiles publish their contact information.

# Actor input Schema

## `profiles` (type: `array`):

Add 1–50 Instagram usernames or profile URLs. The @ symbol is optional. Duplicates are processed once.

## `scanLinkedPages` (type: `boolean`):

Scan the profile's public bio link and at most one relevant child page for additional public email, phone, and WhatsApp details. Turn this off for Instagram-only results.

## Actor input object example

```json
{
  "profiles": [
    "patagonia"
  ],
  "scanLinkedPages": true
}
```

# Actor output Schema

## `results` (type: `string`):

Completed and failed profile reports in input order.

## `runSummary` (type: `string`):

Completion, contact, page-scan, and billing-unit counts for this run.

# 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 = {
    "profiles": [
        "patagonia"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("data-slayer/instagram-email-phone-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 = { "profiles": ["patagonia"] }

# Run the Actor and wait for it to finish
run = client.actor("data-slayer/instagram-email-phone-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 '{
  "profiles": [
    "patagonia"
  ]
}' |
apify call data-slayer/instagram-email-phone-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,data-slayer/instagram-email-phone-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/4aG5cJVoZb11QutvU/builds/y6YfFd0GpKxUHml7e/openapi.json
