# $0.05/1K - Instagram Reels Scrapy (`esdrasdw/instagram-reels-scrapy---ultra-barato`) Actor

$0.05 per 1,000 results. Extract Reels and posts from public Instagram profiles with views, likes and comments. Export to JSON, CSV or Excel.

- **URL**: https://apify.com/esdrasdw/instagram-reels-scrapy---ultra-barato.md
- **Developed by:** [Esdrasdw](https://apify.com/esdrasdw) (community)
- **Categories:** Automation, Developer tools, Social media
- **Stats:** 87 total users, 4 monthly users, 98.8% runs succeeded, 4 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

$0.10 / 1,000 results

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

**$0.10 per 1,000 results.** Around 50x cheaper than the usual Instagram
scraper.

Extract public **Reels and posts from Instagram profiles** — with view, like and
comment counts — and get clean, structured rows you can drop straight into a
spreadsheet, a dashboard or your own database.

Give it a list of profiles, say how many items you want from each, and run it.
No login, no cookies, no browser automation on your side.

**Filter by date.** Ask only for what was published since a date — the last
7 days, since January, since a campaign started — and the run stops as soon as
it reaches older posts.

### What you get

For every item, the standard output includes the thumbnail, the caption's first
line, the profile handle, the post URL, the publish date, view count, like
count, comment count and duration. 32 more fields are available on
demand — hashtags, mentions, tagged users, co-authors, carousel items, tagged
location, sponsors, full caption, every video and thumbnail rendition, audio
track details and more. 41 fields in total.

You choose the columns. Ask for nine fields and you get nine clean columns
instead of a wall of JSON you have to clean up later.

### How to use it

1. Put one or more profiles in **Instagram profiles** — a handle (`nasa`) or a
   profile URL (`https://www.instagram.com/nasa/`).
2. Pick the **Content to scrape** — Reels, the full timeline, or photos only.
3. Set **Results limit per profile**.
4. Optionally, narrow it down under **Filters**: a date range, or no pinned
   posts.
5. Run it, then download the dataset as JSON, CSV, Excel or HTML.

### Input

| Field | Type | Default | What it does |
|---|---|---|---|
| `directUrls` | array | `["https://www.instagram.com/nasa/"]` | Profiles to scrape — handle or URL |
| `resultsType` | string | `reels` | `reels`, `posts` (full timeline) or `photos` |
| `resultsLimit` | integer | `100` | Maximum items per profile |
| `resultsLimitPerProfile` | object | — | A different limit for specific profiles |
| `onlyPostsNewerThan` | string | — | Only items published on or after this date |
| `skipPinnedPosts` | boolean | `false` | Leave out posts pinned to the top of the profile |
| `selectedFields` | array | standard fields | Which columns to return |

Field names match the ones used by the standard Instagram scraper, so an input
written for that Actor also works here. `resultsLimitPerProfile` is the
addition. Handles are not case-sensitive, and
profile URLs may carry extra parts such as `?igsh=...` or `/reels/`.

#### Example input

```json
{
  "directUrls": ["nasa", "natgeo"],
  "resultsType": "reels",
  "resultsLimit": 50,
  "selectedFields": ["url", "title", "videoPlayCount", "likesCount", "commentsCount"]
}
```

#### A different limit for each profile

No need for one run per profile just to vary the limit. List the exceptions in
`resultsLimitPerProfile`; every other profile uses `resultsLimit`:

```json
{
  "directUrls": ["nasa", "natgeo", "instagram"],
  "resultsLimit": 100,
  "resultsLimitPerProfile": { "nasa": 2000, "instagram": 20 }
}
```

That returns 2,000 items from nasa, 100 from natgeo and 20 from instagram, in a
single run. Keys accept a handle, `@handle` or profile URL, and a profile that
appears only in `resultsLimitPerProfile` is scraped too. Each limit goes from 1
to 10,000.

#### Filter by date

`onlyPostsNewerThan` takes:

- a date: `2024-01-31`
- an ISO timestamp: `2024-01-31T18:00:00Z` (no time zone means UTC)
- a relative period: `3 days`, `2 weeks`, `6 months`, `1 year` (also
  `minutes` and `hours`)

All times are UTC, and the date itself is included:

```json
{
  "directUrls": ["natgeo"],
  "resultsType": "posts",
  "resultsLimit": 1000,
  "onlyPostsNewerThan": "2026-01-01"
}
```

That returns everything natgeo published since January 1st, newest first, and
stops as soon as it reaches older posts, in every content type.

`resultsLimit` counts only items that pass the filters. Items skipped on the
way are never delivered and never charged.

Pinned posts sit at the top of a profile regardless of their date. With a date
filter, a pinned post is returned only if its own date is inside the window. To
leave pinned posts out entirely, turn on `skipPinnedPosts`.

### Output

```json
{
  "displayUrl": "https://scontent.cdninstagram.com/v/t51.../image.jpg",
  "title": "Coming out of my shell",
  "ownerUsername": "nasa",
  "url": "https://www.instagram.com/reel/DcEMvAdOryS/",
  "timestamp": "2026-08-10T20:06:13",
  "videoPlayCount": 1512492,
  "likesCount": 34514,
  "commentsCount": 291,
  "videoDuration": 79.5
}
```

Photos and carousels have no view count, so `videoPlayCount` is omitted for
those items.

#### Every field you can request

With `selectedFields` empty you get the 13 standard fields, from `displayUrl` to
`id` below (the form pre-selects the first nine). When you pick fields, you get
exactly those. Everything after `id` is opt-in: add it to `selectedFields` and
it shows up as an extra column.

| Field | Type | What it is |
|---|---|---|
| `displayUrl` | string | Thumbnail image URL |
| `title` | string | First line of the caption, trimmed |
| `ownerUsername` | string | Profile handle, without `@` |
| `url` | string | Public post URL |
| `timestamp` | string | Publish date, ISO 8601 |
| `videoPlayCount` | integer | Plays (Reels mode only) |
| `likesCount` | integer | Likes. `-1` when the author hides them |
| `commentsCount` | integer | Comments |
| `videoDuration` | number | Length in seconds (Reels mode only) |
| `aspectRatio` | string | For example `9:16` |
| `videoUrl` | string | Highest quality video URL |
| `shortCode` | string | Instagram short code |
| `id` | string | Media ID, as `<pk>_<ownerId>` |
| `pk` | string | Media PK on its own |
| `caption` | string | Full caption text |
| `alt` | string | Instagram's accessibility description, when it exists |
| `hashtags` | array | Hashtags found in the caption |
| `mentions` | array | Handles mentioned in the caption |
| `taggedUsers` | array | Users tagged in the media |
| `coauthorProducers` | array | Collab co-authors |
| `childPosts` | array | Carousel items, one entry each |
| `locationName` | string | Tagged place, when the post has one |
| `locationId` | string | Tagged place ID, when the post has one |
| `ownerId` | string | Owner numeric ID |
| `ownerFullName` | string | Owner display name |
| `isPinned` | boolean | Pinned to the top of the profile |
| `paidPartnership` | boolean | Declared paid partnership |
| `sponsors` | array | Handles tagged as sponsor |
| `affiliate` | boolean | Affiliate content |
| `isCommentsDisabled` | boolean | Comments turned off |
| `hasAudio` | boolean | Carries an audio track |
| `musicInfo` | object | Audio track details |
| `productType` | string | `clips`, `feed`, `carousel_container` |
| `type` | integer | Instagram media type |
| `originalWidth` / `originalHeight` | integer | Source dimensions |
| `images` | array | Every thumbnail size |
| `videoVersions` | array | Every video rendition |
| `dashManifest` | string | DASH manifest |
| `spritesheet` | object | Scrubbing sprite sheet |
| `igPlayCount` | integer | Instagram play counter (Reels mode) |

Fields Instagram does not return are left out of the item instead of being sent
as `0` or `""`, so an empty column means "not reported", never "zero".

#### Content types, and which counts come with each

| `resultsType` | What you get | `videoPlayCount` | `videoDuration` |
|---|---|---|---|
| `reels` | Reels only | yes | yes |
| `posts` | the full timeline (Reels, photos, carousels) | omitted | omitted |
| `photos` | photos and carousels, no Reels | n/a (photos have none) | n/a |

For view counts, request `reels`.

You can download the dataset in JSON, CSV, Excel, HTML, XML or RSS.

### API

Every run is available over the Apify API. Replace `<TOKEN>` with your API
token from **Settings → Integrations**.

#### Run and wait for the results

Returns the dataset items directly, in one call:

```bash
curl -X POST "https://api.apify.com/v2/acts/<ACTOR_ID>/run-sync-get-dataset-items?token=<TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
        "directUrls": ["nasa"],
        "resultsType": "reels",
        "resultsLimit": 50
      }'
```

Best for small and medium jobs. The connection stays open until the run ends,
so keep a generous client timeout.

#### Start a run and collect later

For large jobs, start the run and poll for it:

```bash
## 1. start
curl -X POST "https://api.apify.com/v2/acts/<ACTOR_ID>/runs?token=<TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"directUrls": ["nasa"], "resultsLimit": 5000}'

## 2. check status — look for "status": "SUCCEEDED"
curl "https://api.apify.com/v2/actor-runs/<RUN_ID>?token=<TOKEN>"

## 3. fetch the items
curl "https://api.apify.com/v2/datasets/<DATASET_ID>/items?token=<TOKEN>&format=csv"
```

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("<TOKEN>")

run = client.actor("<ACTOR_ID>").call(run_input={
    "directUrls": ["nasa"],
    "resultsType": "posts",
    "resultsLimit": 100,
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["url"], item.get("likesCount"))
```

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<TOKEN>' });

const run = await client.actor('<ACTOR_ID>').call({
    directUrls: ['nasa'],
    resultsType: 'reels',
    resultsLimit: 100,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length);
```

#### Run summary

Besides the dataset, each run writes an `OUTPUT` record to the key-value store
with a per-profile summary:

```json
{
  "ok": true,
  "total_items": 150,
  "users": [
    { "user": "nasa", "count": 100 },
    { "user": "natgeo", "count": 50 }
  ]
}
```

When a profile cannot be completed, its entry carries an `error` field and the
items already collected stay in the dataset. The `error` is one of:

| `error` | What it means | What to do |
|---|---|---|
| `profile not found or unavailable` | The handle does not exist, was renamed, or is not available to the public | Check the handle |
| `profile is private` | The profile is private | Nothing — only public profiles can be read |
| `no public items found (the profile may be private or have none)` | Nothing public came back for this content type | Check the profile or the content type |
| `could not be collected right now, please try again later` | A temporary problem on Instagram's side | Run that profile again later |
| `collection stopped early, please try again later for the rest` | Some items were collected, then a temporary problem stopped it | Run again later for the rest |
| `collection stopped before the end of this profile; items collected so far are in the dataset` | The run reached its page limit for this profile, for example a very old date filter on a large profile | Narrow the date range, or split it into several runs |
| `not a valid Instagram profile` | The entry is not a handle or profile URL (for example a post link) | Fix the entry |
| `the run time limit was reached before this profile finished; items collected so far are in the dataset` | The run's timeout arrived mid-profile | Give the run a longer timeout |
| `not started: run time limit reached` | The timeout arrived before this profile's turn | Give the run a longer timeout |

**Sizing the timeout.** Reels mode collects roughly 150 reels per minute per
profile. A run asking for 2,000 reels needs about 15 minutes, so a 10-minute
timeout will stop it early. When the timeout is close, the run stops on its
own, writes this summary and finishes as *Succeeded*, keeping everything
collected so far.

When a profile has a problem, the run's status line says so.

### Legacy input names

Inputs with the previous names (`usernames`, `content_type`,
`quantity_per_user`, `selected_fields`) still run. The log shows the current
names.

### What people use this for

- **Influencer vetting**: reach versus engagement over the last Reels.
- **Content research**: which formats, hashtags and sounds perform.
- **Period reports** for a month, quarter or campaign.
- **Daily monitoring** with `onlyPostsNewerThan: "1 day"`.
- **Sponsored content**: `paidPartnership`, `sponsors` and `taggedUsers`.

### Frequently asked questions

**Which content type should I pick?**
`reels` if you want view counts and duration — it is the only mode Instagram
reports those for. `posts` for the whole timeline. `photos` for photos and
carousels with the Reels filtered out.

**Why is `videoPlayCount` empty on `posts` and `photos`?**
Those modes do not report play counts. Request `reels` when views matter.

**Why is `likesCount` sometimes `-1`?**
The author hid the like count on that post.

**Can I scrape private profiles?**
No. Only public profiles return data. A private profile is skipped and marked
in `OUTPUT` (as `profile is private`, or as having no public items), and the
rest of the run continues.

**What happens if one profile fails?**
The other profiles keep going. Items already collected stay in the dataset, and
the `OUTPUT` record marks that profile with an `error` field.

**Can I ask for more items than the profile has?**
Yes. You get everything available and the run ends normally.

**Do I need a proxy or a login?**
No. There is nothing to configure — no cookies, no session, no browser.

**How do I get only recent posts?**
Set `onlyPostsNewerThan`, for example to `7 days`.

**Why is the result smaller than my `resultsLimit`?**
With a date filter, you get everything since that date up to the limit. If
the profile published fewer items in that period, that is all there is.

**How many items can one run get from a very large profile?**
In Posts mode, typically 1,500 to 2,000 items per run. `OUTPUT` says when a
profile stops early.

**How do I exclude pinned posts?**
Turn on `skipPinnedPosts`. Posts pinned to the Reels tab count as pinned too.

**Can I get comment text?**
No. This Actor returns media and its metrics, including `commentsCount`, but
not the comments themselves.

**How much does it cost?**
$0.10 per 1,000 items, charged per item delivered. A run that returns nothing
costs nothing.

**Is this affiliated with Instagram?**
No. See the disclaimer at the bottom.

### Notes and limits

- Only **public** profiles are supported. Private accounts return no items.
- A profile that is unavailable is skipped with a warning; the other profiles in
  the same run continue normally.
- Requesting more items than a profile has simply returns everything available.

### Support

Found a bug or need a field that is not on the list? Open an issue on the
Actor's **Issues** tab with the input you used and the run ID.

### Disclaimer

This is an unofficial tool and is not affiliated with, endorsed by, or
sponsored by Instagram or Meta Platforms, Inc. It collects only publicly
available content. You are responsible for how you use the data, including
compliance with applicable laws and the target site's terms.

# Changelog

This Actor's version history is a separate document: https://apify.com/esdrasdw/instagram-reels-scrapy---ultra-barato/changelog.md

# Actor input Schema

## `directUrls` (type: `array`):

Add one or more Instagram profiles to scrape. Accepts a handle (nasa) or a profile URL (https://www.instagram.com/nasa/), one per line.

## `resultsType` (type: `string`):

Reels only is the fastest option and the only one that reports view counts and duration. Posts returns the complete profile timeline. Photos returns only photos and carousels (no Reels).

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

Maximum number of items to return for each profile.

## `resultsLimitPerProfile` (type: `object`):

Optional. Give some profiles their own limit instead of the one above, for example {"nasa": 2000, "natgeo": 50}. Keys accept the same as the profile list (handle, @handle or URL). Profiles listed here are scraped even if they are not in the list above. Each limit must be between 1 and 10000.

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

Return only items published on or after this date. Use a date (2024-01-31), an ISO timestamp, or a relative period such as "3 days", "2 weeks" or "6 months". Times are in UTC. The run stops as soon as it reaches older items, so a recent date is also the fastest run.

## `onlyPostsOlderThan` (type: `string`):

Removed. Use "Only posts newer than" to limit how far back to scrape.

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

Leave out posts and Reels pinned to the top of the profile. Pinned items appear first regardless of their date, which can look out of order.

## `selectedFields` (type: `array`):

Leave as is for the standard set. Add extra fields if you need richer data.

## Actor input object example

```json
{
  "directUrls": [
    "https://www.instagram.com/nasa/"
  ],
  "resultsType": "reels",
  "resultsLimit": 100,
  "skipPinnedPosts": false,
  "selectedFields": [
    "displayUrl",
    "title",
    "ownerUsername",
    "url",
    "timestamp",
    "videoPlayCount",
    "likesCount",
    "commentsCount",
    "videoDuration"
  ]
}
```

# Actor output Schema

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

All extracted items, ready to download as JSON, CSV or Excel.

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

Per-profile counts and any profile that could not be completed.

# 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 = {
    "directUrls": [
        "https://www.instagram.com/nasa/"
    ],
    "resultsType": "reels",
    "resultsLimit": 100,
    "selectedFields": [
        "displayUrl",
        "title",
        "ownerUsername",
        "url",
        "timestamp",
        "videoPlayCount",
        "likesCount",
        "commentsCount",
        "videoDuration"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("esdrasdw/instagram-reels-scrapy---ultra-barato").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 = {
    "directUrls": ["https://www.instagram.com/nasa/"],
    "resultsType": "reels",
    "resultsLimit": 100,
    "selectedFields": [
        "displayUrl",
        "title",
        "ownerUsername",
        "url",
        "timestamp",
        "videoPlayCount",
        "likesCount",
        "commentsCount",
        "videoDuration",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("esdrasdw/instagram-reels-scrapy---ultra-barato").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 '{
  "directUrls": [
    "https://www.instagram.com/nasa/"
  ],
  "resultsType": "reels",
  "resultsLimit": 100,
  "selectedFields": [
    "displayUrl",
    "title",
    "ownerUsername",
    "url",
    "timestamp",
    "videoPlayCount",
    "likesCount",
    "commentsCount",
    "videoDuration"
  ]
}' |
apify call esdrasdw/instagram-reels-scrapy---ultra-barato --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,esdrasdw/instagram-reels-scrapy---ultra-barato"
        }
    }
}
```

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/nixGoSi2KjxbVYctO/builds/W6rpXtJjATg5IybOV/openapi.json
