Skip to main content
instagram collects the posts and reels of public Instagram accounts. One result is one post. For profile details only, use instagram_profile.

Run it

Inputs

Send usernames, directUrls, or both, in config. Both are collected, and an account given in both, in any case, is collected and billed once.
Only profiles are supported. hashtags are refused with 400: hashtag search isn’t supported. So is a link or name that points at a post, reel, story or other page (/p/, /reel/, /reels/, /tv/, /stories/, /explore/, /accounts/, /direct/), with a 400 naming the field: send post links to Instagram comments for their comments. limit: 0 is how to collect every post. Field names are case-sensitive. An unknown field is ignored rather than refused, and named in a warning in the job’s log.
These inputs are accepted for compatibility but have no effect. A value other than the one shown adds a warning to the job’s log:
  • include_reels (true): reels always come back.
  • include_igtv (false) and include_stories (false): IGTV videos and stories are not collected.
  • resultsType ("posts").
  • addParentData, enhanceUserSearchWithFacebookPage and isUserTaggedFeedURL (false).
A date filter within the last 29 days makes the job a recent crawl, which is billed at its own rate; see Billing.

Output

The job returns one profile_info row per profile, then one row per post. Both are billed as results. Real rows from a job for @nasa:
The profile_info row above is shortened to its documented fields. It also echoes the job’s settings, such as maxPosts and onlyPostsNewerThan.

Post fields

Media links point at Instagram’s servers and expire after a while; download what you need to keep.

Profile summary row (type: "profile_info")

Other rows

A profile we can’t resolve returns one profile_error row at $0. /results/ total includes it and the other status rows, so it can exceed items_scraped, which counts billed rows. Page with total.

What logged-out means here

You get: posts and reels from public accounts, with captions, counts and media links as a signed-out visitor sees them, and author fields on every post. Not available:
  • Private accounts.
  • Accounts Instagram age-restricts for signed-out visitors.
  • Accounts Instagram shows only after login.
  • Anything only a signed-in user can see.
restriction says which of these stopped a profile: private, age_gate, login_wall or unavailable. restrictionReason explains it in words. Re-running the job does not change private or age_gate. A “Blocked by Instagram” row carries restriction: "unknown": none of these applied, and a later run may succeed.

Completeness

A completed job reports what happened to each profile in scraper_metadata (Jobs and results): A profile is in restrictions or in targets_failed, never both. If you collect every post of a profile (no date filter) and a profile returns nothing after every attempt, the whole job fails.

Profile details

instagram_profile returns one row per account with its details and up to 12 recent posts. It takes the same usernames and directUrls, and bills one result per profile row it returns. A handle sent twice, in any case, is collected and billed once.
A real row, for @nasa:
Lists are shortened in this example: latestPosts holds up to 12 posts and relatedProfiles up to 50 accounts. A profile that can’t be found returns {"error": "Page not found", "username", "url", "scrapedAt"}; a private one returns the same shape with "error": "Restricted profile". Neither costs anything. A private profile is also listed in scraper_metadata.restrictions with restriction: "private".