# Instagram Reel Scraper - $0.18 per 1,000 Reels (`dami_studio/instagram-reel-scraper-v1`) Actor

Give it Instagram handles and get that profile's reels feed newest-first: play count, likes, comments, duration, video URL, cover image and audio. No login, no cookies, no API key. The cheapest Instagram reel scraper on the market at $0.18 per 1,000 reels.

- **URL**: https://apify.com/dami\_studio/instagram-reel-scraper-v1.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** Social media, Videos
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.18 / 1,000 reel scrapeds

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 Reel Scraper

Give it Instagram handles. Get back that profile's **reels tab, newest first** — one row per reel,
with play count, likes, comments, duration, the video file URL, the cover image, and the audio track
the reel was made with.

No Instagram login. No session cookie. No API key. No proxy of your own.

This is the cheapest Instagram reel scraper on the market: **$0.18 per 1,000 reels**.

### Price

**$0.18 per 1,000 reels**, plus **$0.0000125 to start a run** (that start fee is charged once per
gigabyte of run memory; at the default 1 GB it is a bit over one thousandth of a cent).

You are charged once per real reel written to the dataset, and for nothing else:

- the labelled sample row you get when you run with empty input — **free**
- diagnostic rows (private account, handle does not exist, Instagram refused) — **free**
- pages fetched, retries, proxy addresses burned — **free**

| Reels | Cost |
|---|---|
| 25 | $0.0045 |
| 250 | $0.045 |
| 1,000 | $0.18 |
| 10,000 | $1.80 |
| 100,000 | $18.00 |

#### One event, one price, every plan

$0.18 per 1,000 is the rate on the free plan and on every paid plan alike — there are no volume
tiers to climb and no minimum spend, so the table above is what you pay from your very first run.

There is also only **one** billable event. This Actor has no paid add-ons: play counts, likes,
comments, duration, dimensions, the video file URL, the cover image, the audio track and the
co-author list are all fields of the same reel row, not extras metered separately. What it does not
do is equally deliberate — it reads a profile's reels tab and nothing else. It does not transcribe
audio, download and re-host video files, or resolve single reel URLs, so if you need those, this is
the wrong tool rather than a cheaper one.

**Where the money actually goes at scale.** A page is always 12 reels (see Limits), so 1,000 reels
is roughly 84 requests — the cost is dominated by the per-reel event, not by run time. A daily
monitor over 30 accounts at 25 reels each is 750 reels, $0.135 a day, about $4 a month. Add
`onlyPostsNewerThan: "1 day"` and you pay only for reels that are genuinely new.

### Input

| Field | What it does |
|---|---|
| `usernames` | One entry per profile. `"nasa"`, `"@nasa"`, `"https://www.instagram.com/nasa/"` or a numeric profile id all work. |
| `resultsLimit` | Reels per profile, newest first. Default 25, maximum 1000. |
| `onlyPostsNewerThan` | Stop at a date. `2026-01-31`, a full ISO timestamp, or a relative value like `3 months` / `2 weeks` / `7 days`. UTC. |
| `skipPinnedPosts` | Leave out reels the account pinned to the top of its reels tab — those are usually much older than the rest. |
| `username`, `startUrls` | Accepted aliases for `usernames`. Input field names follow the common conventions used across similar Actors, so an input written for another Instagram reel Actor usually runs here without editing. |
| `proxyConfiguration` | Optional. Leave it empty and the Actor rotates Apify datacenter addresses, which is what works best here. |

```json
{
  "usernames": ["nasa", "natgeo", "nike"],
  "resultsLimit": 50,
  "onlyPostsNewerThan": "3 months",
  "skipPinnedPosts": true
}
```

Run it with no input at all and you get **one labelled sample row, uncharged**, showing the exact
shape of a result before you spend anything.

### Output

One row per reel. A real row from a run on `nasa`, trimmed only where the video URL would run off the
page:

