# Instagram Reels Scraper (`apt_marble/instagram-reels-scraper`) Actor

Scrape public Instagram Reels with no login or cookies. Get plays, likes, comments, captions, hashtags, duration, audio, and MP4 URLs. Includes account analytics (average plays, plays per follower, posting frequency) plus filters by date, plays, likes, or Reel URL.

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

## Pricing

$2.50 / 1,000 scraped records

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/platform/actors/running/actors-in-store#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 Reels Scraper

Answer the question every short-form team actually has: **how are my competitors' reels performing, and what are they doing differently?**

Give it a list of public Instagram accounts. It reads each account's Reels tab and returns every reel with its **play count, likes, comments, caption, hashtags, duration, audio track and a direct MP4 URL** — plus one summary row per account with **average plays, median plays, plays per follower, engagement rate and reels per week**.

No login. No cookies. No session tokens to paste. Nothing to configure beyond the list of accounts.

### What you can do with it

- **Benchmark a competitor set.** Run ten accounts and sort the summary rows by plays per follower to see who is actually punching above their audience size.
- **Find what works.** Sort reels by play count and read the captions, hooks, hashtags and lengths behind the top performers.
- **Track posting cadence.** `reelsPerWeek` and `newestReelAt` show who is publishing daily and who has gone quiet.
- **Spot trending audio.** Every reel carries the audio asset id, so you can group reels by track and rank sounds by adoption and plays.
- **Catch sponsored reels.** `isSponsored` flags the paid-partnership label — the single most valuable field for ad-intelligence work.
- **Build creator shortlists.** Filter with `minPlays` to keep only accounts and reels above a performance floor.
- **Archive the media.** `videoUrl` is a direct, publicly fetchable MP4; `thumbnailUrl` is the cover frame.

### What you get

One row per reel, plus an optional summary row per account. Real output, abridged (long CDN URLs truncated):

```json
{
  "type": "reel",
  "id": "3948507321457537241",
  "shortcode": "DbL6n0ggXDZ",
  "url": "https://www.instagram.com/reel/DbL6n0ggXDZ/",
  "ownerUsername": "nasa",
  "ownerId": "528817151",
  "ownerFullName": "NASA",
  "ownerIsVerified": true,
  "ownerProfileUrl": "https://www.instagram.com/nasa/",
  "caption": "Sound on!\n\nSonifications take images from across the universe and turn them into music…\n\n#NASA #Space #MusicLife",
  "hashtags": ["NASA", "Space", "MusicLife"],
  "mentions": ["nasachandraxray"],
  "takenAt": "2026-07-24T18:46:42.000Z",
  "takenAtTimestamp": 1784918802,
  "playsCount": 11374367,
  "likesCount": 429496,
  "commentsCount": 1948,
  "interactionsCount": 431444,
  "likesPerPlayPercent": 3.776,
  "videoDuration": 34.004,
  "countsHidden": false,
  "commentsDisabled": null,
  "hasAudio": true,
  "isSponsored": false,
  "thumbnailUrl": "https://instagram.f…/753557824_18630272896049152_…jpg?…",
  "thumbnailUrls": ["…8 renditions, largest first…"],
  "videoUrl": "https://instagram.f…/AQPmdAz9D4QKeO_…mp4?…",
  "videoUrls": ["…"],
  "width": 1080,
  "height": 1080,
  "coauthors": [],
  "audioAssetId": "27398485943186432",
  "audioTitle": "Original audio",
  "audioArtist": "nasa",
  "isOriginalAudio": true,
  "productType": "clips",
  "mediaType": "reel",
  "source": "reels-tab",
  "scrapedAt": "2026-07-27T18:12:04.881Z"
}
```

And the account summary row that goes with it (the statistics below describe a
short two-reel sample, so you can check the arithmetic against the reel above):

```json
{
  "type": "profile",
  "username": "nasa",
  "userId": "528817151",
  "fullName": "NASA",
  "followersCount": 104292683,
  "followingCount": 91,
  "postsCount": 4859,
  "reelsCountReported": 171,
  "isVerified": true,
  "categoryName": "Government Agencies",
  "reelsScraped": 2,
  "totalPlays": 16656091,
  "avgPlays": 8328046,
  "medianPlays": 8328045.5,
  "maxPlays": 11374367,
  "avgLikes": 256666,
  "avgComments": 1275,
  "avgVideoDuration": 47.01,
  "playsPerFollower": 0.0799,
  "engagementRatePercent": 0.2473,
  "reelsPerWeek": 1.8,
  "newestReelAt": "2026-07-24T18:46:42.000Z",
  "oldestReelAt": "2026-07-20T21:16:30.000Z",
  "profileUrl": "https://www.instagram.com/nasa/",
  "reelsTabUrl": "https://www.instagram.com/nasa/reels/"
}
```

