# Instagram Post Scraper (`keklik/instagram-post-scraper`) Actor

Scrape Instagram posts from public profiles or post URLs, no login: captions, likes, comment count, dates, images, videos, carousels, co-authors, tags, music and optional latest comments.

- **URL**: https://apify.com/keklik/instagram-post-scraper.md
- **Developed by:** [Keklik Technologies](https://apify.com/keklik) (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 $0.70 / 1,000 posts

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

This **Instagram post scraper** lets you **scrape the posts of public Instagram profiles**, or single posts by URL, and export them to JSON, CSV or Excel. Paste a few usernames or post links, press Start, and get **one row per post**: caption, likes, comment count, date, images and videos, carousel slides, co-authors, tagged accounts, hashtags and music, plus **optional latest comments**. **No Instagram account, no login and no cookies** are needed.

### What data can you extract from Instagram posts?

Each post comes back as one row with 46 fields.

| Group | Fields |
|---|---|
| **Post** | Post link, post ID, shortcode, caption, alt text, date (Unix seconds and ISO 8601 in UTC), type (image, video or carousel), product type (photo, carousel or reel), pinned flag, paid partnership flag, dimensions |
| **Author** | Username, display name, user ID, profile picture |
| **Engagement** | Number of likes and number of comments |
| **Media** | Main image or video cover, video file link, reel link for videos, up to 10 carousel slides (each with its ID, image, video, size, type and alt text) and the first 5 slide images as plain links |
| **People** | Collab co-authors and accounts tagged in the media (ID, username, display name, verified badge, profile picture), and the @mentions in the caption |
| **Hashtags** | Every hashtag in the caption |
| **Music** | The audio of a reel or video: licensed track or original audio, song name, artist, trending and explicit flags |
| **Comments** (optional) | The post's newest comments in a `latest_comments` list: comment ID, text, date, likes, edited and hidden flags, GIF, and the commenter's ID, username, verified badge and profile picture |
| **Run details** | The input the post came from, and when the row was scraped |

### Use cases

- **Social media marketing**: track what competitors post, how often, in which format, and which posts get the most likes and comments.
- **Influencer marketing**: check an influencer's real engagement, spot paid partnerships and collab posts, and see which brands they tag.
- **Content strategy**: find the captions, hashtags, formats and music that perform best in your niche, and plan your own posts around them.
- **Brand monitoring**: follow your brand's accounts and partners, and see who tags and mentions you.
- **Market and academic research**: build a dataset of Instagram posts over a period with the **Newer than** filter, for trend, topic or sentiment analysis.
- **Reporting and archiving**: export a profile's posts to a spreadsheet or a BI tool on a schedule, with the media links saved alongside.

### How to scrape Instagram posts

1. Open **Instagram Post Scraper** in Apify Console and go to the Input tab.
2. In **Profiles or posts**, paste one entry per line: a profile URL (`https://www.instagram.com/natgeo/`), a username (`natgeo` or `@natgeo`), or a post or reel URL (`https://www.instagram.com/p/DUvd6BykR38/`).
3. Set **Max posts per profile** to cap how many posts each profile gives you, or leave it empty to get every post.
4. To keep only recent posts, set **Newer than** to a date (`2026-01-31`) or a duration (`7d`).
5. If you want comments too, set **Comments per post** to the number of newest comments you want on each post.
6. Press **Start** and watch the posts appear in the Output tab.
7. Download the results as JSON, CSV, Excel, XML or HTML, or read them through the Apify API.

### Input

| Field | What it does |
|---|---|
| **Profiles or posts** | One per line. A profile URL or username (`https://www.instagram.com/natgeo/`, `@natgeo`, `natgeo`) scrapes the profile's posts, newest first. A post or reel URL scrapes that one post. Required. |
| **Max posts per profile** | Stops each profile after this many posts. Leave empty for every post. |
| **Newer than** | Keeps only posts published after a date in UTC (`2026-01-31` or `2026-01-31 18:00`) or within a duration back from now (`24h`, `7d`, `2w`). A profile stops at its first older post. |
| **Skip pinned posts** | Leaves out the posts pinned to the top of a profile. Off by default. |
| **Comments per post** | Adds up to this many of each post's newest comments, in a `latest_comments` list. 0 (the default) adds none. |
| **Max results** | Stops the whole run after this many posts. Leave empty for no limit. |

```json
{
    "urls": ["https://www.instagram.com/natgeo/", "https://www.instagram.com/p/DeTUk4sDSp-/"],
    "maxPostsPerProfile": 50,
    "newerThan": "30d",
    "commentsPerPost": 0
}
```

### Output

A shortened row from a real run (the full row has 46 fields):

```json
{
    "url": "https://www.instagram.com/p/DeTUk4sDSp-/",
    "post_id": "4004634989362489982",
    "shortcode": "DeTUk4sDSp-",
    "caption": "POV: you're watching the Earth set\n\nWe just released hundreds of new images and hours of n …",
    "owner_username": "nasa",
    "owner_full_name": "NASA",
    "likes_count": 684438,
    "comments_count": 6785,
    "timestamp": 1791645302,
    "timestamp_datetime": "2026-10-10T15:15:02+00:00",
    "product_type": "clips",
    "media_type": "video",
    "is_video": true,
    "is_pinned": true,
    "dimensions": "1074x1908",
    "reel_url": "https://www.instagram.com/reel/DeTUk4sDSp-/",
    "coauthors": [
        {
            "id": "1611722079",
            "username": "nasasolarsystem",
            "full_name": "NASA Solar System Exploration",
            "is_verified": true
        }
    ],
    "hashtags": ["#NASA", "#ArtemisII", "#Artemis"],
    "music": {
        "audio_id": "28589819277311219",
        "song_name": "Original audio",
        "artist_name": "nasa",
        "uses_original_audio": true,
        "should_mute_audio": false,
        "is_explicit": false
    },
    "source_url": "nasa",
    "scraping_time": "2026-10-16T09:29:28+00:00"
}
```

Carousel posts also fill `child_post_1` to `child_post_10` and `image_1` to `image_5`. With **Comments per post** set, each post that has comments gets a `latest_comments` list, newest first.

You can export the results to **JSON, CSV, Excel, XML and HTML**, or open them in the Output tab as a table.

### How much does it cost to scrape Instagram?

This Actor uses pay-per-event pricing: you pay for the posts you get, not for the time the run takes.

- **$1.00 per 1,000 posts** on the Apify Free plan.&#x20;
- Bronze, Silver and Gold Apify plans pay less: $0.90, $0.80 and $0.70 per 1,000 posts.&#x20;
- Comments are optional and charged per comment: **$1.00 per 1,000 comments** on the Free plan, and $0.90, $0.80 and $0.70 on Bronze, Silver and Gold. With **Comments per post** at 0 (the default) you pay for posts only.&#x20;

Comments make a run slower: in our tests, 500 posts took about 40 seconds without comments and about 3 minutes with the newest comments on every post. Ask only for the comments you need.

### Integrations and API

You can run the Instagram Post Scraper from your own code through the **Apify API** and get the posts back as JSON.

Python, with `apify-client`:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("kekliktech/instagram-post-scraper").call(run_input={
    "urls": ["natgeo"],
    "maxPostsPerProfile": 20,
})
for post in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(post["url"], post["likes_count"])
```

Node.js, with `apify-client`:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('kekliktech/instagram-post-scraper').call({
    urls: ['natgeo'],
    maxPostsPerProfile: 20,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

You can also:

- **Schedule** runs to collect new posts every day or week.
- Send results to your system with **webhooks** when a run finishes.
- Connect it to **Make, Zapier, Google Sheets**, Slack and other apps through Apify integrations.
- Let **AI agents** call it through the **Apify MCP server**, so Claude, ChatGPT or Cursor can scrape Instagram posts on request.

### FAQ

#### Is it legal to scrape Instagram?

This scraper collects only data that Instagram shows publicly to visitors who are not logged in. Posts can still contain personal data, such as usernames of tagged accounts or commenters. Personal data is protected by laws such as the GDPR in Europe, so make sure you have a lawful reason to collect and use it, and check with a lawyer if you are unsure.

#### Do I need an Instagram account?

No. You don't need an Instagram account, a login or cookies.

#### Can I scrape private Instagram profiles?

No. Instagram shows a private profile's posts only to approved followers, so private profiles are skipped.

#### Can I get view or play counts?

No. Instagram does not show view and play counts to logged-out visitors, so they are not in the results.

#### How do comments work?

Set **Comments per post** to add each post's newest comments to its row. Comments come newest first and are top-level comments only: replies and the "most relevant" order are not available without logging in. A post with no comments has no `latest_comments`. If Instagram refuses a post's comments, the post is still saved without them, and the run's status message says how many posts missed their comments. You pay only for the comments you get. For many comments per post, or one row per comment, use the [Instagram Comments Scraper](https://apify.com/kekliktech/instagram-comments-scraper).

#### How do pinned posts and the date filter work together?

Pinned posts come first on a profile, whatever their date. With **Newer than**, older pinned posts are left out but don't stop the profile; the profile stops at its first older post that isn't pinned. Turn on **Skip pinned posts** to leave them out altogether. A post scraped from its own URL always has `is_pinned` set to false.

#### Why did some inputs fail?

Inputs that fail are listed with the reason in the `FAILED` record of the run's key-value store, not in the dataset:

- `invalid_input`: not a profile, username or post URL (for example an explore or stories link).
- `not_found`: the profile or post does not exist or was removed.
- `private_or_no_posts`: the profile is private, or has no posts.
- `blocked`: Instagram refused every attempt. Run it again later.
- `instagram_error`: Instagram answered with an error the scraper does not recognise. Please report it.

#### Why do some media links stop working?

Instagram image and video links expire after a while. Download the media soon after the run if you need to keep it.

#### Can the same post appear twice?

No. A post appears only once per run, even if it is on two of your profiles (a collab post) or entered twice.

#### Are all carousel slides included?

The row keeps the first 10 slides of a carousel. `carousel_media_count` gives the full number of slides.

### Related scrapers

- [Instagram Profile Scraper](https://apify.com/kekliktech/instagram-profile-scraper): profile details such as bio, followers and links, one row per account.
- [Instagram Reels Scraper](https://apify.com/kekliktech/instagram-reels-scraper): the reels of public profiles.
- [Instagram Comments Scraper](https://apify.com/kekliktech/instagram-comments-scraper): every comment on a post, one row per comment.

### Support

Found a bug or missing a field? Open an issue in the **Issues** tab of this Actor and we'll look into it. Need a scraper for another website, or a custom version of this one? Get in touch through the Issues tab and ask for a custom scraper.

# Actor input Schema

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

One per line. A profile URL or username (https://www.instagram.com/natgeo/, @natgeo or natgeo) scrapes that profile's posts, newest first. A post or reel URL (https://www.instagram.com/p/DUvd6BykR38/) scrapes that one post.

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

Stop each profile after this many posts. Leave empty to scrape every post the profile has. Does not apply to single post URLs.

## `newerThan` (type: `string`):

Only posts published after this point. A date in UTC (2026-01-31 or 2026-01-31 18:00) or a duration back from now (24h, 7d, 2w). Pinned posts older than this are left out without ending the profile.

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

Leave out the posts pinned to the top of a profile.

## `commentsPerPost` (type: `integer`):

Add up to this many of each post's newest comments, in a latest_comments list. 0 adds none. Each comment is charged on its own (see Pricing) and comments make a run slower. For many comments per post, or one row per comment, use the Instagram Comments Scraper.

## `maxResults` (type: `integer`):

Stop the whole run after this many posts. Leave empty for no limit.

## Actor input object example

```json
{
  "urls": [
    "https://www.instagram.com/natgeo/"
  ],
  "maxPostsPerProfile": 50,
  "skipPinnedPosts": false,
  "commentsPerPost": 0
}
```

# Actor output Schema

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

All scraped posts as a dataset you can export to JSON, CSV, Excel, XML or HTML.

# 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/natgeo/"
    ],
    "maxPostsPerProfile": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("keklik/instagram-post-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/natgeo/"],
    "maxPostsPerProfile": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("keklik/instagram-post-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/natgeo/"
  ],
  "maxPostsPerProfile": 50
}' |
apify call keklik/instagram-post-scraper --silent --output-dataset

```

## MCP server setup

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