# Instagram Posts Scraper for Public Profiles (`datascraperes/instagram-public-posts-scraper`) Actor

Scrape Instagram posts, Reels, videos, and carousels from public profiles in bulk. No login required. Pay only $1 per 1,000 unique posts delivered.

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

## Pricing

$1.00 / 1,000 instagram posts

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 Posts Scraper for Public Profiles

Collect posts, Reels, videos, and carousels from one or many public Instagram profiles. Enter usernames or profile URLs and receive clean, structured records ready for analysis, reporting, or automation. No Instagram login or account cookies are required.

### What this Actor does

- Scrapes public profile timelines in single-profile or bulk runs.
- Returns images, videos, Reels, and carousel posts.
- Extracts captions, hashtags, mentions, timestamps, media links, engagement counts, owners, collaborators, tagged users, and locations when publicly available.
- Accepts up to 10,000 input profiles and returns up to 100,000 unique posts per run.
- Removes duplicate profiles and posts automatically.
- Charges only for unique posts successfully added to the default dataset.
- Saves results in an Apify dataset for download and downstream analysis.

### Common use cases

- Monitor public content published by brands, creators, or competitors.
- Compare posting frequency, formats, captions, and public engagement.
- Build datasets for social media research and trend analysis.
- Track collaborations, tagged accounts, hashtags, and mentions.
- Feed public post data into dashboards, spreadsheets, databases, or AI workflows.

### Pricing

This Actor uses pay-per-event pricing. A charge is created only when a unique post is successfully delivered.

| Delivered posts | Result charge |
| ---: | ---: |
| 100 | $0.10 |
| 1,000 | $1.00 |
| 5,000 | $5.00 |
| 10,000 | $10.00 |

The `post-result` event costs **$0.001 per post**, equivalent to **$1 per 1,000 unique posts**.

You are not charged for:

- duplicate input profiles;
- the same post encountered more than once in a run;
- private, invalid, unavailable, or failed profiles;
- Reels excluded by your input;
- results that are not written to the default dataset.

Use `resultsLimit` to set the maximum number of paid results. You can also configure a maximum run charge in Apify for an additional spending limit.

### Quick start

1. Open the Actor in Apify Console.
2. Add one or more public Instagram usernames or profile URLs to **Instagram profiles**.
3. Set the maximum total posts and maximum posts per profile.
4. Choose whether to include Reels.
5. Click **Start**.
6. Open the **Output** or **Dataset** tab to preview and download the results.

### Input

| Field | Type | Required | Default | Allowed values | Description |
| --- | --- | --- | ---: | --- | --- |
| `profiles` | array of strings | Yes | — | 1–10,000 items | Instagram usernames, `@handles`, or complete public profile URLs. Duplicate forms of the same profile are processed once. |
| `resultsLimit` | integer | No | `1000` | 1–100,000 | Maximum unique posts across the entire run. This is the main result and spending limit. |
| `maxPostsPerProfile` | integer | No | `100` | 1–10,000 | Maximum unique posts attributed to each profile. |
| `includeReels` | boolean | No | `true` | `true` or `false` | Include Reels found in public profile timelines. |

Accepted profile formats:

```text
nasa
@nasa
https://www.instagram.com/nasa/
```

All three values identify the same profile and are deduplicated within the run.

#### Single-profile example

```json
{
  "profiles": ["nasa"],
  "resultsLimit": 50,
  "maxPostsPerProfile": 50
}
```

#### Bulk example

```json
{
  "profiles": [
    "nasa",
    "@instagram",
    "https://www.instagram.com/nike/"
  ],
  "resultsLimit": 5000,
  "maxPostsPerProfile": 500,
  "includeReels": true
}
```

#### Exclude Reels

```json
{
  "profiles": ["nasa"],
  "resultsLimit": 100,
  "maxPostsPerProfile": 100,
  "includeReels": false
}
```

### How limits are applied

`resultsLimit` applies to the whole run, while `maxPostsPerProfile` applies separately to every profile.

For example, with three profiles, `resultsLimit: 500` and `maxPostsPerProfile: 250`:

- no profile can contribute more than 250 posts;
- the combined dataset cannot contain more than 500 posts;
- the Actor stops when the global limit is reached, even if profiles remain;
- the maximum post-result charge is $0.50.

The dataset can contain fewer rows than requested when a profile has fewer public posts, a profile is private or unavailable, Reels are excluded, duplicates are removed, or the Apify run charge limit is reached.

### Output

Each row in the default dataset represents one unique, successfully delivered post. Results can be previewed in Apify Console or exported in formats such as JSON, CSV, Excel, and XML.

#### Output example

