Instagram Location Scraper - Posts by Place, $0.40/1k avatar

Instagram Location Scraper - Posts by Place, $0.40/1k

Pricing

Pay per event

Go to Apify Store
Instagram Location Scraper - Posts by Place, $0.40/1k

Instagram Location Scraper - Posts by Place, $0.40/1k

An Instagram place page keeps two separate feeds, Top and Recent, and they do not hold the same posts. Both come back here: captions, likes, comments, media URLs, owner, timestamps and location data. No login, no cookie, no app review. $0.40 per 1,000 posts.

Pricing

Pay per event

Rating

0.0

(0)

Developer

Dami's Studio

Dami's Studio

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

0

Monthly active users

2 days ago

Last modified

Share

⚡ Instagram Location Scraper — Posts by Place

Scrape Instagram posts by location/place at a flat per-post rate. Give it location IDs or /explore/locations/<id>/ URLs and get back the posts tagged at each place — from both the ranked (top) and recent tabs — with caption, likes, comments, media URLs, owner, hashtags, mentions and timestamp. Export to JSON, CSV or Excel, call it from the API, or have every run delivered straight to Notion. $0.40 per 1,000 posts plus a $0.001 run-start fee — built for local lead gen, event coverage, geo-trend tracking and market research.

Runs work out of the box. No login, no cookie, no app review, nothing to supply. The Actor reads Instagram's own public place pages — the same pages a logged-out visitor sees — and the feed query those pages run against themselves. Paste location IDs and press start.

What you can scrape

ModeInputReturns
Location IDlocationIds (e.g. 212988663)Posts tagged at that place — ranked (top) tab and recent tab, deduped
Location URLstartUrls/explore/locations/<id>/Same, ID parsed out of the URL

Both tabs are read on every run, ranked first. A post that appears in both is delivered — and charged — exactly once.

Why this scraper

  • 💸 Flat per-post pricing — $0.40 / 1,000 posts plus $0.001 per run start, billed per post actually returned. No volume tiers, no plan gates, no minimum spend.
  • 🔓 Nothing to supply — no account, no cookie, no token. The Actor reads the public place page directly. Your own cookie, if you paste one, is used on its own instead.
  • 🗺️ Both tabs, one run — ranked and recent, deduped across tabs and pages, so a run for 20 posts gives you the 20 best rather than the 20 newest.
  • 🧹 Clean, flat records — one tidy object per post, ready for sheets, dashboards or LLM pipelines. No nested GraphQL soup to unpack. Rows come back in the same shape whichever lane served them.
  • 📬 Built-in Notion delivery — push every run into Notion via Apify connectors, zero extra code.
  • 🚦 Honest errors — a clear diagnostic row (NO_RESULTS, RATE_LIMITED, NOT_FOUND, PARTIAL, BLOCKED) instead of silent emptiness, and those rows are never charged.

How to run it

  1. Open the place on Instagram. The URL is /explore/locations/<id>/ — the number is the location ID.
  2. Paste the IDs into locationIds, or paste the whole URLs into startUrls.
  3. Set resultsLimit (default 50).
  4. Press Start. Nothing else is required.

Input

  • locationIds — Instagram location/place IDs (the number in /explore/locations/<id>/).
  • startUrls — or paste full /explore/locations/<id>/ URLs instead. Both lists can be used in the same run.
  • resultsLimit — max posts for the whole run (default 50, up to 2000), counted across every location rather than per location. You pay per post.
  • sessionCookies (optional) — normally left empty. Paste your own throwaway-account sessionid cookie(s), one per line, to run on your own account's rate limit instead of reading the public page. It does not increase how many posts a place returns. See Coverage and limits.
  • proxyConfiguration — preconfigured; leave as is unless you have your own proxy servers. Your own proxyUrls are used exactly as supplied; metered Apify groups (RESIDENTIAL) are not offered and are replaced with rotating datacenter addresses, with a warning in the log.
  • notionConnector / notionParentId — optional Notion delivery.

Field names are the ordinary Apify ones and mean what they normally mean, so a startUrls / resultsLimit / proxyConfiguration block you already have in a script runs here unchanged.

Example input

