# Instagram Scraper | Posts & Reels | No Login (`hridayrungta/instagram-content-scraper`) Actor

Recent Instagram posts and reels by username - caption, media type, thumbnail, shortcode/URL. No login, no browser. $0.45/1K posts, $0.48/1K reels - beats every official Instagram scraper on price.

- **URL**: https://apify.com/hridayrungta/instagram-content-scraper.md
- **Developed by:** [Hriday Rungta](https://apify.com/hridayrungta) (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 $5.00 / 1,000 actor starteds

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

## Instagram Content Scraper | Posts & Reels by Username

Extracts an Instagram account's recent posts and Reels from that account's own public, logged-out profile page. No login, no browser, no per-post request — one page fetch per username returns the whole recent-content batch.

### What you get

Per post/Reel: content type, caption, media type (photo/video/carousel), pinned status, thumbnail URL, working shortcode and canonical `instagram.com/p/...` or `instagram.com/reel/...` URL.

**What you do NOT get in this batch**: like/comment counts and an exact timestamp. Instagram's own preview data for this page does not include them — see "How this works" below. If your use case needs exact counts, this Actor is honestly not the right fit; nothing here fakes them or leaves them out silently.

### How this works (honestly)

A profile page fetched with a Googlebot user-agent gets Instagram's SEO-rendered HTML, which embeds a `polaris_timeline_connection` block: the account's ~12 most recent posts and Reels together, newest first. This Actor reads that block directly — the same page the sibling `instagram-profile-scraper` in this account already fetches for bio fields, just parsed further.

**A normal browser identity is blocked cold, and so is Apify's own datacenter proxy** — measured directly on Apify's infrastructure: both return an empty page or get rate-limited. Only Apify Proxy's **UNBLOCKER** group gets through. This is why the input's proxy configuration defaults to UNBLOCKER and isn't meant to be turned off — turning it off will not save you money, it will make every run return nothing.

**This dependency is real, and it can change.** This Actor's economics assume Instagram keeps serving this preview data to a Googlebot-identified, Unblocker-proxied request, and that Apify's UNBLOCKER pricing (currently ~$0.0015/request) stays roughly where it is. Neither is contractually guaranteed. If Instagram changes what it serves or Apify repriced Unblocker significantly, this Actor's output or margin would change with it — this README will be updated the day that's confirmed, not left stale.

**One more real-world wrinkle, also measured live**: which exact data block Instagram serves for the same URL is not perfectly consistent even from request to request. Occasionally the clean per-account batch is replaced by a different, "related content" block that mixes in posts from other tagged/co-authored accounts. When that happens, this Actor only keeps the items actually attributed to the username you asked for — it will never report someone else's post under your username, even if that means a smaller batch for that run.

**Shortcodes are recovered without an extra request.** Instagram derives a media's shortcode from its numeric ID with a public, well-documented, deterministic base64-style encoding. This Actor uses that instead of paying for a second fetch per item.

### What was tried and dropped

- **Hashtag pages** (`/explore/tags/{tag}/`) were tested the same way — Googlebot UA, then a real Apify-hosted run through UNBLOCKER proxy. Both failed: a redirect to Instagram's login wall without a proxy, and an HTTP 502 through UNBLOCKER. This looks like a hard login gate, not the kind of block Unblocker clears, so hashtag search is not shipped rather than shipped broken.
- **Full per-post detail by URL** (exact like/comment counts, exact timestamp) works technically — individual post/Reel pages carry it — but costs one full Unblocker-proxied request per item. At that cost, pricing it at or below the cheapest real competitor's per-item rate for that tier would risk losing money on a realistic run. It is not shipped as a paid mode.
- **Comments** were not attempted for the same reason, one level further down.

### Incremental mode

Set `mode: "changes"`. Instagram's preview batch carries no timestamp, so this Actor tracks a high-water mark on the numeric post ID (`pk`) instead — verified live to be strictly newest-first across the whole batch, including pinned posts sitting in their real chronological slot. Every run after the first returns only items newer than the last one it actually saved, per username.

### Input at a glance

| Field | Description |
|---|---|
| `usernames` | Required. One or more usernames, `@handles`, or profile URLs. |
| `includePosts` / `includeReels` | Which content types to keep from each batch. Both default `true`. |
| `mode` | `"latest"` (default, every run) or `"changes"` (incremental). |
| `maxItems` | Budget cap on billed rows for this run. |
| `proxyConfiguration` | Defaults to Apify Proxy, UNBLOCKER group. Leave this as-is — see above. |

### Billing (pay-per-event)

| Event | Price |
|---|---|
| Actor started | $0.005 — charged once per run, regardless of results. Sized to cover a tiny run (e.g. one private/nonexistent account) on its own. |
| Post scraped | $0.00045 |
| Reel scraped | $0.00048 |

For scale: a real 8-account test run during development (7 real, active accounts + 1 that doesn't exist) returned 83 rows from 8 requests — about 10-12 items per successful account, matching the ~12-per-page batch this pricing is built around.

# Actor input Schema

## `usernames` (type: `array`):

Instagram usernames, one per line. A bare username (`nasa`), an @handle (`@nasa`), or a full profile URL (`https://www.instagram.com/nasa/`) all work. Each username costs one request and returns that account's most recent posts/Reels (up to 12, in one page - see the README for why).

## `includePosts` (type: `boolean`):

Save feed posts (photos and carousels) from each account's recent content.

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

Save Reels from each account's recent content.

## `mode` (type: `string`):

"Latest" saves every matching item from each account's recent-content batch every run. "Only new since last run" remembers the newest item per username (by Instagram's own internal ordering) and skips anything already saved on a later run.

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

Stop after saving this many rows in total, across all usernames.

## `maxConcurrency` (type: `integer`):

How many usernames to fetch at the same time.

## `requestsPerSecond` (type: `integer`):

Courtesy throttle across all concurrent workers combined.

## `maxRetries` (type: `integer`):

Retries with exponential backoff for network errors, timeouts, 5xx and 429 responses.

## `stateNamespace` (type: `string`):

Only used in "Only new since last run" mode. Leave blank to key state to your Apify account automatically; set this to share or separate state deliberately across runs/tasks.

## `stateStoreName` (type: `string`):

Only used in "Only new since last run" mode. Advanced: the key-value store name that holds high-water marks. Leave blank for the default.

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

Required in practice: Apify's own datacenter proxy and a bare connection are both blocked by Instagram (measured). Leave the default (Apify Proxy, UNBLOCKER group) - its cost is already covered by this Actor's per-item price.

## Actor input object example

```json
{
  "usernames": [
    "nasa",
    "nike",
    "spacex"
  ],
  "includePosts": true,
  "includeReels": true,
  "mode": "latest",
  "maxItems": 1000,
  "maxConcurrency": 5,
  "requestsPerSecond": 5,
  "maxRetries": 3,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "UNBLOCKER"
    ]
  }
}
```

# Actor output Schema

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

No description

# 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 = {
    "usernames": [
        "nasa",
        "nike",
        "spacex"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "UNBLOCKER"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("hridayrungta/instagram-content-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 = {
    "usernames": [
        "nasa",
        "nike",
        "spacex",
    ],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["UNBLOCKER"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("hridayrungta/instagram-content-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 '{
  "usernames": [
    "nasa",
    "nike",
    "spacex"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "UNBLOCKER"
    ]
  }
}' |
apify call hridayrungta/instagram-content-scraper --silent --output-dataset

```

## MCP server setup

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