```json
{
  "schemaVersion": 1,
  "id": "3945683423788482956",
  "shortCode": "DbB4is3D0GM",
  "url": "https://www.instagram.com/reel/DbB4is3D0GM/",
  "type": "reel",
  "productType": "clips",
  "caption": "Example public caption #Space @nasa",
  "hashtags": ["Space"],
  "mentions": ["nasa"],
  "timestamp": "2026-07-20T18:00:00+00:00",
  "displayUrl": "https://...",
  "videoUrl": "https://...",
  "width": 1080,
  "height": 1920,
  "likesCount": 1000,
  "commentsCount": 25,
  "videoViewsCount": 5000,
  "videoPlaysCount": 5100,
  "videoDurationSeconds": 18.4,
  "hasAudio": true,
  "isPaidPartnership": false,
  "isPinned": false,
  "accessibilityCaption": "Photo by NASA on July 20, 2026.",
  "owner": {
    "id": "528817151",
    "username": "nasa",
    "fullName": "NASA",
    "isVerified": true,
    "isPrivate": false,
    "profilePicUrl": "https://..."
  },
  "location": null,
  "coauthors": [],
  "taggedUsers": [],
  "carousel": [],
  "sourceUsername": "nasa",
  "sourceProfileUrl": "https://www.instagram.com/nasa/",
  "scrapedAt": "2026-07-22T13:45:00+00:00"
}
```

#### Output fields

| Field | Type | Description |
| --- | --- | --- |
| `schemaVersion` | integer | Version of the output record structure. |
| `id` | string | Stable Instagram media ID used to identify and deduplicate the post. |
| `shortCode` | string | Instagram shortcode contained in the post URL. |
| `url` | string | Direct public URL of the post or Reel. |
| `type` | string | Normalized media type: `image`, `video`, `carousel`, or `reel`. |
| `productType` | string or null | Instagram product type when publicly provided, for example `feed` or `clips`. |
| `caption` | string or null | Public caption text. |
| `hashtags` | array | Unique hashtags extracted from the caption, without `#`. |
| `mentions` | array | Unique usernames mentioned in the caption, without `@`. |
| `timestamp` | string or null | Publication time in ISO 8601 format. |
| `displayUrl` | string or null | Best available image or thumbnail URL. |
| `videoUrl` | string or null | Best available public video URL. |
| `width`, `height` | integer or null | Original media dimensions when available. |
| `likesCount` | integer or null | Public like count. |
| `commentsCount` | integer or null | Public comment count. Comments themselves are not scraped. |
| `videoViewsCount` | integer or null | Public video view count when available. |
| `videoPlaysCount` | integer or null | Public video play count when available. |
| `videoDurationSeconds` | number or null | Video or Reel duration in seconds. |
| `hasAudio` | boolean | Whether Instagram marks the media as having audio. |
| `isPaidPartnership` | boolean | Whether the post is marked as a paid partnership. |
| `isPinned` | boolean | Whether the post is marked as pinned in the returned profile timeline. |
| `accessibilityCaption` | string or null | Public accessibility or alt-text description. |
| `owner` | object or null | Post owner's ID, username, full name, verification status, and profile image when available. |
| `location` | object or null | Public location attached to the post when available. |
| `coauthors` | array | Public coauthor account records. |
| `taggedUsers` | array | Publicly tagged users and tag positions when available. |
| `carousel` | array | Individual image or video items belonging to a carousel. Empty for non-carousel posts. |
| `sourceUsername` | string | Input profile from whose timeline the post was collected. |
| `sourceProfileUrl` | string | Normalized URL of the source profile. |
| `scrapedAt` | string | Collection time in ISO 8601 format. |

Instagram may hide or omit some public fields. When a value is unavailable, nullable fields are returned as `null` and list fields as empty arrays. Media links are hosted by Instagram and can expire, so download required files soon after the run.

### Run summary and profile report

The Output tab includes two additional JSON records that are not charged as post results:

- `SUMMARY` shows delivered and billable posts, input limits, duplicate and filtered counts, profile status totals, spending-limit status, duration, and completion time.
- `PROFILE_REPORT` shows the outcome for each submitted value, including completed, duplicate, invalid, private, unavailable, or failed profiles and the number of posts delivered for each one.

Use `PROFILE_REPORT` to identify individual profiles that need attention without searching the full run log.

### Reliability and expected behavior

- Temporary profile failures are retried automatically using the Actor's internal retry policy.
- A failed or unavailable profile does not discard successful results from other profiles.
- Duplicate usernames, URLs, pages, and posts are removed within the run.
- If the same run resumes after an interruption, existing dataset records are recognized to avoid duplicate output and duplicate result charges.
- The Actor respects the total result limit, per-profile limit, and available Apify run-charge budget.