{
"locationIds": ["212988663", "213385402"],
"startUrls": ["https://www.instagram.com/explore/locations/214335386/"],
"resultsLimit": 200,
"sessionCookies": []
}

Output

One record per post. Fields:

FieldTypeWhat it is
okbooleantrue on a data row. Diagnostic rows carry ok: false and an errorCode.
typestringimage, video or carousel
shortCodestringInstagram post shortcode
urlstringCanonical instagram.com/p/<shortCode>/ link
captionstringCaption text, empty string when the post has none
hashtagsstring[]Hashtags parsed out of the caption, without the #
mentionsstring[]@-handles parsed out of the caption, without the @
likesCountnumber | nullLike count, null when hidden or not carried
commentsCountnumber | nullComment count
videoViewCountnumber | nullPlays/views for videos, null for photos
displayUrlstring | nullSigned CDN URL of the main image/thumbnail
videoUrlstring | nullSigned CDN URL of the video file, null for photos
imagesstring[]Every frame — one entry for a single post, every slide for a carousel
isVideobooleanConvenience flag
dimensionsobject | null{ height, width } of the original media
ownerUsernamestring | nullHandle that posted it
ownerIdstring | nullNumeric account ID
timestampstring | nullISO 8601 UTC posting time
accessibilityCaptionstring | nullInstagram's auto-generated alt text, when present
locationIdstringThe place ID this row came from

Sample output

A single dataset row from a real run. Values below are illustrative — the shape and field names are exactly what the Actor emits.

{
"ok": true,
"type": "image",
"shortCode": "C8kR2mLpQ7d",
"url": "https://www.instagram.com/p/C8kR2mLpQ7d/",
"caption": "Golden hour over the harbor 🌇 #travel #instagood",
"hashtags": ["travel", "instagood"],
"mentions": [],
"likesCount": 18423,
"commentsCount": 204,
"videoViewCount": null,
"displayUrl": "https://scontent.cdninstagram.com/v/t51.2885-15/453212789_1080x1080_n.jpg?stp=dst-jpg&_nc_ht=scontent.cdninstagram.com&oe=66A1B2C3",
"videoUrl": null,
"images": [
"https://scontent.cdninstagram.com/v/t51.2885-15/453212789_1080x1080_n.jpg?stp=dst-jpg&_nc_ht=scontent.cdninstagram.com&oe=66A1B2C3"
],
"isVideo": false,
"dimensions": { "height": 1080, "width": 1080 },
"ownerUsername": "harbor.light.studio",
"ownerId": "1789456123",
"timestamp": "2026-06-21T07:14:02.000Z",
"accessibilityCaption": "Photo by Harbor Light Studio on June 21, 2026.",
"locationId": "212988663"
}

Video posts fill videoUrl and videoViewCount and set isVideo: true; carousels set type: "carousel" and list every slide in images. Every record carries the locationId it came from, so a multi-location run stays easy to group.

Coverage and limits

Everything here is a real constraint of the surface, not a paywall. Read it before you scale a run up.

  • A place holds far fewer posts than the 2,000 ceiling. What a run can return is what the place's public Top and Recent tabs actually carry, which is a shallow window, not the whole history. Measured 2026-08-21 across three places on three continents, a full run returned 69, 73 and 75 posts — so plan on roughly 70 per place, not thousands. Asking for more is harmless: the run delivers everything available and stops, and you are only charged for what arrived.
  • The window is Instagram's, not ours. Both tabs are read and deduped, and the feed simply stops handing out more once that window is exhausted — no ceiling of ours is doing it.
  • Depth is capped by Instagram, not by us. Place feeds paginate a limited way back; asking for 2,000 posts at a small, quiet place will simply return everything available and stop, which is usually far fewer.
  • resultsLimit is a whole-run total, not a per-location quota. Locations are filled in order, so a run with a low limit and many locations may exhaust the budget on the first place. Run one location per job if you need even coverage.
  • Numeric place IDs only. The Actor does not search for a place by name — you supply the ID or the /explore/locations/<id>/ URL. There is no "find all cafés in Berlin" mode.
  • Fields the source does not carry come back null, never invented. likesCount is null when Instagram hides it for that post or viewer; videoViewCount is null on photos; dimensions and accessibilityCaption are null when the underlying record has no such value. Comment counts are the most consistently present engagement number.
  • Media URLs expire. displayUrl, videoUrl and images are signed CDN links that stop working after a while — download what you need soon after the run.
  • Private accounts are never included, and posts can disappear between the run and when you read the data.
  • No comment text and no follower counts. This Actor returns the place feed's post objects only. Per-post comment threads and profile stats are separate Actors — see the table at the end.

