# Instagram Latest Posts Monitor (`simple.actors/instagram-profile-posts`) Actor

Check any public Instagram profile for its latest posts: caption, likes, comments, images, video files and carousels, plus the full account record. No login, cookies or API key. Built for polling many accounts often, not deep history: 12 posts per profile, 24 with the video tab. Unofficial.

- **URL**: https://apify.com/simple.actors/instagram-profile-posts.md
- **Developed by:** [Simple Actors](https://apify.com/simple.actors) (community)
- **Categories:** Social media, News
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 profile reads

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?

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 Latest Posts Monitor

Check any public Instagram profile for what it has posted lately — caption,
likes, comments, images, video files, carousels and the full account record.
**12 latest posts per profile** — or **24** with the video tab switched on — newest first, 57 fields each.

**$3 per 1,000 profiles read** — Apify platform usage and residential proxy
included, nothing else to pay. The charge is per profile and does not depend on
how many posts come back, so a full read and a check that finds one new post cost
the same. A profile that cannot be read is never charged.

**No login, no cookies, no API key, no account of yours involved.** There is no
session to keep alive and nothing of yours to get restricted.

*Unofficial. Not affiliated with, endorsed by, or sponsored by Instagram or Meta.*

> **Read this before you start: 12 posts per profile** from the main grid, and
> 12 more if you turn on the video tab. That is Instagram's limit for a
> logged-out reader, not a setting. This is built to check *many* accounts
> *often* — if you need one account's back catalogue, this is the wrong tool.

### What you get

One dataset item per post:

- **The post** — caption, permalink, publish time, type (image, video, carousel)
- **Engagement** — likes, comments, and play counts on video
- **Media** — full-size image URL and every smaller rendition; for video, a
  direct file URL; for a carousel, **every slide** with its own image or video
- **Context** — hashtags and mentions read from the caption, tagged accounts,
  collaborators, location, music credits on reels, alt text
- **The account** — bio, bio links, followers, following, total posts, category,
  business contact details, verification, profile pictures

```json
{
  "type": "Sidecar",
  "shortCode": "DcJ2mQxFabc",
  "url": "https://www.instagram.com/p/DcJ2mQxFabc/",
  "caption": "Hold the dots and scroll to switch between…",
  "likesCount": 216896,
  "commentsCount": 716,
  "timestamp": "2026-08-14T21:48:00.000Z",
  "childPostCount": 20,
  "hashtags": ["NASA", "space", "astronomy"],
  "ownerUsername": "nasa",
  "owner": { "followersCount": 104409230, "isVerified": true }
}
```

#### Two timelines for one request

Instagram serves a **second timeline** alongside the grid — the account's
long-form and IGTV videos — in the same request, sharing no posts with the grid.
Set `includeVideoTab` to double what a run returns **at no extra cost**. Every
item says which one it came from in `source` (`grid` or `videos`).

It is off by default because that tab reaches years further back, and folding it
in silently would change what "latest" means.

#### Related accounts

Instagram also ships up to fifty suggested accounts with every profile. Set
`includeRelatedProfiles` to get them under `owner.relatedProfiles`, each with a
username, name, ID, verification and link — a ready-made way to widen a watchlist.

### How to use it

```json
{ "startUrls": [{ "url": "https://www.instagram.com/nasa/" }] }
```

If you have usernames rather than links, put them in `usernames` instead — both
lists are read as one:

```json
{
  "startUrls": [{ "url": "https://www.instagram.com/nasa/" }],
  "usernames": ["natgeo", "@who"]
}
```

### Residential proxy is required

Instagram allows one address about **nineteen requests** before refusing it, and
the refusal lasts far longer than the message claims — measured still in force
**33 minutes** after "please wait a few minutes". So every attempt goes out from
a different IP, which is what residential proxy provides.

The default is already set correctly. If you switch to datacenter, the pool is
too small to rotate through and runs will come back `rate_limited`.

### A post on a profile is not always *by* that profile

Instagram collaborations appear on both accounts' grids but are owned by one of
them. `ownerUsername` is read from the post, so a collaboration is attributed to
its real owner rather than to the profile you asked for — and `coauthors` lists
the credited accounts.

### What an empty result means

An empty dataset means the account was read and has posted nothing in the window
you asked for. It never means "we could not look".

An account that cannot be read is reported as its **own row** with `error` and
`errorDescription`, so one bad account never costs you the rest:

| `error` | Meaning |
|---|---|
| `not_found` | No such account |
| `private` | Posts are visible only to approved followers, so a login would be needed |
| `instagram_error` | Instagram itself returned an internal error for this account — see below |
| `rate_limited` | Instagram refused every IP tried; use residential proxy, or fewer accounts per run |
| `window_too_wide` | `onlyPostsNewerThan` reaches further back than this run can see |
| `read_failed` | Instagram did not serve a readable response after several attempts |

If *every* account in a run fails, the run itself fails, so a scheduled job
cannot mistake a bad run for a quiet day.

#### About `instagram_error`

Some business accounts currently make Instagram's own endpoint return an
internal error about a deleted asset. It is reproducible per account and is a
fault on Instagram's side — not the account, and not this Actor. Nothing here can
work around it; those accounts are reported so you can see exactly which ones and
why, rather than being told the read simply failed.

### What it costs

**$3 per 1,000 profiles read.** One profile is one charge, so asking for five
profiles in one run costs five. There is no per-post fee and no per-run fee.

Two consequences worth knowing:

- **Turn on the video tab.** It doubles what you get — 24 posts instead of 12 —
  for exactly the same charge, because it arrives in the same request.
- **Take all the posts.** Fewer posts cost you no less; the price is for reading
  the profile.

Because the charge is per profile, what you pay per *post* depends on how many
come back:

| Posts returned | Works out at |
|---|---|
| 24 — grid plus video tab | $0.13 per 1,000 posts |
| 12 — grid only | $0.25 per 1,000 posts |
| 1 — a poll that found one new post | $3.00 per 1,000 posts |

If you are polling with `onlyPostsNewerThan` and most checks come back empty,
budget by **profiles checked**, not posts collected — that is what you are billed
on. A profile that cannot be read is never charged, so a list containing dead or
private accounts costs only for the live ones.

### Settings

| Setting | Default | What it does |
|---|---|---|
| `startUrls` | — | Profiles to read, as links. |
| `usernames` | — | Profiles to read, as usernames. Combined with `startUrls`. |
| `maxPosts` | all | How many latest posts per account. Ceiling is 12, or 24 with the video tab. |
| `includeVideoTab` | `false` | Also read the long-form/IGTV timeline — roughly doubles the output, same request. |
| `includeRelatedProfiles` | `false` | Attach the accounts Instagram suggests alongside this one. |
| `onlyPostsNewerThan` | — | Keep only posts after a point: `"20 hours"`, `"3 days"`, or `2026-08-01`. |
| `includeRaw` | `false` | Attach Instagram's untouched post object under `raw`. |
| `proxy` | Residential | Leave this alone unless you know why you are changing it. |

#### `onlyPostsNewerThan` will refuse rather than mislead

A run sees an account's latest posts and no further back. If your window reaches
past them, posts inside it were never fetched — and returning what was found
would read as "this is everything since then". So the account is reported with
`window_too_wide` instead, telling you how far back it could actually see. A
pinned post does not count towards that reach, since it can be years old.

### Limits

**12 posts per profile from the grid, 24 with the video tab** — Instagram's
numbers, not settings.

**Media URLs expire.** Image and video URLs are signed by Instagram and die
within hours. Download them promptly; the post's `url` is the durable one.

**No comment text.** This endpoint carries comment *counts* but no comments.
`firstComment` and `latestComments` are present and empty so existing code does
not break on their absence.

**Private accounts cannot be read.** They need an approved follower's login,
which this deliberately does not have.

### Call it from the API

```bash
curl -s "https://api.apify.com/v2/acts/simple.actors~instagram-profile-posts/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"usernames":["nasa"]}'
```

### Note

This reads publicly visible posts only — the same ones anyone can see without
logging in. It does not log in, does not use cookies, and cannot reach private
accounts, stories, direct messages, or anything else behind a login.

# Actor input Schema

## `startUrls` (type: `array`):

Public Instagram accounts to read, as links: https://www.instagram.com/nasa/. For bare usernames use "Usernames" below, which the platform accepts as plain text. Private accounts cannot be read without a login and are reported as such.

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

Accounts given as usernames rather than links — "nasa", "@nasa" or "NASA" all work. Use this when you have a column of usernames. Both lists are read as one.

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

How many of the latest posts to return per account, newest first. Instagram returns 12 from the grid, and 12 more from the video tab if you turn that on — so the ceiling is 12, or 24 with the video tab. Leave it unset to take everything available.

## `includeVideoTab` (type: `boolean`):

Instagram serves a second timeline alongside the grid — the account's long-form and IGTV videos — in the same request, and it shares no posts with the grid. Turn this on to include it and roughly double what a run returns at no extra cost. Off by default because that tab reaches years further back, which would change what "latest" means; every item says which timeline it came from in `source`.

## `includeRelatedProfiles` (type: `boolean`):

Attach the accounts Instagram suggests alongside this one (up to fifty, already included in the same request) under `owner.relatedProfiles`. Off by default because the owner record rides on every post, so this multiplies the size of a run's output.

## `proxy` (type: `object`):

Defaults to Apify residential proxy, which this Actor needs. Instagram allows an address about nineteen requests before refusing it for far longer than it says, so every attempt goes out from a different IP. A datacenter pool is too small for that and a run on one will fail with rate\_limited.

## `includeRaw` (type: `boolean`):

Attach Instagram's untouched post object under `raw`. Turn this on when you need a detail that has no named field; it makes each item much larger, so leave it off otherwise.

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

Keep only posts published after this point. Takes a window like "20 hours", "last 3 days" or an ISO date such as 2026-08-01. Because a run reads only an account's latest posts, the run reports the account rather than returning a partial answer if the window reaches further back than those posts do — so an empty result always means "nothing new", never "we did not look".

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.instagram.com/nasa/"
    }
  ],
  "usernames": [],
  "includeVideoTab": false,
  "includeRelatedProfiles": false,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "includeRaw": false,
  "onlyPostsNewerThan": "3 days"
}
```

# Actor output Schema

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

One row per post with its headline numbers.

## `media` (type: `string`):

Image and video URLs for each post.

## `engagement` (type: `string`):

Likes, comments and video views per post.

## `full` (type: `string`):

Caption, owner, tags and flags.

## `raw` (type: `string`):

Every field of every item, exactly as pushed.

# 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 = {
    "startUrls": [
        {
            "url": "https://www.instagram.com/nasa/"
        }
    ],
    "usernames": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("simple.actors/instagram-profile-posts").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 = {
    "startUrls": [{ "url": "https://www.instagram.com/nasa/" }],
    "usernames": [],
}

# Run the Actor and wait for it to finish
run = client.actor("simple.actors/instagram-profile-posts").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 '{
  "startUrls": [
    {
      "url": "https://www.instagram.com/nasa/"
    }
  ],
  "usernames": []
}' |
apify call simple.actors/instagram-profile-posts --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,simple.actors/instagram-profile-posts"
        }
    }
}

```

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/VHb78lWUcUW6rdfTb/builds/EudHi4YVk1FhylSeg/openapi.json
