# Instagram Email Scraper — Emails, Phone Numbers & Contacts (`afanasenko/instagram-email-scraper`) Actor

Scrape Instagram profiles for the contact details they publish — email, phone, website and business category — from a list of usernames you paste. One read per profile, no posts fetched, no login needed.

- **URL**: https://apify.com/afanasenko/instagram-email-scraper.md
- **Developed by:** [Andrey Afanasenko](https://apify.com/afanasenko) (community)
- **Categories:** Lead generation, Social media, Automation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$10.00 / 1,000 profiles

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/actors/running/actors-in-store.md#pay-per-event

## 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

## Instagram Email Scraper — Emails, Phone Numbers & Contacts

[![Apify Actor](https://apify.com/actor-badge?actor=afanasenko/instagram-email-scraper)](https://apify.com/afanasenko/instagram-email-scraper)

> **Paste a list of Instagram usernames and get back the contact details each account publishes — email, phone, website and business category — as one row per account. No login, no cookies, no Instagram account of yours involved.**

You already know whose contacts you want. This actor reads each account once and returns what it publishes: the email on its Contact button or in its bio, the phone number, the bio link, the category, the follower count. Nothing is discovered, no posts are fetched, and nothing is guessed — every value in the output is something the account owner chose to put on their own profile.

### 🎯 What it does

Give it handles, get back contacts. One account in, one row out.

| You paste | You get back |
|---|---|
| `natgeo` | The email, phone, website, category, bio and follower count that account publishes |
| 500 handles from a CSV | 500 rows, in the order you gave them, ready to import |
| A list with private or deleted accounts in it | Those rows named in `SKIPPED_ACCOUNTS`, and no charge for them |

***

#### ⚠️ Read this before you run it

> **Two accounts in three publish no contact details at all — and which two depends entirely on the kind of account.**
>
> Instagram has three kinds. **Business** and **creator** accounts are both *professional*: both can show a Contact button, and both do, often. A **personal** account cannot show one at all — whatever it publishes is written into the bio by hand.
>
> So the yield of a list is decided before you run anything, by who is on it. **Read the table below before you size one.**

***

Measured on 44 accounts drawn at random from real customer lists:

| Account kind | Share of the list | Published an email or a phone |
|---|---|---|
| **Business** | 14 of 44 | **43%** |
| **Creator** | 18 of 44 | **39%** |
| **Personal** | 12 of 44 | **17%** — and every one of those was an address typed into the bio, never a contact field |
| *All three together* | 44 | **34%** |

And by field, across all 44:

| | Share that publishes it |
|---|---|
| An email (Contact button or bio) | 30% |
| A phone | 14% |
| A website (bio link) | 55% |
| A business category | 36% |

Three things follow, and they are what to know before you spend anything:

1. **A professional list is worth about 2.4× a personal one** — 41% against 17%. Local businesses, agencies, shops and creators all do far better than a list of ordinary followers.
2. **Creators count as professional.** They are the largest group here — 18 of 44 — and they publish contacts almost as often as businesses do. Filtering for "business only" throws most of them away; use **Professional** if what you want is "anyone who can be contacted".
3. **Every account that published a phone also published an email.** A phone-only lead does not exist in practice, so turning phone extraction on adds a column rather than adding leads. It is off by default for that reason.

If a run comes back with no contacts at all, the actor says so and tells you whether that was ordinary for the length of your list or whether it says something about the list itself.

### ⚡ Quick start

A minimal input that works on the **free plan** (30 accounts, no setup):

```json
{
  "specificUsernamesList": ["natgeo", "nasa", "nike"],
  "extractEmail": true,
  "extractWebsiteUrl": true
}
```

Leave **Accounts to scrape** at its default and the run does a small capped demo instead of failing, so you can see the output shape before deciding what to ask for.

### 📦 Output sample

A real row, copied unchanged from a run of this actor on 4 September 2026 (`Profile Picture` truncated — the live URL is a 700-character signed link):

```json
{
  "Account": "https://instagram.com/semrush",
  "Source": "Direct Input",
  "Full Name": "Semrush",
  "Followers Count": 170891,
  "Following Count": 1189,
  "Biography": "An Adobe Company. Helping 28M+ marketers be found everywhere search happens",
  "Email": "mail@semrush.com",
  "Email Source": "public_field",
  "Phone": "+18558144510",
  "External URL": "https://linktr.ee/socialmediasemrush",
  "Category": "N/A",
  "Account Type": "business",
  "Verified": "yes",
  "Profile Picture": "https://scontent-lax3-1.cdninstagram.com/v/t51.82787-19/650988809_...jpg",
  "Total Posts": 1958,
  "Analysis Status": "analyzed",
  "Why Not Analyzed": "N/A"
}
```

`Matched By` is the eighteenth column and is absent above because no filter was set on that run — it appears only when a category or keyword filter decided the row.

That run read ten well-known business accounts and **one** of them published an email. That is the shape of this data, not a bad draw — those ten were all businesses, the kind that does best.

Every account is pushed to the dataset as one item. Columns marked *optional* appear only when the matching extraction switch is on; everything else is on every row.

| Column | Always? | What it is |
|---|---|---|
| `Account` | yes | `https://instagram.com/<username>` |
| `Source` | yes | `Direct Input` for a row you asked for |
| `Matched By` | when a category or keyword filter is on | Which filter kept this row: `category`, `keyword`, `both` |
| `Full Name` | yes | The display name |
| `Followers Count` | yes | Followers at the moment the account was read |
| `Following Count` | yes | Accounts it follows |
| `Biography` | yes | Bio text, including line breaks and emoji |
| `Email` | *optional*, on by default | The published address, or `N/A` |
| `Email Source` | *optional*, on by default | `public_field` (the Contact button) or `bio` (parsed from the text) |
| `Phone` | *optional*, off by default | The published phone number, or `N/A` |
| `External URL` | *optional*, on by default | The bio link, or `N/A` |
| `Category` | *optional*, on by default | The Instagram category — «Athlete», «Restaurant», … |
| `Account Type` | yes | `business`, `creator` or `personal` — the first two are professional and can show a Contact button |
| `Verified` | yes | `yes` or `no` |
| `Profile Picture` | when Instagram exposed one | Avatar URL |
| `Total Posts` | yes | Lifetime post count on the profile |
| `Analysis Status` | yes | `analyzed`, or `not_analyzed` on a notice row |
| `Why Not Analyzed` | yes | The reason on a notice row, `N/A` otherwise |

`Email Source` is worth reading in an outreach list. A `public_field` address is the one the account chose to publish as its point of contact; a `bio` address is whatever the bio text happened to contain, which is sometimes a personal address and sometimes someone else's.

### 🧭 When to use it

- **Turning a list into an outreach list.** You have handles from a CSV, a scrape, an event page or a hashtag export, and you need the way to reach them.
- **Local business prospecting.** Restaurants, gyms, clinics and shops are professional accounts with a filled-in Contact button far more often than ordinary personal ones — 43% against 17% in the measured sample.
- **Qualifying a list before you buy tooling for it.** Run 30 accounts on the free plan and read the contact rate off your own list rather than off anybody's marketing.
- **Enriching a CRM.** Give it the handles you already store and get the email, site and category back as columns.

**Want the same contact details for everyone who follows an account?** That is [Instagram Followers & Following Extractor](https://apify.com/afanasenko/instagram-followers-scraper) — it walks a followers or following list and returns email, phone and bio link per profile, the same fields as here. For any other way of arriving at a list — a hashtag, a niche, a post's likers — the table at the bottom points at the actor for it.

### 🛠 Input

The form is four sections:

**🎯 Accounts to scrape** — your list, and a spending cap.

| Field | Key | Default |
|---|---|---|
| Accounts to scrape | `specificUsernamesList` | the demo pair |
| Max accounts to process | `maxCountList` | `0` (no limit) |

One account per line — a bare handle, an @handle and a full profile link all work. **Max accounts to process** is a spending cap: each account costs $0.01, so setting it to 100 means the run cannot go past $1.00. On the free plan a run stops at 30 accounts whatever you put there.

**⚙️ What to extract** — `extractEmail` (on), `extractWebsiteUrl` (on), `extractBusinessCategory` (on), `extractPhoneNumber` (off). **You pay per account, not per field** — switching one off removes a column from the output, it does not make the run cheaper.

**🔬 Filters (optional)** — contact-info presence, follower band, account type (**professional** / business / creator / personal), verified, has-website, category, keywords in bio or name, and an AND/OR combinator for the last two.

**▶️ Run & storage options** — `clearSavedData` (start fresh, or resume an interrupted run) and `excludeAccounts` (handles to skip before they are read or charged for).

### 💰 Pricing

The actor uses Apify's **Pay Per Event** model with **one single event**:

| Event | Price | Triggered |
|---|---|---|
| **Profile** | **$0.01** | Once per account successfully read, whether or not it passes your filters. |

Not charged: accounts that could not be read (not found, private, failed), and anything on your `excludeAccounts` list. On your **first run** you are also not charged for accounts your own filters rejected, up to 100 — so the run where you are most likely to over-filter is the one where over-filtering costs nothing.

You are billed per account, not per API call, so `maxCountList` translates directly into your maximum spend: 500 accounts is $5.00, whatever their bios turn out to contain.

#### What filters do to your bill

**Filters run after the read.** An account has to be read before the actor can tell whether it has an email or matches your follower band, so a narrow filter stack costs exactly the same as no filters and returns fewer rows.

That matters most for **Filter by Contact Info Presence**. At a 34% contact rate, setting it to "Has Email or Phone" means roughly two thirds of what you pay for is discarded before it reaches the dataset. It is still the right switch when you want a clean list and are happy to pay for the reading — but it is a formatting convenience, not a saving.

What to do instead, on the first run: leave the filters alone, read 30 accounts on the free plan, and look at the contact rate your own list actually returns. Then decide.

### 🆓 Free vs paid plan

| | Free plan | Paid plan |
|---|---|---|
| Accounts per run | 30 | unlimited |
| Runs per calendar month | 15 | unlimited |
| Every extraction option | ✓ | ✓ |
| Every filter | ✓ | ✓ |
| Resume from checkpoint | ✓ | ✓ |

Accounts held back by the free ceiling are handed back to you **by name**, as extra rows in the dataset with `Analysis Status: not_analyzed` — so you always know which of your handles were not reached, not merely how many.

Demo runs do not consume the monthly allowance. A run that **ends by itself** before reading anything gives its slot back — a configuration error, say. A run **you abort** keeps its slot: the allowance is reserved before the first read precisely so that stopping a run mid-flight is not a way around it.

### 🧪 Recipes

**A plain contact list from handles you already have**

```json
{
  "specificUsernamesList": ["handle1", "handle2", "handle3"],
  "extractEmail": true,
  "extractWebsiteUrl": true
}
```

**Only the accounts that published an email**

```json
{
  "specificUsernamesList": ["handle1", "handle2"],
  "extractEmail": true,
  "contactInfoType": "emailOnly"
}
```

You still pay for every account read; the filter decides what reaches the dataset, not what is read.

**Businesses only, above a size floor**

```json
{
  "specificUsernamesList": ["handle1", "handle2"],
  "accountType": "business",
  "minFollowers": 1000,
  "extractEmail": true,
  "extractBusinessCategory": true
}
```

**Cap the spend on a long list**

```json
{
  "specificUsernamesList": ["...500 handles..."],
  "maxCountList": 100
}
```

Reads the first 100 and stops — a hard $1.00 ceiling.

**Do not pay twice for handles you already have**

```json
{
  "specificUsernamesList": ["...this week's list..."],
  "excludeAccounts": ["...everything from last week..."]
}
```

Excluded handles are dropped before they are read, so they cost nothing.

### 🔁 Resume / checkpoint

A run that is interrupted — a platform migration, a timeout, an abort — saves its progress. Start it again with `clearSavedData` unchecked and it continues from where it stopped without paying again for the accounts it had already read.

### 📡 Live status & storage records

While a run is going, its **Output** tab shows a live status page: how many accounts are done, how many contacts have been found, and what it has spent so far.

When it finishes, the key-value store holds:

| Record | What it is |
|---|---|
| `RUN_SUMMARY` | Everything the run counted — read, charged, filtered, contact yield, cost |
| `USER_MESSAGE` | The one thing the run most needs to tell you, in plain words |
| `SKIPPED_ACCOUNTS` | Every handle that was skipped, with the reason and its category |
| `FREE_LIMITS_APPLIED` | Which free-plan ceilings applied, as stable ids |
| `FILTER_BURN_PREDICTION` | The pre-read yield estimate for your filter combination |
| `API_USAGE` | Upstream request count for this run |

### 🤖 Programmatic / API use

Start a run and wait for it with the Apify API:

```bash
curl -X POST "https://api.apify.com/v2/acts/afanasenko~instagram-email-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"specificUsernamesList":["natgeo","nasa"],"extractEmail":true}'
```

Or with the JavaScript client:

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('afanasenko/instagram-email-scraper').call({
    specificUsernamesList: ['natgeo', 'nasa'],
    extractEmail: true,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Every value a run needs to explain itself is in the key-value store, not only in the log — a caller that never reads the console still gets `USER_MESSAGE`, `RUN_SUMMARY` and `SKIPPED_ACCOUNTS`.

### 💡 Tips & best practices

- **Run 30 first.** The contact rate on your own list is the only number that matters, and the free plan measures it for you.
- **Prefer business-heavy lists.** Local businesses and agencies publish a Contact button far more often than creators and personal accounts do.
- **Leave phone extraction off** unless you need the column. Every account in the sample that published a phone also published an email.
- **Read `External URL` when `Email` is `N/A`.** 55% of accounts have a bio link and most business sites have a contact form behind it — that is the fastest second route.
- **Do not set filters on the first run.** They cost the same as no filters and hide the thing you are trying to measure.

### ❓ FAQ

**Why is `Email` "N/A" for so many accounts?**
Because those accounts publish no email. Instagram shows an email only on a professional account whose owner filled in the Contact button; a personal account has nowhere to put one. Measured on 44 real accounts, 30% publish an email. This actor returns what is published and never guesses an address.

**Can it find an email that is not on the profile?**
No. It reads the Contact button and the bio text, and that is all there is to read. Nothing is inferred from the name, the domain or anywhere off Instagram.

**Do I need an Instagram account or a login?**
No. It reads public profiles only.

**What happens to private accounts in my list?**
They are skipped before anything is charged and named in `SKIPPED_ACCOUNTS`. A private account publishes no contact details to anyone.

**Does it read posts?**
No. One read per account, the profile only. That is why it costs what it costs.

**What if my list has duplicates or profile URLs mixed with handles?**
Both are handled: duplicates are read once, and `@handle`, bare handle and full profile link are all accepted.

**How do I know which of my handles were not reached?**
They come back as rows with `Analysis Status: not_analyzed` and the reason in `Why Not Analyzed`, plus the full list in `SKIPPED_ACCOUNTS`.

**Is scraping this data legal?**
It reads publicly available profile information. What you may then do with it is governed by GDPR, CCPA and Instagram's terms — see the disclaimer.

### 🔗 Other Instagram scrapers (same author)

Pick the one that matches what you start with:

| You start with… | You want to find… | Use this actor |
|---|---|---|
| A list of usernames | The contact details each one publishes | **This actor** |
| A keyword or a hashtag | The accounts in that niche, enriched and filtered | [Instagram Influencer Search](https://apify.com/afanasenko/instagram-influencer-search) |
| 1–5 seed handles you like | Similar / suggested accounts in the same niche | [Instagram Related Profiles Scraper](https://apify.com/afanasenko/instagram-related-profiles-scraper) |
| One known account | Its full **followers** or **following** list — **with the same email, phone and bio link as here**, on every profile | [Instagram Followers & Following Extractor](https://apify.com/afanasenko/instagram-followers-scraper) |
| One known account | **Track** who follows / unfollows it over time (scheduled) | [Instagram Follower Tracker](https://apify.com/afanasenko/instagram-follower-tracker) |
| A post or reel link | The accounts that liked or commented on it | [Instagram Likes Scraper](https://apify.com/afanasenko/instagram-post-likers-scraper) |
| Anything else (handles, post links, locations, keywords) | All of the above plus five other discovery modes in one tool | [Instagram Profile Scraper](https://apify.com/afanasenko/instagram-profile-scraper) |
| A reel link or a creator handle | What their reels **say and show** — spoken transcript, on-screen text, hook and structure | [Instagram Reel Script Extractor](https://apify.com/afanasenko/instagram-reel-script-extractor) |
| An AI agent (Claude, Cursor, ChatGPT) | The same Instagram data from inside your agent — MCP tools it picks by intent | [Instagram Profile MCP Server](https://apify.com/afanasenko/instagram-profile-scraper-mcp-server) |

### 🛟 Support & feedback

- **Open an issue** on the actor's Issues tab in Apify Console — the fastest way to get a fix shipped.
- **Feature requests** in the same tab; we read every one.
- **Ratings & reviews** on the [Apify Store page](https://apify.com/afanasenko/instagram-email-scraper) help other users find the actor.

### ⚖️ Disclaimer

This actor reads **publicly available** Instagram profile data. It does not bypass authentication, read private profiles, or interact with content on your behalf. Use it in compliance with Instagram's Terms of Service and applicable privacy laws (GDPR, CCPA) — for outreach lists, make sure you have a legitimate interest and a clear unsubscribe path. The contact details returned are what the profile owner chose to publish on Instagram.

# Actor input Schema

## `specificUsernamesList` (type: `array`):

One account per line. All of these work: natgeo · @natgeo · https://instagram.com/natgeo. Private and deleted accounts are skipped and cost nothing.

## `maxCountList` (type: `integer`):

The most accounts this run is allowed to read. Use it as a spending cap: each account costs $0.01, so 100 here means the run cannot go past $1.00. Leave it at 0 for no limit. On the free plan a run stops at 30 accounts whatever you put here.

## `extractEmail` (type: `boolean`):

Look for an email on the account's Contact button, and in its bio text when the button is empty. Adds the Email and Email Source columns. On by default.

## `extractPhoneNumber` (type: `boolean`):

Look for a phone number on the account's Contact button. Adds the Phone column. Off by default, because it rarely adds leads: of 44 accounts tested only 14% published a phone, and every one of them published an email too.

## `extractWebsiteUrl` (type: `boolean`):

Take the link from the account's bio. Adds the External URL column. On by default — 55% of accounts have one, which is more than publish an email.

## `extractBusinessCategory` (type: `boolean`):

Take the category Instagram shows on the profile — «Athlete», «Restaurant», «Beauty, Cosmetic & Personal Care». Adds the Category column. On by default.

## `contactInfoType` (type: `string`):

Keep only accounts that published the contact you name here. «Any» keeps everything and is the default. This does not lower the price — the account has to be read before anyone can tell whether it qualifies.

## `minFollowers` (type: `integer`):

Drop accounts with fewer followers than this. Leave at 0 for no minimum. Easiest to set after a first run, when you can see the follower counts your own list actually returns.

## `maxFollowers` (type: `integer`):

Drop accounts with more followers than this. Leave at 0 for no maximum. Keep the range wide — a band narrower than about 10× the minimum usually keeps less than a tenth of what it reads, and you pay for the rest.

## `accountType` (type: `string`):

Keep only one kind of account. «Any» is the default. Instagram has three: business and creator are both professional and both can show a Contact button; personal cannot. Measured on 44 accounts from real customer lists: business published a contact 43% of the time, creator 39%, personal 17% — and every personal one of those was an address written into the bio text, never a contact field. «Professional» keeps the first two, which is 41% against 17%.

## `mustBeVerified` (type: `boolean`):

Keep only accounts with the blue checkmark. Off by default — most accounts that publish a business email do not have one.

## `hasWebsite` (type: `boolean`):

Keep only accounts with a link in their bio. Off by default. Of 44 accounts tested, 55% had one, so this drops fewer rows than any other filter here.

## `categoryFilter` (type: `string`):

Keep only accounts whose Instagram category is exactly the one you pick. «any» keeps everything and is the default. ⚠ Most accounts set no category at all — 64% of those tested — and all of them are dropped the moment you pick anything else. (AI agents see only the first ~10 entries of this list; any exact value can still be sent directly.)

## `keywords` (type: `array`):

Keep only accounts whose bio or name contains at least one of these words. Upper and lower case are treated the same. Leave empty for no keyword filter.

## `keywordLocation` (type: `string`):

Where to look for the words above: in the bio text, in the display name, or in either one — the default.

## `filterCombination` (type: `string`):

Only matters when you set both the category filter and the keyword filter. AND (the default) keeps an account only if it matches both. OR keeps it if it matches either. Whenever one of them is set, the output gains a Matched By column saying which filter kept each row.

## `clearSavedData` (type: `boolean`):

Leave this on to start from scratch. Turn it off to continue a run that was interrupted — it picks up where it stopped and does not charge again for accounts it already read.

## `excludeAccounts` (type: `array`):

Accounts to skip before they are read, so you are never charged for them. Use it to avoid paying twice for handles you already collected in an earlier run. Same formats as the list above; upper and lower case are treated the same. Leave empty to skip nothing.

## Actor input object example

```json
{
  "specificUsernamesList": [
    "openai",
    "claudeai"
  ],
  "maxCountList": 0,
  "extractEmail": true,
  "extractPhoneNumber": false,
  "extractWebsiteUrl": true,
  "extractBusinessCategory": true,
  "contactInfoType": "any",
  "minFollowers": 0,
  "maxFollowers": 0,
  "accountType": "any",
  "mustBeVerified": false,
  "hasWebsite": false,
  "categoryFilter": "any",
  "keywords": [],
  "keywordLocation": "bio_or_name",
  "filterCombination": "AND",
  "clearSavedData": true,
  "excludeAccounts": []
}
```

# Actor output Schema

## `liveStatus` (type: `string`):

No description

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

No description

## `runSummary` (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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("afanasenko/instagram-email-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 = {}

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,afanasenko/instagram-email-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/EKDVt1RTpafT4XEOn/builds/HeZnAruiJyK9YPyJm/openapi.json
