# Instagram Search Scraper: Reels, Users, Hashtags (`automation_craft/instagram-search-scraper`) Actor

Instagram search by keyword, no login: reels, user accounts and hashtags in one run. Reels carry plays, likes, comments and music; accounts carry followers, bio, links and engagement; hashtags carry exact post counts and related tags. Filters, JSON, CSV or API; pay per result.

- **URL**: https://apify.com/automation_craft/instagram-search-scraper.md
- **Developed by:** [Automation Craft](https://apify.com/automation_craft) (community)
- **Categories:** Social media, Marketing, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.37 / 1,000 reels

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 Search Scraper: Reels, Users, Hashtags

**Search Instagram by keyword** for public reels, user accounts and hashtags in one Actor, without an Instagram account, cookies
or login. Give your words ("coffee roaster", "wedding photographer miami", "nail art") and pick what to look for: reels with their
plays, likes and comments, accounts with their followers, bio and engagement, hashtags with their exact post counts, or all three
in one run. Every result is a row of your dataset, with a free row per keyword that says what was searched, found and left out.

- **Three search types, one run.** Reels (Instagram's own reels search for your words), profiles (Threads' user search, Meta's
  Ad Library and the creators of matching reels, each account then read on Instagram) and hashtags (Instagram's own hashtag
  search, up to 55 hashtags a keyword). Ask for one, two or all three: they run in the order you list them, and every row says
  which search made it (`searchType`).
- **Deep rows.** Reels with plays, likes, comments, date, length, music, location, collaborators and the video and audio links;
  accounts with exact followers, following, posts, bio, links, category, account type, the 6 newest posts and engagement;
  hashtags with Instagram's tag id, the exact post count and a popularity band.
- **Filters that cost nothing.** Dates, plays, likes and caption language for reels; 24 account filters for profiles (followers,
  account type, words in the bio, category, place words, a contact shown, engagement and more); post count bounds, keyword match and
  the exact hashtag for hashtags. Results a filter leaves out are never charged.
- **Add-ons when you need them.** Creator details and comments on reels; reel figures, profile info and the linked Facebook
  Page's public contact on accounts; related hashtags (the hashtags used most with each one in its top reels)
  with their post counts where Instagram lists them, and those top reels on hashtags.
- **Fair billing.** Each reel, account and hashtag is charged once per run, even when several keywords find it. Searches that
  find nothing, failed calls, repeats and refused entries are free rows.

### Quick start

1. Open the Actor's **Input** tab and pick the **Search types**: Reels (default), Profiles, Hashtags, or several.
2. Type your keywords, one per line (`coffee roaster`, `wedding photographer miami`, `nail art`). For profiles you can also paste
   usernames or post links.
3. Set **Max results per keyword** (counted per search type: reels, profiles and hashtags each get that many per keyword).
4. Optional: open the section of each search type for its filters and add-ons.
5. Click **Start**. Results arrive as rows of the dataset, followed by one free row per keyword and a summary per search type.
6. Download the dataset as JSON, CSV or Excel (the Output tab has a view per search type), or read it through the Apify API, a
   schedule, Make, n8n or Zapier.

### What you get

Every row a search makes carries `searchType` (`reels`, `profiles` or `hashtags`; the run's own rows below carry null) and `type` (`reel`, `profile`, `hashtag`, or a free row:
`keyword`, `subject`, `status`, `summary`). The fill rates below were measured on the platform on 2026-10-28, on the distinct rows
of the 26 checking runs of this Actor's final build that delivered rows (247 reels, 23 accounts, 172 hashtags).

#### Reels (`searchTypes` holds `reels`)

One `reel` row per reel found by your keyword on Instagram's own reels search (24 reels on its first page, about 96 a search when
every page is read).

| Field | What it holds | Filled |
|---|---|---|
| `keyword`, `searchedAs`, `foundVia`, `rank`, `searchPage` | your keyword, the text sent to Instagram, how the reel was found (`search`, `related`, `variant`, `creator`), its place in Instagram's list | 100% |
| `code`, `id`, `url` | the reel's code, id and link | 100% |
| `caption`, `hashtags` | the caption as Instagram serves it, and its hashtags | 98.4%, 82.2% |
| `playCount` | plays | 99.2% |
| `likeCount`, `likesHidden`, `commentCount` | likes (null, never 0, when the creator hides them), comments | 86.2%, 89.5% |
| `postedAt`, `durationSeconds` | when the reel was posted, and its length | 90.3%, 89.9% |
| `videoUrl`, `videoUrlExpiresAt`, `audioUrl`, `coverUrl` | the MP4 link and when Instagram's signed link expires, the audio only track, the cover | 100%, audio 86.6%, cover 100% |
| `audio` | `type` (music or original), `title`, `artist`, `audioId` | 84.2% |
| `location` | `id`, `name`, `lat`, `lng`, `url` of the tagged place | 35.2% |
| `coauthors`, `isPaidPartnership`, `aiLabel` | collaborators, the paid partnership label, Instagram's AI content label | 8.5%, 89.5% read |
| `owner` | the creator's `username`, `id`, `fullName`, `isVerified`, `isPrivate`, `profilePicUrl`, `url` | 100% |
| `keywordMatch`, `matchedTerms` | how the caption, hashtags and username hold your keyword's words: `all`, `some` or `none` | 100% (`all` 58.7%) |
| `viewCount`, `creator`, `comments` | the reel's view counter (opt in), creator details and comments (add-ons) | when asked |
| `mentions`, `taggedUsers` | the @usernames in the caption and the accounts tagged in the reel: empty unless you turn on **Include personal data** | when asked |