You never need one, and it is not the way to get more posts — the depth limit above is Instagram's feed window, which a cookie does not widen. Supply your own sessionid in sessionCookies only if you specifically want the run to go out on your account's rate limit instead of reading the public page.

{
"locationIds": ["212988663"],
"sessionCookies": ["sessionid=12345%3AAbCdEf%3A12%3AAbCd..."]
}
  • In Chrome: F12 → Application → Cookies → https://www.instagram.com → copy the sessionid value. Pasting the whole cookie string works too. One per line; several are rotated.
  • Your cookie takes priority over everything else. When you supply one the run uses it and nothing else — that is the point.
  • Use a throwaway account you own and are authorized to automate. Never one you care about. Automated reading can get an account rate-limited or checkpointed.
  • Cookie lifetime is not under anyone's control. Instagram invalidates sessions on its own schedule, especially for accounts that suddenly read a lot. Expect to refresh yours periodically, and paste several so one death does not stall a run.
  • If your cookie turns out to be dead, or gets checkpointed mid-run, the run does not come back empty: it falls back to the public place page and still delivers posts.

Cookie values, proxy URLs and request headers are never logged or written to output. Logs carry only slot numbers, counts, reasons and totals.

What people use it for

  • Local lead generation — pull the accounts posting from a venue, neighbourhood or trade-show hall, then filter by engagement signals (likes/comments) to find the people worth contacting.
  • Event coverage — scrape the venue's location page during and after an event to collect the user-generated content, photographers and hashtags around it.
  • Geo trend tracking — run the same set of locationIds on a schedule and watch which places, captions and hashtags are climbing.
  • Hospitality & retail research — see what customers actually post at a restaurant, hotel or store, not what the brand posts about itself.
  • Content sourcing — collect media URLs and owner handles at a place so you can ask permission to repost.

Pricing

$0.40 per 1,000 posts ($0.0004 each), plus $0.001 each time a run starts.

Flat rate — no volume tiers, no plan gates — and you are only charged for posts actually returned. Sample rows, diagnostic rows, duplicates and no-result runs cost only the start fee.

Worked examples

RunPosts returnedCost
One location, default limit50$0.021
Five locations, resultsLimit 1,000 (each place returns what it has)~350~$0.141
Diagnostic row only (no results)0$0.001

Billing

Two charges apply: a $0.001 run-start fee each time a run begins, and a $0.0004 per-post fee for posts actually delivered, charged once per unique post — including posts that appear in both the ranked and recent tabs. Samples, diagnostics, duplicates, blocked look-ups and no-result runs never incur the per-post fee; a run that returns nothing costs only the $0.001 start fee.

Running it on a schedule or from code

The Actor is a normal Apify Actor, so anything the platform offers works: Schedules for a daily geo sweep, Webhooks to fire when a run finishes, and the API to start runs from your own backend.

curl -X POST "https://api.apify.com/v2/acts/Dami_Studio~instagram-location-scraper/runs?token=YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"locationIds":["212988663"],"resultsLimit":100}'

Results land in the run's dataset and can be pulled as JSON, CSV, Excel, XML or RSS from the dataset items endpoint, or pushed straight into Notion with the optional connector inputs.

FAQ

Is it legal to scrape Instagram? We can't give legal advice, so here is exactly what the Actor does and does not do. It reads posts that are publicly tagged at a public place — the same posts anyone can see on Instagram's own location pages. It does not break, bypass or brute-force any login, it does not read private accounts, and it does not collect anything behind a follow request. Public-data scraping is broadly treated as lawful in the US and EU, but that is the general position, not a ruling about your use. The records include personal data (handles, captions, media), so you are the controller of what you collect: your use has to comply with GDPR/CCPA and with Instagram's own terms, including how long you keep it, what you do with it, and honouring deletion requests. If you plan to use the output for outreach, profiling or anything commercial, check with your own counsel first.

