# Instagram Post Scraper by URL: Reels, Carousel, Likes, Plays (`neverempty/instagram-post-details-scraper`) Actor

This Instagram post scraper takes post and reel URLs and returns one row per post: likes, comments, caption, carousel images, video URL and length, audio, tagged users, location and recent reel plays. No login, no cookies, no start fee. You pay only for posts returned.

- **URL**: https://apify.com/neverempty/instagram-post-details-scraper.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** Social media, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.95 / 1,000 post returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Post Scraper by URL: Reels, Carousel, Likes, Plays

This Instagram post scraper takes post and reel URLs and returns one row per post: likes, comments, caption, hashtags, carousel images, video URL and length, audio, tagged users, location with coordinates, and plays for recent reels. No login, no cookies, no start fee. You pay **$1.65 per 1,000 posts** on the Free plan, down to **$0.95** on Gold and above, and only for posts actually returned.

### What can I do with this Instagram post scraper?

- **Track campaigns and creators.** Keep a list of post and reel URLs and refresh likes and comments whenever you want, plus plays while a reel is still recent.
- **Check influencer deliverables.** Confirm that a sponsored post is live, who is tagged, who co-authored it and how it is performing.
- **Build a content dataset.** Get captions, hashtags, mentions, image URLs and video URLs ready for analysis or for AI tools.
- **Save the slides of a carousel.** Each carousel row lists its images and videos, not only the cover.
- **Find out where a post was made.** Location name, ID and coordinates are included when the post has them.

### What data do you get?

| Group | Fields |
|---|---|
| Post | `shortCode`, `id`, `url`, `inputUrl`, `type` (Image, Video, Sidecar), `productType`, `timestamp`, `caption`, `captionIsEdited`, `hashtags`, `mentions`, `alt` |
| Engagement | `likesCount`, `likesHidden`, `commentsCount`, `isCommentsDisabled`, `videoPlayCount`, `viewCountStatus` |
| Media | `displayUrl`, `dimensionsWidth`, `dimensionsHeight`, `images`, `childPosts`, `videoUrl`, `videoDuration`, `hasAudio`, `musicInfo` |
| Author | `ownerUsername`, `ownerFullName`, `ownerId`, `ownerIsVerified`, `ownerProfilePicUrl`, `coauthorProducers`, `taggedUsers` |
| Place | `locationName`, `locationId`, `latitude`, `longitude` |
| Run | `status`, `note`, `source`, `scrapedAt`. The last row can also hold `notCheckedUrls` (URLs to put in a new run) |

Familiar field names such as `shortCode`, `likesCount`, `commentsCount`, `caption`, `displayUrl`, `childPosts` and `ownerUsername` make it easy to switch an existing workflow.

### How to scrape Instagram posts by URL

1. Paste your post and reel URLs into `urls`. Links with tracking parameters, `/reels/` links, links with the username in the path and bare shortcodes all work. `directUrls` is accepted as another name for `urls`.
2. Set `maxPosts` to the most posts you want from this run.
3. Leave `includeViewCounts` on if you want plays for recent reels, or turn it off for a faster run.
4. Press **Start**, then download the rows as JSON, CSV or Excel, or read them through the API.

### Input

| Field | What it does | Default |
|---|---|---|
| `urls` | Instagram post and reel URLs, one per line. Up to 1,000 are read per run. | One free sample post when no URLs are given |
| `maxPosts` | The most posts to return and charge for in this run (1 to 1,000). | 100 |
| `includeViewCounts` | Also look up `videoPlayCount` for reels. Free of charge. | true |

```json
{
    "urls": [
        "https://www.instagram.com/p/DeuOR-CjKE9/",
        "https://www.instagram.com/reel/DeTUk4sDSp-/",
        "https://www.instagram.com/p/DfGx7fskZhY/"
    ],
    "maxPosts": 100,
    "includeViewCounts": true
}
```

### Output example