Rows read without details (`includeDetails` off) hold what the search listed: code, caption, plays, creator, video and cover. A
reel whose details could not be read is delivered free (`detailsStatus` `failed`); the fill rates above count those rows too.

#### Profiles (`searchTypes` holds `profiles`)

One `profile` row per account. Each keyword runs Threads' user search (about 100 accounts a search), Meta's Ad Library search
(up to 15 businesses with a Facebook Page) and Instagram's reels search (the creators of matching reels), merged, and every
account is read on Instagram.

| Field | What it holds | Filled |
|---|---|---|
| `keyword`, `searchedAs`, `foundVia`, `rank` | your keyword, the text searched, how the account was found (`threads`, `ad_library`, `reel_creator`, `threads_variant`, `related_profile`, `username`, `commenter`), its place in the keyword's merged list | 100% |
| `username`, `id`, `url`, `fullName`, `biography` | the account's username, Instagram id, link, name and bio as Instagram serves it | 100% |
| `followerCount`, `followingCount`, `postsCount` | exact followers, following and posts | 100%, posts 95.7% |
| `accountType`, `category`, `isVerified`, `isPrivate` | `personal`, `business` or `creator`, the category the account shows, the badges | 100%, category 60.9% |
| `externalUrl`, `bioLinks`, `threadsUsername`, `facebookPage` | the website, the bio links, the Threads profile, the linked Facebook Page | 87.0%, 91.3%, 39.1%, 39.1% |
| `recentPosts`, `avgLikes`, `avgComments`, `engagementRate`, `engagementGrade`, `postsPerWeek`, `lastPostAt` | the 6 newest posts and the engagement and posting pace computed from them | 95.7%, engagement 91.3% |
| `biographyLanguage`, `keywordMatch` | the language of the bio and captions (our own estimate), how the username, name and bio hold your keyword | 87.0%, 87.0%: every account a keyword found, none for a username list (`all` 65.2%) |
| `hasEmail`, `hasPhone`; `emails`, `phones` | whether the bio, bio links or captions show an e-mail or phone; the addresses and numbers only with **Include personal data** | flags always |
| `reelStats`, profile info fields, `facebookPageContact`, `relatedProfiles` | add-ons: reel figures, the business address and more, the Facebook Page's public contact, suggested accounts (needs **Include personal data**) | when asked |

#### Hashtags (`searchTypes` holds `hashtags`)

One `hashtag` row per hashtag Instagram's hashtag search lists for your keyword (up to 55 a keyword, in Instagram's order).

| Field | What it holds | Filled |
|---|---|---|
| `name`, `hashtag`, `id` | the hashtag as Instagram stores it (styled letters, accents and emoji kept), with its #, and Instagram's tag id | 100% |
| `postsCount`, `postsCountText` | the exact number of posts using it, and Instagram's own rounded text ("2.1M posts") | 100% |
| `popularity` | `very_high` (10 million posts or more), `high` (1 million), `medium` (100,000), `low` (10,000), `very_low`: our own bands | 100% |
| `url`, `popularReelsUrl` | the tag's page and its public popular reels page | 100% |
| `keyword`, `keywordPosition`, `searchedAs`, `rank` | your keyword, the text searched and the hashtag's place in Instagram's list | 100% |
| `keywordMatch`, `matchedTerms`, `isExactKeyword` | whether the name holds every word of your keyword (`all`, `some`, `none`), and whether it IS your keyword's words run together | 100% (`all` 98.8%) |
| `relatedHashtags`, `topReels`, `topReelsMedianPlays`, `relatedReelsRead`, `relatedHashtagsStatus` | add-on: the hashtags used most often with this one in its top public reels (up to 10, each with how many of those reels use it, its exact post count and band when Instagram lists it), those reels (link and plays) and their median plays | when asked |

Example row (from a run on `coffee roaster`, related hashtags on):

```json
{
  "type": "hashtag",
  "searchType": "hashtags",
  "name": "coffeeroaster",
  "hashtag": "#coffeeroaster",
  "id": "17842356424066679",
  "postsCount": 2161276,
  "postsCountText": "2.1M posts",
  "popularity": "high",
  "url": "https://www.instagram.com/explore/tags/coffeeroaster/",
  "keyword": "coffee roaster",
  "rank": 1,
  "keywordMatch": "all",
  "isExactKeyword": true,
  "relatedHashtags": [
    { "name": "coffee", "hashtag": "#coffee", "reels": 9, "postsCount": 185545143, "popularity": "very_high" },
    { "name": "specialtycoffee", "hashtag": "#specialtycoffee", "reels": 8, "postsCount": 9313995, "popularity": "high" }
  ],
  "topReelsMedianPlays": 18808,
  "relatedHashtagsStatus": "read"
}
```

#### The free rows

One `keyword` row per keyword of the reels and hashtags searches, and one `subject` row per keyword, account list or post link
of the profile search: its `status` (`ok`, nothing found, nothing matching, `failed`, `stopped`), `finished`, `stopReason`, what
each search listed, delivered and left out and why. The hashtag search's keyword row also lists every hashtag Instagram listed for
the keyword with its post count and band (`listedHashtags`), before your filters. Every search type ends with a free `summary`
row of its counts and charges; when the run stops before a search type you asked for, a free `search_types_skipped` row names it.
When the input is refused, nothing is searched: the run writes one free `entries_refused` row per list and reason (the positions
of the entries, never their text), the `invalid` row naming each problem and a summary, all with `searchType` null.

### How much does it cost to scrape Instagram search results?

You pay per result (pay per event). Prices per 1,000 events:

