# Instagram Scraper – Posts, Reels, Comments, Profiles & Places (`badr_xba/instagram-scraper`) Actor

Scrape public Instagram data without an account: profile details and latest posts, reels with plays, likes and comments, post comments, hashtag reels with related keywords, and places with coordinates. Pay only for successful results.

- **URL**: https://apify.com/badr_xba/instagram-scraper.md
- **Developed by:** [Badr Bouargan](https://apify.com/badr_xba) (community)
- **Categories:** Social media, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.25 / 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.
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 Scraper – Posts, Reels, Comments, Profiles, Hashtags & Places

Scrape public Instagram data **without an Instagram account, login or API key**. Paste profile, post, reel, hashtag or place URLs and get clean rows: posts and reels with likes, comments and plays, the comments under a post, profile details with followers and bio, hashtag size with related keywords, and places with coordinates. **You pay only for the rows you get.**

### What does Instagram Scraper do?

It opens each Instagram URL the way a logged-out visitor's browser does, through residential addresses, and reads the data Instagram puts in the page. One URL gives one kind of result, chosen with **What to get**:

| You paste | Posts | Reels | Comments | Details |
|---|---|---|---|---|
| Profile `instagram.com/natgeo/` | 12 latest posts | 12 latest reels with plays | – | Profile row + 12 latest posts |
| Post or reel `instagram.com/p/…`, `/reel/…` | The full post | The full reel | ~15 comments shown | The full post |
| Hashtag `instagram.com/explore/tags/crossfit/` or `#crossfit` | Top 12 reels | Top 12 reels | – | Posts count + related keywords |
| Place `instagram.com/explore/locations/7538318/` | ~15 recent posts | Recent reels | – | Name, category, coordinates, address, phone, posts count |

Usernames work too: `natgeo` or `@natgeo`. Story links are reduced to their profile. Paste links the way you have them: mobile links, `instagr.am` links, links with `?igsh=…` tracking, `<brackets>` or a final full stop, and whole lists on one line (`natgeo, nasa, #crossfit`) are all cleaned up for you. The same profile given three ways is read (and billed) once.

### What data can I get?

- **Posts and reels**: shortcode, URL, type (Image, Video, Sidecar), caption, hashtags, mentions, likes, comments count, video plays and views, publication date, image and video URLs, size, carousel children, tagged users, co-authors, location, music, author.
- **Comments**: text, author (username, id, picture, verified), likes, replies count, date, comment link.
- **Profiles**: username, full name, bio, website links, followers, following, posts count, verified, private, profile picture, latest posts.
- **Hashtags**: posts count and related keywords people search with it.
- **Places**: name, category, latitude, longitude, address, zip, phone, price range, posts count, linked business account, latest posts.

### How to use it in 3 steps

1. **Paste** profile, post, reel, hashtag or place URLs (or usernames, or `#tags`) in **Instagram URLs**.
2. **Pick** what you want in **What to get** — Posts, Reels, Comments or Details — and how many per URL.
3. **Click Start**, then open the **Overview** table or download JSON, CSV, Excel or HTML.

The form works as it is: the prefilled run returns the 12 latest posts of National Geographic in about half a minute, for about three cents. Optional: **Only posts newer than** (`7 days`, `2026-09-01`) and **Maximum results for the whole run**.

### Input example

```json
{
  "directUrls": ["https://www.instagram.com/natgeo/", "#crossfit", "https://www.instagram.com/p/DLNsnpUTdVS/"],
  "resultsType": "posts",
  "resultsLimit": 12,
  "onlyPostsNewerThan": "30 days"
}
```

Turn on **Open each profile post for full details** to add likes, comments count, date, location, tagged users and the latest comments to the posts of a profile (one extra page per post).

### Output example

A post:

```json
{
  "resultType": "post",
  "inputUrl": "https://www.instagram.com/p/DLNsnpUTdVS/",
  "id": "3660778310592222546",
  "type": "Image",
  "productType": "feed",
  "shortCode": "DLNsnpUTdVS",
  "url": "https://www.instagram.com/p/DLNsnpUTdVS/",
  "caption": "Your phone isn't rotting your brain—but cell death and fungi might. 🧠 …",
  "likesCount": 72507,
  "commentsCount": 218,
  "timestamp": "2025-06-22T19:00:10.000Z",
  "displayUrl": "https://scontent.cdninstagram.com/…jpg",
  "ownerUsername": "natgeo",
  "ownerFullName": "National Geographic",
  "firstComment": "…",
  "latestComments": [{ "text": "…", "ownerUsername": "…", "likesCount": 3, "timestamp": "…" }]
}
```

A profile (Details):

```json
{
  "resultType": "profile",
  "username": "natgeo",
  "fullName": "National Geographic",
  "followersCount": 268000000,
  "followsCount": 195,
  "postsCount": 32000,
  "verified": true,
  "private": false,
  "externalUrl": "https://…",
  "latestPosts": [{ "shortCode": "…", "type": "Video", "caption": "…" }]
}
```

In the Console, the **Overview** table shows every row whatever its type; the **Posts and reels**, **Comments**, **Profiles**, **Places** and **Hashtags** tables show the columns that matter for each.

Every run also writes **RUN_SUMMARY** (results by type, stop reason, failures, pages loaded, amount charged) and **FAILED_INPUTS** (each URL that gave nothing, with the reason) to the run's storage:

```json
[
  { "input": "https://www.instagram.com/share/reel/BAxyz/", "type": "input",
    "reason": "Share links (instagram.com/share/…) only redirect: open it once in a browser and paste the address it leads to (…/p/…, …/reel/… or the profile).",
    "failedAt": "2026-10-09T16:55:12.000Z" }
]
```

### How much does it cost?

Pay per result: one row = one post, reel, comment, profile, hashtag or place. URLs that give nothing are listed in FAILED_INPUTS and **never billed**. Images, videos and Instagram's scripts are never downloaded, which keeps runs fast and cheap.

| Apify plan | Price per 1,000 results |
|---|---|
| Free | $2.50 |
| Starter | $2.00 |
| Scale | $1.63 |
| Business | $1.25 |

Example: 10 profiles × 12 posts = 120 results ≈ $0.30 on the Free plan. Set **Maximum results for the whole run** or a maximum cost per run, and the run stops cleanly at that limit.

### What it can and cannot do

**It can**: read any public profile, post, reel, hashtag or place without an account; return profile details with followers, bio and links; give likes, comments, plays, music, tagged users and location for posts and reels; list the comments Instagram shows on a post; filter by date; stop exactly at your limit or budget.

**It cannot** go beyond what Instagram shows a logged-out visitor. This Actor never logs in, so it returns what a visitor sees:

- up to **12 posts** or **12 reels** per profile;
- about **15 comments** per post (the ones Instagram shows first);
- the **top 12 reels** per hashtag;
- about **15 recent posts** per place;
- **no keyword search** (Instagram refuses it to logged-out visitors): paste URLs instead;
- private profiles return their details but no posts;
- share links (`instagram.com/share/…`), audio pages and story contents are not read.

Likes and dates of a profile's posts are not on the profile page: turn on **Open each profile post for full details** to get them.

### Use it via API

**Python** (`pip install apify-client`):

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("badr_xba/instagram-scraper").call(run_input={
    "directUrls": ["https://www.instagram.com/natgeo/", "#crossfit"],
    "resultsType": "posts",
    "resultsLimit": 12,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["resultType"], item.get("url"), item.get("likesCount"))
```

**JavaScript** (`npm install apify-client`):

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('badr_xba/instagram-scraper').call({
    directUrls: ['https://www.instagram.com/natgeo/', '#crossfit'],
    resultsType: 'posts',
    resultsLimit: 12,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

**cURL** (runs the Actor and returns the rows in one call):

```bash
curl -X POST "https://api.apify.com/v2/acts/badr_xba~instagram-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"directUrls": ["https://www.instagram.com/natgeo/"], "resultsType": "posts", "resultsLimit": 12}'
```

### Integrations

Results are plain JSON rows, ready for your tools:

- **Make**, **Zapier** and **n8n**: use the Apify app or node, pick `badr_xba/instagram-scraper`, and send each new row to Google Sheets, Airtable, Slack, a CRM or a database.
- **AI agents (MCP)**: add `https://mcp.apify.com/?actors=badr_xba/instagram-scraper` to Claude, Cursor or any MCP client and ask in plain words ("latest posts of @natgeo").
- **Schedules and webhooks**: run it daily on the same profiles to build a history, and get notified when a run ends.

### FAQ

**Do I need an Instagram account?** No. The Actor reads public pages as a logged-out visitor. It never asks for your password or cookies.

**Why do counts differ a little from what I see in the app?** Instagram shows logged-out visitors slightly different numbers than signed-in users, and counts keep moving after the run. Compare with the page opened in a private window at the run's time.

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

**Can I get more than 12 posts per profile?** Not as a logged-out visitor. Add the profile's reels tab (`…/reels/`) for 12 more items, or run the Actor on a schedule to collect new posts as they appear.

**Which proxy should I use?** Keep the default residential group. Instagram blocks datacenter addresses.

**Is it legal to scrape Instagram?** The Actor only reads data that Instagram shows publicly, without logging in. Some of it can be personal data under laws such as the GDPR: collect it only for a legitimate purpose and handle it accordingly.

### Troubleshooting

| What you see | What to do |
|---|---|
| A URL is in **FAILED_INPUTS** with type `input` | It is not a profile, post, reel, hashtag or place address. Share links must be opened once in a browser: paste the address they lead to. |
| Type `notFound` | The post was deleted, the profile does not exist, or Instagram keeps that page (often a very big hashtag) for signed-in users. Check it in a private browser window. |
| Type `private` | The profile is private: use **Details** to get its public figures. |
| Type `blocked` | Instagram refused every address tried. Keep the **RESIDENTIAL** proxy group and run again; it is rare and never billed. |
| `likesCount` and `timestamp` are empty for a profile's posts | Turn on **Open each profile post for full details**. |
| Fewer rows than **Results per URL** | Instagram shows logged-out visitors 12 posts per profile and about 15 comments per post. |
| The run stopped early | **Maximum results for the whole run** or your maximum cost per run was reached: see `stopReason` in RUN_SUMMARY. |

**Something does not work?** Open an issue on the **Issues** tab with the run ID and the URL. Issues are answered quickly.

# Actor input Schema

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

One per line: profile (https://www.instagram.com/natgeo/, natgeo or @natgeo), profile reels tab (…/natgeo/reels/), post or reel (…/p/DLNsnpUTdVS/, …/reel/CigMSGeD4Hd/), hashtag (…/explore/tags/crossfit/ or #crossfit) or place (…/explore/locations/7538318/). Mobile links, instagr.am links, links with ?igsh=… tracking and whole lists pasted on one line (comma or space separated) are all understood; duplicates are read once.

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

Posts: the posts of a profile, hashtag or place, or the full post for a post URL. Reels: reels with likes, comments and plays. Comments: the comments shown on a post or reel URL. Details: one row describing the profile, hashtag, place or post itself.

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

Maximum results for each URL. Instagram shows logged-out visitors up to 12 posts or 12 reels per profile, about 15 comments per post, the top 12 reels per hashtag and about 15 recent posts per place.

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

Keeps only posts published after this date (UTC): 2026-09-01, 2026-09-01T13:00:00, or a period such as "7 days", "2 months". Rows Instagram shows without a date are kept.

## `includePostDetails` (type: `boolean`):

A profile page lists its posts with caption, type and image only. Turn this on to open every post and add likes, comments count, date, location, tagged users and the latest comments. Slower: one page per post.

## `maxItems` (type: `integer`):

Hard ceiling on rows saved (and billed) across all URLs. 0 = no ceiling. The run also stops cleanly when your maximum cost per run is reached.

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

Instagram blocks datacenter addresses. Keep the residential group for reliable results.

## Actor input object example

```json
{
  "directUrls": [
    "https://www.instagram.com/natgeo/"
  ],
  "resultsType": "posts",
  "resultsLimit": 12,
  "includePostDetails": false,
  "maxItems": 0,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `overview` (type: `string`):

Every row, whatever its type: link, author or name, text, likes, comments, plays, followers and date.

## `posts` (type: `string`):

Table: post link, type, author, caption, likes, comments, plays, date and image.

## `comments` (type: `string`):

Comment text, author, likes, replies and date.

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

Followers, following, posts, verification, bio and website.

## `places` (type: `string`):

Name, category, address, phone, coordinates and posts count.

## `hashtags` (type: `string`):

Posts count and related keywords.

## `allFields` (type: `string`):

Every field of every row, as JSON, CSV or Excel.

## `runSummary` (type: `string`):

Results saved, stop reason, failures, pages loaded and amount charged.

## `failedInputs` (type: `string`):

URLs that gave nothing, with the reason.

# 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/natgeo/"
    ],
    "resultsType": "posts",
    "resultsLimit": 12,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("badr_xba/instagram-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 = {
    "directUrls": ["https://www.instagram.com/natgeo/"],
    "resultsType": "posts",
    "resultsLimit": 12,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("badr_xba/instagram-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 '{
  "directUrls": [
    "https://www.instagram.com/natgeo/"
  ],
  "resultsType": "posts",
  "resultsLimit": 12,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call badr_xba/instagram-scraper --silent --output-dataset

```

## MCP server setup

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