### Input

| Field | Type | Default | What it does |
| --- | --- | --- | --- |
| `usernames` | array | `["nasa"]` | Accounts to scrape. A username (`nasa`), an `@handle`, or a full profile URL — including a `/reels/` tab URL. Any mix. |
| `reelUrls` | array | `[]` | Individual reels to scrape, independent of the accounts above. Accepts `/reel/`, `/p/` and `/tv/` URLs or a bare shortcode. |
| `maxReelsPerProfile` | integer | `30` | Reels to keep per account. Instagram serves 12 per page, so this rounds up to whole pages. `0` returns only the summary row. |
| `onlyReelsNewerThan` | string | `""` | Keep reels published on or after this date. Absolute (`2026-01-31`) or relative (`3 months`, `6 weeks`, `90 days`). |
| `minPlays` | integer | `0` | Skip reels below this play count. |
| `minLikes` | integer | `0` | Skip reels below this like count. |
| `sortBy` | select | `newest` | `newest` = Instagram's own order. `mostPlayed` = ranked by plays, within the reels this run collected. |
| `includeProfileRecord` | boolean | `true` | Push the account summary row with the performance statistics. |
| `maxConcurrency` | integer | `3` | Accounts worked on in parallel. |
| `proxyCountry` | string | `us` | Two-letter country code for the requests. |

### Output fields

#### Reel rows (`type: "reel"`)

| Field | Type | Notes |
| --- | --- | --- |
| `id` | string | Numeric media id. |
| `shortcode` / `url` | string | `DbL6n0ggXDZ` and the canonical `/reel/` permalink. |
| `ownerUsername` / `ownerId` / `ownerFullName` / `ownerIsVerified` / `ownerProfileUrl` | | The author. On a collab reel the co-authors are in `coauthors`. |
| `caption` | string | Full caption text, newlines intact. |
| `hashtags` / `mentions` | array | Parsed out of the caption, de-duplicated, no `#`/`@`. |
| `takenAt` / `takenAtTimestamp` | string / number | ISO-8601 UTC and the raw unix seconds. |
| `playsCount` | number | Plays / views. `null` when Instagram did not report it (see limits). |
| `likesCount` / `commentsCount` | number | `interactionsCount` is their sum. |
| `likesPerPlayPercent` | number | Likes as a percentage of plays, 3 decimals — the standard short-form performance ratio. |
| `videoDuration` | number | Seconds, fractional. |
| `countsHidden` | boolean | `true` when the creator hid likes and views, so the counts above are unreliable. |
| `commentsDisabled` / `hasAudio` | boolean | `null` when the route does not report the flag. |
| `isSponsored` | boolean | Paid-partnership label. `null` — never `false` — when the route carries no such flag. |
| `thumbnailUrl` / `thumbnailUrls` | string / array | Cover frame; the array is every rendition, largest first. |
| `videoUrl` / `videoUrls` | string / array | Direct MP4, no auth needed. Signed and time-limited. |
| `width` / `height` | number | Source dimensions. |
| `audioAssetId` | string | Group reels by this to rank sounds. |
| `audioTitle` / `audioArtist` | string | Track title and artist for licensed audio; Instagram's `"Original audio"` label and the creator's handle for original audio. |
| `isOriginalAudio` | boolean | `true` original, `false` licensed, `null` not reported. |
| `productType` / `mediaType` | string | Always `clips` / `reel` for reel rows. |
| `source` | string | Which route produced the row: `reels-tab`, `timeline-scan`, `profile-preview`, `single-reel` or `oembed-partial`. |

#### Account summary rows (`type: "profile"`)

`username`, `userId`, `fullName`, `profileUrl`, `reelsTabUrl`, `biography`, `externalUrl`, `profilePicUrl`, `followersCount`, `followingCount`, `postsCount`, `reelsCountReported`, `isVerified`, `isPrivate`, `isBusinessAccount`, `categoryName`, `businessEmail`, `businessPhoneNumber`, plus the statistics: `reelsScraped`, `totalPlays`, `avgPlays`, `medianPlays`, `maxPlays`, `avgLikes`, `avgComments`, `avgVideoDuration`, `playsPerFollower`, `engagementRatePercent`, `reelsPerWeek`, `newestReelAt`, `oldestReelAt`.

Every statistic is a **sample** figure describing the reels this run collected — not a lifetime total. A run capped at 30 reels describes those 30. Counts Instagram declined to report are left out of the averages instead of being counted as zero.