```json
{
  "type": "Video",
  "productType": "clips",
  "id": "3963765146907609231",
  "shortCode": "DcCH2ZygIiP",
  "url": "https://www.instagram.com/reel/DcCH2ZygIiP/",
  "caption": "America's newest and most exciting aerospace technology expo and air show is landing at @NASAKennedy in Florida on Nov. 7-8. …  #NASA #MaxPower #America250",
  "hashtags": ["NASA", "MaxPower", "America250"],
  "mentions": ["NASAKennedy"],
  "videoPlayCount": 2441421,
  "videoViewCount": 2441421,
  "likesCount": 43361,
  "commentsCount": 429,
  "videoDuration": 76.69300079345703,
  "videoUrl": "https://scontent-ord5-2.cdninstagram.com/o1/v/t2/f2/m86/AQNh2ps9dzDCDRClcPrhnvk6…mp4",
  "displayUrl": "https://scontent-ord5-2.cdninstagram.com/v/t51.2885-15/…jpg",
  "dimensionsWidth": 1080,
  "dimensionsHeight": 1920,
  "timestamp": "2026-08-14T18:41:26.000Z",
  "takenAtTimestamp": 1786737686,
  "ownerUsername": "nasa",
  "ownerFullName": "NASA",
  "ownerId": "528817151",
  "ownerIsVerified": true,
  "coauthorUsernames": [],
  "isPinned": false,
  "isPaidPartnership": false,
  "hasAudio": true,
  "audioId": "37739766162335392",
  "audioTitle": "Original audio",
  "audioArtist": "nasa",
  "audioIsOriginal": true,
  "audioDurationMs": 76676,
  "inputUsername": "nasa",
  "inputUrl": "https://www.instagram.com/nasa/reels/",
  "scrapedAt": "2026-08-15T02:12:44.318Z"
}
```

#### Field coverage

Measured over a real 100-reel run on `nasa`, not estimated:

| Field | Present |
|---|---|
| `id`, `shortCode`, `url`, `timestamp`, `ownerUsername`, `ownerId` | 100% |
| `videoPlayCount`, `videoViewCount`, `likesCount`, `commentsCount` | 100% |
| `videoDuration`, `videoUrl`, `displayUrl`, `dimensionsWidth`, `dimensionsHeight` | 100% |
| `audioTitle`, `audioArtist`, `audioIsOriginal` | 100% |
| `hashtags` (non-empty) | 41% |

`hashtags` is empty on the rest because those captions genuinely have no hashtags — an empty array is
the honest answer. Missing values are `null`, never a guess.

`videoPlayCount` and `videoViewCount` carry the same number — Instagram now serves a single play
count for reels, and both names are kept so a pipeline written against either one keeps working.

Output field names follow the conventions used across Instagram reel Actors, so switching over
usually means changing the Actor id and nothing else.

### Limits

Read this before you build on it.

- **A page is always 12 reels.** Instagram's clips endpoint accepts a `page_size` parameter and then
  ignores it — 12, 20, 30, 50 and 100 all returned exactly 12 items and a byte-identical body. So
  `resultsLimit: 500` is 42 requests, not 5. It still runs, it is just not instant.
- **Public accounts only.** A private account returns a free diagnostic row saying so. There is no
  login here and no way around that.
- **Collab reels appear.** If another account posted a reel and this account is a co-author,
  Instagram shows it in the reels tab and so does this Actor. `ownerUsername` is then the poster,
  not the account you asked for, and `coauthorUsernames` lists the collaborators. That mirrors what
  a person sees on the page.
- **No single reel URLs.** This scrapes a profile's reels feed. Paste a `/reel/<code>/` URL and you
  get a free diagnostic row telling you so, not a charge. Instagram's keyless single-media endpoint
  is login-walled (6/6 attempts returned 429 from fresh addresses in testing).
- **No share counts, no transcripts, no downloaded video files.** Share count is not in the payload
  this reads. Transcription and video downloading are deliberately out of scope — they are what makes
  other reel scrapers expensive.
- **Video URLs expire.** Instagram's CDN links are signed and time-limited, typically hours. Fetch
  the file promptly if you need a copy; the reel `url` and `shortCode` stay valid forever.
- **`videoDuration` is in seconds, as a float** (`76.693`), not milliseconds.
- **Very deep history is capped at 1,000 reels per profile** by `resultsLimit`.

### FAQ

**Do I need an Instagram account, cookie or token?**
No. Nothing to log in to, nothing to paste, nothing to keep alive.

**Does it work on any public profile?**
Yes — personal, creator and business accounts. Private accounts are not readable by anyone who does
not follow them, so they return a free diagnostic row instead.

**What does it cost to scrape 1,000 Instagram reels?**
$0.18, plus a start fee of $0.0000125 per gigabyte of run memory.

**How fast is it?**
100 reels from one profile in 31 seconds, on the platform, on default settings. A page is 12 reels,
so a bigger pull is mostly page count: 12 reels across two profiles finished in 11 seconds.