Do I need an Instagram account or a cookie? No. The Actor reads Instagram's public place pages, so runs work with sessionCookies empty. A cookie is purely optional.

Then what does supplying my own cookie buy me? One thing: a rate limit that is entirely yours. It does not unlock any extra tab or field — both the ranked and recent tabs are read either way — and it does not make a place return more posts, because the depth limit is the feed window Instagram serves, not a limit of ours.

How much does it cost, and what am I charged for? $0.001 when a run starts, plus $0.0004 per post actually delivered to the dataset — charged once per unique post, even if that post appears in both tabs. Diagnostic rows, sample rows, duplicates and empty runs are never charged the per-post fee, so a run that finds nothing costs $0.001.

What happens if it returns nothing? You never get a silent empty dataset. The Actor writes one uncharged diagnostic row explaining why: NO_RESULTS means the place genuinely had nothing to return (very new, very quiet or restricted places do this), NOT_FOUND means the ID does not resolve to a place, RATE_LIMITED / BLOCKED mean the request was refused, and PARTIAL means the run stopped early — so what you have is complete and correct as far as it goes. All of those rows are free.

How do I find a location ID? Open the place on Instagram; the URL is /explore/locations/<id>/, and that <id> is what goes in locationIds. You can also paste the whole URL into startUrls and let the Actor parse it.

Can I search for a place by name instead? No. This Actor takes IDs and /explore/locations/ URLs only — there is no place-name lookup or geo-radius search. Find the place on Instagram once and reuse its ID.

Can I pass several locations at once? Yes — locationIds and startUrls are both lists and can be combined in one run. Remember resultsLimit is the total for the run, not a per-location quota, so locations fill in order. Run one location per job if you need even coverage across many places.

Does it return the top/ranked tab or the recent tab? Both, on every run, with no cookie required. Ranked is read first (higher engagement, so a small resultsLimit gives you the best posts rather than the newest), then recent. Results are deduped across both tabs and across pages.

Why is likesCount — or another field — null? Because the underlying record does not carry it, and the Actor will not invent a value. Instagram hides like counts on some posts and for some viewers; videoViewCount is always null for photos; dimensions and accessibilityCaption are absent on some records. commentsCount is the most consistently populated engagement figure.

Do I need a proxy? No — proxying is preconfigured with rotating datacenter addresses. If you enter your own proxy servers they are used exactly as given. Metered residential groups are not offered and are quietly swapped for datacenter, with a note in the log.

Can I get the comment text, or the follower counts of the people posting? Not from this Actor — it returns the place feed's post objects only. Comment threads and profile stats are separate jobs with their own Actors; see the table below.

Need other Instagram data?

This actor is built for one surface: posts tagged at a physical place, from both the ranked and recent tabs. If you need a different Instagram surface, use the dedicated actor for it:

ActorWhat it returnsPrice
Instagram Hashtag ScraperPosts under any hashtag, top feed$0.40 / 1,000
Instagram Mentions ScraperPosts that tagged or mentioned an account$0.44 / 1,000
Instagram Comments ScraperComments on a specific post or reel$0.40 / 1,000
Instagram Reels SearchReels found by keyword or hashtag search$0.40 / 1,000
Instagram ScraperProfiles, posts and reels from usernames or URLs — the general-purpose one$0.45 / 1,000
Instagram Profile ScraperProfile stats and account flags, full payload$0.70 / 1,000
Instagram Profile ScraperProfile stats and account flags, lean payload$0.55 / 1,000
Instagram Post ScraperA single post by URL, full payload$0.18 / 1,000
Instagram Post ScraperA single post by URL, lean payload$0.09 / 1,000
Instagram Reel ScraperA single reel by URL, full payload$0.35 / 1,000
Instagram Reel ScraperA single reel by URL, lean payload$0.18 / 1,000
Instagram Stories ScraperStories a public account has live right now$0.40 / 1,000
Instagram Followers & Following ScraperFollower and following lists from a public profile$0.60 / 1,000
Instagram Transcript ScraperSpoken-word transcript of a reel or video$2.50 / 1,000