### Limitations

- Only public Instagram profiles are supported. Private profiles do not return posts.
- This Actor accepts profile usernames and profile URLs, not individual post URLs.
- Reels are collected only when they appear in the returned public profile timeline.
- Public counts and metadata can be hidden by an account or omitted by Instagram and may be `null`.
- Image and video URLs can expire because Instagram controls the source files.
- This Actor does not scrape comments, followers, Stories, highlights, hashtag feeds, search results, or private data.
- Instagram can change or temporarily restrict its public pages, so the number of available results can vary between runs.

### FAQ

#### Do I need an Instagram account or login?

No. The Actor collects data exposed on public profile pages and does not require Instagram credentials or cookies.

#### Can it scrape private profiles?

No. Only public profiles are supported. A private, invalid, or unavailable profile is reported without a post-result charge.

#### Why did I receive fewer posts than requested?

The profile may have fewer available public posts, some items may be duplicates, Reels may be excluded, Instagram may temporarily omit pages, or the global result or run-charge limit may have been reached. Check `PROFILE_REPORT` and `SUMMARY` for the specific outcome.

#### Are duplicate inputs charged twice?

No. Equivalent usernames, `@handles`, and profile URLs are processed once. A post is also delivered and charged only once per run, even if it appears under multiple input profiles.

#### How can I control the maximum cost?

Set `resultsLimit` to the maximum paid posts you want. At $0.001 per result, `resultsLimit: 1000` allows at most $1.00 in post-result charges. You can also set a maximum run charge in Apify.

#### Can I exclude Reels?

Yes. Set `includeReels` to `false`. Images, regular videos, and carousels remain eligible for output.

#### Why are some fields `null`?

The Actor returns only data publicly provided by Instagram. Counts, locations, accessibility text, media details, or owner fields can be absent or hidden for individual posts.

#### Are media files stored permanently?

No. `displayUrl` and `videoUrl` point to Instagram-hosted files and can expire. Download the media promptly if you need permanent storage.

#### Does the Actor scrape comments?

No. `commentsCount` is the public number of comments when available; comment text and commenter details are outside this Actor's scope.

### Responsible use

This Actor extracts information publicly visible on Instagram. Results can still contain personal data. Make sure your collection and use have a lawful purpose and comply with applicable laws, privacy requirements, and platform terms. This Actor is not affiliated with Instagram or Meta.

### Support

If a run does not behave as expected, create an issue from the Actor page and include:

- the Actor run ID;
- the public profile input that reproduces the problem;
- the approximate run time;
- the relevant `PROFILE_REPORT` status or `SUMMARY` values;
- the expected result and the result you received.

Do not include Instagram credentials, Apify API tokens, or other secrets in a support request.

# Actor input Schema

## `profiles` (type: `array`):

Usernames, @handles, or complete public Instagram profile URLs. Duplicate profiles are skipped and never fetched or charged twice.

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

Maximum number of unique posts returned across the entire run. This is the primary protection for bulk jobs and maximum spend.

## `maxPostsPerProfile` (type: `integer`):

Maximum unique posts attributed to each submitted profile. Pagination stops separately for every profile at this limit.

## `includeReels` (type: `boolean`):

When enabled, Reels present in each public profile timeline are returned as posts. Disable it to keep images, videos, and carousels only.

## Actor input object example

```json
{
  "profiles": [
    "nasa"
  ],
  "resultsLimit": 5,
  "maxPostsPerProfile": 5,
  "includeReels": true
}
```

# Actor output Schema

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

Every dataset row is one unique successfully delivered post and maps to one post-result event.

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

Delivered-post, billing, duplicate, limit, status, duration, and completion totals.

## `profileReport` (type: `string`):

A free status record for every submitted profile, including invalid, duplicate, unavailable, and completed inputs.

# 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 = {
    "profiles": [
        "nasa"
    ],
    "resultsLimit": 5,
    "maxPostsPerProfile": 5,
    "includeReels": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("datascraperes/instagram-public-posts-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 = {
    "profiles": ["nasa"],
    "resultsLimit": 5,
    "maxPostsPerProfile": 5,
    "includeReels": True,
}

# Run the Actor and wait for it to finish
run = client.actor("datascraperes/instagram-public-posts-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 '{
  "profiles": [
    "nasa"
  ],
  "resultsLimit": 5,
  "maxPostsPerProfile": 5,
  "includeReels": true
}' |
apify call datascraperes/instagram-public-posts-scraper --silent --output-dataset

```

## MCP server setup

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