**Are the play counts real?**
Yes, they are the numbers Instagram itself serves to the reels tab: `play_count`, `like_count`,
`comment_count`, straight through with no estimation.

**Can I get reels published after a certain date only?**
Yes — `onlyPostsNewerThan`. Pinned reels are handled correctly: an old pinned reel sitting at the top
of the tab does not end the walk early.

**Can I export to CSV or Excel?**
Yes. Apify exports the dataset as JSON, CSV, Excel or XML, and the same data is on a REST endpoint.

**What happens if a run finds nothing?**
You get an uncharged diagnostic row explaining why, and the run finishes as succeeded. You are never
charged for a reel you did not receive.

**Is this affiliated with Instagram or Meta?**
No. It reads publicly visible data. Check Instagram's terms and your own local rules before using the
output commercially.

### How it works

Instagram's reels tab is served by `POST /api/v1/clips/user/` — the same call the website makes when
you open `instagram.com/<handle>/reels/`. Three things had to line up to call it without an account,
and each one was measured rather than assumed:

1. **TLS fingerprint.** Plain Node gets `401 require_login` on the first request. The same request
   through a real Chrome handshake returns HTTP 200 and a full payload from the same address in the
   same second. The block was never about the IP.
2. **A CSRF token.** The POST answers 403 without one. `GET /data/shared_data/` is a 34 KB public
   endpoint that hands back `config.csrf_token` and the cookies that go with it, so the session is
   built from that rather than from a login.
3. **A fresh exit address on refusal.** These endpoints are metered per IP, and a spent address is
   answered with `401 "Please wait a few minutes"` — not a 403, and not a 429. Waiting on it does
   nothing, so a refusal builds a brand-new session on a different datacenter address instead.

Datacenter addresses, not residential. The payload is about 53 KB per reel; at the $8/GB residential
rate that would be $0.42 of bandwidth per 1,000 reels — more than twice this Actor's whole price.
Rotating datacenter exits costs nothing per gigabyte and gives a much larger pool to hop around,
which is exactly what a per-IP meter needs. That is why this can be the cheapest Instagram reel
scraper on Apify and still make money.

# Actor input Schema

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

One entry per profile. Accepts "nasa", "@nasa", "https://www.instagram.com/nasa/" or a numeric profile id.

## `resultsLimit` (type: `integer`):

How many reels to return for each profile, newest first. Maximum 1000.

## `onlyPostsNewerThan` (type: `string`):

Stop at this date. Accepts 2026-01-31, a full ISO timestamp, or a relative value like "3 months", "2 weeks", "7 days". UTC.

## `skipPinnedPosts` (type: `boolean`):

Leave out reels the account pinned to the top of its reels tab — they are usually much older than the rest.

## `username` (type: `array`):

Same as the field above. It exists so an input copied from apify/instagram-reel-scraper runs here unchanged. Leave it empty if you filled in "usernames".

## `startUrls` (type: `array`):

Instagram profile URLs. Merged with the fields above. Leave empty if you filled in "usernames".

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

Optional. Leave empty and the Actor rotates Apify datacenter addresses, which is what works best here and costs nothing per GB.

## Actor input object example

```json
{
  "usernames": [
    "nasa"
  ],
  "resultsLimit": 25
}
```

# Actor output Schema

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

One row per reel: id, shortCode, url, caption, hashtags, mentions, videoPlayCount, videoViewCount, likesCount, commentsCount, videoDuration, videoUrl, displayUrl, dimensionsWidth, dimensionsHeight, timestamp, takenAtTimestamp, ownerUsername, ownerFullName, ownerId, ownerIsVerified, coauthorUsernames, isPinned, isPaidPartnership, hasAudio, audioId, audioTitle, audioArtist, audioIsOriginal, audioDurationMs, type, productType, inputUsername, inputUrl, scrapedAt. Runs with no input return a single uncharged \_sample row; blocked or empty profiles return an uncharged diagnostic row with ok:false and an errorCode.

# 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"
    ],
    "resultsLimit": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/instagram-reel-scraper-v1").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"],
    "resultsLimit": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/instagram-reel-scraper-v1").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": [
    "nasa"
  ],
  "resultsLimit": 25
}' |
apify call dami_studio/instagram-reel-scraper-v1 --silent --output-dataset

```

## MCP server setup

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

```

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/usQ1jvBSKiU121fUH/builds/yzTmn6PvaRX38cdEt/openapi.json
