Instagram Profile Posts & Reels Scraper
Pricing
from $0.60 / 1,000 content results
Instagram Profile Posts & Reels Scraper
Collect posts and Reels from one or more Instagram profiles, with up to 1,000 items per profile. Use postedAfter to fetch only newly published content and stop paginating early, so scheduled runs pay the lower filter rate for old items instead of the full result rate.
Pricing
from $0.60 / 1,000 content results
Rating
0.0
(0)
Developer
LanceAPI
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
12 hours ago
Last modified
Categories
Share
Instagram Posts & Reels Scraper
Fetch posts and Reels from Instagram profiles. Use postedAfter to retrieve only newly published content, so your Dataset and your result fees stay focused on what is new.
This Actor collects public posts and Reels from one or more Instagram profiles and writes one clean, structured row per item. Paste usernames, profile URLs or numeric user IDs, choose what you want, and — when you only care about what is new — set a cutoff date so pagination stops as soon as the feed moves past it. It is built for content collection and new-content monitoring, not for profile analytics.
- Posts and Reels in one pass — photos, carousels, feed videos and Reels come back from the profile feed, or ask for posts only / Reels only.
postedAfterincremental monitoring — only content published strictly after your cutoff is returned, and pagination stops early once the feed is older than that moment, so a scheduled run does not re-collect and re-pay full result fees for content you already have.- Zero new posts is a normal, successful result — not an error, and not a reason to retry the same profile against every data source.
- Up to 1,000 items per profile, 200 profiles per run — with input normalization and de-duplication built in.
- Pay per delivered item, plus a small per-item fee when the date filter runs — result fees follow the unique posts and Reels actually written to your Dataset, never the pages or requests behind them; when
postedAfteris set, the items it examines and removes are charged at a lower filter rate.
✨ Key features
1. One profile, many content rows
Every unique post or Reel is delivered as its own Dataset row, linked back to the profile it came from through sourceUsername, sourceProfileUrl and sourceProfileId. Photos, carousels, feed videos and Reels are all first-class rows, with type telling them apart (photo, carousel, video, reel).
Because each item is its own row, filtering, joining against your own tables, and exporting stay simple — you never have to unpack a nested list to get at a single post.
2. postedAfter: real incremental scraping, not a local filter
Set postedAfter to a calendar date, an ISO-8601 moment, or a relative value such as 7 days, and only content published strictly after it is returned. The filter is applied inside the pagination pipeline, page by page:
- each page is normalized and filtered as it arrives;
- once the feed has moved past your cutoff, pagination stops instead of continuing to pull older pages you would throw away;
- pinned posts are excluded from the stop decision, so a pinned old item at the top of a feed can never hide newer content;
- an item with no usable timestamp is not delivered and not guessed — it cannot be proven to be newer than your cutoff.
For scheduled monitoring this is the difference between "collect what is new" and "download the whole feed again": old items are examined, removed and charged at the lower filter rate, while only genuinely new items reach your Dataset at the full result rate. A run that finds nothing new still succeeds — no content rows, no result fees, just the filter fee for the items it checked.
3. Content-type control that matches how you will use the data
- All content returns the profile's mixed feed: photos, carousels, feed videos and Reels in one pass.
- Posts only returns the same feed with explicit Reels removed.
- Reels only uses the dedicated Reels feed rather than filtering the first page of a mixed one, so Reel-only runs are not limited by what happens to sit on top of the general feed.
4. Batch-friendly with de-duplication at two levels
Process up to 200 profiles in one run. Inputs are normalized before any work starts, so nasa, @nasa, https://www.instagram.com/nasa/ and a numeric user ID for the same account are recognized as the same profile and processed once — no repeated work, no duplicate rows, no duplicate charges. At the result level, the same media ID is delivered only once even across pagination and recovery.
If one profile in a batch fails, the rest of the batch continues. Failures are reported per profile in the run summary, so one bad input does not waste a 200-profile run.
5. Limits that control both Dataset size and cost
resultsLimit caps how many unique items are delivered per profile (1–1000, default 100). It is the main lever over both your Dataset size and your result fees: set it to what you actually need rather than leaving it high. How far back a profile can be read also depends on what the data source returns — this Actor does not claim to deliver an account's entire history.
6. Stable identifiers and source links
Every row carries the Instagram media id and shortcode plus the canonical url, so you can de-duplicate against previous runs, join to your own tables, and trace any row back to the profile and input value that produced it. scrapedAt records when the row was collected.
🚀 Quick start
- Open the Actor's Input tab.
- Add one or more public Instagram profiles — usernames,
@usernames, profile URLs, or numeric user IDs. - Keep the defaults for a first run:
contentType: all,resultsLimit: 100. - Click Start. When the run finishes, open Output → Content.
For monitoring, set Only content published after to the last moment you already have (for example 2026-10-11T18:30:00Z). The run then returns only newer items and stops paginating once the feed is past that moment.
Minimal input:
{"profiles": ["nasa", "@natgeo", "https://www.instagram.com/lego/"],"contentType": "all","resultsLimit": 100}
Incremental input — only what is new since yesterday evening:
{"profiles": ["nasa"],"contentType": "all","resultsLimit": 100,"postedAfter": "2026-10-11T18:30:00Z"}
Short output example
Each unique post or Reel is written as its own Dataset row. The example below is an illustrative example showing structure and field names — values are redacted, not from a live run.
{"sourceUsername": "nasa","sourceProfileUrl": "https://www.instagram.com/nasa/","sourceProfileId": "528817151","type": "reel","url": "https://www.instagram.com/reel/XXXXXXXXXXX/","timestamp": "2026-10-12T15:04:00.000Z","caption": "Example caption text #hashtag","likesCount": 123456,"commentsCount": 2345,"viewsCount": 987654,"videoUrl": "https://…example-video-url…","thumbnailUrl": "https://…example-thumbnail-url…","id": "3000000000000000000","shortcode": "XXXXXXXXXXX","contentType": "all","input": "nasa","scrapedAt": "2026-10-13T06:00:00.000Z"}
Input
| Field | Type | Required | Default | Limits / notes |
|---|---|---|---|---|
profiles | array of strings | Yes | prefill: ["instagram"] | 1–200 values. Accepts usernames (nasa), @usernames, profile URLs (https://www.instagram.com/nasa/) and numeric Instagram user IDs. Normalized and de-duplicated before processing: the same profile is processed once, whatever form it arrived in. |
contentType | string | No | all | all (mixed feed: photos, carousels, feed videos, Reels), posts (same feed with explicit Reels removed), reels (dedicated Reels feed). |
resultsLimit | integer | No | 100 | 1–1000 unique items per profile. The main control over Dataset size and result fees. |
postedAfter | string | No | empty | Cutoff for incremental runs. In the form, Absolute provides a YYYY-MM-DD calendar picker and Relative accepts a number of days, weeks, months, or years (e.g. 1 days, 2 months, 3 years). JSON also accepts ISO-8601 date-times such as 2026-10-11T18:30:00Z or 2026-10-11T08:00:00+08:00. Dates start at midnight UTC; relative values are resolved once at run start in UTC. Only items published strictly after that cutoff are returned. Invalid values fail with INVALID_POSTED_AFTER before processing. |
How the fields interact:
postedAfterandresultsLimitwork together: the cutoff decides which items qualify, the limit decides how many are delivered. When both are set, the run stops at whichever comes first.postedAfterwith zero new items is a successful run: the profile is reported as checked, and no content rows are written.contentType: reelsmeasures Reels directly from the Reels feed; it is not "the first page of the general feed, filtered".
Output
One row per unique post or Reel. The Dataset Views change only the column order and presentation, never the underlying rows:
- All Content — the balanced overview: source, type, link, timestamp, caption, likes, comments, views, video and thumbnail.
- Posts — photo and carousel oriented columns.
- Reels — Reel oriented columns: views, duration, video URL, music.
- Downloads — media URLs and identifiers for downloading or archiving.
Field reference
Core fields:
| Field | Type | Meaning |
|---|---|---|
sourceUsername | string | null | Username the item was collected from. Null when the profile was supplied as a numeric ID and nothing proved the username. |
sourceProfileUrl | string | null | Profile URL the item was collected from. |
sourceProfileId | string | null | Numeric Instagram user ID of the source profile, when known. Stable across username changes. |
type | string | reel, photo, carousel, video, or unknown when the payload did not establish one. Never folded into another bucket. |
url | string | null | Canonical Instagram URL of the item. |
timestamp | string | null | Publication time, ISO-8601 UTC. Null when the payload carried no usable timestamp. |
caption | string | null | Caption text, when present. |
id | string | null | Instagram media ID — stable identifier for de-duplication and joins. |
shortcode | string | null | Instagram shortcode; the slug used in the content URL. |
Engagement and media fields:
| Field | Type | Meaning |
|---|---|---|
likesCount | integer | null | Like count, when reported. |
commentsCount | integer | null | Comment count, when reported. |
viewsCount | integer | null | Play/view count for video content, when reported. Null for content that reports none. |
playsCount | integer | null | Play count, when separately reported. |
sharesCount | integer | null | Share count, when reported. |
videoUrl | string | null | Direct video URL, when the payload returned one. Provider media URLs can expire — download promptly if you need the file. |
thumbnailUrl | string | null | Thumbnail or first-frame image. |
imageUrl | string | null | Display image for photos and carousels. |
duration | number | null | Video duration in seconds, when reported. |
Context fields:
| Field | Type | Meaning |
|---|---|---|
hashtags | string[] | Hashtags parsed from the caption. |
mentions | string[] | Usernames mentioned in the caption. |
music | object | null | Attached audio (title, artist) when the payload carried it. |
location | object | null | Tagged location (id, name, city, address) when the payload carried one. |
contentType | string | The contentType setting this run used. |
input | string | The exact profile value you supplied that produced this row. |
scrapedAt | string | When the row was collected, ISO-8601 UTC. |
Missing values are delivered as null, never as zero or placeholder text: a null likesCount means the source did not report one, which is different from a real count of zero.
Status and errors
Every run writes a RUN_SUMMARY record to the key-value store with per-run totals: profiles requested / processed / succeeded / failed, how many items the date filter removed, how many content rows were delivered (split into posts and Reels), how many pages and upstream calls were used, how many times the date filter stopped pagination early, and which inputs were rejected.
Run-level input errors fail the whole run before any profile is processed:
| Code | Meaning |
|---|---|
INVALID_REQUEST | The input could not be read. |
BATCH_LIMIT_EXCEEDED | More than 200 profiles in one run. |
INVALID_POSTED_AFTER | postedAfter could not be parsed as an absolute date, an ISO-8601 instant, or a relative UTC duration. |
Per-profile outcomes are reported in the summary; a failed profile does not stop the rest of the batch:
| Code | Meaning | Retry? |
|---|---|---|
PROFILE_NOT_FOUND | The profile could not be found. | Check the username; no retry needed. |
CONTENT_UNAVAILABLE | Public content is not available right now (for example a private or restricted account). | Retry later may help. |
UPSTREAM_RATE_LIMITED / UPSTREAM_QUOTA_EXHAUSTED / UPSTREAM_TIMEOUT | A temporary data-source issue. | Retry later. |
MALFORMED_RESPONSE / UPSTREAM_ERROR | A temporary data-source issue prevented this profile from being resolved. | Retry later. |
SERVICE_CONFIGURATION_ERROR | No data source was available because of a routing configuration issue. Nothing was requested upstream and nothing was charged. | Do not retry: the Actor's routing configuration has to be fixed first. |
INVALID_PROFILE | The value is not a usable Instagram profile. | Fix the input. |
FREE_USAGE_LIMIT_EXCEEDED | The free usage allowance has been reached. | See Pricing. |
INTERNAL_ERROR | The profile could not be processed right now. | Retry later. |
Important, non-error outcomes:
- Zero new items with
postedAfterset — a successful check. The profile is counted as checked, no rows are written, and no result fee applies. - Reaching the natural end of a feed — a successful result, even when fewer items than
resultsLimitare available. - Items older than your cutoff — filtered out by design; the summary reports how many were seen and how many were filtered.
💰 Pricing
This Actor uses pay-per-event pricing, so what you pay follows what the run actually delivered.
Result pricing (per 1,000 delivered posts or Reels)
| Plan | content-result |
|---|---|
| Free | $1.10 |
| Bronze | $1.00 |
| Silver | $0.80 |
| Gold | $0.60 |
That is $0.0011 per delivered item on the Free plan, down to $0.0006 on Gold. See the Actor's Pricing tab for the rates that apply to your account.
Date-filter processing fee
When you set postedAfter, every item the filter examines and removes is charged at $0.60 per 1,000 filtered items ($0.0006 per item) — the same on every plan. Content older than your cutoff counts, and so does content with no usable timestamp: both were fetched and judged on your behalf. A run where everything returned is new pays no filter fee at all.
Actor start fee
$0.001 per run, whatever the batch size.
How the billing works:
- One result fee per unique post or Reel actually delivered. The unit is the row written to your Dataset — never a page, a request, or an attempt.
resultsLimitis the main lever over this fee: lower it and you collect — and pay for — less. - One filter fee per item the date filter removed. Charged only when
postedAfteris set, because that is the only mode in which items are filtered by date. A monitoring run that examined 100 old items and found 3 new ones pays 3 result fees plus 100 filter fees — roughly half of what delivering all 103 items would have cost. - One start fee per run, independent of how many profiles the run handles.
What you are never charged for:
- Invalid input rejected before processing. A value that is not a usable profile produces no fee.
- Duplicates. A profile listed twice — or listed once as a username and once as its numeric ID — is checked once and charged once.
- Retries and recoveries. A temporary failure that is retried, or a profile resolved through a second attempt, does not create another fee for the same profile or the same item.
- Items removed by content type. A Reel skipped in Posts-only mode is not date-filtered, so it never creates a filter fee.
- Items beyond your limit. Anything past
resultsLimitis never fetched, filtered or charged. - Pagination. How many pages a profile needed is never billed directly.
Cost example (Free-plan rates): a daily monitoring run over 5 profiles starts at $0.001. On a day where the run examined 300 items, removed 288 older ones and delivered 12 new posts, the total is 12 × $0.0011 + 288 × $0.0006 + $0.001 ≈ $0.19. On a quiet day where the same run examines 60 items and delivers nothing, it is 60 × $0.0006 + $0.001 ≈ $0.04. A plain archive run with no postedAfter — 100 items from one profile — is 100 × $0.0011 + $0.001 ≈ $0.11 with no filter fee.
Free-plan users
Free-plan users are subject to the current free usage allowance configured for this Actor. Paid users are not subject to that allowance. When a run reaches the allowance, the remaining profiles continue to be processed and the ones that were not served are reported explicitly in RUN_SUMMARY.
API usage
Run the Actor programmatically and retrieve the default Dataset when it finishes. Replace the sample profiles with your own.
JavaScript
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('lance_api/instagram-profile-posts-reels-scraper').call({profiles: ['nasa', '@natgeo'],contentType: 'all',resultsLimit: 100,postedAfter: '2026-10-11T18:30:00Z',});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items);
Python
from apify_client import ApifyClientclient = ApifyClient(token=os.environ['APIFY_TOKEN'])run = client.actor('lance_api/instagram-profile-posts-reels-scraper').call(run_input={'profiles': ['nasa', '@natgeo'],'contentType': 'all','resultsLimit': 100,'postedAfter': '2026-10-11T18:30:00Z',})for item in client.dataset(run['defaultDatasetId']).iterate_items():print(item)
cURL
curl -X POST \-H "Authorization: Bearer $APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"profiles": ["nasa", "@natgeo"],"contentType": "all","resultsLimit": 100,"postedAfter": "2026-10-11T18:30:00Z"}' \"https://api.apify.com/v2/acts/lance_api~instagram-profile-posts-reels-scraper/runs?waitForFinish=120"
Fetch the default Dataset of the finished run:
$curl "https://api.apify.com/v2/datasets/{datasetId}/items?format=json"
Use cases
- New-content monitoring. Schedule the Actor daily with
postedAfterset to your last checkpoint and receive only what was published since — without re-collecting and re-paying for the same posts on every run. - Competitor content tracking. Collect the recent posts and Reels of competitor profiles into your own store and compare posting volume, formats and captions over time.
- Creator content review at scale. Pull the recent output of a shortlist of creators and review formats, cadence and captions in a spreadsheet before outreach.
- Reels research. Run with
contentType: reelsto study a profile's Reel output specifically — views, duration and music — rather than a mixed feed. - Content archiving. Use
resultsLimitand the Downloads view to capture posts, images and video URLs for an internal archive, downloading media before the URLs expire. - Feeding analytics pipelines. Every row carries stable IDs, timestamps and engagement counts, so the Dataset drops straight into a database, dashboard or BI tool.
- UGC and mention sourcing. Use captions, hashtags, mentions and location fields to find brand-relevant public content for outreach or research.
How it works
- You submit profiles in any supported form.
- The Actor validates, normalizes and de-duplicates them, so the same profile is processed once.
- For each profile it collects the requested content type, normalizing every item into a stable row shape.
- When
postedAfteris set, items are filtered page by page and pagination stops once the feed has moved past your cutoff. - Unique items are written to the Dataset, and the run summary reports totals, checks, filtered counts and failures.
Related Actors
| If you need to… | Recommended Actor |
|---|---|
| Evaluate a creator's followers, engagement rate and posting cadence | Instagram Creator & Engagement Scraper |
| Look up profile metadata, bio and public contact details | Instagram Profile Scraper API |
| Collect comments and replies from posts and Reels | Instagram Comments Scraper API |
| Find who liked a post or Reel | Instagram Likers Scraper API |
| Download Reels and videos as files | Instagram Reels Downloader – Fast Batch API |
| Collect content by hashtag | Instagram Hashtag Scraper API |
Explore more Instagram data Actors from lance_api on Apify.
FAQ
Which profile formats are accepted?
Usernames (nasa), @usernames (@nasa), profile URLs (https://www.instagram.com/nasa/) and numeric Instagram user IDs. All forms of the same profile are recognized and processed once.
Do I need a login or cookie? No. This Actor works with publicly available data only. Never send tokens, cookies or sessions to support.
Is postedAfter inclusive or exclusive?
Exclusive. Only items published strictly after the moment are returned. An item published at exactly 2026-10-11T18:30:00Z is excluded when the cutoff is that same moment.
What date formats does postedAfter accept?
Use Absolute to select a date with the calendar (YYYY-MM-DD, interpreted as midnight UTC), or Relative to choose days, weeks, months, or years before run start, for example 1 days, 2 months, or 3 years. JSON can also supply ISO-8601 date-times with a timezone (2026-10-11T18:30:00Z, 2026-10-11T08:00:00+08:00). All time values are taken in UTC timezone. The relative cutoff is resolved once per run, so scheduled runs automatically move the window forward. Days/weeks use elapsed UTC days; months/years use calendar subtraction, keeping the UTC time of day and clamping to the last valid day when needed (March 31 minus one month becomes February 28 or 29). 0 days means the run start time. Invalid values fail with INVALID_POSTED_AFTER before any profile is processed.
What happens when there are no new posts? The run succeeds. The profile is reported as checked, no content rows are written, and no result fees apply — the only charge is the filter fee for the items the cutoff examined and removed. The profile is not retried against other data sources: an empty result is an answer, not a failure.
Can I get an account's entire post history?
No guarantee. The Actor delivers up to resultsLimit unique items per profile, and how far back the feed can actually be read depends on what the data source returns. Do not market or plan against "complete history".
How are pinned posts handled with postedAfter?
A pinned item is never allowed to trigger the early stop, so a pinned old post at the top of a feed cannot hide newer content below it.
What happens to items with no timestamp when postedAfter is set?
They are not delivered. An item without a usable timestamp cannot be proven to be newer than your cutoff, so it is excluded rather than guessed, and the count is reported in the run summary.
Are duplicate profiles charged twice? No. Repeated references to the same profile — in any supported form — are normalized to one profile, processed once, and charged once.
Are duplicate results possible? The same media ID is delivered once per run, even across pagination and recovery. Two different posts with identical captions remain separate rows.
If one profile fails, does the whole run fail?
No. Each profile is processed independently; failures are reported per profile in RUN_SUMMARY and the rest of the batch continues.
Why are some counts null? The source did not report them. Null means "not reported", which is different from zero — this keeps your analysis honest.
How long do media URLs stay valid? Video and image URLs come from the source and can expire. Download media promptly if you need the files; the metadata rows remain valid.
How do I control cost?
Lower resultsLimit to what you need, use postedAfter for scheduled runs so old items are charged at the lower filter rate instead of the full result rate, and remember that duplicates, type-filtered items, over-limit items and failed profiles never create a result fee.
Limitations
- Public data only. Private, restricted or deleted profiles and content cannot be collected.
- Content availability, counts and media URLs depend on what the source returns at run time and can change without notice.
- History depth is not guaranteed: the Actor delivers up to
resultsLimitunique items per profile, and the readable depth of a feed depends on the source. - Counts such as likes, comments, views and shares are reported only when the source reports them; missing values are
null. - Direct media URLs can expire; download promptly when you need the files.
- The Actor does not log in, does not bypass access controls, and does not guarantee a fixed number of results per profile.
Public data and responsible use
This Actor collects only publicly available data. You are responsible for complying with applicable laws, Instagram's terms of service, and the privacy and copyright expectations that apply to your use case — including how you store, share and republish collected content, and for avoiding spam or automated abuse of the platform.
Support
Open an issue on the Actor page or email lanceapi2026@hotmail.com.
For a bug report, include:
- Apify Run ID
- a non-sensitive Instagram profile you used
- the input fields you set
- the relevant status or error code from
RUN_SUMMARY, if available - what you expected and what actually happened
Do not send:
- Apify Token or
APIFY_TOKEN - Cookie
- Session ID
- Password
- API Key
- Proxy credentials
- Authorization header