| Event | Free | Bronze | Silver | Gold |
|---|---|---|---|---|
| Reel (one reel delivered as a row, with its details) | $0.51 | $0.46 | $0.42 | $0.365 |
| Reel search (one reels search that delivered at least one charged reel) | $0.42 | $0.38 | $0.34 | $0.30 |
| Creator profile (reels add-on, once per creator per run) | $1.33 | $1.21 | $1.10 | $0.95 |
| Comment (reels add-on, per comment delivered) | $0.27 | $0.24 | $0.22 | $0.19 |
| Profile (one account delivered as a row, with its details) | $1.86 | $1.70 | $1.54 | $1.33 |
| Basic profile (one account without details, when details are off) | $0.40 | $0.36 | $0.32 | $0.28 |
| Facebook Page contact (profiles add-on, a linked Page that shows contact details) | $2.10 | $1.92 | $1.74 | $1.50 |
| Re-filtered profile (a profile of an earlier run that passes this run's filters) | $0.10 | $0.09 | $0.08 | $0.07 |
| Hashtag (one hashtag delivered as a row, with its post count) | $1.65 | $1.51 | $1.36 | $1.18 |
| Related hashtag list (hashtags add-on, a hashtag where related hashtags were found) | $1.40 | $1.28 | $1.16 | $1.00 |

Platinum and Diamond plans pay the Gold price. Each run also pays the Actor start event of $0.0075 (256 MB), which has no plan
discount. The Store pricing card shows these same prices per 1,000 events: "$0.365 / 1,000" on the Reel row means one reel costs
about four hundredths of a cent, and "$1.18 / 1,000" on the Hashtag row means one hashtag costs about a tenth of a cent.

Examples at the Gold price: 50 reels of one keyword cost $0.0075 + 50 x $0.000365 + $0.0003 = $0.026; 30 hashtags of one keyword
cost $0.0075 + 30 x $0.00118 = $0.043; one keyword searched for 20 reels, 20 profiles and 20 hashtags costs $0.0075 + 20 x
$0.000365 + $0.0003 + 20 x $0.00133 + 20 x $0.00118 = $0.065.

Free: keyword, subject and status rows, the summaries, results another keyword of the run already delivered, results a filter
leaves out, searches that list nothing, failed calls, refused entries and invalid input; reels whose details and accounts whose
recent posts could not be read are delivered free; a related hashtags read that finds no public reels is free. An invalid input
charges only the run's start fee.

### Input

#### Shared settings

| Setting | What it does | Default |
|---|---|---|
| `searchTypes` | one to three of `reels`, `profiles`, `hashtags`, run in the order given | `reels` |
| `keywords` | words or phrases, up to 500 (see below) | none |
| `maxResultsPerKeyword` | the most results one keyword delivers in EACH search type (1 to 1,000; Instagram lists about 96 reels a search and up to 55 hashtags a keyword) | 50 |
| `maxResults` | stop a search type once it delivered this many results across all keywords (counted per search type) | none |
| `requireKeywordMatch` | keep only results that hold every keyword word (a reel's caption, hashtags or username; an account's username, name or bio; a hashtag's name) | off |
| `includeDetails` | read each reel's details and each account on Instagram (reels and profiles; off is faster and leaner) | on |
| `includePersonalData` | fill the fields that name or reach a person: a reel's `mentions` and `taggedUsers`, an account's `emails`, `phones` and `relatedProfiles`, and links to Instagram, Facebook, Messenger, Threads or WhatsApp and contact links in websites and bio links | off |
| `maxRunSeconds`, `requestTimeoutSecs`, `maxRetries` | a time limit, the request timeout (5 to 60 s) and retries per request (0 to 5) | none, 30, 2 |
| `proxyConfiguration` | Apify Proxy setting (see below) | Apify Proxy |

A setting of a search type you did not pick is ignored when it holds nothing or its default, and refuses the run otherwise (it
would ask for work this run does not do); the refusal names the setting and charges only the run's start fee.

#### Reels search settings

| Setting | What it does | Default |
|---|---|---|
| `sortBy` | `relevance` (Instagram's order), `newest` or `mostPlayed` | `relevance` |
| `searchRelatedKeywords` | also search this many of Instagram's related keywords per keyword (0 to 10) | 0 |
| `tryVariantsWhenEmpty` | for a keyword without reels, also try "<keyword> ideas", "tips", "inspiration" | off |
| `includeCreatorReels`, `maxReelsPerCreator` | add the newest reels of the creators found that match your keyword (1 to 12 per creator) | off, 2 |
| `postedSince`, `postedUntil` | a posting window: a day as `2026-10-01` or `30 days` (from the run's start), and an end day | none |
| `minPlays`, `minLikes`, `minComments` | minimum counts (reels with hidden likes fail a likes minimum) | none |
| `captionLanguages` | keep only captions estimated in these languages (44 two letter codes) | any |
| `includeViewCount` | add the reel's view counter from its embed page (one more read per reel, included in the Reel price) | off |
| `includeCreatorDetails` | the Creator profile add-on | off |
| `maxCommentsPerReel` | the Comment add-on: up to this many comments per reel (0 to 50) | 0 |

#### Profile search settings

| Setting | What it does | Default |
|---|---|---|
| `usernames` | accounts to read directly: usernames, @handles, profile links or numeric ids, up to 5,000 | none |
| `postUrls` | post or reel links: the authors of their public comments are read, up to 100 links | none |
| `maxCommentersPerPost` | the most distinct comment authors collected from one post | 50 |
| `searchThreads`, `searchAdLibrary`, `searchReelCreators` | which searches a keyword runs | all on |
| `reelSearchPages` | pages of the reels search read for its creators (1 to 8) | 1 |
| `adLibraryCountry` | the country the Ad Library search favours (`ALL` or a two letter code in capitals) | `ALL` |
| `searchVariants` | also search Threads for the keyword's joined, underscored and dotted forms | off |
| `expandWithRelatedProfiles`, `expansionDepth` | also deliver the accounts Instagram suggests next to each profile delivered, one level or two | off, 1 |
| `includeRecentPosts` | put the 6 newest posts on the row (engagement is computed either way) | on |
| `includeReelStats`, `includeProfileInfo` | the reel figures and the profile info (one more read per account each, included in the Profile price) | off |
| `includeFacebookPageContact` | the Facebook Page contact add-on | off |
| `includeRelatedProfiles` | the suggested accounts on the row (needs **Include personal data**) | off |
| `minFollowers`, `maxFollowers`, `minPosts`, `maxPosts` | follower and post count bands | none |
| `verified`, `accountTypes`, `influencersOnly`, `includePrivate` | verified only or not verified, account types (`professional` = business or creator), creators and influencer categories only, private accounts in or out | any, any, off, in |
| `mustContain`, `mustContainIn`, `excludeWords` | words that must appear and where (username, name or bio by default; bio; name; recent captions; anywhere), words that leave an account out | none |
| `categories`, `combineCategoryAndWords` | Instagram categories to keep, and whether categories and `mustContain` must both pass or either | none, both |
| `languages`, `locationMustContain` | bio and caption languages (44 codes), place words in the bio, business address or Facebook Page country | any, none |
| `contactFilter`, `requireWebsite` | a contact shown (an e-mail or a phone, an e-mail, a phone, both) and a website | any, off |
| `minEngagementRate`, `activeWithinDays`, `minRecentPosts`, `recentPostsDays` | engagement rate floor (percent), newest post within days, at least N of the 6 newest posts within days | none, none, none, 30 |
| `minMedianReelPlays`, `reelPostedWithinDays`, `minPlaysToFollowersPercent`, `maxPlaysToFollowersPercent` | reel filters (need the reel figures) | none |
| `excludeUsernames` | accounts never to deliver, up to 10,000 | none |
| `minMatchRatePercent` | stop once 100 accounts were judged and fewer than this percent passed your filters | none |
| `includeLeftOutAccounts` | a free row for every account read and left out, with the reason | off |
| `refilterDatasetId` | check an earlier run's profiles again with this run's filters, without reading Instagram (Re-filtered profile) | none |

#### Hashtag search settings

| Setting | What it does | Default |
|---|---|---|
| `minHashtagPosts`, `maxHashtagPosts` | keep hashtags used on at least, or at most, this many posts | none |
| `exactHashtagOnly` | keep only the hashtag that is your keyword's words run together ("coffee roaster" keeps #coffeeroaster): the stats of hashtags you name | off |
| `includeRelatedHashtags` | the related hashtags add-on (one more read per hashtag, and one hashtag search per related hashtag the run does not know yet); when the run's work allowance for add-on reads is spent, the remaining hashtags come without the list (`relatedHashtagsStatus` `allowance`, not charged for it, counted in the keyword row and the status message) | off |

**Keywords.** A word or phrase in any language; a leading # is dropped and keywords are searched lower cased. The reels search
also takes a link to an `instagram.com/popular/<words>/` or `instagram.com/explore/tags/<tag>/` page, and Boolean keywords
(`AND`, `OR`, `NOT` in capitals, parentheses, `"quoted phrases"`, `-word`): each OR branch is searched and a reel must match the
whole expression. The hashtag search takes a tag page link too but no Boolean keyword (a free row says so; a run whose keywords the
hashtag search can use none of is refused). The hashtag search matches your words run together: "fat loss" lists #fatloss, and
hashtags hold no spaces or punctuation (letters, marks, digits, underscores and emoji), so a word with a hyphen lists nothing. Specific phrases find more reels than
single broad words: Instagram shows no public reels for some broad words ("coffee") and such a keyword gets a free `no_reels`
row, while the hashtag search answers broad words in full. An entry that is empty, a link of another kind, holds an @, looks like
a token or API key, or repeats an earlier keyword is refused with one free row per reason that names its position (the value is
never shown); the other keywords run.

**Proxy.** By default the Actor reads from its own server address first and moves a request type to Apify's datacenter proxy
when it is refused there. Choose a proxy group or country to read only through Apify Proxy, or give your own proxy links. A
residential group is read through the datacenter pool instead, at no extra cost, and a free status row says so.

**Work that earns nothing.** Searches that list nothing, filters nothing passes and calls that are answered badly cost time but
deliver nothing. A run spends at most a small fixed amount of such work before its first charged result, and more as it delivers;
when it is spent the run stops with the stop reason `work_allowance` and a free `skipped` row naming the keywords not searched (a
test list of 200 made up hashtag keywords that all found nothing stopped after 44 of them, for $0.0075, the start fee). The search types after it do not run (a
`search_types_skipped` row names them), and so it is for the caller's charge limit and a time limit. Add-on reads (creator
details, comments, a Facebook Page's contact, related hashtags) draw on an allowance of their own that grows with the add-ons
delivered: when it is spent, the next rows come without the add-on and are not charged for it (for related hashtags:
`relatedHashtagsStatus` `allowance`, counted in the keyword row's `relatedHashtagsLeftOut` and named in the status message). Three hashtag searches in a
row that Instagram answers badly stop the hashtag search as `source_failing`; the run's status message names the failing side.

### API examples

```bash
curl -X POST "https://api.apify.com/v2/acts/automation_craft~instagram-search-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchTypes": ["reels", "hashtags"], "keywords": ["nail art", "latte art"], "maxResultsPerKeyword": 30}'
```

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('automation_craft/instagram-search-scraper').call({ searchTypes: ['profiles'], keywords: ['wedding photographer miami'], requireKeywordMatch: true, minFollowers: 1000 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.filter((row) => row.type === 'profile').map((row) => row.username));
```

```python
from apify_client import ApifyClient
client = ApifyClient("YOUR_TOKEN")
run = client.actor("automation_craft/instagram-search-scraper").call(run_input={"searchTypes": ["hashtags"], "keywords": ["sourdough"], "includeRelatedHashtags": True})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    if row["type"] == "hashtag":
        print(row["hashtag"], row["postsCount"], [r["name"] for r in row["relatedHashtags"] or []])
```

### What this Actor does NOT do

- **No place search.** Instagram answers its place search only to logged in viewers; this Actor reads logged out only. Put the
  place in your keyword and use the place filter of the profile search instead.
- **No latest posts of a hashtag, no posts per day.** Instagram's hashtag feed needs a login; the top public reels of a hashtag
  are read (related hashtags add-on).
- **No Instagram search box results.** Instagram's own account and top search need a login; accounts come from Threads' user
  search, Meta's Ad Library and the creators of matching reels.
- **No posts or carousels by keyword, no audio search.** Reels only, and no reels of a sound.
- **No share counts, subtitles or transcripts of reels, no stories.** A logged out reader does not get them.
- **No follower or following lists, no likers, no business e-mail or phone of Instagram's contact button, no account country.**
  Instagram shows them only to logged in viewers; the bio, bio links, recent captions and the linked Facebook Page's contact are
  read instead.
- **No search engine results and no AI.** The Actor reads only Meta's own pages: no Google or other search engine, and no
  outside AI service.
- **No pages outside Meta.** The website a bio links to is not opened, and no e-mail address is verified against its mail server.
- **No monitoring across runs.** There is no memory of earlier runs, no "new results only" mode and no follower tracking; pass the
  accounts you already have in **Accounts to leave out** and schedule runs as you like.
- **No delivery into other apps and no custom code.** The Actor writes only to its own dataset (Apify's integrations, webhooks and
  the API deliver it anywhere), and it runs no mapping function you send.
- **No logins, cookies or accounts of yours.**
- **No more than Instagram lists.** About 96 reels per reels search (related searches and variants widen a keyword), about 100
  accounts per Threads search and 15 per Ad Library search, 55 hashtags per hashtag search.

### FAQ

#### Can you search Instagram without an account?

This Actor does. It reads Instagram's public reels search and hashtag search, Threads' user search and Meta's Ad Library, and
each account's public profile data, without an account, cookies or login, so your own Instagram account is never involved.

#### Is there an Instagram search API?

Instagram's search endpoints for developers were retired, and Meta's official API offers hashtag search only to Business or
Creator accounts, 30 hashtags a week. This Actor works as an Instagram search API through the Apify API, schedules, Make, n8n or
Zapier, for reels, accounts and hashtags.

#### Can I search Instagram reels by keyword?

Yes: pick Reels, type your words, and get the reels Instagram's own search shows for them, with plays, likes, comments, date and
music, sorted by relevance, newest or most played, with date and plays filters.

#### How do I find Instagram accounts by keywords in their bio?

Pick Profiles and give the keywords: the Actor finds accounts on Threads' user search, Meta's Ad Library and the creators of
matching reels, then reads every account's bio. **Must contain any of** keeps the accounts whose bio (or name, or recent posts)
holds your words.

#### How many posts does an Instagram hashtag have?

Pick Hashtags and type the hashtag's words: every hashtag Instagram lists for them comes with its exact post count, and **Only
the exact hashtag** keeps just the one you named. Turn on related hashtags to see the hashtags used most with it and, where Instagram lists them, their post counts.

#### Why does this Actor run with limited permissions?

It needs no more: it reads Meta's public pages and writes only to its own run's dataset and key-value store, so it cannot touch
anything else in your Apify account. Filtering an earlier run's profiles again reads that run's dataset, in your own account, by
its id.

### More data tools by Automation Craft

- [Instagram Reels Search Scraper: Reels by Keyword](https://apify.com/automation_craft/instagram-reels-search-scraper)
- [Instagram User Search Scraper: Profiles by Keyword](https://apify.com/automation_craft/instagram-user-search-scraper)
- [Instagram Posts Scraper: Profiles, Reels, No Login](https://apify.com/automation_craft/instagram-posts-scraper)
- [Threads Profile Scraper: Followers, Bio, Posts](https://apify.com/automation_craft/threads-profile-scraper)
- [Facebook Search Scraper: Keyword Posts, No Login](https://apify.com/automation_craft/facebook-search-scraper)
- [Facebook Page ID Finder: Names to Page IDs](https://apify.com/automation_craft/facebook-page-id-finder)
- [TikTok Hashtag Scraper: Posts, Free Date Filter](https://apify.com/automation_craft/tiktok-hashtag-scraper)

### Changelog

See the Changelog tab: 0.1 (2026-10-28) is the first release.

# Changelog

This Actor's version history is a separate document: https://apify.com/automation_craft/instagram-search-scraper/changelog.md

# Actor input Schema

## `searchTypes` (type: `array`):

What to look for on Instagram, one or more of: reels (public reels matching the keyword, with plays, likes and comments), profiles (public accounts matching the keyword, with followers, bio and engagement) and hashtags (hashtags matching the keyword, with their exact post counts). They run one after the other, in the order given; every row carries its searchType. Default: reels.

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

What to search, one keyword per line (up to 500): a niche, a trade, a brand, a topic or a place ("nail art", "coffee roaster", "wedding photographer miami"). A leading # is dropped. Reels search also takes a link to an instagram.com/popular/ or /explore/tags/ page and Boolean keywords with AND, OR, NOT in capitals, parentheses, "quoted phrases" or -word. Profiles search also takes accounts (usernames) and post links (postUrls) instead of keywords.

## `maxResultsPerKeyword` (type: `integer`):

The most rows one keyword delivers in each search type: reels (its related searches, variants and creator reels included; Instagram lists about 96 reels a search), profiles (all its searches together, or one post link's commenters) and hashtags (Instagram lists up to 55 hashtags a keyword).

## `maxResults` (type: `integer`):

Stops a search type once it delivered this many rows across all keywords (keywords are read in the order given): reels, profiles and hashtags are counted apart. Empty means no run limit.

## `requireKeywordMatch` (type: `boolean`):

Reels: keep only reels whose caption, hashtags or creator username hold every word of the keyword (Boolean keywords always apply their expression). Profiles: keep only accounts whose username, name or bio hold every word. Hashtags: keep only hashtags whose name holds every word. Rows left out are never charged; every row carries keywordMatch either way.

## `includeDetails` (type: `boolean`):

On (default): each reel also carries its likes, comments, posting time, length, music, location, collaborators, the paid partnership label and audio link, and each account its followers, following, posts, bio, links, category, account type, recent posts and engagement (billed as a Profile). Off: rows hold only what the search listed (faster; accounts billed as a Basic profile; filters that need details are refused).

## `sortBy` (type: `string`):

Most relevant keeps Instagram's own order for the keyword. Newest first and Most played first list every search of the keyword before delivering, then deliver in that order (newest by the reel's creation time).

## `searchRelatedKeywords` (type: `integer`):

Also search this many of the related keywords Instagram lists next to your keyword (0 to 10). Their reels are delivered under your keyword with foundVia "related". 0 searches only your keyword.

## `tryVariantsWhenEmpty` (type: `boolean`):

Instagram lists no reels for some broad single words ("coffee", "fitness"). When on, such a keyword is searched again as "<keyword> ideas", then "tips", then "inspiration", and the first that lists reels is read (foundVia "variant").

## `includeCreatorReels` (type: `boolean`):

After a keyword's reels are delivered, read the newest reels of each creator found and add those whose caption, hashtags or username match the keyword (foundVia "creator"), up to the number below per creator. Needs the reel details.

## `maxReelsPerCreator` (type: `integer`):

With creator reels on: the most reels added from one creator's newest reels (1 to 12).

## `postedSince` (type: `string`):

Keep only reels posted on or after this day (UTC), as 2026-10-01, or in the last N days, as "30 days" (counted back from the run's start). Needs the reel details.

## `postedUntil` (type: `string`):

Keep only reels posted on or before this day (UTC), as 2026-10-20. Needs the reel details.

## `minPlays` (type: `integer`):

Keep only reels with at least this many plays.

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

Keep only reels with at least this many likes. Reels whose creator hides the like count are left out. Needs the reel details.

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

Keep only reels with at least this many comments. Needs the reel details.

## `captionLanguages` (type: `array`):

Keep only reels whose caption is in one of these languages, as estimated from the caption text (a caption is read only with 40 letters or more and a clear result; other reels have no language and are left out). Empty keeps every language.

## `includeCreatorDetails` (type: `boolean`):

Add each creator's followers, following, post count, category, account type, links and bio to their reels. Billed once per creator per run as a Creator profile event.

## `maxCommentsPerReel` (type: `integer`):

Add up to this many of each reel's public comments (text, time, likes, author) to its row, in the order Instagram shows them. Each comment delivered is billed as one Comment event. 0 adds none.

## `includeViewCount` (type: `boolean`):

Add each reel's view count as its own embed page shows it (viewCount), a separate counter from plays. One more page read per reel, included in the Reel price.

## `usernames` (type: `array`):

Instagram accounts to read directly (up to 5,000): usernames, @handles, profile links (instagram.com/<username>/) or numeric account ids. Each one gives its own profile row; an account Instagram does not show logged out gets a free note on the account list row.

## `postUrls` (type: `array`):

Instagram post or reel links (up to 100). The authors of each post's public comments are read as profiles, with commentsOnPost (how many of the comments read they wrote). Likers are not public logged out.

## `maxCommentersPerPost` (type: `integer`):

For post links: the most distinct comment authors collected from one post before their profiles are read (about 15 comments a call).

## `searchThreads` (type: `boolean`):

Threads' user search: accounts whose name or username matches the keyword (about 100 a search). Finds accounts that use Threads; their Threads username is their Instagram username.

## `searchAdLibrary` (type: `boolean`):

Meta's Ad Library search: Facebook Pages matching the keyword that link an Instagram account (up to 15 a search), with the Page's id, category, likes and country on the row.

## `adLibraryCountry` (type: `string`):

The country the Ad Library search favours: ALL (default) or a two letter code in capitals such as US, GB or DE (local Pages come first).

## `searchReelCreators` (type: `boolean`):

Instagram's popular reels search for the keyword: the creators of its matching reels (24 on the first page; more with Reel search pages), accounts that post about the keyword. Broad single words list no reels.

## `reelSearchPages` (type: `integer`):

How many pages of Instagram's popular reels search to read for the reel creators: the first page lists 24 reels, each next one about 50, about 96 reels a search in all. One search call a page; more pages find more creators.

## `searchVariants` (type: `boolean`):

For a keyword of several words, also search Threads for its joined, underscored and dotted forms ("coffeeroaster", "coffee_roaster", "coffee.roaster"), which find accounts named that way (32 to 134 more accounts on six keywords measured). Three more searches a keyword.

## `expandWithRelatedProfiles` (type: `boolean`):

Read the accounts Instagram suggests next to each profile delivered for a keyword, a username list or a post link and deliver them too (foundVia related_profile, expansionLevel 1 or 2), up to that entry's cap (for a username list, Max profiles per keyword counts its suggested accounts). Logged out Instagram suggests accounts for about 13 of 100 profiles (67 of 500 measured).

## `expansionDepth` (type: `integer`):

With Suggested accounts on: 1 expands the profiles your keywords, usernames or post links delivered; 2 also expands the suggested accounts the first level delivered. Accounts found at the last level are not expanded.

## `includeRecentPosts` (type: `boolean`):

Adds the account's 6 newest posts (link, date, likes, comments, caption, pinned, paid partnership, place) to its row. Engagement figures are computed from them either way.

## `includeReelStats` (type: `boolean`):

Reads the account's 12 newest reels: median, average and highest play count and the newest reel's date (reelStats). One more read per account, no extra charge.

## `includeProfileInfo` (type: `boolean`):

Reads Instagram's profile info for each account: the business address (street, city, zip, coordinates) where the account shows one, the contact method, and whether it joined recently. One more read per account, no extra charge; Instagram limits this read hard and answers it for about a quarter of the accounts (profileInfoStatus says when it did not).

## `includeFacebookPageContact` (type: `boolean`):

For accounts that link a Facebook Page, reads the Page's public contact details (e-mail, phone, WhatsApp, website, address, hours, price range, services). Charged as one Facebook Page contact only when the Page shows an e-mail, a phone or WhatsApp number, a website, an address or its hours; a Page that shows only a price range, services or ratings is free.

## `includeRelatedProfiles` (type: `boolean`):

Adds the accounts Instagram suggests next to each profile (relatedProfiles; often none logged out). These name other accounts, so they need includePersonalData. One more read per account, no extra charge.

## `minFollowers` (type: `integer`):

Leave out accounts with fewer followers.

## `maxFollowers` (type: `integer`):

Leave out accounts with more followers.

## `minPosts` (type: `integer`):

Leave out accounts with fewer posts (private accounts show no post count and are left out by this filter).

## `maxPosts` (type: `integer`):

Leave out accounts with more posts.

## `verified` (type: `string`):

Any account (default), verified accounts only, or unverified accounts only.

## `accountTypes` (type: `array`):

Keep only these account types (empty keeps every type). Professional means business or creator.

## `influencersOnly` (type: `boolean`):

Keep only creator accounts and accounts whose category is one of a list of people who publish for an audience (Digital creator, Video creator, Blogger, Public figure, Artist, Musician, Athlete, model, Writer, Photographer, Gamer, Chef, Coach and more; README "Filters").

## `includePrivate` (type: `boolean`):

Private accounts show their name, bio and counts but no posts. Off leaves them out (free).

## `mustContain` (type: `array`):

Keep only accounts that hold at least one of these words or phrases (whole words, any case; a word of four letters or more also counts inside the username) in the part mustContainIn names.

## `mustContainIn` (type: `string`):

Where mustContain looks: the username, name and bio (default), the bio only, the username and name only, the captions of the 6 recent posts, or anywhere.

## `excludeWords` (type: `array`):

Leave out accounts whose username, name or bio holds any of these words or phrases.

## `categories` (type: `array`):

Keep only accounts whose Instagram category holds one of these words ("photographer", "coffee shop"). Accounts without a category are left out.

## `combineCategoryAndWords` (type: `string`):

When both categories and mustContain are set: an account must pass both (default), or either one.

## `languages` (type: `array`):

Keep only accounts whose bio and recent captions read as one of these languages (two letter codes such as en, es, de; our own estimate, accounts we cannot tell are left out).

## `locationMustContain` (type: `array`):

Keep only accounts that name one of these places in their bio, their business address (with includeProfileInfo) or their Facebook Page country.

## `contactFilter` (type: `string`):

Keep only accounts that show this contact in their bio, bio links or recent captions: any account (default), an e-mail or a phone, an e-mail, a phone, or both. The addresses and numbers themselves are delivered with includePersonalData.

## `requireWebsite` (type: `boolean`):

Keep only accounts with a website or another link in their bio.

## `minEngagementRate` (type: `number`):

Keep only accounts whose engagement rate (average likes and comments of their 6 newest posts over followers, in percent) is at least this. Accounts without visible likes are left out.

## `activeWithinDays` (type: `integer`):

Keep only accounts whose newest post (pinned posts aside) is at most this many days old.

## `minRecentPosts` (type: `integer`):

Keep only accounts with at least this many of their 6 newest posts inside the last recentPostsDays days (1 to 6).

## `recentPostsDays` (type: `integer`):

The period minRecentPosts counts in, in days back from the run's start (default 30).

## `minMedianReelPlays` (type: `integer`):

Keep only accounts whose 12 newest reels have at least this median play count. Needs includeReelStats.

## `reelPostedWithinDays` (type: `integer`):

Keep only accounts whose newest reel is at most this many days old. Needs includeReelStats.

## `minPlaysToFollowersPercent` (type: `number`):

Keep only accounts whose median reel play count is at least this percent of their followers. Needs includeReelStats.

## `maxPlaysToFollowersPercent` (type: `number`):

Leave out accounts whose median reel play count is above this percent of their followers (one viral reel). Needs includeReelStats.

## `excludeUsernames` (type: `array`):

Accounts never to deliver (usernames, @handles, profile links or ids), for example the accounts an earlier run already gave you (up to 10,000).

## `minMatchRatePercent` (type: `integer`):

After 100 accounts were read in the run, stop it when fewer than this percent of them passed your filters, so strict filters cannot read on for long. Empty never stops on this.

## `includeLeftOutAccounts` (type: `boolean`):

Adds a free row for every account read and left out (its username and the reason: a filter, not readable logged out, already delivered), so you see why an account is missing.

## `refilterDatasetId` (type: `string`):

The dataset id of an earlier run of this Actor (its Storage tab). Its profile rows are checked again with this run's filters and the ones that pass are delivered, without reading Instagram, each as a Re-filtered profile.

## `minHashtagPosts` (type: `integer`):

Keep only hashtags used on at least this many posts (the exact count Instagram shows). Empty means no lower bound.

## `maxHashtagPosts` (type: `integer`):

Keep only hashtags used on at most this many posts, for smaller tags where a post is seen longer. Empty means no upper bound.

## `exactHashtagOnly` (type: `boolean`):

Keep only the hashtag whose name is the keyword's words run together ("coffee roaster" keeps #coffeeroaster): one row per keyword with that hashtag's exact post count, for the stats of hashtags you already know. A keyword whose exact hashtag Instagram does not list gets no row (free).

## `includeRelatedHashtags` (type: `boolean`):

On: each hashtag row also carries the hashtags used most often with it in its top public reels (relatedHashtags: up to 10, each with how many of those reels use it, and its exact post count and popularity band when Instagram's hashtag search lists it; a count that could not be read is empty) and those reels (topReels: link and plays, with their median plays), read from the hashtag's public popular reels page (one more read per hashtag, and one hashtag search per related hashtag the run does not know yet). Billed as a Related hashtag list per hashtag where at least one related hashtag was found; a hashtag without public reels is free. The reads draw on the run's work allowance for add-ons: when it is spent, the remaining hashtags come without the list (relatedHashtagsStatus allowance, counted in the keyword row and the status message) and are not billed for it. Off (default): not read.

## `includePersonalData` (type: `boolean`):

Off (default): the dedicated fields that name or reach a person are empty. Reels: taggedUsers and mentions, and links to Instagram, Facebook, Messenger, Threads or WhatsApp in the creator's website and bio links and in the keyword description's sources. Profiles: emails and phones (read from the bio, bio links and recent captions), relatedProfiles, and links to Instagram, Facebook, Messenger, Threads or WhatsApp and contact links (mailto, tel, sms, callto, Telegram, Signal, Viber, Skype) in the website and bio links. On: they are filled. Captions, bios and comments are always delivered as Instagram serves them. Hashtag rows hold no personal data. You are responsible for a lawful use of personal data, GDPR included.

## `maxRunSeconds` (type: `integer`):

Stops the run after this many seconds, with a row for each entry not finished. Empty means the platform timeout.

## `requestTimeoutSecs` (type: `integer`):

How long one request may take before it is retried.

## `maxRetries` (type: `integer`):

How often a failed request is retried on a fresh connection.

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

Default: Apify Proxy without a group or country, which reads from the run's own address first and moves a request type to Apify's datacenter proxy when it is refused there. Choose a group or a country to read only through Apify Proxy, or give your own proxy links. A residential group is read through the datacenter pool instead (no extra cost).

## Actor input object example

```json
{
  "searchTypes": [
    "reels"
  ],
  "keywords": [
    "nail art"
  ],
  "maxResultsPerKeyword": 12,
  "requireKeywordMatch": false,
  "includeDetails": true,
  "sortBy": "relevance",
  "searchRelatedKeywords": 0,
  "tryVariantsWhenEmpty": false,
  "includeCreatorReels": false,
  "maxReelsPerCreator": 2,
  "includeCreatorDetails": false,
  "maxCommentsPerReel": 0,
  "includeViewCount": false,
  "maxCommentersPerPost": 50,
  "searchThreads": true,
  "searchAdLibrary": true,
  "adLibraryCountry": "ALL",
  "searchReelCreators": true,
  "reelSearchPages": 1,
  "searchVariants": false,
  "expandWithRelatedProfiles": false,
  "expansionDepth": 1,
  "includeRecentPosts": true,
  "includeReelStats": false,
  "includeProfileInfo": false,
  "includeFacebookPageContact": false,
  "includeRelatedProfiles": false,
  "verified": "any",
  "influencersOnly": false,
  "includePrivate": true,
  "mustContainIn": "bio_or_name",
  "combineCategoryAndWords": "and",
  "contactFilter": "any",
  "requireWebsite": false,
  "recentPostsDays": 30,
  "includeLeftOutAccounts": false,
  "exactHashtagOnly": false,
  "includeRelatedHashtags": false,
  "includePersonalData": false,
  "requestTimeoutSecs": 30,
  "maxRetries": 2,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `items` (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 = {
    "searchTypes": [
        "reels"
    ],
    "keywords": [
        "nail art"
    ],
    "maxResultsPerKeyword": 12,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation_craft/instagram-search-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 = {
    "searchTypes": ["reels"],
    "keywords": ["nail art"],
    "maxResultsPerKeyword": 12,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("automation_craft/instagram-search-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 '{
  "searchTypes": [
    "reels"
  ],
  "keywords": [
    "nail art"
  ],
  "maxResultsPerKeyword": 12,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call automation_craft/instagram-search-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation_craft/instagram-search-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/IwvWQvCtydxf3X6PC/builds/CgVhN8ZU1wvS9FxbD/openapi.json
