# Instagram Profile & Content Intelligence (`akatra/instagram-profile-content-intelligence`) Actor

Extract public Instagram profiles, posts, reels and engagement metrics.

- **URL**: https://apify.com/akatra/instagram-profile-content-intelligence.md
- **Developed by:** [Akatra](https://apify.com/akatra) (community)
- **Categories:** Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 profile checks

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?

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 Profile & Content Intelligence

**Extract public Instagram profiles, posts, reels and engagement metrics.**

Instagram Scraper for profiles, posts and reels, by Akatra. Give usernames or links and get public profiles, their newest posts and reels with likes, comments and reel plays in one clean schema, plus engagement metrics you can trust: engagement rate by followers, medians, top content and posting frequency, always over the exact sample that was collected. Add a monitor key and later runs tell you which profiles changed (followers, bio, link, verification, profile picture) and which content is new, with only the changes written if you want.

**Pay per result** · **No login, no cookies** · **Public data only** · **Structured output for AI agents (MCP)**

### 🛠️ What does Instagram Profile & Content Intelligence do?

- 👤 **Public profiles.** Username, name, bio, verified badge, link in bio, followers, following, number of posts, profile picture, numeric user id.
- 🖼️ **Newest posts and reels in one schema.** Photos, carousels, videos and reels with caption, publication time, likes, comments, reel plays, hashtags, mentions, location, paid-partnership flag and collaborators. One row per post: a carousel stays one row with its photos in `mediaItems`.
- 📈 **Engagement metrics without guesswork.** `engagementTotal`, `engagementRateByFollowers`, `playsToFollowers` and `commentsToLikes` per item; averages, medians, top post by likes and comments, top reel by plays, content-type mix, hashtag frequency and posts per week per profile. A metric whose inputs are missing is `null`, nothing is divided by zero, and every summary states its sample size and date window.
- 🔁 **Change tracking.** With a monitor key the first run stores a baseline; later runs report `FOLLOWERS_CHANGED`, `FOLLOWING_CHANGED`, `POST_COUNT_CHANGED`, `BIO_CHANGED`, `EXTERNAL_URL_CHANGED`, `VERIFICATION_CHANGED`, `PROFILE_IMAGE_CHANGED`, `DISPLAY_NAME_CHANGED`, `USERNAME_CHANGED` and `NEW_CONTENT`.
- 🧹 **Only what changed.** With `onlyChanged`, unchanged profiles and known content are not written; known content is not charged.
- 🔗 **Post and reel links.** Paste links and get the full record of each public post or reel.
- 🔒 **Public data only.** No login, no cookies, no private accounts, no DMs, no follower or following lists, no comment texts or comment authors.

#### What people use it for

- **Competitor and brand tracking.** Follower growth, posting frequency and the best-performing content of a list of brands, week by week.
- **Creator vetting.** Median likes, comments and engagement rate over the newest content, with the sample stated.
- **Campaign monitoring.** New posts and reels of partner accounts with likes, comments and plays.
- **AI agents.** "Which profile gained followers since last week?", "which reel of this brand has the most plays?" are answered by plain fields.

### 📊 What data can you extract?

Every row has a `recordType`: `profile`, `content` or `error`.

| Group | Fields |
|---|---|
| Profile | `username`, `profileUrl`, `instagramUserId`, `displayName`, `biography`, `profileImageUrl`, `isVerified`, `isPrivate`, `externalUrl`, `followersCount`, `followingCount`, `postsCount` |
| Profile analytics (over the collected sample) | `sampleSize`, `sampleWindowStart`, `sampleWindowEnd`, `averageLikes`, `medianLikes`, `averageComments`, `medianComments`, `averageReelPlays`, `medianEngagementRate`, `postsPerWeek`, `analytics` (content-type mix, top content, top reel, hashtag frequency) |
| Content | `contentId`, `shortcode`, `contentUrl`, `contentType` (`post`, `carousel`, `reel`, `video`), `ownerUsername`, `ownerUserId`, `coauthors`, `caption`, `publishedAt`, `likeCount`, `likesHidden`, `commentCount`, `viewCount`, `playCount`, `mediaCount`, `mediaItems`, `hashtags`, `hashtagCount`, `mentions`, `thumbnailUrl`, `displayUrl`, `videoDurationSeconds`, `locationName`, `isPaidPartnership` |
| Content metrics | `engagementTotal`, `engagementRateByFollowers`, `playsToFollowers`, `commentsToLikes` |
| Errors | `errorCode` (`not_found`, `private_profile`, `login_required`, `blocked`, `rate_limited`, `timeout`, `parse_error`, `unsupported_url`, `temporary_failure`), `errorMessage`, `input` |
| Changes (with a monitor key) | `monitorStatus`, `changeTypes`, `changes`, `previousFollowersCount`, `firstSeenAt`, `lastSeenAt`, `changedAt` |
| Provenance | `source`, `sourceUrl`, `scrapedAt` |

A field Instagram does not show is `null`. Nothing is guessed.

### 🚀 How to use it

1. Add **usernames** (`nike`, `@natgeo`) or **profile links**, and optionally **post** or **reel links**.
2. Choose what to collect: profile rows, posts (up to 12 per account) and reels (up to 12 per account).
3. Optional: set a **monitor key** and turn on **Only changed records**, then schedule the Actor.
4. Run it.
5. Download the results as JSON, CSV or Excel, or read them through the API.

### ⬇️ Input examples

Profiles with their newest posts and reels:

```json
{
  "usernames": ["nike", "natgeo", "bluebottle"],
  "maxPostsPerProfile": 12,
  "maxReelsPerProfile": 12
}
```

Daily competitor tracking, only changes:

```json
{
  "usernames": ["nike", "adidas", "puma"],
  "monitorKey": "sportswear-daily",
  "onlyChanged": true,
  "followersChangeThresholdPercent": 1
}
```

Single posts and reels:

```json
{
  "postUrls": [{"url": "https://www.instagram.com/p/Dd7YYNtn-9H/"}],
  "reelUrls": [{"url": "https://www.instagram.com/reel/Dd9Etk7R91I/"}],
  "includeProfile": false
}
```

### ⬆️ Output examples

A profile row from a live run with 4 posts and 3 reels per profile (shortened):

```json
{
  "recordType": "profile",
  "username": "bluebottle",
  "profileUrl": "https://www.instagram.com/bluebottle/",
  "instagramUserId": "354032059",
  "displayName": "Blue Bottle Coffee",
  "isVerified": true,
  "isPrivate": false,
  "externalUrl": "https://bluebottlecoffee.com/us/eng/monthofjazz",
  "followersCount": 517948,
  "followingCount": 806,
  "postsCount": 2588,
  "sampleSize": 5,
  "sampleWindowStart": "2026-09-16T15:53:33+00:00",
  "sampleWindowEnd": "2026-09-30T21:40:31+00:00",
  "averageLikes": 1724.6,
  "medianLikes": 444,
  "averageComments": 9.4,
  "medianComments": 4,
  "averageReelPlays": 48424.33,
  "medianEngagementRate": 0.000859,
  "postsPerWeek": 3.4,
  "analytics": {
    "contentTypeDistribution": {"carousel": 2, "reel": 3},
    "topReelByPlays": {"shortcode": "DdWpu4yMpxk", "contentUrl": "https://www.instagram.com/reel/DdWpu4yMpxk/", "contentType": "reel", "playCount": 102805}
  },
  "monitorStatus": "baseline",
  "source": "instagram",
  "scrapedAt": "2026-10-03T23:34:57Z"
}
```

A reel row from the same run (shortened):

```json
{
  "recordType": "content",
  "username": "bluebottle",
  "contentId": "3987558062057757796",
  "shortcode": "DdWpu4yMpxk",
  "contentUrl": "https://www.instagram.com/reel/DdWpu4yMpxk/",
  "contentType": "reel",
  "ownerUsername": "bluebottle",
  "publishedAt": "2026-09-16T15:53:33+00:00",
  "likeCount": 7034,
  "commentCount": 34,
  "playCount": 102805,
  "mentions": ["shuyakyotojazz"],
  "engagementTotal": 7068,
  "engagementRateByFollowers": 0.013646,
  "playsToFollowers": 0.198485,
  "commentsToLikes": 0.004834
}
```

### 🔁 Change tracking

- **First run of a key:** every profile and content row is the baseline (`monitorStatus = baseline`). Nothing is reported as a change.
- **Later runs:** profiles are `unchanged`, `changed` or `new`; content is `unchanged` or `new` (`NEW_CONTENT`). A profile you add to the list later, or one that could not be read in an earlier run, starts with its own baseline, so its existing posts are never reported as new. With **Only changed records**, only `changed`, `new` and baseline rows (and error rows) are written.
- **Follower noise is filtered.** Large accounts gain followers every minute. A follower or following change is reported when it reaches **followersChangeThresholdPercent** (default 1, `0` = any change) of the last reported value, so small moves still add up.
- **Likes, comments and plays of known content** are refreshed in the row but not reported as changes; they move all the time.
- **No false removals.** Removal is not reported: the public view shows only the newest items, so an item leaving the list is not proof of deletion, and a blocked, rate-limited or unreadable page is never treated as one.
- **A renamed account keeps its history** (it is tracked by its numeric id) and reports `USERNAME_CHANGED`. Two accounts with the same display name are never merged.
- **Keys are isolated.** History is stored per monitor key in a key-value store of your own account (`instagram-monitor`), only with the values needed for comparison.

### 🤖 For AI agents, MCP and the API

Call the Actor through the Apify API, the Apify MCP server or any Apify integration. Dataset views `profiles`, `content` and `changes` give ready-made column sets; the `SUMMARY` record lists profiles and content read, errors by code, changes, requests and bytes per proxy type.

### 💲 Pricing: pay per result

You pay per public profile checked and per post or reel row written to the dataset. See the Pricing tab for the current prices.

- **Profile check:** one per public profile that was read successfully. It is also charged in a monitoring run where nothing changed and no row is written for the profile, because the profile was still checked. Turning off the profile row does not avoid it when posts or reels of that profile are read.
- **Content result:** one per post or reel row written. Content that **Only changed records** suppresses is not written and not charged.
- Private profiles, accounts or links that do not exist, refused or unreadable pages and error rows are not charged.
- There is no start fee. You can cap a run with the maximum charge setting: the run reads only as many profiles as the cap pays for and stops writing content at the cap.

### 🚦 Run status and messages

A run ends as failed only when the input is invalid. Everything else ends as succeeded with a message such as:

| Message | Meaning |
|---|---|
| `Saved N row(s): P profile(s), C content item(s)` | Done |
| `N input(s) could not be read (not_found 1, rate_limited 2)` | Error rows in the dataset say which input and why |
| `monitor baseline created` | First run of a monitor key |
| `N changed or new, M unchanged not repeated` | Monitoring run with only changed records |
| `Stopped early: the run's maximum charge was reached` | The saved rows are complete; the rest was not written |

### ℹ️ Good to know

- **Public data only.** Private accounts return only their identity (`isPrivate: true`) and a `private_profile` error row; their content is never read.
- **The 12 newest items.** Instagram shows visitors who are not logged in the 12 newest posts and the 12 newest reels of an account. Pinned posts and reels are skipped, so "newest" means newest (read a pinned post through its link). Pinned items are recognised by a heuristic (an item older than the ones after it), not by a flag Instagram shows logged-out visitors; in rare cases, such as a post whose date is far out of order, it can be taken for a pinned one and skipped. Older content is available through post and reel links. Profile summaries are over the content each profile listed in the run, never lifetime statistics; a post shared by two of your profiles counts for both. `postsPerWeek` is measured on the sampled posts (on the reels when posts are not collected).
- **Fields Instagram does not show to logged-out visitors** are not included: business category, business or creator account flags, contact buttons (email, phone), follower and following lists, comment texts, share and save counts.
- **Reel plays** come from the account's reels tab; a reel read only from a link has `playCount: null`. Video duration is rarely given and is then `null`.
- **Personal contact data is masked.** Email addresses and phone numbers written in bios and captions are replaced with `[email removed]` and `[phone removed]`; a link in bio that is a phone, WhatsApp or email link is not returned. Tagged people are not collected.
- **Media links are signed and expire.** This is not a media downloader.
- **Access.** Profile pages are read through residential proxies (Instagram refuses cloud IPs for them); post and reel details go out directly. Instagram can refuse single requests at any time; such inputs come back as error rows and are not charged.
- **Monitoring is not real-time.** Changes are found when the Actor runs.
- **Limits per run:** 200 profiles, 1,000 post or reel links.

### 📝 Changelog

- **1.0** (October 2026). First version: public profiles, newest posts and reels, post and reel links, deterministic engagement metrics, change tracking with only-changed output.

### ⚖️ Legal and privacy

The Actor reads only what Instagram shows publicly to visitors who are not logged in. It does not log in, does not use cookies or accounts, does not bypass any access control and never reads private accounts, direct messages, follower or following lists, comment authors or tagged people. You are responsible for using the data in line with Instagram's terms and the laws that apply to you. This Actor is not affiliated with Instagram or Meta.

# Changelog

This Actor's version history is a separate document: https://apify.com/akatra/instagram-profile-content-intelligence/changelog.md

# Actor input Schema

## `usernames` (type: `array`):

Public Instagram accounts, e.g. nike or @natgeo. Each gives the profile and its newest posts and reels.

## `profileUrls` (type: `array`):

Optional. Profile links such as https://www.instagram.com/nike/.

## `postUrls` (type: `array`):

Optional. Post links such as https://www.instagram.com/p/<shortcode>/.

## `reelUrls` (type: `array`):

Optional. Reel links such as https://www.instagram.com/reel/<shortcode>/.

## `includeProfile` (type: `boolean`):

One row per account with counts and engagement summaries over the collected content.

## `includePosts` (type: `boolean`):

The newest posts of each account (photos, carousels, videos and reels on the grid).

## `maxPostsPerProfile` (type: `integer`):

Instagram shows visitors who are not logged in the 12 newest posts of an account; more is not available.

## `includeReels` (type: `boolean`):

The newest reels of each account, with play counts.

## `maxReelsPerProfile` (type: `integer`):

Up to the 12 newest reels.

## `monitorKey` (type: `string`):

Name of a tracking job, e.g. "competitors-daily". Runs with the same key are compared: the first run stores a baseline, later runs report profile changes and new content.

## `onlyChanged` (type: `boolean`):

With a monitor key: after the first run, write only changed profiles and new content. Unchanged rows are not written and not charged.

## `followersChangeThresholdPercent` (type: `number`):

Report a follower or following change only when it is at least this many percent of the last reported value (0 = any change). Large accounts gain followers every minute.

## Actor input object example

```json
{
  "usernames": [
    "nike"
  ],
  "includeProfile": true,
  "includePosts": true,
  "maxPostsPerProfile": 12,
  "includeReels": true,
  "maxReelsPerProfile": 12,
  "onlyChanged": false,
  "followersChangeThresholdPercent": 1
}
```

# Actor output Schema

## `profiles` (type: `string`):

One row per account with counts and sample analytics.

## `content` (type: `string`):

One row per post, carousel, reel or video with engagement metrics.

## `changes` (type: `string`):

Change-tracking columns (with a monitor key).

## `all` (type: `string`):

Every row, including error rows.

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

Profiles and content read, errors by code, changes, requests and bytes per proxy type.

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

// Run the Actor and wait for it to finish
const run = await client.actor("akatra/instagram-profile-content-intelligence").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 = { "usernames": ["nike"] }

# Run the Actor and wait for it to finish
run = client.actor("akatra/instagram-profile-content-intelligence").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 '{
  "usernames": [
    "nike"
  ]
}' |
apify call akatra/instagram-profile-content-intelligence --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,akatra/instagram-profile-content-intelligence"
        }
    }
}
```

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/U3QpyFherobn1hOeo/builds/XeKcaqBdATGkoWw3m/openapi.json
