# Instagram Hashtag Scraper (`coregent/instagram-hashtag-scraper`) Actor

Scrape public Instagram posts and Reels for one or more hashtags. Extract captions, creators, likes, comments, Reel plays/views, media URLs, hashtags, mentions, tagged users, locations and audio metadata. Export clean JSON, CSV or Excel.

- **URL**: https://apify.com/coregent/instagram-hashtag-scraper.md
- **Developed by:** [Delowar Munna](https://apify.com/coregent) (community)
- **Stats:** 10 total users, 5 monthly users, 99.3% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 hashtag results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Instagram Hashtag Scraper

![Instagram Hashtag Scraper](https://raw.githubusercontent.com/coregentdevspace/instagram-hashtag-scraper-assets/main/thumbnail-instagram-hashtag-scraper.png)

Scrape public Instagram posts and Reels for one or more hashtags. Extract captions, creators, likes, comments, Reel plays/views, media URLs, hashtags, mentions, tagged users, locations and audio metadata. Export clean JSON, CSV or Excel.

### What this actor does

**Instagram Hashtag Scraper** takes hashtags (`fitness`, `#travel`, or a hashtag URL) and returns **one clean row per post or Reel per hashtag**:

- 🏷️ **Query lineage**: every row keeps the hashtag it came from (`queryHashtag`), its position in your list (`queryIndex`) and its position in Instagram's feed (`rank`)
- 🎬 **Posts and Reels in one schema**: `contentType` is `POST` or `REEL`, and `mediaType` is `IMAGE`, `VIDEO` or `CAROUSEL`
- 📝 **Text**: caption, hashtags and mentions (parsed, without `#` / `@`)
- 📊 **Engagement as real numbers**: likes, comments, shares where public, Reel plays and views kept separate. No "1.2K" strings
- 🙈 **Hidden likes done right**: `likesCount: null` with `likesHidden: true`. Never a fake zero
- 👤 **Creators**: username, full name, verified flag and profile URL, plus collaborators and tagged users
- 🎵 **Reel audio**: track title, artist and whether it's original audio, as Instagram labels it
- 🖼️ **Media**: thumbnail, image/video URLs and every carousel slide nested in `carouselChildren`, at no extra charge
- 📍 **Location** when the post is tagged with one
- 🔎 **Filters**: date window, minimum likes / comments / views, posts-only or Reels-only. Filtered-out rows are never charged

It is a **hashtag discovery** actor. For a creator's full profile use [Instagram Profile Scraper](https://apify.com/coregent/instagram-profile-scraper); for a post's comments use [Instagram Comments Scraper](https://apify.com/coregent/instagram-comments-scraper).

### How it works

1. **Normalize hashtags.** `#Fitness`, `fitness` and `https://www.instagram.com/explore/tags/fitness/` are the same hashtag. Duplicates are removed (case doesn't matter), and invalid entries (spaces, punctuation) get a free `INVALID_HASHTAG` row so every input is accounted for.
2. **Read Instagram's hashtag feed.** The **Top** tab by default, the **Recent** tab if you choose it, or the **Reels** tab when you only want Reels. Pages of one hashtag are read in order; several hashtags run in parallel.
3. **Normalize every item** into the same row: content type, engagement, creator, media, audio, location and lineage.
4. **Filter and deduplicate.** Date, content-type and engagement filters run before anything is saved. Pagination overlap is removed per hashtag. Nothing removed is charged.
5. **Stop safely.** At your per-hashtag limit, the end of the feed, a repeated cursor, three pages with nothing new, the date boundary (Recent feed), or your spending limit.

### FAQ

**Which hashtag formats work?** `fitness`, `#fitness`, `#Fitness` and `https://www.instagram.com/explore/tags/fitness/`. One hashtag per line. Non-English (`東京`, `café`), numeric (`2026`) and underscore (`small_business`) hashtags all work.

**Do I need an Instagram login or cookies?** No. There's no login, cookie, session ID or Meta API token, and you never hand over account access.

**Are both posts and Reels included?** Yes, in one schema. Use **Content type** to keep only feed posts or only Reels.

**Can I scrape multiple hashtags?** Yes, up to 100 per run. Each hashtag runs independently, and one failing hashtag never fails the run.

**How many results can I get per hashtag?** Set **Max results per hashtag** (up to 5,000). Instagram only exposes a limited, rolling window of each hashtag, so popular hashtags can stop before your limit and small ones may have only a handful of posts. This actor doesn't promise complete historical coverage of a hashtag.

**Does it return top or recent posts?** Your choice, using Instagram's own tabs. **Top** ranks by engagement and **Recent** shows newest first. **Default** reads Top, which is mostly Reels; with **Content type: Reels** the posts are filtered out for free. `rank` is the position in the feed you chose.

**Are likes and comments included?** Yes, as exact integers. Likes the creator has hidden come back as `null` with `likesHidden: true`.

**Are Reel views/plays included?** Yes, when Instagram shows them. `videoPlayCount` and `videoViewCount` are separate metrics and are never merged.

**Are creators included?** Yes: username, full name, verified flag, profile URL and Instagram ID. Collaborators (`coauthors`) and tagged users are included too. If Instagram's primary feed is unavailable for a hashtag, the actor continues on a backup feed whose items name the creator by ID only. Those rows have `owner.username: null` and are counted in the run summary.

**Are media URLs included?** Yes: a thumbnail, `mediaUrls` (image or video, one per carousel slide) and `carouselChildren`. Instagram's CDN links expire, so download media soon after the run if you need the files.

**Are audio/music fields included?** Yes, for Reels where Instagram shows them: track title, artist, audio ID and `isOriginalAudio`. There's no external music recognition.

**Can I filter by date?** Yes: **Only posts newer than** / **Only posts older than** take an ISO date (`2026-09-01`) or a period (`7 days`). On the Recent feed, paging stops once older posts are reached. The Top feed isn't chronological, so there the date filter runs on what was fetched, and coverage of a date range isn't guaranteed.

**Can I filter by engagement?** Yes: minimum likes, comments and views. Views apply to Reels and videos only, since photos have no view count. Posts with hidden likes can't be verified and don't pass a likes threshold.

**Can the same post appear for multiple hashtags?** Yes, by design. A post tagged `#fitness` and `#gym` is returned once for each hashtag you asked for, so each row keeps its discovery attribution. Within one hashtag every post appears once.

**Why might I get fewer results than expected?** Instagram limits how far back each hashtag goes, hides some hashtags entirely (`HASHTAG_UNAVAILABLE`), removes posts, and your filters may exclude much of what's there. A hashtag that returns nothing gets a free `NO_RESULTS` row that says why.

**How does pricing work?** See [Pricing](#pricing): you pay per returned post/Reel per hashtag, and nothing else.

**Can I schedule it, use the API, or connect it to AI agents (MCP)?** Yes. Schedule runs in Apify Console (for example a daily campaign-hashtag check), call it through the Apify API or client libraries ([code examples](#use-it-from-code-api)), connect it to Make, Zapier or n8n, or expose it to AI agents through Apify's MCP server.

### Input

| Field | Default | Description |
|---|---|---|
| `hashtags` | `["fitness"]` | Hashtags with or without `#`, or hashtag URLs. Up to 100. |
| `maxResultsPerHashtag` | `100` | Max unique posts/Reels saved per hashtag (1–5,000). |
| `contentTypes` | `["ALL"]` | `ALL`, `POSTS` (photos, videos, carousels) or `REELS`. |
| `sort` | `DEFAULT` | `DEFAULT`, `TOP` or `RECENT` (Instagram's own hashtag tabs). |
| `postsNewerThan` / `postsOlderThan` | empty | ISO date or period such as `24 hours`, `7 days`, `30 days`. |
| `minLikes` / `minComments` / `minViews` | `0` (off) | Engagement thresholds. Views apply to Reels and videos only. |
| `includeMediaUrls` | `true` | Thumbnail, media and carousel-slide URLs. |
| `includeOwnerDetails` | `true` | Creator full name and verified flag. |
| `includeAudio` | `true` | Reel audio metadata. |
| `includeLocation` | `true` | Tagged location. |
| `maxConcurrency` | `5` | Hashtags processed in parallel (1–10). |
| `debugMode` | `false` | Verbose logging. |

#### Example 1: Minimal

```json
{
  "hashtags": ["fitness"],
  "maxResultsPerHashtag": 100
}
```

#### Example 2: Several hashtags

```json
{
  "hashtags": ["fitness", "workout", "health"],
  "maxResultsPerHashtag": 200,
  "contentTypes": ["ALL"]
}
```

#### Example 3: Reels only, with audio

```json
{
  "hashtags": ["travel"],
  "maxResultsPerHashtag": 500,
  "contentTypes": ["REELS"],
  "includeAudio": true
}
```

#### Example 4: The last 30 days, newest first

```json
{
  "hashtags": ["ai"],
  "maxResultsPerHashtag": 500,
  "sort": "RECENT",
  "postsNewerThan": "30 days"
}
```

#### Example 5: High-engagement posts only

```json
{
  "hashtags": ["fitness"],
  "maxResultsPerHashtag": 500,
  "minLikes": 1000,
  "minComments": 50
}
```

#### Example 6: Local discovery

```json
{
  "hashtags": ["canberra", "#sydneyfood", "melbournebusiness"],
  "maxResultsPerHashtag": 100,
  "includeLocation": true
}
```

### Output

One row per post/Reel per hashtag. Every row, results and status rows alike, has the same keys, so CSV and Excel exports keep stable columns. Unavailable values are `null`. The dataset has five tabs: **Posts & Reels**, **Engagement**, **Creators, people & location**, **Media & audio**, and **Status & errors**.

#### Posts & Reels table view

![Instagram Hashtag Scraper output: Posts & Reels table view](https://raw.githubusercontent.com/coregentdevspace/instagram-hashtag-scraper-assets/main/instagram-hashtag-scraper-output-posts-n-reels-table-view.png)

#### Full record: a Reel with licensed music

Real records from a `#fitness` run (October 2026). Instagram CDN links are shortened here with `…`; in your dataset they are full signed URLs that expire after a few days.

```json
{
  "rowType": "post",
  "status": "SUCCESS",
  "errorMessage": null,
  "queryHashtag": "fitness",
  "queryIndex": 0,
  "rank": 15,
  "id": "4006105148114832586",
  "shortCode": "DeYi2guP4zK",
  "url": "https://www.instagram.com/reel/DeYi2guP4zK/",
  "contentType": "REEL",
  "mediaType": "VIDEO",
  "caption": "#gymlife #gym #workout #motivation #fitness",
  "timestamp": "2026-10-12T06:07:22.000Z",
  "likesCount": 1134,
  "likesHidden": false,
  "commentsCount": 6,
  "sharesCount": null,
  "videoViewCount": null,
  "videoPlayCount": 7786,
  "videoDurationSeconds": 30.200000762939453,
  "hashtags": [
    "gymlife",
    "gym",
    "workout",
    "motivation",
    "fitness"
  ],
  "mentions": [],
  "owner": {
    "id": "46813524395",
    "username": "fitnesssbydil",
    "fullName": "Dilbag Godara Hindu",
    "profileUrl": "https://www.instagram.com/fitnesssbydil/",
    "isVerified": false
  },
  "taggedUsers": [],
  "coauthors": [],
  "thumbnailUrl": "https://scontent-iad3-1.cdninstagram.com/…/845483791_18097843475564396_8871906969274851323_n.jpg?…",
  "mediaUrls": [
    "https://scontent-iad3-2.cdninstagram.com/…/AQP7OUNn7isg4zYlmShC3BGgd7vsH4U--RnJmwMMYgd5pkIKqrwn5dvtlkbs3Iix3bWr910wrdCCQJYtqq-RQzkybH79p_3K3YDwsbI.mp4?…"
  ],
  "carouselChildren": [],
  "audio": {
    "id": "830633319434719",
    "title": "Dhurandhar - Title Track (From \"Dhurandhar\")",
    "artist": "Shashwat Sachdev, Hanumankind, Jasmine Sandlas, Sudhir Yaduvanshi, Charanjit Ahuja, Muhammad Sadiq, Ranjit Kaur, Babu Singh Maan, Ranveer Singh, Sanjay Dutt, Akshaye Khanna, R. Madhavan, Arjun Rampal, Sara Arjun",
    "isOriginalAudio": false
  },
  "location": null,
  "input": "fitness",
  "scrapedAt": "2026-10-12T12:29:24.695Z"
}
```

#### Posts & Reels tab: an image post

```json
{
  "thumbnailUrl": "https://scontent-iad3-1.cdninstagram.com/…/843509581_18636815431039820_4707282948777376121_n.heic?…",
  "queryHashtag": "fitness",
  "rank": 3,
  "contentType": "POST",
  "mediaType": "IMAGE",
  "url": "https://www.instagram.com/p/DeXeIuioxSK/",
  "owner": {
    "id": "1567527819",
    "username": "martinfit1",
    "fullName": "Martin Proaño - COACH ONLINE 🥗🏋️‍♀️",
    "profileUrl": "https://www.instagram.com/martinfit1/",
    "isVerified": true
  },
  "timestamp": "2026-10-11T20:02:28.000Z",
  "caption": "💪🏻🏋🏻‍♂️💯 #fy #fitness #gym #ᴘᴀʀᴀᴛɪ",
  "likesCount": 313,
  "commentsCount": 4,
  "videoPlayCount": null,
  "shortCode": "DeXeIuioxSK",
  "status": "SUCCESS"
}
```

#### Engagement tab: a Reel with hidden likes

Hidden likes come back as `likesCount: null` with `likesHidden: true`, never as zero. Reel plays (`videoPlayCount`) and views (`videoViewCount`) are kept apart; the hashtag feed often reports plays only.

```json
{
  "queryHashtag": "fitness",
  "rank": 14,
  "url": "https://www.instagram.com/reel/DeYIkkBx0Yn/",
  "contentType": "REEL",
  "owner": {
    "id": "2076203505",
    "username": "gabyherrerai",
    "fullName": "Gaby Herrera",
    "profileUrl": "https://www.instagram.com/gabyherrerai/",
    "isVerified": false
  },
  "timestamp": "2026-10-12T02:19:32.000Z",
  "likesCount": null,
  "likesHidden": true,
  "commentsCount": 8,
  "sharesCount": null,
  "videoPlayCount": 5867,
  "videoViewCount": null,
  "hashtags": [
    "gymreel",
    "gymvideo",
    "frases",
    "gymvibes",
    "fitness"
  ],
  "mentions": []
}
```

#### Creators, people & location tab: a collaboration Reel

```json
{
  "queryHashtag": "fitness",
  "url": "https://www.instagram.com/reel/DeYrLBnuK2C/",
  "owner": {
    "id": "1121877990",
    "username": "loksattalive",
    "fullName": "Loksatta",
    "profileUrl": "https://www.instagram.com/loksattalive/",
    "isVerified": true
  },
  "coauthors": [
    {
      "id": "61513557200",
      "username": "nilam_talks",
      "fullName": "Nilam",
      "profileUrl": "https://www.instagram.com/nilam_talks/",
      "isVerified": false
    },
    {
      "id": "59610877491",
      "username": "loksatta_entertainment",
      "fullName": "Loksatta_entertainment",
      "profileUrl": "https://www.instagram.com/loksatta_entertainment/",
      "isVerified": false
    }
  ],
  "taggedUsers": [],
  "location": null
}
```

#### Media & audio tab: a carousel

Each slide is nested in `carouselChildren`, and `mediaUrls` has one entry per slide. A carousel is one result, whatever its slide count.

```json
{
  "thumbnailUrl": "https://scontent-iad3-1.cdninstagram.com/…/843495071_18640613431063547_6215846602318724686_n.jpg?…",
  "url": "https://www.instagram.com/p/DeXi6lsge7p/",
  "contentType": "POST",
  "mediaType": "CAROUSEL",
  "mediaUrls": [
    "https://scontent-iad3-1.cdninstagram.com/…/843495071_18640613431063547_6215846602318724686_n.jpg?…",
    "https://scontent-iad3-1.cdninstagram.com/…/843117145_18640613422063547_5260251975956960308_n.jpg?…"
  ],
  "videoDurationSeconds": null,
  "carouselChildren": [
    {
      "id": "4005823431441813661",
      "mediaType": "IMAGE",
      "url": "https://scontent-iad3-1.cdninstagram.com/…/843495071_18640613431063547_6215846602318724686_n.jpg?…",
      "thumbnailUrl": "https://scontent-iad3-1.cdninstagram.com/…/843495071_18640613431063547_6215846602318724686_n.jpg?…",
      "width": 2419,
      "height": 3027
    },
    {
      "id": "4005823432649744781",
      "mediaType": "IMAGE",
      "url": "https://scontent-iad3-1.cdninstagram.com/…/843117145_18640613422063547_5260251975956960308_n.jpg?…",
      "thumbnailUrl": "https://scontent-iad3-1.cdninstagram.com/…/843117145_18640613422063547_5260251975956960308_n.jpg?…",
      "width": 2314,
      "height": 2895
    }
  ],
  "audio": null
}
```

#### Status & errors tab: a hashtag with no matching posts

```json
{
  "input": "fitness",
  "queryHashtag": "fitness",
  "queryIndex": 0,
  "rowType": "error",
  "status": "NO_RESULTS",
  "errorMessage": "Checked 240 post(s) under #fitness; none matched the date, content-type or engagement filters.",
  "url": null,
  "scrapedAt": "2026-10-12T00:00:00.000Z"
}
```

Illustrative: status rows appear only when a hashtag has a problem, and the run above had none. The remaining keys are present and `null`.

#### Status values

| Status | Meaning | Charged? |
|---|---|---|
| `SUCCESS` | A post or Reel row | Yes |
| `NO_RESULTS` | The hashtag had no public posts, or none matched your filters | No |
| `INVALID_HASHTAG` | The input isn't a valid hashtag (spaces, punctuation, empty) | No |
| `HASHTAG_UNAVAILABLE` | Instagram has no public hashtag by that name. It may be hidden or restricted | No |
| `PARTIAL_RESULT` | The hashtag stopped early after returning some rows (reason in `errorMessage`), or a row is missing a core field | No |
| `RATE_LIMITED` / `BLOCKED` / `TEMPORARY_INSTAGRAM_ERROR` / `PROVIDER_ERROR` | The hashtag couldn't be fetched this time | No |

#### Run summary and retry input

The key-value store record `RUN_SUMMARY` holds the run's totals: hashtags completed, partial and failed; results saved (posts vs Reels); duplicates skipped; rows filtered by date, content type and each engagement threshold; per-hashtag results and stop reasons; and field-availability rates (creator username, views, audio, location, media). Hashtags that failed for a temporary reason are saved to `RETRY_INPUT`, a ready-to-run input with just those hashtags.

### Use it from code (API)

The actor works with the Apify API and the official clients. Get your API token in Apify Console under **Settings → API & Integrations**. Results are the same rows as in the dataset tabs above.

**JavaScript** (`npm install apify-client`):

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

const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('coregent/instagram-hashtag-scraper').call({
  hashtags: ['fitness', 'workout'],
  maxResultsPerHashtag: 100,
  contentTypes: ['REELS'],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
const reels = items.filter((row) => row.rowType === 'post'); // status rows have rowType "error"
```

**Python** (`pip install apify-client`):

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("coregent/instagram-hashtag-scraper").call(run_input={
    "hashtags": ["fitness", "workout"],
    "maxResultsPerHashtag": 100,
    "contentTypes": ["REELS"],
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
reels = [row for row in items if row["rowType"] == "post"]  # status rows have rowType "error"
```

**HTTP** (one call that runs the actor and returns its rows; suits small runs that finish within the API's 5-minute synchronous limit):

```bash
curl -X POST "https://api.apify.com/v2/acts/coregent~instagram-hashtag-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer <YOUR_API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"hashtags": ["fitness"], "maxResultsPerHashtag": 50}'
```

For larger runs, start the actor asynchronously (`POST /v2/acts/coregent~instagram-hashtag-scraper/runs`) and read the dataset when it finishes, or use the client libraries above, which wait for you.

### Pricing

Pay per event, **per returned post or Reel per hashtag**. One carousel is one result, whatever its slide count. You aren't charged for hashtag lookups, pagination, retries, invalid or unavailable hashtags, duplicates, filtered-out posts, incomplete rows or status rows. There is no start fee. The per-result rate can differ by Apify plan; see the **Pricing** tab for the current rate on yours. This README deliberately doesn't quote figures.

The actor respects the spending limit you set for a run: when it's reached, the run stops cleanly and no further results are fetched.

### Limitations

- **Public content only.** Private accounts, Stories and close-friends content are out of scope.
- **No complete hashtag archive.** Instagram exposes a limited, changing window of each hashtag. Ordering and availability can change between runs, and engagement counts keep changing after scraping.
- **Some fields aren't always public.** Hidden likes, Reel views (the hashtag feed mostly reports plays), shares, locations and some audio IDs come back as `null` when they aren't exposed reliably. Rows from the backup feed name the creator by ID only.
- **Media URLs expire.** No media files are downloaded or stored.
- **Filters have a scan limit.** With filters on, a hashtag is scanned for up to 10× your limit (at least 300 posts). The run also stops paging that keeps returning nothing billable. Affected hashtags get a free `PARTIAL_RESULT` row that says so.
- **Not included:** comments, likers, followers, full creator profiles, hashtag analytics or trend scores. These belong to separate actors.

### Related actors

- [Instagram Profile Scraper](https://apify.com/coregent/instagram-profile-scraper): full profiles for the creators you discover
- [Instagram Posts Scraper](https://apify.com/coregent/instagram-posts-scraper): every post of a profile, or posts by URL
- [Instagram Reels Scraper](https://apify.com/coregent/instagram-reels-scraper): a profile's Reels with plays and audio
- [Instagram Comments Scraper](https://apify.com/coregent/instagram-comments-scraper): comments on the posts you find
- [Instagram Creator Leads Scraper](https://apify.com/coregent/instagram-creator-leads-scraper): creators by niche, as leads

### Legal and privacy

This is an unofficial tool. It is not affiliated with, endorsed by or sponsored by Instagram or Meta. It processes only publicly accessible content and never accesses private accounts or interacts with any account. Public posts can contain personal data (names, faces, tagged people, locations). You are responsible for having a lawful basis for collecting and processing it (for example under GDPR or CCPA) and for complying with Instagram's terms in your use of the data.

### Local development

```bash
cd actor
npm install
npm test     # unit tests
npm start    # runs with INPUT.json (needs TIKHUB_API_KEY or the local "tikhub-config" key-value store)
```

# Actor input Schema

## `hashtags` (type: `array`):

One hashtag per line, with or without # (fitness, #travel). Hashtag URLs such as https://www.instagram.com/explore/tags/ai/ work too. Case doesn't matter and duplicates are removed. Up to 100 hashtags per run.

## `maxResultsPerHashtag` (type: `integer`):

Maximum number of unique posts/Reels saved for each hashtag. Carousel slides don't count — one carousel is one result. Instagram shows a limited window of each hashtag, so some hashtags return fewer. Common values: 20, 50, 100, 500, 1,000.

## `contentTypes` (type: `array`):

All content, feed posts only (photos, videos, carousels) or Reels only. The media type (IMAGE / VIDEO / CAROUSEL) is always in the output. Filtered-out content is never charged.

## `sort` (type: `string`):

Which of Instagram's hashtag tabs to read. Default = the Top tab. Top = most engaging posts. Recent = newest posts first, which makes "Only posts newer than" stop paging early.

## `postsNewerThan` (type: `string`):

Keep only posts published after this point: an ISO date (2026-09-01) or a period (24 hours, 7 days, 30 days). On the Recent feed paging stops once older posts are reached; on the Top feed older posts are skipped without charge. Leave empty for no limit.

## `postsOlderThan` (type: `string`):

Keep only posts published before this point (same formats as above). Newer posts are skipped without charge. Leave empty for no limit.

## `minLikes` (type: `integer`):

Keep only results with at least this many likes. Posts whose likes are hidden by the creator can't be verified and are skipped. 0 = no filter.

## `minComments` (type: `integer`):

Keep only results with at least this many comments. 0 = no filter.

## `minViews` (type: `integer`):

Keep only Reels and videos with at least this many plays (views when plays aren't public). Photos and carousels have no view count and are not affected. 0 = no filter.

## `includeMediaUrls` (type: `boolean`):

Return thumbnail, image and video URLs (and per-slide URLs for carousels). Instagram CDN URLs are signed and expire — download media soon after the run if you need it. No files are downloaded by this Actor.

## `includeOwnerDetails` (type: `boolean`):

Add the creator's full name and verified flag. When off, owner keeps only ID, username and profile URL.

## `includeAudio` (type: `boolean`):

Add Reel audio as Instagram labels it: track title, artist and whether it is original audio.

## `includeLocation` (type: `boolean`):

Add the tagged location (ID, name, slug) when the post has one.

## `maxConcurrency` (type: `integer`):

How many hashtags are processed in parallel. Pages of one hashtag are always fetched one after another.

## `debugMode` (type: `boolean`):

Enable verbose logging.

## Actor input object example

```json
{
  "hashtags": [
    "fitness"
  ],
  "maxResultsPerHashtag": 20,
  "contentTypes": [
    "ALL"
  ],
  "sort": "DEFAULT",
  "postsNewerThan": "",
  "postsOlderThan": "",
  "minLikes": 0,
  "minComments": 0,
  "minViews": 0,
  "includeMediaUrls": true,
  "includeOwnerDetails": true,
  "includeAudio": true,
  "includeLocation": true,
  "maxConcurrency": 5,
  "debugMode": false
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `engagement` (type: `string`):

No description

## `creators` (type: `string`):

No description

## `media` (type: `string`):

No description

## `errors` (type: `string`):

No description

# 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 = {
    "hashtags": [
        "fitness"
    ],
    "maxResultsPerHashtag": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("coregent/instagram-hashtag-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 = {
    "hashtags": ["fitness"],
    "maxResultsPerHashtag": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("coregent/instagram-hashtag-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 '{
  "hashtags": [
    "fitness"
  ],
  "maxResultsPerHashtag": 20
}' |
apify call coregent/instagram-hashtag-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,coregent/instagram-hashtag-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/KJ820owVsnoJ9hZcY/builds/8cKxlWXzgvLRWfkJA/openapi.json
