# Instagram Post Scraper - Tagged Places & Coordinates (`scrapio/instagram-post-scraper`) Actor

Instagram Post Scraper extracts posts with tagged places and coordinates, including captions, usernames, timestamps, engagement metrics, media URLs, location names, and geographic data. Ideal for location-based research, audience insights, competitor analysis, and social media intelligence.

- **URL**: https://apify.com/scrapio/instagram-post-scraper.md
- **Developed by:** [Scrapio](https://apify.com/scrapio) (community)
- **Categories:** Social media, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 75.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.99 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 Post Scraper — Tagged Places, Coordinates & Geotags

Instagram Post Scraper - Tagged Places & Coordinates pulls every post from a public profile — or a single post, reel or tv URL — and returns it as typed JSON alongside the one thing most Instagram scrapers throw away: the **tagged place**. Every geotagged row carries the venue name, its category, its street address and city where Instagram has them, the **latitude and longitude Instagram stamped on the post itself**, and a second, independently looked-up coordinate from Instagram's own place record — so a creator's grid turns directly into a map, a route or a destination shortlist. Unlike scraping raw HTML or a headless browser, this Actor returns ready-to-use JSON, straight into your database, spreadsheet or LLM pipeline. This guide covers every input and output field, how the place data is resolved, and how teams deploy it inside real mapping and monitoring pipelines.

***

### 🧭 What Does Instagram Post Scraper - Tagged Places & Coordinates Do?

Instagram Post Scraper - Tagged Places & Coordinates scrapes public Instagram profiles and individual post, reel and tv URLs, returning the complete post payload — caption, hashtags, mentions, likes, comments, media, carousel children — merged with a flat block of geotag columns and, optionally, Instagram's own record for that place. No Instagram account, login or cookie is required: every request runs logged out over plain HTTP against Instagram's public web endpoints.

- Scrapes posts from usernames, `@handles`, profile URLs, or single `/p/`, `/reel/`, `/tv/` URLs
- Extracts the tagged place on every geotagged post: place id, name, short name, Facebook Places id, latitude, longitude, address and city
- Looks up Instagram's own place record per distinct place id — category, total post count at that place, slug and canonical coordinates
- Returns **two independent coordinate readings** per place instead of collapsing them into one
- Filters by geotag presence, place-name keyword, or a latitude/longitude bounding box
- Writes a second `places` dataset — one deduplicated row per venue, with how many posts in the run happened there
- Runs entirely through Apify Proxy; no Instagram credentials are collected or required

***

### ⚡ Features & Capabilities

Instagram Post Scraper - Tagged Places & Coordinates covers three things at once: the full post record, the place tagged on it, and how that place compares to Instagram's own canonical record for the same venue.

#### Core features

- **Three input shapes accepted in one list**: bare usernames, full profile URLs, and single post/reel/tv URLs, all in the same `username` array
- **Flat, always-present place columns** on every row — `hasLocation`, `locationId`, `locationName`, `locationShortName`, `locationCategory`, `locationLat`, `locationLng`, `locationAddress`, `locationCity`, `locationFacebookPlacesId`, `locationUrl` — `null` when the post has no tag or the record failed, never silently omitted
- **Dual coordinate reporting**: `locationLat`/`locationLng` (the coordinate stamped on that specific post) alongside `locationPlaceLat`/`locationPlaceLng` (the place record's canonical centre), plus `locationCoordsAgree` and `locationCoordsDeltaDeg` so you can see when they diverge instead of picking one silently
- **Three geo filters**, all optional and off by default: `onlyPostsWithLocation` (geotagged posts only), `locationKeywords` (substring match on name/address/city/category), `geoBoundingBox` (lat/lng box)
- **A dedicated `places` dataset** — `emitPlacesDataset` writes one deduplicated row per venue with `postsInThisRun`, useful for anything that wants places rather than posts
- **Two detail levels** — `basicData` for post and place data only, `detailedData` which adds alt text, the latest comments, music info and paid-partnership status
- **Full base post payload preserved** — `caption`, `hashtags`, `mentions`, `likesCount`, `commentsCount`, `images`, `videoUrl`, `childPosts`, `ownerUsername` and more, unchanged alongside the geotag block

#### How Instagram Post Scraper - Tagged Places & Coordinates compares to other Instagram scrapers

Two of the top-ranked Instagram "tagged" scrapers on the Apify Store use "tagged" to mean posts where a *person* is tagged or mentioned — not posts with a tagged *place*. Checked against their Apify Store listings on 2026-07-08:

| Feature | This Actor | powerful\_bachelor Instagram Tagged & Mentions Posts Scraper | easyapi Instagram Tagged Posts Scraper |
| --- | --- | --- | --- |
| What "tagged" means | Tagged **place** (geotag) | Tagged/mentioned **person** | Tagged **person** |
| Place name, category, address, city | ✅ | ❌ not documented | ❌ not documented |
| Post-level latitude/longitude | ✅ | ❌ not documented | ❌ not documented |
| Instagram's own place record lookup | ✅ | ❌ not documented | ❌ not documented |
| Keyword / bounding-box place filtering | ✅ | ❌ not documented | ❌ not documented |
| Dedicated per-place rollup dataset | ✅ | ❌ not documented | ❌ not documented |
| Output format | Typed JSON via Apify dataset | Typed JSON via Apify dataset | Typed JSON via Apify dataset |
| Input | Usernames, profile URLs, single post URLs | Usernames | Usernames |

If your use case is feeding structured location data to a mapping tool or an LLM, the place and coordinate fields are the decision-maker — neither competitor listing documents a location field at all, so their output has nothing to plot on a map.

#### When another tool might suit you better

If what you actually need is posts where a specific account was tagged or mentioned by *other* users — brand-mention tracking, sponsor tagging, "who's talking about us" monitoring — powerful\_bachelor's and easyapi's tagged-posts scrapers are built for exactly that and this Actor is not: it reads a creator's own feed and the geotag on it, not the network of accounts tagging them. Pick this Actor when the location a post was taken at matters more than who else appears in it.

#### Instagram Post Scraper - Tagged Places & Coordinates within the Scrapio data stack

This Actor covers posts and their tagged places. For reels instead of feed posts, with the same place and coordinate handling, use [Instagram Profile Reels Scraper - Tagged Places & Coordinates](https://apify.com/Scrapio/instagram-profile-reels-scraper-tagged-places-coordinates). For an account's follower or following graph rather than its posts, Scrapio also runs dedicated followers and following scrapers with lead-enrichment fields (see Related scrapers below).

***

### Why do developers and data teams scrape Instagram?

#### 🏢 Travel, hospitality and destination marketing

A destination marketing organization or hotel group runs a list of creator usernames through `resultsLimit` and `onlyPostsWithLocation: true`, gets back only the posts geotagged somewhere, and reads `locationName`, `locationCategory` and `locationPostCount` per venue. That turns a creator's grid into a ranked list of places worth a partnership, without opening a single post manually. `locationLat`/`locationLng` feed straight into a map for a visual pitch deck.

#### 📊 AI training data and RAG indexing

`caption`, `hashtags`, `locationName` and `locationCategory` are the high-information text fields here — free-form, human-written, and tied to a real coordinate. For RAG enrichment, indexing `caption` alongside `locationName`/`locationCity` lets a retrieval system answer "what have people posted about at this venue" with grounded, geotagged text. For training data, every field returns as a typed primitive (string, number, boolean) with a stable schema across runs, so no HTML parsing or type coercion sits between the scrape and the model.

#### 📱 Competitive and market intelligence

Track a competing venue's own Instagram tags or a rival brand's creator partnerships by watching `locationPostCount` (Instagram's own running total of posts made at a place) move over successive scheduled runs, and cross-reference `ownerUsername` to see which creators keep returning to a competitor's location.

#### 🔬 Research and academic use

Public geotagged social posts are a real, if creator-biased, dataset for tourism-flow and destination-popularity research. Everything returned is data Instagram already serves to a logged-out browser — no private-account content, no authentication bypass, no scope beyond what a visitor to `instagram.com` can already see.

#### 🎥 Product and SaaS development

Mapping and travel-planning products can build a "where creators actually go" layer on top of `locationLat`/`locationLng`/`locationCategory`, or a venue-monitoring feature using the `places` dataset's `postsInThisRun` as a per-run activity signal — the deduplicated place rows are built for exactly this without extra client-side aggregation.

***

### 🍚 Input Parameters

All 13 parameters, read directly from the Actor's input schema. Only `username` is required.

| Parameter | Required | Type | Description | Example Value |
| --- | --- | --- | --- | --- |
| `username` | Yes | array | Usernames (`dametraveler`), profile URLs (`https://www.instagram.com/dametraveler/`) or single post/reel/tv URLs (`https://www.instagram.com/p/SHORTCODE/`). Travel, hospitality and destination accounts carry the most geotags; news and tech accounts often carry none. | `["dametraveler", "https://www.instagram.com/beautifuldestinations/"]` |
| `resultsLimit` | No | integer (min 1, max 100000, default `10`) | How many post rows to save per profile. This is the number of rows returned, not the number of posts scanned — see `maxPostsToScan` when a place filter is on. Ignored for single post URLs. | `50` |
| `onlyPostsNewerThan` | No | string, datepicker (default `null`) | Cutoff in UTC. Absolute tab takes `YYYY-MM-DD` or a full ISO timestamp; Relative tab takes `'6 hours'`, `'3 days'`, `'1 week'`, `'2 months'`, `'3 years'`. An unreadable value stops the run rather than silently disabling the filter. | `"3 months"` |
| `skipPinnedPosts` | No | boolean (default `false`) | Skips posts the creator pinned to the top of their profile. Pin status is checked against the profile being scraped, so a collaboration post pinned on the co-author's grid is kept. | `false` |
| `dataDetailLevel` | No | string, enum `detailedData` / `basicData` (default `"detailedData"`) | `detailedData` adds alt text, the latest comments, music info and paid-partnership status by loading each post page — roughly one extra request per post. Both levels charge exactly one result row each; detailed is slower, not more expensive. Place and coordinate fields return on both. | `"detailedData"` |
| `onlyPostsWithLocation` | No | boolean (default `false`) | Keeps only posts that carry a tagged place. Off by default because geotagging is entirely up to the creator — turning it on for a non-travel account can legitimately return zero rows. | `true` |
| `locationKeywords` | No | array (default `[]`) | Case-insensitive substring match against the place name, short name, slug, address, city and category. Posts with no tagged place never match. | `["Paris", "Bali", "Hotel"]` |
| `geoBoundingBox` | No | string (default `""`) | Four comma-separated numbers, `'minLat,minLng,maxLat,maxLng'`. Tested only against coordinates Instagram returned — never a geocoded guess. Posts with a place but no coordinates are excluded and counted separately in the run log. | `"40.47,-74.30,40.95,-73.68"` |
| `maxPostsToScan` | No | integer (min 1, max 100000, default `300`) | Upper bound on posts read per profile while searching for rows that pass the filters. Only matters with a place filter on — the run keeps paging until it has enough rows or hits this ceiling. | `300` |
| `enrichPlaceDetails` | No | boolean (default `true`) | Looks up Instagram's place record for each distinct geotagged place — category, total post count, slug, canonical coordinates, address/city backfill. One request per distinct place, cached for the whole run. Turn off for the fastest possible run. | `true` |
| `maxPlaceLookups` | No | integer (min 0, max 100000, default `0`) | Hard ceiling on distinct place lookups. `0` means no cap. Rows past the cap still carry every field the post itself provides and are marked `locationPlaceRecordStatus: "recordUnavailable"`. | `0` |
| `emitPlacesDataset` | No | boolean (default `true`) | Writes a second dataset named `places` with one deduplicated row per distinct place plus `postsInThisRun`. These rows are reference data, not charged as results. | `true` |
| `proxyConfiguration` | No | object, proxy editor (default `{"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}`) | Apify Proxy configuration. Instagram's post feed was reachable through Apify RESIDENTIAL, through the BUYPROXIES94952 datacenter group, and from an unproxied IP, all checked on 2026-07-25 — residential is a reliability preference here, not a hard requirement. | `{"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}` |

```json
{
  "username": ["dametraveler", "https://www.instagram.com/beautifuldestinations/"],
  "resultsLimit": 50,
  "onlyPostsNewerThan": "3 months",
  "skipPinnedPosts": false,
  "dataDetailLevel": "detailedData",
  "onlyPostsWithLocation": true,
  "locationKeywords": [],
  "geoBoundingBox": "40.47,-74.30,40.95,-73.68",
  "maxPostsToScan": 300,
  "enrichPlaceDetails": true,
  "maxPlaceLookups": 0,
  "emitPlacesDataset": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"]
  }
}
```

#### Supported URL types and input formats

The `username` array accepts three distinct input shapes, resolved by pattern in the Actor's input parser:

1. **Bare usernames or `@handles`** — `dametraveler` or `@dametraveler` — matched against `^[A-Za-z0-9_.]+$` and expanded to a profile URL internally.
2. **Profile URLs** — `https://www.instagram.com/dametraveler/` — the first path segment becomes the username. URLs whose first path segment is one of Instagram's own navigation pages (`/stories/`, `/explore/`, `/accounts/`, `/directory/`, `/about/`, `/developer/`, `/legal/`, `/share/`, `/reels/`) are recognized and rejected with a log warning rather than being misread as a username.
3. **Single post, reel or tv URLs** — `https://www.instagram.com/p/SHORTCODE/`, `/reel/SHORTCODE/`, `/tv/SHORTCODE/` — scrapes that one post only. `resultsLimit` and `maxPostsToScan` do not apply to these; the run returns exactly one row (zero if the geo filters exclude it).

Duplicate targets (same username or shortcode, however written) are de-duplicated automatically and logged.

***

### 📦 Output Format

Every run writes to the Apify dataset as typed, normalized JSON — one object per row, consistent keys across every run, ready to export as JSON, JSONL, CSV, Excel, HTML or XML from the Apify Console, or to pull with the API/SDK. The default dataset view surfaces 23 of the row's fields; two additional named views (`postDetail`, `videoAndDetailed`) surface the rest. The full row carries more keys than any single view shows — every key is documented below.

#### Output for posts (default dataset)

```json
{
  "inputUrl": "https://www.instagram.com/dametraveler/",
  "id": "3452871122345678901",
  "type": "Video",
  "shortCode": "C9x7YtRugQ2",
  "caption": "Sunrise over the Sahara dunes 🐫✨ @merzougadesertcamp",
  "hashtags": ["Sahara", "Morocco", "TravelGram"],
  "mentions": ["merzougadesertcamp"],
  "url": "https://www.instagram.com/p/C9x7YtRugQ2/",
  "commentsCount": 214,
  "dimensionsHeight": 1920,
  "dimensionsWidth": 1080,
  "displayUrl": "https://scontent.cdninstagram.com/v/t51.29350/sample_1080x1920.jpg",
  "images": [
    "https://scontent.cdninstagram.com/v/t51.29350/sample_1080x1920.jpg",
    "https://scontent.cdninstagram.com/v/t51.29350/sample_720x1280.jpg"
  ],
  "likesCount": 48213,
  "timestamp": "2026-06-14T05:12:33.000Z",
  "childPosts": [],
  "ownerFullName": "Dame Traveler",
  "ownerUsername": "dametraveler",
  "ownerId": "178562034",
  "isCommentsDisabled": false,
  "firstComment": "This is stunning!",
  "latestComments": [
    {
      "id": "18049629001633346",
      "text": "Adding this to my bucket list",
      "ownerUsername": "wanderluster22",
      "ownerProfilePicUrl": "https://scontent.cdninstagram.com/v/t51.2885-19/sample.jpg",
      "timestamp": "2026-06-14T09:41:02.000Z",
      "repliesCount": 0,
      "replies": [],
      "likesCount": 3,
      "owner": {
        "id": "998211044",
        "is_verified": false,
        "profile_pic_url": "https://scontent.cdninstagram.com/v/t51.2885-19/sample.jpg",
        "username": "wanderluster22"
      }
    }
  ],
  "alt": "Camel caravan crossing sand dunes at sunrise",
  "videoUrl": "https://scontent.cdninstagram.com/o1/v/t16/sample_video.mp4",
  "videoViewCount": 612044,
  "videoPlayCount": 612044,
  "videoDuration": 18.5,
  "productType": "clips",
  "taggedUsers": [
    {
      "full_name": "Merzouga Desert Camp",
      "id": "552310099",
      "is_verified": true,
      "profile_pic_url": "https://scontent.cdninstagram.com/v/t51.2885-19/sample.jpg",
      "username": "merzougadesertcamp"
    }
  ],
  "musicInfo": {
    "artist_name": "dametraveler",
    "song_name": "Original audio",
    "uses_original_audio": true,
    "should_mute_audio": false,
    "should_mute_audio_reason": "",
    "audio_id": "778812345"
  },
  "isPaidPartnership": false,
  "hasLocation": true,
  "locationId": "216542178",
  "locationName": "Erg Chebbi, Merzouga",
  "locationShortName": "Erg Chebbi",
  "locationSlug": "erg-chebbi-merzouga",
  "locationCategory": "Natural Feature",
  "locationPostCount": 84213,
  "locationLat": 31.1548,
  "locationLng": -4.0122,
  "locationPlaceLat": 31.155,
  "locationPlaceLng": -4.0119,
  "locationCoordsAgree": false,
  "locationCoordsDeltaDeg": 0.0003,
  "locationAddress": null,
  "locationCity": "Merzouga",
  "locationFacebookPlacesId": "216542178009341",
  "locationUrl": "https://www.instagram.com/explore/locations/216542178/",
  "locationPlaceRecordStatus": "recordResolved"
}
```

`firstComment`, `latestComments`, `alt` and `isPaidPartnership` are only present at `dataDetailLevel: "detailedData"`. `videoUrl`, `videoViewCount` and `videoDuration` appear only on Video posts; `videoPlayCount` only on Video posts at the detailed level. `productType` appears whenever Instagram supplies one (`clips`, `feed`, `carousel_container`) — it is not limited to videos. `taggedUsers`, `musicInfo` and `coauthorProducers` appear only when the post actually has them. Every `location*` field is present on every row regardless of detail level; `locationPlaceRecordStatus` is one of `notGeotagged`, `recordResolved`, `recordUnavailable` or `enrichmentDisabled` and tells you why any place field is `null`.

#### Output for places (the `places` dataset)

When `emitPlacesDataset` is on, a second dataset named `places` gets one deduplicated row per distinct tagged place:

```json
{
  "locationId": "216542178",
  "locationName": "Erg Chebbi, Merzouga",
  "locationShortName": "Erg Chebbi",
  "locationSlug": "erg-chebbi-merzouga",
  "locationCategory": "Natural Feature",
  "locationPostCount": 84213,
  "locationLat": 31.1548,
  "locationLng": -4.0122,
  "locationPlaceLat": 31.155,
  "locationPlaceLng": -4.0119,
  "locationCoordsAgree": false,
  "locationCoordsDeltaDeg": 0.0003,
  "locationAddress": null,
  "locationCity": "Merzouga",
  "locationFacebookPlacesId": "216542178009341",
  "locationUrl": "https://www.instagram.com/explore/locations/216542178/",
  "locationPlaceRecordStatus": "recordResolved",
  "postsInThisRun": 3
}
```

These rows are reference data — every one of them is written directly to the `places` dataset without a charged event, regardless of how many posts fed into it.

#### Schema stability and export options

Field names in both datasets stay fixed regardless of what Instagram changes on its front end — the Actor reads Instagram's internal feed and GraphQL payloads directly, not the rendered page, and the row-building code maps whatever keys Instagram sends onto this fixed schema. If Instagram stops sending a field, the corresponding output key returns `null` rather than disappearing, so downstream consumers never hit a missing-column error. Both datasets export from the Apify Console or API in JSON, JSONL, CSV, Excel, HTML and XML. The default dataset view (`📍 Places & coordinates`) surfaces 23 columns for quick scanning; switch to the `📝 Post content` view for the caption/media fields, or `🎬 Video & detailed-only fields` for video and detailed-only columns — all three views read from the same underlying rows.

***

### 💡 Instagram Post Scraper - Tagged Places & Coordinates Strategy Guide

#### 🎯 Strategy 1: Real-time enrichment pipeline

Trigger a run from your own system whenever a new creator or venue needs mapping — via a webhook, a scheduled check, or a manual button in your app. Feed the creator's username into `username` with `onlyPostsWithLocation: true`, then read `locationName`, `locationLat`, `locationLng` and `locationCategory` off each returned row and write them back into your CRM, mapping layer or partnership-tracking sheet. Because place fields are always present and typed, no post-processing step is needed between the dataset and your destination table.

#### 🎯 Strategy 2: Scheduled monitoring and alerting

Set up an Apify schedule to re-run the same username list weekly or monthly with `onlyPostsNewerThan` set to a rolling window (`"1 week"`, `"1 month"`), and diff the new rows' `locationName` and `locationPostCount` against the previous run's `places` dataset. An alert fires when a tracked venue's `locationPostCount` — Instagram's own running total for that place — jumps, or when a new place name matching your `locationKeywords` list appears for the first time.

#### 🎯 Strategy 3: Bulk dataset build

For a research or mapping dataset spanning many creators, pass the full username list in a single run and set `dataDetailLevel: "basicData"` to skip the per-post page fetch when comments and alt text are not needed — post and place fields are identical at both detail levels, so nothing geographic is lost. Aggregate the default dataset and the `places` dataset separately: the first for a post-level CSV or database table, the second for a deduplicated venue table ready for a GIS tool.

#### Strategy comparison at a glance

| Strategy | Best for | Run pattern | Output format |
| --- | --- | --- | --- |
| Real-time enrichment | Vetting one creator or venue on demand | Single triggered run per request | JSON row written back to CRM/mapping tool |
| Scheduled monitoring | Tracking a venue's or creator's activity over time | Recurring Apify schedule, diffed against the prior run | JSON, diffed on `locationPostCount` / new place names |
| Bulk dataset build | Research or training datasets across many creators | One run with a large `username` list, `basicData` for speed | CSV/database export of both datasets |

***

### 🌴 Related Instagram Scrapers & Tools

| Scraper | What it extracts |
| --- | --- |
| [Instagram Profile Reels Scraper - Tagged Places & Coordinates](https://apify.com/Scrapio/instagram-profile-reels-scraper-tagged-places-coordinates) | The same tagged-place and dual-coordinate model as this Actor, applied to reels instead of feed posts |
| Instagram Followers Lead Scraper (Scrapio) | A profile's followers, with lead-enrichment fields |
| Instagram Shared-Following / Common-Connections Mapper (Scrapio) | Shared following relationships between two profiles |
| Instagram Hashtag Stats Scraper Plus (Scrapio) | Posts, likes and comments under a given hashtag |
| Instagram Highlights Scraper Plus (Scrapio) | Story highlights and their metadata from a profile |
| Instagram Related Profiles Network Scraper (Scrapio) | The network of profiles Instagram surfaces as related to a given account |

***

### How to integrate Instagram Post Scraper - Tagged Places & Coordinates with your stack

Instagram Post Scraper - Tagged Places & Coordinates works with any language or tool that can make an HTTP request — it runs on Apify, so it is reachable through the Apify Console, the `apify-client` SDK (Python and JavaScript), or the raw REST API.

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_API_TOKEN>")

run_input = {
    "username": ["dametraveler", "beautifuldestinations"],
    "resultsLimit": 30,
    "onlyPostsWithLocation": True,
    "dataDetailLevel": "detailedData",
}

run = client.actor("instagram-post-scraper-tagged-places-coordinates").call(run_input=run_input)

rows = []
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    rows.append({
        "post": item.get("shortCode"),
        "owner": item.get("ownerUsername"),
        "place": item.get("locationName"),
        "lat": item.get("locationLat"),
        "lng": item.get("locationLng"),
    })

import csv
with open("geotagged_posts.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=["post", "owner", "place", "lat", "lng"])
    writer.writeheader()
    writer.writerows(rows)

print(f"Saved {len(rows)} geotagged posts to geotagged_posts.csv")
```

#### Node.js

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_API_TOKEN>' });

const run = await client.actor('instagram-post-scraper-tagged-places-coordinates').call({
    username: ['dametraveler', 'beautifuldestinations'],
    resultsLimit: 30,
    onlyPostsWithLocation: true,
    dataDetailLevel: 'detailedData',
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();

const places = items.map((item) => ({
    post: item.shortCode,
    owner: item.ownerUsername,
    place: item.locationName,
    lat: item.locationLat,
    lng: item.locationLng,
}));

console.log(`Fetched ${places.length} geotagged posts`);
```

#### Async and scheduled pipelines

For large username lists, start the run with `client.actor(...).start(...)` instead of `.call(...)` and poll the run status, or fetch results once the run finishes with `client.dataset(datasetId).listItems()` — this avoids holding an open connection for a long-running scrape. For recurring collection, use an Apify schedule against this Actor with a fixed input; each scheduled run writes to its own dataset, which your pipeline can then pull on whatever cadence fits.

***

### 🎯 Who Needs Instagram Post Scraper - Tagged Places & Coordinates? (Use Cases & Industries)

#### 🏢 Travel and hospitality marketers

A destination marketing organization vetting creators for a campaign runs a shortlist of usernames with `onlyPostsWithLocation: true`, then filters `locationCategory` and `locationName` to confirm the creator actually shoots at qualifying venues before signing a partnership — no manual scrolling required.

#### 📊 Data teams and analysts

Teams building an internal mapping or business-intelligence layer pull `locationLat`, `locationLng`, `locationPostCount` and `locationCategory` straight into a warehouse table, using the `places` dataset as a ready-made dimension table joined against post-level engagement (`likesCount`, `commentsCount`) from the default dataset.

#### 📱 Influencer marketing agencies

Agencies vetting a "travel creator" claim run the account through this Actor and check the geotag rate directly in the run log, then cross-reference `locationName` against the destinations the creator claims to cover — confirming (or disproving) that the content matches the pitch.

#### 🔬 Researchers

Academic and market researchers studying tourism flows or destination popularity use `locationLat`/`locationLng` pooled across many creators as a public, geotagged dataset — scoped strictly to what Instagram already serves to a logged-out visitor.

#### 🎥 Product and SaaS builders

Teams building a location-intelligence or influencer-discovery product use the `places` dataset's `postsInThisRun` and `locationPostCount` as ready-made signals for a "trending venues" or "who posts here" feature, without building their own geotag-resolution pipeline.

***

### Is it legal to scrape Instagram?

Scraping publicly accessible Instagram data is generally lawful in the United States: in *hiQ Labs, LLC v. LinkedIn Corp.*, 938 F.3d 985 (9th Cir. 2019), the Ninth Circuit held that scraping data a website makes publicly available does not violate the Computer Fraud and Abuse Act. That ruling addresses unauthorized-access law — it does not resolve a separate question, which is that scraping may still breach Instagram's own Terms of Service, and a ToS violation is a civil contract matter between the user and the platform, not a criminal one. Instagram Post Scraper - Tagged Places & Coordinates returns publicly accessible data — including usernames, captions, comments and profile details that Instagram's own logged-out feed and post pages serve to any visitor. Some of that output is personal data (creator and commenter usernames, full names, profile pictures), so data-protection obligations such as GDPR and CCPA can apply depending on your jurisdiction and how you store or use the output. What you do with the data is your responsibility — consult legal counsel for commercial applications involving personal data.

***

### ❓ Frequently asked questions

#### Does Instagram Post Scraper - Tagged Places & Coordinates work without an Instagram account?

Yes. Every endpoint this Actor uses — the profile feed, the post page, the comment surface and the place-record GraphQL query — was verified working while logged out, over plain HTTP, with no session cookie.

#### How does this Actor handle Instagram's anti-scraping measures?

Every request retries up to three times with a growing backoff between attempts, and rotates through a fresh Apify Proxy connection on retry when a proxy is configured. HTTP 401/403/404 responses are treated as terminal for that request rather than retried indefinitely, and any error that would otherwise leak a proxy credential into the log is scrubbed first.

#### Can I run it at scale without getting blocked?

The Actor fetches post-page extras (comments, alt text) with up to eight requests in flight at once per run, and pages the profile feed one request at a time. There is no published uptime or success-rate figure for this or any Instagram scraper — run behaviour depends on Instagram's own rate limiting and your proxy configuration. Residential proxy is the schema default because it was the surface confirmed reachable on 2026-07-25; the datacenter group and an unproxied connection also worked at that time.

#### How fresh is the data this Actor returns?

Fully live. Every run fetches the profile feed, post pages and place records fresh from Instagram at request time — nothing is cached between runs. Within a single run, each distinct place record is fetched once and reused for every post tagged at that place, which is a within-run cache, not a cross-run one.

#### Which fields work best for AI training and RAG indexing?

For RAG, index `caption` alongside `locationName` and `locationCity` — free-form text tied to a real, geotagged coordinate. For training data, `hashtags`, `mentions`, `locationCategory` and the numeric engagement fields (`likesCount`, `commentsCount`) return as consistently typed primitives (arrays, strings, numbers) across every row, with no HTML entities or nested rendering to strip out first.

#### Does scraping Instagram involve personal data?

Yes — `ownerUsername`, `ownerFullName`, commenter usernames and profile pictures are personal data under GDPR and CCPA. Instagram Post Scraper - Tagged Places & Coordinates only extracts data Instagram already serves publicly to a logged-out visitor; it does not access private accounts or bypass any access control. Lawful basis for storing and using that data — for marketing, research, or any other purpose — sits with you, the user.

#### Does Instagram Post Scraper - Tagged Places & Coordinates work with Claude, ChatGPT and other AI agent tools?

Yes, as an HTTP endpoint callable by any agent framework through the Apify API or `apify-client` SDK — there is no dedicated MCP server for this Actor. An agent that can call `client.actor(...).call()` and read the resulting dataset gets typed JSON straight into its context window, with no HTML parsing step in between.

#### What if a run returns zero rows?

Almost always because the account does not geotag its posts and `onlyPostsWithLocation` was on, or a keyword/bounding-box filter matched nothing — the run log prints exactly how many posts were scanned and how many each filter removed. If the posts themselves could not be fetched at all (private account, deleted account, a blocked request), the run **fails** with a status message rather than exiting green with an empty dataset.

#### How does this Actor compare to other Instagram tagged-post scrapers?

Checked on the Apify Store on 2026-07-08: powerful\_bachelor's Instagram Tagged & Mentions Posts Scraper and easyapi's Instagram Tagged Posts Scraper both scrape posts where a person is tagged or mentioned, and neither documents a location, coordinate or place-record field on its listing. If person-tagging is what you need, either is built for it directly. If the tagged place and its coordinates are what you need, this Actor is the one built for that — it does not scrape person-tag or mention networks at all.

***

### ℹ️ Disclaimer

Instagram Post Scraper - Tagged Places & Coordinates extracts only publicly available data from Instagram. This tool is intended for lawful use cases only. Users are responsible for complying with Instagram's Terms of Service and applicable data protection laws in their jurisdiction.

# Actor input Schema

## `username` (type: `array`):

Usernames (dametraveler), profile URLs (https://www.instagram.com/dametraveler/) or single post / reel / tv URLs (https://www.instagram.com/p/SHORTCODE/). Travel, hospitality and destination accounts carry the most geotags; news and tech accounts often carry none.

## `resultsLimit` (type: `integer`):

How many post rows to save per profile. This is the number of rows you get back, not the number of posts scanned - see the scan ceiling below when you turn a place filter on. Ignored for single post URLs. Default is 10.

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

Cutoff in UTC. Absolute tab takes a YYYY-MM-DD date or a full ISO timestamp; Relative tab takes '6 hours', '3 days', '1 week', '2 months', '3 years'. An unreadable value stops the run instead of silently disabling the filter. Leave empty for no cutoff.

## `skipPinnedPosts` (type: `boolean`):

Skip the posts the creator pinned to the top of their profile. Pinned status is checked against the profile you are scraping, so a collaboration post that is pinned on the co-author's grid is kept.

## `dataDetailLevel` (type: `string`):

Detailed adds alt text, the latest comments, music info and paid-partnership status by loading each post page - about one extra request per post (roughly 2.8s vs 6.2s per 12 posts). Both levels cost exactly one result row each; detailed is slower, not more expensive. Place and coordinate fields are returned on both levels.

## `onlyPostsWithLocation` (type: `boolean`):

Off by default on purpose: whether a creator geotags is entirely up to them. Measured on 2026-07-25 - dametraveler 36/36, beautifuldestinations 33/36, expertvagabond 33/36, voyaged 16/36, lonelyplanet 11/36, mkbhd 1/36, natgeo 0/36. Turning this on for a non-travel account can legitimately return zero rows.

## `locationKeywords` (type: `array`):

Case-insensitive substring match against the place name, short name, slug, address, city and category. Example: \['Paris', 'Bali', 'Hotel']. Leave empty to keep every place. Posts with no tagged place never match.

## `geoBoundingBox` (type: `string`):

Four numbers, 'minLat,minLng,maxLat,maxLng'. Example: '40.47,-74.30,40.95,-73.68' for New York City. Tested against the coordinates Instagram returned - never against a name or a geocoded guess. Posts with a place but no coordinates cannot be tested, so they are excluded and counted separately in the log.

## `maxPostsToScan` (type: `integer`):

Upper bound on posts read per profile while looking for rows that pass the filters. Only matters when a place filter is on: with a filter, the run keeps paging until it has enough rows or hits this ceiling. 300 posts is about 25 feed requests. Default is 300.

## `enrichPlaceDetails` (type: `boolean`):

Adds the place category, its total Instagram post count, its slug, its canonical coordinates and an address/city backfill. One request per DISTINCT place, cached for the whole run - measured 103 lookups behind 130 geotagged posts, so about 0.79 requests per geotagged post. Turn off for the fastest possible run.

## `maxPlaceLookups` (type: `integer`):

Hard ceiling on distinct place lookups. Rows past the cap still get every field that rides on the post itself and are marked locationPlaceRecordStatus = recordUnavailable. 0 means no cap. Default is 0.

## `emitPlacesDataset` (type: `boolean`):

Writes a second dataset named 'places' with one deduplicated row per distinct place plus postsInThisRun. Handy for mapping tools that want places rather than posts. These rows are reference data and are not charged as results.

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

Instagram's post feed was reachable through Apify RESIDENTIAL, through the BUYPROXIES94952 datacenter group and from an unproxied IP on 2026-07-25, so residential is a reliability preference here rather than a hard requirement. Residential is the default.

## Actor input object example

```json
{
  "username": [
    "https://www.instagram.com/dametraveler/"
  ],
  "resultsLimit": 10,
  "onlyPostsNewerThan": null,
  "skipPinnedPosts": false,
  "dataDetailLevel": "detailedData",
  "onlyPostsWithLocation": false,
  "locationKeywords": [],
  "geoBoundingBox": "",
  "maxPostsToScan": 300,
  "enrichPlaceDetails": true,
  "maxPlaceLookups": 0,
  "emitPlacesDataset": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

One row per scraped post, place and coordinate columns view. All scraped items live in the same default dataset - this view just orders the place-related columns first.

## `postDetail` (type: `string`):

Same default-dataset rows, shown with the post's own caption/hashtags/mentions/media/owner columns first.

## `videoAndDetailed` (type: `string`):

Same default-dataset rows, shown with video fields and the alt text / comments / music / paid-partnership columns that only Detailed-level runs populate.

## `places` (type: `string`):

One deduplicated row per distinct tagged place, written to the separate 'places' dataset when 'Also write a one-row-per-place dataset' is on. Not charged as results.

# 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 = {
    "username": [
        "https://www.instagram.com/dametraveler/"
    ],
    "locationKeywords": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapio/instagram-post-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 = {
    "username": ["https://www.instagram.com/dametraveler/"],
    "locationKeywords": [],
}

# Run the Actor and wait for it to finish
run = client.actor("scrapio/instagram-post-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 '{
  "username": [
    "https://www.instagram.com/dametraveler/"
  ],
  "locationKeywords": []
}' |
apify call scrapio/instagram-post-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapio/instagram-post-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/KkW6eBtI1s2Wl3K64/builds/2iH8sPqTqemSxD7W8/openapi.json
