# Instagram Hashtag Stats Scraper Plus 📊 (`scrapio/instagram-related-hashtag-stats-scraper`) Actor

Scrapes related hashtag statistics on Instagram, capturing hashtag names, post counts, growth trends, top posts, engagement metrics, and relevance clusters. Ideal for content planning, niche discovery, SEO optimization, and automated hashtag strategy research.

- **URL**: https://apify.com/scrapio/instagram-related-hashtag-stats-scraper.md
- **Developed by:** [Scrapio](https://apify.com/scrapio) (community)
- **Categories:** Lead generation, Social media, Videos
- **Stats:** 11 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$14.99/month + usage

To use this Actor, you pay a monthly rental fee to the developer. The rent is subtracted from your prepaid usage every month after the free trial period.You also pay for the Apify platform usage, which gets cheaper the higher Apify subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#rental-actors

## 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 Hashtag Scraper — Extract Stats, Related Tags and Posts

Instagram Hashtag Scraper Plus turns any hashtag, keyword, or Instagram tag URL into structured data: the hashtag's real post count, its related tags with numeric media counts, and the embedded top and latest posts (captions, likes, comments, owner, timestamps). Unlike scraping frameworks that hand you raw HTML, it returns typed JSON — ready for a database, a spreadsheet, or an LLM context window without any parsing. Every derived metric — average engagement, a competition score, a real posting-cadence figure — is computed from real posts that the run actually harvested, or returned as `null`; none of it is a synthesized stand-in. This guide covers every input and output field and how teams run the scraper for hashtag research, monitoring, and dataset building.

### What Does Instagram Hashtag Scraper Plus Do?

Instagram Hashtag Scraper Plus queries Instagram's own hashtag endpoints — `/api/v1/tags/web_info/` for metadata and embedded posts, `/api/v1/tags/search/` for related tags — and normalizes the response into flat dataset rows. It requires your own logged-in Instagram `sessionid` cookie: Instagram only serves hashtag stats and posts to authenticated traffic, so the Actor authenticates as you, not as a shared or baked-in account.

- 🏷️ Resolves hashtag stats for words, `#tags`, and full Instagram tag URLs in one input list
- 🔎 Optionally expands keywords into every matching hashtag via Instagram's own tag search (`keywordDiscovery`)
- 🧵 Returns related tags with real numeric `mediaCount`, not just Instagram's rounded display string
- 🔝 Embeds each hashtag's top posts (id, caption, hashtags, mentions, likes, comments, owner, thumbnail, timestamp)
- 🆕 Embeds each hashtag's latest (recent-tab) posts from a separate endpoint, for a real posting-cadence signal
- 📊 Derives `avgLikes`, `avgComments`, `competitionScore`, and a `difficultyTier` from real top-post engagement — never from a static post-count bucket
- 🛡️ Filters output by media-count range, related-tag keyword, difficulty tier, or followability, entirely inside the run

### ⚡ Features & Capabilities

Instagram Hashtag Scraper Plus is built around three things: honest metrics, a flat row shape that is easy to filter in SQL or a spreadsheet, and pay-per-real-result billing.

#### Core features

- One object per hashtag (`type: "hashtag"`, parent row) plus interleaved child rows (`type: "relatedTag"`, `"topPost"`, `"latestPost"`), each carrying `isChild` and `parentTag` so you can group by hashtag in any query engine
- Real `postsCount` (int64) straight from Instagram; Instagram's own INT32\_MAX "too big to count" sentinel (`2147483647`) is nulled rather than shipped as a fake number — `formattedMediaCount` (e.g. `"2.1B"`) is the fallback
- Engagement analytics computed only from posts the run actually retrieved: `avgLikes`, `avgComments`, `avgEngagement`, `competitionScore`, `difficultyTier` (`very_low`…`very_high`), `postsPerDay`
- A soft-block retry guard: every request is tried across a ladder of fresh proxy IPs before giving up, because Instagram serves an HTML app-shell (not JSON) on a soft block
- `debugDump` — logs only the top-level **key names** of Instagram's payload (never values or PII) to help diagnose missing fields

#### How Instagram Hashtag Scraper Plus compares to other Instagram hashtag scrapers

| Feature | Instagram Hashtag Scraper Plus | Instagram Related Hashtag Stats Scraper (scraper-engine / api-empire) | Instagram Hashtag Stats & Top Posts (leadsbrary) |
| --- | --- | --- | --- |
| Requires your own Instagram login | ✅ Yes (`ig_sessionid`) | Not documented on the listing | ❌ No — explicitly "no login, no session cookies" |
| Related tags with a real numeric count field | ✅ `mediaCount` (int) on every related tag | Related tags shown only as formatted strings (e.g. `"129M posts"`) in the sample output | Not offered — no related-tags output |
| Top **and** latest posts with real likes/comments | ✅ Both, from two separate endpoints | Not shown in the sample output | Top posts only; owner/likes/comments/caption are `null` when Instagram's page falls back to DOM-only rendering |
| Engagement-based difficulty tier | ✅ From real top-post engagement | Frequency-tier buckets (rare/average/frequent) by occurrence, not engagement | Not documented |
| Real posting-cadence metric | ✅ `postsPerDay` from real recent-post timestamps | Not documented | Not documented |
| Pricing model | Pay per event — only real rows are charged | Flat price per month (as observed on the Apify Store on 2026-07-26) | Pay per event (as observed on the Apify Store on 2026-07-26) |

If your use case is feeding structured hashtag data to an LLM or an analytics pipeline, the "real numeric count" and "real engagement" rows above are the decision-makers — a formatted string like `"129M posts"` needs re-parsing before a model or a database can use it, and a static frequency bucket doesn't tell you what a post actually needed to rank.

#### When another tool might suit you better

If you will not or cannot supply a personal Instagram `sessionid` — a real constraint of this Actor — leadsbrary's Instagram Hashtag Stats & Top Posts is a legitimate alternative: it uses stealth browser rendering instead of an authenticated session, at the cost of `null` owner/likes/comments/caption fields whenever Instagram's page falls back to DOM-only rendering (as observed on their listing on 2026-07-26). Choose that route if avoiding a login cookie matters more to you than complete, consistently populated post fields.

#### Instagram Hashtag Scraper Plus within the Scrapio data stack

Instagram Hashtag Scraper Plus covers hashtag stats, related tags, and hashtag-embedded posts. For follower lists with lead enrichment, use Scrapio's Instagram Followers Lead Scraper. For shared-following / common-connection mapping, use the Instagram Shared-Following Mapper. For Story highlights and their metadata, use Instagram Highlights Scraper Plus. For a network of related profiles, use the Instagram Related Profiles Network Scraper.

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

#### 🏢 Marketing and social media teams

Marketers feed `urls_keywords` a candidate list of hashtags for an upcoming campaign and read back `postsCount`, `difficultyTier`, and `competitionScore` to pick tags with real reach instead of guessing from Instagram's in-app suggestions. `keywordDiscovery` expands a seed keyword into every matching hashtag Instagram's own tag search returns, so a single niche term ("wander") becomes a shortlist scored on `avgLikes` and `avgEngagement` from that tag's actual top posts — not on follower counts of the account posting them.

#### 📊 AI training data and RAG indexing

`caption`, `hashtags[]`, and `mentions[]` on every top/latest post are the high-information text fields for RAG enrichment — a retrieval index over real, dated Instagram content for a hashtag or niche. For training data, `likes`, `comments`, `views`, `mediaType`, and `takenAt` are consistently typed across every row (int64 or ISO 8601, never a mixed string/number), so a batch of posts can be normalized into a feature table without a parsing pass.

#### 📱 Competitive and market intelligence

Track a competitor's or a niche's hashtag over time by re-running the same `urls_keywords` list on a schedule and diffing `postsCount`, `avgEngagement`, and `postsPerDay` between runs — a rising `postsPerDay` on a niche tag with a `difficultyTier` of `low` or `medium` is an early signal of a tag about to get harder to rank in.

#### 🔬 Research and academic use

Hashtag-level engagement research (how post volume and creator engagement move together for a topic) can be built entirely from `postsCount`, `avgLikes`, `avgComments`, and the harvested `topPosts`/`latestPosts`, all of which are Instagram's own public tag-page data. Scope any dataset to what Instagram displays publicly on the tag's explore page.

#### 🎥 Product and SaaS development

Hashtag-research tools, content-planning dashboards, and influencer-vetting products can call this Actor as their data layer instead of building and maintaining Instagram's private endpoint integration, session handling, and soft-block retry logic in-house.

### 🍚 Input Parameters

All 16 parameters, read directly from `.actor/actor.json`. Two are required.

| Parameter | Required | Type | Default / constraints | Description |
| --- | --- | --- | --- | --- |
| `urls_keywords` | Yes | array | default `["love", "travel"]`, string-list editor | Hashtags to analyze. Accepts words, `#tags`, or Instagram tag URLs. |
| `ig_sessionid` | Yes | string | secret text field | Your own `sessionid` cookie from a logged-in instagram.com session. Instagram only returns hashtag data to logged-in traffic. |
| `includeTopPosts` | No | boolean | default `true` | Emit each hashtag's top posts as child rows (`id`, `shortcode`, `caption`, `hashtags`, `mentions`, `likes`, `comments`, `owner`, `thumbnail`, real `timestamp`). Also powers the engagement analytics. |
| `includeLatestPosts` | No | boolean | default `true` | Emit each hashtag's most recent posts as child rows. Their real timestamps power the `postsPerDay` cadence metric. |
| `keywordDiscovery` | No | boolean | default `false` | Treat each input as a keyword and expand it into all matching hashtags via Instagram tag search before scraping. Combine with `maxItems` to cap the total. |
| `maxItems` | No | integer | default `0`, minimum `0` | Cap the number of top-level hashtags scraped. Child rows (related tags, posts) are additional and NOT counted here. `0` = unlimited. |
| `maxRelatedPerHashtag` | No | integer | default `0`, minimum `0` | Cap related-tag child rows per hashtag. `0` = all returned by Instagram. |
| `maxPostsPerHashtag` | No | integer | default `0`, minimum `0` | Cap top-post and latest-post child rows per hashtag, applied to each list independently. `0` = all embedded. |
| `minPostsCount` | No | integer | minimum `0` | Filter: drop related tags whose media count is below this. |
| `maxPostsCount` | No | integer | minimum `0` | Filter: drop related tags whose media count is above this — useful for finding lower-competition niche tags. |
| `keywords` | No | array | string-list editor | Only keep related tags whose name contains one of these substrings. |
| `excludeKeywords` | No | array | string-list editor | Drop related tags whose name contains any of these substrings. |
| `difficultyTier` | No | array | select editor, enum `very_low` / `low` / `medium` / `high` / `very_high` | Only keep hashtags whose real difficulty tier is one of the selected values. Requires top posts to be present — hashtags with no engagement data have a `null` tier and are dropped when this filter is set. |
| `onlyFollowable` | No | boolean | default `false` | Drop hashtags Instagram marks as not-followable (banned / restricted / shadow tags). |
| `debugDump` | No | boolean | default `false` | Diagnostic only. Logs the top-level key names of Instagram's `web_info` payload — never values or PII. |
| `proxy_configuration` | No | object | default `{"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}`, proxy editor | Proxy used for all Instagram requests. Residential is strongly recommended and used by default; the Actor rotates to a fresh IP on soft blocks. |

**Example JSON input:**

```json
{
  "urls_keywords": ["fitness", "#travel", "instagram.com/explore/tags/love"],
  "ig_sessionid": "YOUR_OWN_INSTAGRAM_SESSIONID_COOKIE",
  "includeTopPosts": true,
  "includeLatestPosts": true,
  "keywordDiscovery": false,
  "maxItems": 20,
  "maxRelatedPerHashtag": 15,
  "maxPostsPerHashtag": 12,
  "minPostsCount": 100000,
  "maxPostsCount": 0,
  "keywords": [],
  "excludeKeywords": ["giveaway"],
  "difficultyTier": ["low", "medium"],
  "onlyFollowable": true,
  "debugDump": false,
  "proxy_configuration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"]
  }
}
```

#### Supported URL types and input formats

`urls_keywords` accepts three interchangeable forms, and the Actor cleans each one down to a canonical lowercase tag (`parse_hashtag_input`, NFKC-normalized so lookalike characters fold to the same tag):

- **Bare word:** `"fitness"` or `"love"`
- **Hashtag with `#`:** `"#travel"` — the leading `#` is stripped
- **Full Instagram tag URL:** `"instagram.com/explore/tags/love"` or `"https://www.instagram.com/explore/tags/love/"` — the tag is extracted from the `/tags/<name>` path segment

If `keywordDiscovery` is enabled, treat entries as keywords rather than exact tags — each one is expanded via Instagram's own tag-search endpoint into every matching hashtag before the run scrapes any of them.

### 📦 Output Format

Every run pushes typed, normalized JSON rows to the default dataset — one schema per `type` value, not one wide schema per hashtag. The dataset's default view lists the union of all fields across all four row types (42 columns); any single row only populates the fields that belong to its own `type`.

#### Pay per event: which rows are billed

This Actor charges a single event, `row_result`, per row that carries real data — not per hashtag and not per run. The exact rule, taken from `row_is_real()` in `src/main.py`:

- A `hashtag` parent row is billed only if it has a real `postsCount` **or** at least one harvested top/latest post (`topPostsCount > 0` or `latestPostsCount > 0`).
- A `relatedTag` row is billed only if it carries a real numeric `mediaCount`.
- A `topPost` or `latestPost` row is billed if it carries a `postId` or `shortcode` — in practice always, since a post row is only ever built from a real harvested post.

Two situations are pushed to the dataset but **never charged**:

- A hashtag that returns no data at all (soft block or invalid tag) — a diagnostic row with `error` set and no `row_result` event.
- A hashtag whose stats/posts are unavailable (typically an expired or invalid `ig_sessionid`) but whose related tags still resolved anonymously — the parent row carries an `error` string and is not charged, while its `relatedTag` child rows are billed normally if they carry a real `mediaCount`.

If your own `ig_sessionid` is missing entirely, the run fails before any request is made and nothing is charged. If the Apify user spend limit for `row_result` events is reached mid-run, the Actor stops pushing further hashtags cleanly rather than continuing to accumulate charges.

#### Output for a hashtag (parent row, `type: "hashtag"`)

```json
{
  "tag": "travel",
  "type": "hashtag",
  "isChild": false,
  "parentTag": null,
  "hashtagId": "17843826142012345",
  "url": "https://www.instagram.com/explore/tags/travel/",
  "postsCount": 612483921,
  "formattedMediaCount": "612M",
  "subtitle": "Popular",
  "allowFollowing": true,
  "isFollowing": false,
  "isTrending": false,
  "warningMessage": null,
  "coverImageUrl": "https://scontent.cdninstagram.com/v/example_cover.jpg",
  "relatedCount": 15,
  "avgLikes": 48213.4,
  "avgComments": 612.7,
  "avgEngagement": 48826.1,
  "competitionScore": 48826.1,
  "difficultyTier": "very_high",
  "postsPerDay": 214.86,
  "topPostsCount": 9,
  "latestPostsCount": 12,
  "scrapedAt": "2026-07-26T09:14:02Z"
}
```

When Instagram's stats/posts are unavailable for a tag (expired `sessionid`, soft block), the parent row carries an `error` string explaining why, `postsCount`/analytics fields stay `null`, and — per the billing section below — **that row is not charged.**

#### Output for a related tag (child row, `type: "relatedTag"`)

```json
{
  "tag": "travelgram",
  "type": "relatedTag",
  "isChild": true,
  "parentTag": "travel",
  "relatedTag": "travelgram",
  "relatedHashtagId": "17841562223345566",
  "relatedHashtagUrl": "https://www.instagram.com/explore/tags/travelgram/",
  "mediaCount": 184203912,
  "formattedMediaCount": "184M",
  "subtitle": null,
  "scrapedAt": "2026-07-26T09:14:02Z"
}
```

#### Output for a post (child row, `type: "topPost"` or `"latestPost"`)

```json
{
  "tag": "travel",
  "type": "topPost",
  "isChild": true,
  "parentTag": "travel",
  "postId": "3412987654321098765",
  "shortcode": "C9xY2AbTZ1q",
  "url": "https://www.instagram.com/p/C9xY2AbTZ1q/",
  "mediaType": "carousel",
  "caption": "Golden hour in the Dolomites #travel #hiking @mountainguide",
  "hashtags": ["travel", "hiking"],
  "mentions": ["mountainguide"],
  "likes": 91422,
  "comments": 843,
  "views": null,
  "ownerUsername": "example_traveler",
  "ownerId": "48213765021",
  "thumbnailUrl": "https://scontent.cdninstagram.com/v/example_thumb.jpg",
  "takenAt": "2026-07-20T15:32:11Z",
  "scrapedAt": "2026-07-26T09:14:02Z"
}
```

`latestPost` rows share the exact same field set — only `type` and which endpoint sourced the post differ. `views` is populated from `play_count`/`view_count` when Instagram exposes it (typically video/reel posts) and is `null` for image posts.

#### Schema stability and export options

Field names are fixed by this Actor's row-building code, not by Instagram's raw payload shape — when Instagram renames or restructures an internal key, the mapping layer in `src/main.py` absorbs the change so downstream field names stay stable across runs. Every Apify dataset, including this Actor's, can be exported as JSON, CSV, Excel (XLSX), HTML table, or RSS directly from the run's Storage tab, or read programmatically through the Apify API / `apify-client`.

### 💡 Instagram Hashtag Scraper Plus Strategy Guide

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

Trigger a run from your CRM or content-calendar tool whenever a new hashtag candidate is added, passing it in `urls_keywords` with a single-item list. Read back `postsCount`, `difficultyTier`, and `avgEngagement` from the parent row and write them to the candidate's record. Because pay-per-event billing charges only rows carrying real data (see Billing below), a hashtag that fails to resolve costs nothing to check.

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

Set up an Apify Schedule to re-run the same `urls_keywords` list daily or weekly. Diff `postsCount`, `avgEngagement`, and `postsPerDay` against the previous run's dataset on the `tag` key, and alert when `postsPerDay` on a tracked tag crosses a threshold you define, or when `difficultyTier` moves up a tier — both are honest, run-over-run comparable numbers because they are computed the same way every run.

#### 🎯 Strategy 3: Bulk dataset build

Feed a large `urls_keywords` list (with `keywordDiscovery` enabled to expand seed terms) in a single run, or split the list across several parallel runs keyed by category. Export each run's dataset to CSV or pull it via the Apify API and aggregate into a database keyed on `tag` (parent rows) and `parentTag` (child rows). Use `maxRelatedPerHashtag` and `maxPostsPerHashtag` to keep per-hashtag row counts predictable across a large batch.

#### Strategy comparison at a glance

| Strategy | Best for | Run pattern | Output format |
| --- | --- | --- | --- |
| Real-time enrichment | Scoring a single incoming hashtag candidate | One run, one or few tags, triggered on demand | JSON row read back into your app |
| Scheduled monitoring | Tracking a fixed watchlist over time | Apify Schedule, same input, recurring | Dataset diffed run-over-run |
| Bulk dataset build | Research or training datasets | Large `urls_keywords` list, single or parallel runs | CSV / database export |

### 🌴 Related Instagram Scrapers & Tools

| Scraper | What it extracts |
| --- | --- |
| Instagram Followers Lead Scraper (Scrapio) | Follower lists with lead enrichment |
| Instagram Shared-Following Mapper (Scrapio) | Common-connection mapping between accounts |
| Instagram Highlights Scraper Plus (Scrapio) | Story Highlights and their metadata |
| Instagram Related Profiles Network Scraper (Scrapio) | Networks of related/suggested profiles |

### How to integrate Instagram Hashtag Scraper Plus with your stack

Instagram Hashtag Scraper Plus works with any language or tool that can make an HTTP request through the Apify API, or the official Apify SDKs.

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")

hashtags = ["fitness", "#travel", "instagram.com/explore/tags/love"]

run_input = {
    "urls_keywords": hashtags,
    "ig_sessionid": "<YOUR_OWN_IG_SESSIONID_COOKIE>",
    "includeTopPosts": True,
    "includeLatestPosts": True,
    "maxPostsPerHashtag": 10,
}

run = client.actor("instagram-hashtag-stats-scraper-plus").call(run_input=run_input)

rows = list(client.dataset(run["defaultDatasetId"]).iterate_items())
hashtag_rows = [r for r in rows if r.get("type") == "hashtag"]

for row in hashtag_rows:
    print(row["tag"], row.get("postsCount"), row.get("difficultyTier"), row.get("postsPerDay"))
```

#### Node.js

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

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

const run = await client.actor('instagram-hashtag-stats-scraper-plus').call({
    urls_keywords: ['fitness', '#travel', 'instagram.com/explore/tags/love'],
    ig_sessionid: '<YOUR_OWN_IG_SESSIONID_COOKIE>',
    includeTopPosts: true,
    includeLatestPosts: true,
    maxPostsPerHashtag: 10,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
const hashtagRows = items.filter((r) => r.type === 'hashtag');
hashtagRows.forEach((r) => console.log(r.tag, r.postsCount, r.difficultyTier, r.postsPerDay));
```

#### Async and scheduled pipelines

For large `urls_keywords` lists, start the run and poll for completion instead of waiting synchronously — `client.actor(...).call()` blocks until the run finishes, or use `.start()` plus polling / the Apify webhook on run completion to notify your own system. For recurring monitoring, use an Apify Schedule with a fixed input rather than triggering runs from your own cron.

### 🎯 Who Needs Instagram Hashtag Scraper Plus? (Use Cases & Industries)

#### 🏢 Marketing and social media agencies

Score a client's candidate hashtag list on `postsCount`, `difficultyTier`, and `avgEngagement` before a campaign launches, instead of relying on Instagram's in-app hashtag suggestions.

#### 📊 Growth and content teams

Feed a niche seed term through `keywordDiscovery` to surface every hashtag Instagram's own tag search associates with it, then filter the result with `minPostsCount`/`maxPostsCount` to find tags sized for the account's current reach.

#### 📱 Competitive intelligence teams

Track a competitor's branded or campaign hashtag over time: re-run the same tag on a schedule and watch `postsPerDay` and `avgEngagement` move as the campaign runs.

#### 🔬 Researchers

Build hashtag-level engagement datasets from `postsCount`, `avgLikes`, `avgComments`, and the harvested top/latest posts — all data Instagram already displays publicly on the tag's explore page.

#### 🎥 SaaS and tool builders

Use the Actor as the data layer behind a hashtag-research or content-planning product, instead of building and maintaining Instagram's private endpoint integration and session handling directly.

### Is it legal to scrape Instagram hashtags?

Scraping publicly accessible web data is generally lawful; courts have repeatedly held that accessing data a website makes available without a login is not a computer-crime violation (*hiQ Labs v. LinkedIn*, 9th Cir. 2019, on public-data access under the U.S. Computer Fraud and Abuse Act). This Actor, however, requires an authenticated session — you access hashtag stats and posts using your own logged-in Instagram cookie — which puts the run inside Instagram's Terms of Service rather than outside a login wall.

That distinction matters for two separate questions. Violating a platform's Terms of Service is ordinarily a civil contract matter between you and Instagram, not a criminal one, but it means Instagram can act against the account whose `sessionid` you supply (rate-limit it, restrict it, or terminate it) — use a throwaway account, not your primary one. Separately, the hashtag stats and related-tag rows are aggregate, non-personal data, but the embedded post rows carry `ownerUsername`, `ownerId`, and `caption` — public identifiers tied to real Instagram accounts. If you store or process those fields at scale, they can constitute personal data under GDPR/CCPA depending on your jurisdiction and use case, and the lawful basis for that processing is yours to establish.

Instagram Hashtag Scraper Plus returns only publicly accessible data. What you do with that data is your responsibility — consult legal counsel for commercial applications involving personal data.

### ❓ Frequently asked questions

#### Does Instagram Hashtag Scraper Plus work without an Instagram account?

No. Instagram only returns hashtag stats, related tags, and posts to authenticated web traffic, so the Actor requires your own `ig_sessionid` cookie in every run. Without it, the run fails cleanly before making any requests and you are not charged.

#### How does Instagram Hashtag Scraper Plus handle Instagram's anti-scraping measures?

It authenticates with your own session cookie rather than an anonymous request, and retries every request across a ladder of fresh proxy IPs (residential by default) when Instagram serves a soft-block HTML app-shell instead of JSON. `proxy_configuration` controls the proxy group used for that ladder.

#### Can I run Instagram Hashtag Scraper Plus at scale without getting blocked?

Reliability depends on a valid, non-rate-limited `sessionid` and a working residential IP — the Actor's retry guard rotates proxy IPs on a soft block, but it cannot recover from an expired or already-restricted session. No uptime or success-rate figure is published for this Actor.

#### How fresh is the data Instagram Hashtag Scraper Plus returns?

Every run fetches live from Instagram's `web_info`, `sections`, and `search` endpoints at run time — nothing is cached or served from a previous run. Each row carries the run's own `scrapedAt` timestamp.

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

For RAG, index `caption`, `hashtags[]`, and `mentions[]` from the top/latest post rows — the high-information text fields. For training data, `likes`, `comments`, `views`, `mediaType`, and `takenAt` are the most structurally consistent fields, always returned as the same typed primitive (int64 or ISO 8601 string) or `null`, so they load into a feature table without a normalization pass.

#### Does Instagram Hashtag Scraper Plus return personal data?

The hashtag and related-tag rows are aggregate counts, not personal data. Post rows carry `ownerUsername`, `ownerId`, and `caption`, which are public but identify a real account — treat them as personal data for storage and processing purposes. The Actor returns only publicly accessible data; establishing a lawful basis for storing and using it sits with you.

#### Does Instagram Hashtag Scraper Plus work with Claude, ChatGPT, and other AI agent tools?

It is callable as a standard Apify Actor run by any agent framework that can call the Apify API or `apify-client` — start a run, poll or await its completion, and read the dataset. Every row returns as typed JSON, so no HTML or text parsing is required before passing results into a model's context window.

#### How does Instagram Hashtag Scraper Plus compare to other Instagram hashtag scrapers?

Instagram Related Hashtag Stats Scraper (scraper-engine / api-empire) does not require a login on its listing and prices on a flat monthly plan (as observed on 2026-07-26), but its sample output shows related tags as formatted strings rather than a real numeric count field, and no post-level engagement data. Instagram Hashtag Stats & Top Posts (leadsbrary) also needs no login and prices pay-per-event (as observed on 2026-07-26), but owner/likes/comments/caption fields on its posts are `null` whenever Instagram's page falls back to DOM-only rendering, and it has no related-tags output at all. Instagram Hashtag Scraper Plus trades the no-login convenience for a session-authenticated request that consistently returns real post engagement, real numeric related-tag counts, and an engagement-derived difficulty tier.

### Disclaimer

Instagram Hashtag Scraper Plus extracts only publicly available data from Instagram, accessed through a session you authenticate yourself. 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, and for how they store and use any personal data returned in post rows.

# Actor input Schema

## `urls_keywords` (type: `array`):

Hashtags to analyze. Accepts words, #tags, or Instagram tag URLs. Example: \["fitness", "#travel", "instagram.com/explore/tags/love"].

## `ig_sessionid` (type: `string`):

REQUIRED. Paste the value of YOUR OWN `sessionid` cookie from a logged-in instagram.com tab (DevTools → Application → Cookies → https://www.instagram.com → sessionid). Instagram only returns hashtag data to logged-in traffic. Nothing is baked in — without your cookie the run fails cleanly and you are not charged. Use a throwaway account; treat the cookie as a password.

## `includeTopPosts` (type: `boolean`):

Emit each hashtag's top posts (id, shortcode, caption, hashtags, mentions, likes, comments, owner, thumbnail, real timestamp) as child rows. Also powers the engagement analytics. Default: true.

## `includeLatestPosts` (type: `boolean`):

Emit each hashtag's most recent posts as child rows. Their real timestamps power the honest postsPerDay cadence metric. Default: true.

## `keywordDiscovery` (type: `boolean`):

Treat each input as a keyword and expand it into all matching hashtags (via Instagram tag search) before scraping. Combine with maxItems to cap the total. Default: false.

## `maxItems` (type: `integer`):

Cap the number of top-level hashtags scraped. Child rows (related tags, posts) are additional and NOT counted here. 0 = unlimited. Default: 0.

## `maxRelatedPerHashtag` (type: `integer`):

Cap related-tag child rows per hashtag. 0 = all returned by Instagram. Default: 0.

## `maxPostsPerHashtag` (type: `integer`):

Cap top-post and latest-post child rows per hashtag (applied to each list). 0 = all embedded. Default: 0.

## `minPostsCount` (type: `integer`):

Filter: drop related tags whose media count is below this. Example: 100000. Default: none.

## `maxPostsCount` (type: `integer`):

Filter: drop related tags whose media count is above this (find lower-competition niche tags). Default: none.

## `keywords` (type: `array`):

Only keep related tags whose name contains one of these substrings. Example: \["travel", "wander"]. Default: none.

## `excludeKeywords` (type: `array`):

Drop related tags whose name contains any of these substrings. Default: none.

## `difficultyTier` (type: `array`):

Only keep hashtags whose REAL difficulty tier (derived from top-post engagement) is one of the selected values. Leave empty for no filtering. Note: requires top posts to be present; hashtags with no engagement data have a null tier and are dropped when this filter is set.

## `onlyFollowable` (type: `boolean`):

Drop hashtags Instagram marks as not-followable (banned / restricted / shadow tags). Default: false.

## `debugDump` (type: `boolean`):

Diagnostic only. Logs the top-level KEY NAMES of Instagram's web\_info payload (never any values/PII) to help troubleshoot missing fields. Default: false.

## `proxy_configuration` (type: `object`):

Proxy used for all Instagram requests. Residential is strongly recommended and used by default; the actor rotates to a fresh IP on soft blocks.

## Actor input object example

```json
{
  "urls_keywords": [
    "love",
    "travel"
  ],
  "includeTopPosts": true,
  "includeLatestPosts": true,
  "keywordDiscovery": false,
  "maxItems": 0,
  "maxRelatedPerHashtag": 0,
  "maxPostsPerHashtag": 0,
  "onlyFollowable": false,
  "debugDump": false,
  "proxy_configuration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# 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 = {
    "urls_keywords": [
        "love",
        "travel"
    ],
    "proxy_configuration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapio/instagram-related-hashtag-stats-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 = {
    "urls_keywords": [
        "love",
        "travel",
    ],
    "proxy_configuration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("scrapio/instagram-related-hashtag-stats-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "urls_keywords": [
    "love",
    "travel"
  ],
  "proxy_configuration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call scrapio/instagram-related-hashtag-stats-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=scrapio/instagram-related-hashtag-stats-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/1FDbqHhrZ4wR1smfF/builds/vk7UKnarfd1bfUv7k/openapi.json