```json
{
    "status": "ok",
    "inputUrl": "https://www.instagram.com/reel/DeTUk4sDSp-/",
    "id": "4004634989362489982",
    "type": "Video",
    "productType": "clips",
    "shortCode": "DeTUk4sDSp-",
    "url": "https://www.instagram.com/reel/DeTUk4sDSp-/",
    "hashtags": [],
    "mentions": [],
    "likesCount": 100000,
    "likesHidden": false,
    "commentsCount": 1000,
    "timestamp": "2026-10-10T15:15:02.000Z",
    "ownerUsername": "nasa",
    "ownerIsVerified": true,
    "videoDuration": 43.767,
    "hasAudio": true,
    "videoPlayCount": 11094767,
    "viewCountStatus": "ok",
    "musicInfo": { "artist_name": "nasa", "song_name": "Original audio" }
}
```

Counts in this example are rounded, and some fields are left out to keep it short.

### How much does it cost?

**$1.65 per 1,000 posts** on the Free plan, with lower prices on paid Apify plans:

| Apify plan | Price per 1,000 posts |
|---|---|
| Free | $1.65 |
| Bronze | $1.45 |
| Silver | $1.24 |
| Gold and above | $0.95 |

There is no start fee and no extra charge for carousels, video details, audio or plays. You are charged only for rows with `status: "ok"`. Every other row is free, and the [full list is below](#rows-you-are-not-charged-for).

### FAQ

**How do I get likes and comment counts from an Instagram post by URL?**
Paste the URL into `urls` and run the Actor. Each row has `likesCount` and `commentsCount`. When Instagram marks the likes as hidden, `likesCount` is empty and `likesHidden` is true.

**How do I download all images from an Instagram carousel?**
Carousel rows have `type: "Sidecar"`. The `images` field lists the URL of every slide, and `childPosts` gives each slide's type, size and video URL.

**Can I scrape Instagram reels without logging in?**
Yes. You don't need an Instagram account, a cookie or an API key.

**Can I run this from an API or an AI agent?**
Yes. Call the Actor through the Apify API, or from an AI agent through the Apify MCP server, with a list of URLs. The rows come back as JSON.

**How do I scrape many Instagram posts at once?**
Paste a list of URLs (the limit is 1,000 per run). Each URL the run reaches gets a row, in the order you gave them. URLs it did not reach are listed together in one last row. Instagram limits how many posts can be opened without logging in, so long lists may not be read in full. If Instagram refuses most of the recent posts, the run stops early instead of going on. URLs that were not reached are in `notCheckedUrls` in the last row, ready for a new run a few hours later.

**What happens if a run reaches its timeout?**
The run stops about 30 seconds before its timeout and writes a last `not-checked` row. Rows already returned stay in the dataset. URLs it did not finish are not charged and are listed in `notCheckedUrls`. To read them, start a new run with those URLs, or set a longer timeout.

**Why is `isCommentsDisabled` empty?**
It is filled in only when Instagram states it on the post's page. Instagram usually leaves it out for visitors who are not logged in.

### Rows you are not charged for

A URL that was read but did not return a post gets its own row, so you can see what happened to it. URLs the run did not check (past `maxPosts`, past the 1,000 URL limit, past the maximum total charge, stopped because Instagram refused most posts, or stopped before the run timeout) are counted in one last row, and `notCheckedUrls` lists them for a new run.

| `status` | What it means |
|---|---|
| `sample` | No URLs were given, so one sample post was read to show the output. Add your own URLs to `urls`. |
| `no-public-post` | Instagram shows no public post at this address. It may be deleted, private or mistyped. |
| `blocked` | Instagram did not let this run open the post. Run the URL again later; you were not charged. |
| `unreadable` | The post could not be read in this run. |
| `invalid-input` | The entry is not an Instagram post or reel URL, such as a profile link. For a `/share/` link, open the link and paste the URL of the post. |
| `duplicate` | The same post was already in this run's list. |
| `not-checked` | One row at the end: how many entries were not read and why (past `maxPosts`, past the 1,000 URL limit, Instagram refused most posts, or the run was about to reach its timeout). The URLs are in `notCheckedUrls`. |
| `budget-reached` | The run reached the maximum total charge you set. Entries it did not reach are counted in one row at the end, with their URLs in `notCheckedUrls`. |

`viewCountStatus` explains the `videoPlayCount` of each post, and it never changes the price:

| `viewCountStatus` | What it means |
|---|---|
| `ok` | `videoPlayCount` is filled in. |
| `not-in-recent-reels` | The reel is not among the account's most recent reels, where plays are shown. |
| `plays-unavailable` | The plays could not be read in this run. Try the URL again later; the post itself is still returned. |
| `not-requested` | `includeViewCounts` is off. |

### Limitations

- Instagram limits how many posts can be opened without logging in, so long lists may stop early. The rest are not charged and are listed in `notCheckedUrls`.
- Only public posts can be read. Private accounts and deleted posts return a free `no-public-post` row.
- `videoPlayCount` is available for an account's most recent reels, about the latest 12. Older reels return the row with `viewCountStatus: "not-in-recent-reels"`.
- Instagram does not show share, save or repost counts without a login, so they are not in the output.
- Comment text is not collected. `commentsCount` is what Instagram shows to logged-out visitors. A `0` may not mean nobody commented, because comments can be turned off or hidden.
- `musicInfo` holds the song title and artist, or the creator's original audio. `uses_original_audio` says which one it is. It is empty when Instagram gives no audio details for that reel.
- Image and video URLs come from Instagram's CDN and expire after a while, so download the files soon after the run.
- Email addresses and phone numbers in captions, names, alt text and audio titles are masked in the output on a best-effort basis. Not every way of writing them can be caught, so some may remain. Masking can also go too far: a long number such as a reference code may be masked as well.
- Use the data in line with the rules that apply to you, including privacy law for any personal data in posts.

### Need help or a missing field?

Open the **Issues** tab and tell us what you need. If a row looks wrong, include the run ID.

### Thanks for using this Actor

We build these tools for people who run them every day, and we improve them from what users tell us.

- **Missing a field, or need another filter?** Tell us in the **Issues** tab. If the data is there, we add it.
- **Found a bug or a wrong value?** Post the run ID in the **Issues** tab. Wrong data is the thing we fix first.

If this Actor saved you time, a short review helps other people find it.

# Actor input Schema

## `urls` (type: `array`):

Instagram post or reel addresses, one per line, such as https://www.instagram.com/p/DeuOR-CjKE9/ or https://www.instagram.com/reel/DeTUk4sDSp-/. A bare shortcode also works. Up to 1,000 per run.

## `maxPosts` (type: `integer`):

The run stops after this many posts have been returned. The charge follows the posts returned.

## `includeViewCounts` (type: `boolean`):

Looks up the play count of each reel on its owner's Reels tab. Instagram lists the owner's most recent reels there, so older reels come back with videoPlayCount empty and viewCountStatus saying why. Turning this off makes runs a little faster. The price is the same either way.

## Actor input object example

```json
{
  "urls": [
    "https://www.instagram.com/p/DeuOR-CjKE9/",
    "https://www.instagram.com/reel/DeTUk4sDSp-/",
    "https://www.instagram.com/p/DfGx7fskZhY/"
  ],
  "maxPosts": 100,
  "includeViewCounts": true
}
```

# Actor output Schema

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

One row per Instagram post or reel: type, shortcode, caption, hashtags, mentions, likes, comment count, publish time, owner, image and video URLs, carousel items, video length, audio, tagged users, co-authors, location and, for recent reels, the play count. URLs that show no public post come back as free rows that say why.

# 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 = {
    "urls": [
        "https://www.instagram.com/p/DeuOR-CjKE9/",
        "https://www.instagram.com/reel/DeTUk4sDSp-/",
        "https://www.instagram.com/p/DfGx7fskZhY/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/instagram-post-details-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 = { "urls": [
        "https://www.instagram.com/p/DeuOR-CjKE9/",
        "https://www.instagram.com/reel/DeTUk4sDSp-/",
        "https://www.instagram.com/p/DfGx7fskZhY/",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("neverempty/instagram-post-details-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 '{
  "urls": [
    "https://www.instagram.com/p/DeuOR-CjKE9/",
    "https://www.instagram.com/reel/DeTUk4sDSp-/",
    "https://www.instagram.com/p/DfGx7fskZhY/"
  ]
}' |
apify call neverempty/instagram-post-details-scraper --silent --output-dataset

```

## MCP server setup

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