### FAQ

**Do I need an Instagram account, cookies or a session id?**
No. Everything comes from Instagram's own public, logged-out endpoints.

**How many reels can I get from one account?**
As many as the account has, subject to rate limiting. Pagination has no ceiling of its own — a verified run walked 9 consecutive pages with zero duplicates and no end in sight. The practical limit is Instagram's throttling, which is why the actor keeps partial results instead of discarding them.

**Can I get comments on the reels?**
Not with this actor.

**Why is `playsCount` null on some rows?**
Either the reel came from a route that does not report it (`source` tells you), or the creator hid their view counts (`countsHidden: true`). It is never a silent zero — a zero in this dataset means zero.

**What happens if an account is private or does not exist?**
A private account gets a summary row marked `isPrivate: true` and no reels. A handle that does not exist is logged as a failure and skipped; one bad entry never stops the run.

**Can I paste photo or carousel URLs into `reelUrls`?**
You can, but they are skipped with a warning. This actor returns reels only.

**Does it work on accounts with pinned reels?**
Yes. Pinned reels appear first regardless of date; the date filter accounts for that and does not cut the walk short on them.

# Actor input Schema

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

Accounts whose Reels tab you want. Enter a username (nasa), an @handle (@nasa) or a full profile URL (https://www.instagram.com/nasa/) — any mix works. Only public accounts can be scraped.

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

Individual reels to scrape, independent of the accounts above. Accepts /reel/, /p/ and /tv/ URLs, or a bare shortcode. Anything that turns out not to be a reel is skipped with a warning.

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

How many reels to keep for each account. Instagram serves 12 reels per page, so this rounds up to whole pages. Set it to 0 to get only the account summary row.

## `onlyReelsNewerThan` (type: `string`):

Keep only reels published on or after this date. Accepts an absolute date (2026-01-31) or a relative window such as "3 months", "6 weeks" or "90 days". Leave blank for no date limit.

## `minPlays` (type: `integer`):

Skip reels with fewer plays than this. Use it to keep only an account's hits. Reels whose creator hid their view counts are kept, because Instagram reports no number for them at all.

## `minLikes` (type: `integer`):

Skip reels with fewer likes than this. Reels whose creator hid their like counts are kept, because Instagram reports no number for them at all.

## `sortBy` (type: `string`):

"Newest first" is Instagram's own order and is left untouched. "Most played" re-orders the reels this run collected — it does not search the whole back catalogue, so raise the reel limit if you want a wider pool to rank.

## `includeProfileRecord` (type: `boolean`):

Push one summary row per account alongside the reels: followers, bio, plus average plays, median plays, plays per follower, engagement rate and reels per week computed over the reels this run collected. Turn it off if you only want reel rows.

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

How many accounts to work on at the same time. Instagram rate limits aggressively — raise this only if your runs are slow and you are not seeing rate-limit warnings.

## `proxyCountry` (type: `string`):

Two-letter country code used for the requests (for example us, gb, de). Instagram serves region-specific media hosts; results were verified from the United States.

## Actor input object example

```json
{
  "usernames": [
    "nasa"
  ],
  "reelUrls": [],
  "maxReelsPerProfile": 30,
  "onlyReelsNewerThan": "",
  "minPlays": 0,
  "minLikes": 0,
  "sortBy": "newest",
  "includeProfileRecord": true,
  "maxConcurrency": 3,
  "proxyCountry": "us"
}
```

# Actor output Schema

## `dataset` (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 = {
    "usernames": [
        "nasa"
    ],
    "maxReelsPerProfile": 30,
    "minPlays": 0,
    "minLikes": 0,
    "sortBy": "newest",
    "maxConcurrency": 3,
    "proxyCountry": "us"
};

// Run the Actor and wait for it to finish
const run = await client.actor("apt_marble/instagram-reels-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 = {
    "usernames": ["nasa"],
    "maxReelsPerProfile": 30,
    "minPlays": 0,
    "minLikes": 0,
    "sortBy": "newest",
    "maxConcurrency": 3,
    "proxyCountry": "us",
}

# Run the Actor and wait for it to finish
run = client.actor("apt_marble/instagram-reels-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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": [
    "nasa"
  ],
  "maxReelsPerProfile": 30,
  "minPlays": 0,
  "minLikes": 0,
  "sortBy": "newest",
  "maxConcurrency": 3,
  "proxyCountry": "us"
}' |
apify call apt_marble/instagram-reels-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=apt_marble/instagram-reels-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/FQPGoGg8lwkTJYrIj/builds/5oGx27gTUpaJjLRt3/openapi.json
