Instagram Search Scraper
Pricing
from $1.00 / 1,000 results
Instagram Search Scraper
Search Instagram by keyword for profiles, Reels, hashtags, places, music, and exact-hashtag content. Get structured, deduplicated results with automatic pagination.
Pricing
from $1.00 / 1,000 results
Rating
0.0
(0)
Developer
LanceAPI
Maintained by CommunityActor stats
0
Bookmarked
15
Total users
8
Monthly active users
a day ago
Last modified
Categories
Features
Share
Instagram Search Scraper API
Search profiles, top results, Reels, hashtags, places, music and exact-hashtag posts in one structured workflow — with run-wide de-duplication and predictable pay-per-event billing.
Search Instagram by keyword and get structured, de-duplicated data back. Seven search modes: profile search, top results (the media Instagram surfaces for a keyword), Reel search, hashtag search, hashtag posts, Instagram place search and Instagram music search — pick one per run, so a keyword means one search and one result shape.
Give it a list of queries, choose the kind of result you want, and it returns one dataset. A profile found by three different queries is delivered once, with all three queries listed on the row, so the file you download is already the file you would have built by hand.
- Seven search modes — profiles, top results, Reels, hashtags, places, music and exact-hashtag posts.
- Optional full profile data — turn it on for a profile search and each account also carries its exact follower, following and post counts, bio, links, category and business flags. Off by default.
- Batch up to 200 canonical searches and return up to 3,000 unique rows.
- Duplicate protection — if several queries discover the same result, it is delivered and billed once.
- No Instagram login, cookies or session required.
- Predictable billing — one Search charge when a canonical search starts, then Result charges only for unique delivered rows.
Top results is the widest keyword content search: it returns whatever media Instagram's own top tab holds for the keyword, which in practice is mostly Reels with the occasional image or carousel post. Reels is the narrower one — the same keyword, restricted to the Reels tab. Ordinary image and carousel posts are found most reliably through exact-hashtag posts (Hashtag posts) rather than any keyword search — the details are in Limits.
Quick Start
A first run needs nothing more than a keyword and the kinds of results you want:
{"queries": ["travel"],"searchType": "content","maxResultsPerQueryType": 20}
- Paste that input in the Actor's Input tab.
- Click Start.
- Open the run's Dataset when it finishes.
- Each unique result is one structured row. When several searches found the same result,
matchedQueriesandmatcheslist all of them on that single row.
For a first run, keep maxTotalResults at its default. hashtagTab only applies when you pick Hashtag posts.
What a row looks like
Illustrative example values — invented for illustration, not taken from a run.
{"resultType": "profile","query": "openai","matchedQueries": ["openai", "artificial intelligence"],"url": "https://www.instagram.com/openai/","id": "1234567890","username": "openai","fullName": "OpenAI","followersCount": 4100000,"isVerified": true,"fullProfileIncluded": false,"rank": 1,"scrapedAt": "2026-01-14T09:12:44.512Z"}
The full field reference is in Output.
Choose the right search type
There are seven search types. Each one reads a different kind of query and returns a different kind of result, so pick the one that matches what you are looking for — the wrong type is the most common way a first run fails or comes back empty.
| Search type | What to search | Example input | What you get |
|---|---|---|---|
Profiles (profiles) | A username or an account-related keyword | @openai | Account records: username, full name, id, profile URL and picture, verification and privacy flags |
Top results (content) | A keyword or search phrase | hiring freelance developers | The media Instagram's top tab holds for the keyword — mostly Reels, sometimes image or carousel posts |
Reels (reels) | A video topic, keyword or phrase | AI video generator | Reels only: shortcode, caption, video URL, play and view counts, owner |
Hashtags (hashtags) | A topic or keyword to discover hashtags for | travel photography | Hashtag records: name, URL, post count |
Places (places) | A city, landmark or venue name | Central Park | Place records: id, name, address, city, coordinates |
Music (music) | A song, artist or music keyword | lofi beats | Track records: title, artist, duration, cover, preview audio |
Hashtag posts (hashtag_content) | One exact hashtag name, no spaces | #freelance | Everything under that hashtag — image, carousel and Reel posts |
Every one of these examples is a valid input for its type. Each query is treated as an independent search, and all of them are case-insensitive and de-duplicated across the run.
Hashtags vs. Hashtag posts
These two sound alike but do opposite jobs.
- Hashtags is discovery: you give it a topic and it finds the hashtags that relate to it.
travel photographyreturns a list of hashtag records, so you can see which tags exist and how much content sits under each. - Hashtag posts is depth: you already know one exact hashtag, and you want the posts inside it.
#travelphotographyreturns the media itself, not a list of tags.
Rule of thumb: to find hashtags, use Hashtags; to read the posts under one you already know, use Hashtag posts.
Top results vs. Reels
Both take a keyword, but they read two different lists, not two filters over one list.
- Top results reads Instagram's own top tab for the keyword — the widest net. In practice it returns mostly Reels, with the occasional image or carousel post.
- Reels reads only the Reels tab — the same keyword narrowed to short videos.
A short or ambiguous keyword can return nothing on the Reels tab while the top tab still has a full page, so Top results is the safer default when you are not sure.
Profiles vs. content searches
Profiles finds accounts; content, reels, hashtags, places and music find other kinds of results.
- Profiles returns account records — who the accounts are, their follower counts, verification and privacy flags. Use it when you know a topic but not the handles.
- Top results / Reels / Hashtag posts return media — posts and videos. Use them when you want content, not accounts.
A leading @ on a Profiles query is removed automatically, so @openai and openai find the same accounts.
JSON examples for each type
Each search type is one minimal input. Only queries and searchType are required; every other field falls back to its default.
Profiles
{ "queries": ["@openai"], "searchType": "profiles" }
Top results
{ "queries": ["hiring freelance developers"], "searchType": "content" }
Reels
{ "queries": ["AI video generator"], "searchType": "reels" }
Hashtags
{ "queries": ["travel photography"], "searchType": "hashtags" }
Places
{ "queries": ["Central Park"], "searchType": "places" }
Music
{ "queries": ["lofi beats"], "searchType": "music" }
Hashtag posts
{ "queries": ["#freelance"], "searchType": "hashtag_content" }
You can send several queries at once — each one runs as its own search under the chosen type:
{ "queries": ["@openai", "fitness coach", "nike"], "searchType": "profiles" }
How it works
- Submit queries and choose the search type you want.
- The Actor validates and normalises the input, and removes duplicate search operations.
- Valid searches run and paginate where that search mode supports pagination.
- Results are normalised and de-duplicated across the whole run.
- If you asked for full profile data, each account found is read once and its profile fields are merged onto the row that already exists. A read that fails leaves the row as it was.
- Unique rows are written to the Dataset with query and source-match context for downstream analysis.
What you can search
| You want | Search type | What comes back |
|---|---|---|
| Accounts matching a keyword | Profiles (profiles) | Username, full name, id, profile URL and picture, verification and privacy flags — plus the full public profile of each account when you turn on includeFullProfile |
| The media Instagram surfaces for a keyword | Top results (content) | Top results: mostly Reels, sometimes image or carousel posts — shortcode, caption, media type, counts, owner |
| Reels matching a keyword | Reels (reels) | Shortcode, caption, video URL, play and view counts, owner |
| Hashtags matching a keyword | Hashtags (hashtags) | Hashtag name, URL, post count |
| Places matching a keyword | Places (places) | Place id, name, address, city, coordinates |
| Music tracks matching a keyword | Music (music) | Track title, artist, duration, cover, preview audio |
| Posts under an exact hashtag — the image and carousel entry point | Hashtag posts (hashtag_content) | Everything carrying that hashtag (image, carousel and Reel posts), read from the tab you choose |
Hashtag posts is the only type that needs an exact hashtag rather than free text, and the only one where you choose an ordering: top, recent, or reels. See Choose the right search type for how it differs from hashtags.
One type per run. You pick a single search type, so one keyword means one search and one result shape — and the bill is one search, not one per type. Want a second type? Run the Actor again; it is a second run, not a second charge on the first.
Use cases
Each search type answers a different question, so it helps to know which one you want before you run anything:
- Find accounts by keyword — search
profilesfor a topic and get the accounts Instagram surfaces for it, with follower counts, so you can shortlist creators or brands. This is the way in when you know the topic but not the handles. - See everything a keyword surfaces — search
contentfor the widest net: the top results Instagram itself shows for that word, which mixes Reels with the occasional image or carousel post. Start here when you do not yet know what kind of media a topic lives in. - See only the videos a keyword surfaces — search
reelsto see which videos are being surfaced for a topic, with views, plays, likes and the video asset. - Read the posts behind an exact hashtag — search
hashtag_contenton a single hashtag and page through its feed. This is the search type that returns ordinary image and carousel posts most reliably. - Hashtag research before a campaign — search
hashtagsto see which hashtags exist around a topic and how much content sits under each. - Location and local-business research — search
placesto resolve a city, district or venue into a record with coordinates. - Music and audio discovery — search
musicto find the tracks and artists behind a genre, mood or campaign. - Topic sweep — run the Actor once per search type on the same keyword to get a first-pass picture before narrowing down. Each run is billed separately.
- Seed lists for downstream analysis — hand the output to a profile scraper, a Reel scraper or an audience scraper when you need depth on what you just found.
The Actor collects and structures the data. Analysis on top of it — sentiment, scoring, influencer ranking, outreach decisions — happens in your own tools; this Actor does not perform it.
Input
{"queries": ["travel", "specialty coffee", "@openai", "#vanlife"],"searchType": "content","maxResultsPerQueryType": 50,"maxTotalResults": 1000}
| Field | Type | Required | Default | Prefill | Notes |
|---|---|---|---|---|---|
queries | string list | Yes | — | ["travel"] | Keywords, usernames, hashtags, place names or music terms. One per line in the Console. |
searchType | select | Yes | — | content | One search type per run. Console labels: Profiles, Top results, Reels, Hashtags, Places, Music, Hashtag posts. API values: profiles, content, reels, hashtags, places, music, hashtag_content. |
includeFullProfile | boolean | No | false | — | Full profile data. Only applies when searchType is profiles: each account found is also read for its public profile fields. Every other search type ignores it, and the run summary says so. |
maxResultsPerQueryType | integer, 1–200 | No | 50 | — | Upper bound per search; see Limits. |
maxTotalResults | integer, 1–3000 | No | 1000 | — | Run-wide ceiling on delivered rows. |
hashtagTab | select | No | recent | — | Only used by Hashtag posts (hashtag_content): top, recent or reels. Every other search type ignores it; if you picked a tab other than the default, the run summary says the choice did not apply. |
Those six fields are the whole input. There is no second, similarly named field next to them.
Required means required. queries and searchType have no default, and the platform checks both before the Actor starts: a request that omits either one is rejected outright, the run never begins, and nothing is charged. The Console pre-fills them so that a first run is one click, but a pre-filled field is not a default.
The legacy searchTypes array is still accepted for API compatibility — older integrations send it, and it takes precedence when present. It is no longer part of the public input schema, so it cannot be picked in the form any more; only code written before the single-type input existed sends it. If your integration still uses it, also send searchType with any one of its values: the platform requires that field to be there, and the array still decides what actually runs. See Batch size.
The run summary reports what actually ran. RUN_SUMMARY in the run's Key-Value store lists the effective search type, the result ceilings that were applied, and a notes list for anything that differed from what you asked for — a hashtagTab you set to something other than the default on a run that searched no hashtag posts, a limit that had to be pulled inside its range, or a run that came in through the legacy searchTypes array. Nothing this Actor changes is changed silently.
Full profile data
includeFullProfile turns a profile search into a search plus one profile read per account. It is off by default, and it only does anything for searchType: "profiles".
{"queries": ["ai"],"searchType": "profiles","maxResultsPerQueryType": 20,"includeFullProfile": true}
What it adds to each account row: biography, bioLinks, externalUrl, followersCount, followingCount, postsCount, category, isVerified, isPrivate, isBusinessAccount, isProfessionalAccount, profilePicUrlHD, highlightReelCount, joinedRecently — and fullProfileIncluded: true to say the row really was enriched.
Four things it does not do:
- It does not add rows. One account is still one row; the profile fields land on the row the search already produced.
- It does not overwrite good data with nothing. When the profile read has no value for a field — a bio the account does not have, a count a source does not publish — the row keeps what the search already knew.
nullin a profile read means "not published", not "empty". - It does not fail the run. If a profile cannot be read, the account is still delivered with its search fields,
fullProfileIncludedstaysfalse, and nothing extra is charged for it. A run where some reads fail is still a successful search — the summary reports how many were enriched and how many were not. - It does not include recent posts. The profile read is the profile, not its media. Use a profile-posts Actor for that.
Two things worth knowing before turning it on:
- It costs more. Each account whose profile data was successfully added carries one Full Profile charge on top of the Search and Result charges. Accounts that could not be enriched are not charged for. See Pricing.
- It is slower. Each account needs one extra request, done a few at a time so a large batch stays stable. A search of 200 accounts with this on takes longer than the same search with it off.
includeFullProfile on a run that searched hashtags, Reels or anything other than profiles is not an error: the flag is ignored, the summary says so, and nothing is charged for it.
How queries are read
The same word means different things to different searches, so normalisation is per search type rather than one rule for everything.
| Search type | What happens to the query | Example |
|---|---|---|
profiles | A leading @ is removed | @openai → openai |
hashtags | A leading # is removed; the rest is free text | #travel → travel |
hashtag_content | A leading # is removed, the result is lowercased, and it must be exactly one valid hashtag | #VanLife → vanlife |
content | Used exactly as typed | coffee shop stays coffee shop |
reels | Used exactly as typed | travel photography stays travel photography |
places | Used exactly as typed | New York stays New York |
music | Used exactly as typed | lo-fi beats stays lo-fi beats |
- Duplicates are merged. Queries that become identical after normalisation are collapsed, and the run summary reports how many were merged.
- Exact hashtags are strict on purpose. A hashtag cannot contain a space, so
#new yorkis not a hashtag; that one search is skipped instead of being silently rewritten intonewyork, which would search for something you never asked for. The same query still runs for the other types you selected, and the summary reports how many searches were dropped.
Batch size
With one search type per run, a run accepts at most 200 canonical search operations, counted after query normalisation, type validation and duplicate removal — which, with a single type, means at most 200 queries.
| Queries | Search types | Canonical searches |
|---|---|---|
| 200 | 1 | 200 |
| 100 | 1 | 100 |
| 40 | 1 | 40 |
The legacy searchTypes array can still name several types when it is sent by code, and then the same 200-operation ceiling applies across all of them: 33 queries × 6 types is 198, 28 × 7 is 196.
A batch that expands past 200 is rejected before any data is fetched, and the message states the count you asked for. The operation limit is validated before any search starts, so an oversized batch creates no Search or Result charges. The standard Actor Start charge may still apply to the run.
Output
One row per unique result — profile, Reel, post, hashtag, place or music track. Every row has the same shape, so the dataset loads straight into a spreadsheet or a database. Missing values follow one rule: a value the source did not report — a number, a name, a URL — is null, never 0, an empty string or "N/A"; a list the source reported as empty is [], never null. So hashtags, mentions, matchedQueries and matches are always arrays, and are empty arrays rather than null when there is nothing in them.
Main fields
| Field | Meaning |
|---|---|
resultType | One of profile, reel, post, hashtag, place, music. Filter on this to split the dataset by type. |
query | The query that found this row, after normalisation. |
matchedQueries | Every query that found it. A row found by three queries is still one row. |
url | Canonical Instagram URL. null for music, which has no page of its own. |
rank | Position in the delivered dataset. |
sourceRank | Position this result had in the source search result when first discovered. |
matches | Every discovery that pointed at this row — query, search type and position in that result list. |
scrapedAt | When the run started, ISO 8601 in UTC. Every row in a run carries the same value, so a dataset can be tied back to the moment it was collected. |
fullProfileIncluded | true when this account's row also carries data from a full profile read, false otherwise — including every row from a run where includeFullProfile was off. |
Type-specific fields: id, username, fullName, biography, bioLinks, followersCount, followingCount, postsCount, isVerified, isPrivate, isBusiness, isBusinessAccount, isProfessionalAccount, category, externalUrl, profilePicUrl, profilePicUrlHD, highlightReelCount, joinedRecently for accounts; shortcode, caption, videoUrl, thumbnailUrl, playCount, viewCount, likeCount, commentCount, mediaType, publishedAt, ownerUsername, ownerId, hashtags, mentions, locationId, locationName, musicTitle, musicArtist for Reels and posts; name, mediaCount for hashtags; name, address, city, shortName, latitude, longitude for places; title, artist, durationSeconds, coverUrl, audioUrl, previewUrl, explicit, audioClusterId, audioAssetId for music.
Two business flags, not one. isBusinessAccount is Instagram's own account-level business flag. isProfessionalAccount is its professional-account flag; an account can be one, the other, both or neither. isBusiness is kept for older integrations and always carries the same value as isBusinessAccount — it is never a separate signal, and it is never true because a source said something adjacent (is_business) that does not mean this.
Views in the Dataset tab
The Dataset tab offers seven column layouts over the same rows, so the fields that matter for your search type come first instead of being buried behind twenty empty columns.
| View | Leads with | Best for |
|---|---|---|
Overview | type, query, the name columns, caption, followers, likes, views | a run that mixes several search types |
Profiles | username, full name, id, followers, posts, following, verified, private, business, professional, category, bio | account searches, with or without full profile data |
Reels | owner, caption, views, plays, likes, duration, published | Reel searches |
Hashtag posts | owner, caption, likes, comments, media type, published | exact-hashtag feeds |
Hashtags | hashtag, posts with hashtag | hashtag discovery |
Places | place, city, address, coordinates | place searches |
Music | track, artist, duration, cover, preview | audio searches |
Two things a view does not do. It does not filter rows — a Reels run and a profile run in the same dataset share one table, and the type column tells you which is which. And it does not change an export: a JSON, CSV or Excel download always contains all 63 fields regardless of the view you were looking at.
Ordering: hashtag tabs, rank, sourceRank and matches
Four different things, deliberately kept apart:
hashtagTabchooses the source feed.top,recentandreelsare three different lists of posts, not three sortings of one list. Changing the tab changes what exists to be found.sourceRankpreserves the source order for that one search. It is the position the result had in the list the source returned, so a Reel that was third for#vanlifekeepssourceRank: 3on its row.rankis run-level delivery order after global de-duplication — the order of rows in your dataset, not an Instagram ranking and not a per-hashtag order.matches[]preserves per-query positions. When one row was found by#vanlifeinrecentand by the keywordvaninreels, each discovery keeps its ownquery,searchTypeandsourceRank.
A dataset mixing several hashtags, tabs and search types is therefore not sorted by any single tab. If you need "what the top tab of one hashtag looked like", filter that one search out of matches and sort by sourceRank.
Run summary
Every run also writes a RUN_SUMMARY record with:
- an
outcomefor the whole run —SUCCESS,PARTIAL,NO_DATAorFAILED— so one word tells you whether the run delivered everything it promised; - how many queries were supplied, used, merged and rejected, and how many search operations ran;
- how many rows were saved, how many duplicates were removed, and the count per result type;
- an outcome for every search — including the ones that returned nothing and why;
- what was billed: searches started, searches charged, unique results delivered, results charged, and whether the two halves agree;
- when
includeFullProfilewas on: how many accounts were asked about, how many were enriched, how many were not, and how many Full Profile charges that produced. A run that searched no profiles says the flag did not apply.
What each outcome means
| Outcome | What it means | What you get | Is retrying useful? |
|---|---|---|---|
SUCCESS | The search ran and finished normally. | All the results it found. | No need. |
PARTIAL | The search ran and found results, but stopped before it was complete — a result or page limit of this Actor was reached, or the source stopped returning complete pages. | The results it did find; nothing is discarded. | Usually yes, with a narrower query or a higher limit. |
TRUNCATED | The search was not executed. Either the run reached a limit before it could start, or its charge could not be confirmed. It is not a statement that nothing matched. | Nothing from this search. | Yes — raise the limit, or run again. |
NO_DATA | The search completed normally but returned no usable matches from the available search result. | Nothing, because there was nothing usable. | No; try a different keyword or type. |
UNAVAILABLE | The search could not be completed right now. A re-run may well succeed. | Nothing from this search. | Yes. |
FAILED | The search failed for a technical reason. | Nothing from this search. | Yes. |
INVALID | The input for that search was not usable — for example hashtag_content with a query that is not a single hashtag. | Nothing from this search. | No; fix the input. |
queriesWithNoResults in the summary lists only NO_DATA searches — searches that ran and came back with nothing usable. A search that never ran is listed as TRUNCATED instead, because "we did not ask" and "we asked and found nothing" are different facts, and only one of them is about Instagram.
These outcomes describe each search. includeFullProfile never turns a successful search into a failure — but because the profile data is part of what you asked for, the run summary also carries a single outcome for the whole run, and that one is PARTIAL when some accounts could not be enriched. A run that found 20 accounts and enriched 18 is outcome: "PARTIAL", still delivers all 20 rows, and still reports the search itself as SUCCESS. Both facts are shown, because only one of them is about Instagram.
This Actor recovers from common temporary failures on its own, so a single flaky response does not by itself lose you a search. How that is done is internal and is deliberately not exposed in the output.
Limits
Keyword searches return almost only video. That distinction matters, so it is stated plainly instead of being left for you to discover:
contentandreelsare both keyword searches, and both come back overwhelmingly as Reels. In one captured top-search page for a keyword, all 20 items were Reels; a later run on the same keyword returned 18 Reels plus 1 carousel and 1 image post. Posts do appear — they are simply the minority.- Content under a specific hashtag is searchable, images included. A hashtag feed still leans towards video, but it is where image and carousel posts actually appear — roughly one row in ten in the sample this Actor was built against.
- There is no "image posts" search tab on Instagram, so no button here can honestly promise one. Any product that offers "search image posts by keyword" is quietly reading a hashtag or a profile feed, and so is this Actor when you use
hashtag_content.
If you need image posts, start from a hashtag or an account instead of a keyword: give it a hashtag and it returns whatever that hashtag contains.
Other boundaries worth knowing:
- One run covers at most 200 canonical searches and 3,000 rows. Both are upper bounds, not targets.
maxResultsPerQueryTypeis a ceiling, not a guarantee. A search returns fewer results when the source runs out of them, when the result count is genuinely smaller, or when this Actor's own page budget is reached first — in that last case the search is markedPARTIALrather than reported as complete.- Hashtag tabs have different depth.
recentsupports paging through older posts.topandreelsreturn a single page that cannot be continued, so they are short by nature — a finite, curated set. - Large batches can take longer than small ones. Searches are processed in a controlled way to keep batch execution stable.
contentis the slowest type. It reads a live result page rather than a cached list, and one page can take up to a minute when Instagram is slow. That is waiting, not an error: the Actor keeps the request open and returns the page. If a page still has not arrived after 75 seconds it is reported as a temporary issue rather than as "no results", so a slow source never masquerades as an empty one.- Results reflect the source at the moment you ask. Search results are not a complete index of Instagram, and the same query on two different days legitimately returns overlapping rows.
Notes on data quality
- Missing values stay missing. If a source does not report a follower count, the field is
nullrather than0, so "unknown" and "zero" never look the same in your data. - Placeholder text is discarded. Some sources answer with a sentence such as "Access delayed…" where a name or city belongs; that is recognised and turned into
nullinstead of being stored as if it were a real name. - Counts are as reported. Follower counts and other engagement numbers are what Instagram last reported, not live counters.
- No filler requests. Search results can be thinner than a profile page: counts are often absent on a keyword search even when the account's own page shows them. The Actor does not make extra requests to fill those gaps silently — the one way to ask for more is
includeFullProfile, which is explicit, is priced per account, and saysfullProfileIncludedon every row it touched.
Pricing
Pay for searches that actually run and unique results that are actually delivered.
- Actor Start — $0.0005 per run. Applied once by the Apify platform.
- Search — one charge when one canonical query × result type actually starts. Additional pages and automatic recovery do not create another Search charge.
- Result — one charge per unique result successfully delivered. When several queries discover the same profile, Reel, hashtag, place, post or music item, it is delivered once and creates one Result charge.
- Full Profile — one charge per account whose profile data was successfully added. Only when you turn on
includeFullProfile. An account that could not be enriched, or a row that already had the flag from a different run, creates no Full Profile charge here.
Results start at $1.00 per 1,000 on eligible plans. Search pricing starts at $2.00 per 1,000 on eligible paid plans. Full Profile data is $1.50 per 1,000 accounts on the Free plan, falling to $1.00 on higher plans. See the Pricing tab for current plan-specific rates.
Platform usage is currently included under this Actor's active Pricing configuration.
What controls cost
maxResultsPerQueryTypelimits result volume for one search and helps control Result-fee exposure.maxTotalResultscaps unique result rows across the run and helps control Result-fee exposure.- Selecting fewer query × search-type combinations reduces the number of possible Search charges.
- Duplicate canonical searches are not charged twice.
- Duplicate results are not charged twice.
- An executed search that returns zero usable results can still create one Search charge, but creates no Result charge.
includeFullProfileadds one Full Profile charge per successfully enriched account. Turn it off to keep a profile search at Search + Result only.- An account that could not be enriched creates no Full Profile charge, even though the row is still delivered.
- Invalid or never-started search operations create no Search charge.
Neither result limit is a guaranteed total-cost cap: Actor Start and Search charges still apply. Use them to bound how much data one run can produce, not as a spending guarantee.
Example
1 run + 10 Search operations + 100 unique Results
Actor Start $0.000510 Searches $0.0200100 Results $0.1500--------------------------Total $0.1705
Example using current Bronze rates. See the Pricing tab for current plan-specific rates.
API access
Every run follows the same three steps: start the Actor → wait for the run to finish → read its dataset. Only the run id and the dataset id tie the steps together, and both come back in the start response.
Authentication is always a bearer header. Tokens are never sent in a query string:
export APIFY_TOKEN=... # Settings → Integrations in the Apify Consoleexport ACTOR_ID=lance_api~instagram-search-scraper-api # REST form: <owner>~<actor-name>
The SDKs take the same identifier with a slash: lance_api/instagram-search-scraper-api.
cURL
# 1. Start a run and keep its idsRUN=$(curl -sS -X POST "https://api.apify.com/v2/acts/$ACTOR_ID/runs" \-H "Authorization: Bearer $APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"queries":["travel"],"searchType":"reels","maxResultsPerQueryType":50}')RUN_ID=$(printf '%s' "$RUN" | jq -r '.data.id')DS=$(printf '%s' "$RUN" | jq -r '.data.defaultDatasetId')# 2. Wait for it to finishcurl -sS "https://api.apify.com/v2/actor-runs/$RUN_ID?waitForFinish=300" \-H "Authorization: Bearer $APIFY_TOKEN"# 3. Read the datasetcurl -sS "https://api.apify.com/v2/datasets/$DS/items?format=json&clean=true" \-H "Authorization: Bearer $APIFY_TOKEN"
JavaScript
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });// 1 + 2: start the run and wait for it to finishconst run = await client.actor('lance_api/instagram-search-scraper-api').call({queries: ['travel', '#vanlife'],searchType: 'reels',maxResultsPerQueryType: 50,});// 3: read the datasetconst { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items.length, items[0]);
Python
import osfrom apify_client import ApifyClientclient = ApifyClient(os.environ["APIFY_TOKEN"])# 1 + 2: start the run and wait for it to finishrun = client.actor("lance_api/instagram-search-scraper-api").call(run_input={"queries": ["travel", "#vanlife"],"searchType": "reels","maxResultsPerQueryType": 50,})# 3: read the datasetitems = client.dataset(run["defaultDatasetId"]).list_items().itemsprint(len(items), items[0])
Both queries and the search type are required in an API call: send them explicitly. The field is searchType (one value), and the platform rejects a request that leaves it out before the run starts, so nothing is charged for a call that forgot it.
The older searchTypes array still works and takes precedence when present, so published integrations keep running unchanged — with one addition: because the platform now requires searchType to be present, send it alongside the array (any one of the array's own values). The array still decides what runs; searchType is only there to satisfy the platform. The run summary names the source, so you can always see which one was used.
FAQ
Can I search ordinary image posts by keyword?
Not reliably. Both keyword types (content and reels) come back mostly as Reels, with image and carousel posts only as the minority, because that is what Instagram's keyword search holds. To reach image and carousel posts, search an exact hashtag (hashtag_content) or a specific account instead. Limits explains why.
What is the difference between content and reels?
Both take a keyword, and they read two different lists. content reads Instagram's top results for that word — the widest net, mostly Reels with the occasional image or carousel post. reels reads only the Reels tab. A short or ambiguous keyword can return nothing on the Reels tab while the top tab still has a full page, so content is the better default.
What is the difference between hashtags and hashtag_content?
hashtags takes a topic and discovers which hashtags exist around it — travel photography returns hashtag records. hashtag_content takes one exact hashtag and returns the posts inside it — #vanlife returns media. One is discovery, the other is depth.
Can I combine several search types?
Not in one run: the input form takes a single search type, so a keyword means one search and one bill. Run the Actor again for a second type — each run is billed on its own. (Code written before the single-type input existed can still send the legacy searchTypes array to search several types in one run; the run summary says when that happened.)
Why did I get fewer than maxResultsPerQueryType?
Because the number is a ceiling. The source may genuinely have fewer results, may stop returning complete pages, or the Actor's own page budget may be reached first — the run summary says which, and marks the search PARTIAL when it was cut short.
What happens at maxTotalResults?
The dataset stops growing. Searches that have not started are not started and are reported as TRUNCATED; a search that was mid-flight keeps what it had and is reported as PARTIAL. Raise the limit and run again, or split the batch.
Are duplicate results returned twice?
No. De-duplication is run-wide: one row per unique result, with every query that found it in matchedQueries and matches. Duplicates are billed once for the same reason — one row, one Result charge.
What does NO_DATA mean?
The search ran and completed, but returned no usable matches from the search result that was available. It does not claim that all of Instagram has nothing for that word — only that this search came back empty.
Do I need an Instagram login, cookies, or a session? No. The Actor needs no Instagram account, no cookies and no session, and it never asks for your Instagram password.
Which hashtag tabs support pagination?
recent pages through older posts. top and reels are single-page only, so their result sets are naturally short.
How is this Actor billed?
This Actor uses pay-per-event pricing. A Search charge applies once when one canonical query × result type actually starts, and a Result charge applies to each unique result successfully delivered. Additional pages and automatic recovery do not create additional Search charges, and duplicate results are not charged twice. With includeFullProfile on, each successfully enriched account adds one Full Profile charge; an account that could not be enriched adds none. A $0.0005 Actor Start charge applies once per run. See the Pricing tab for current plan-specific rates.
What does includeFullProfile actually add?
The public profile of each account the search found: exact follower, following and post counts, bio, links, category, verification and privacy flags, avatar, highlight count, and the business and professional flags. It does not add rows, does not include recent posts, and does not replace a search row's existing values with empty ones.
Why is fullProfileIncluded false on some rows even though I turned it on?
Each account is read once. When that one read fails — the source timed out, answered with an error, or returned a profile that is not the account that was asked for — the row is still delivered with its search fields and stays false, and no Full Profile charge is raised for it. The run summary reports the count, and re-running is the way to retry those accounts.
Troubleshooting
| Symptom | Meaning |
|---|---|
Run status FAILED, no rows, summary says UNAVAILABLE | The search could not be completed at all. Re-run; if it persists, the data source is having an outage. |
PARTIAL outcomes in the summary | Some searches delivered results and some did not, or some stopped early at a limit. The rows that did arrive are in the dataset — nothing is discarded. |
TRUNCATED outcomes in the summary | Those searches never ran: the run hit maxTotalResults first. Raise it, or run fewer queries. |
BATCH_LIMIT_EXCEEDED | Queries × search types exceeded 200. The message states the count you asked for. |
NO_DATA for a query you expected results from | The search ran and came back empty. Try a broader keyword, or switch the type — a keyword may match accounts but no hashtags. |
A hashtag search returns fewer rows than maxResultsPerQueryType | The tab you chose does not paginate indefinitely. top in particular is a finite, curated set. |
INVALID for one search type only | The query is not usable for that type — usually hashtag_content with a phrase instead of a single hashtag. The other types still ran. |
fullProfileIncluded: false on rows you expected to be enriched | Those accounts could not be read on this run. The search rows themselves are complete and were still billed as search results; no Full Profile charge was raised for the ones that failed. Re-run to retry them. |
includeFullProfile was on but the summary says it did not apply | The run searched something other than profiles. The flag only works with searchType: "profiles"; nothing was read and nothing extra was charged. |
Responsible Use
- Collect only public data, or data you are otherwise permitted to collect.
- Follow the laws that apply to you and the terms of the platforms you use.
- Respect privacy and copyright: do not republish personal data or media you have no right to use.
- Do not use this Actor for spam, harassment, or any abusive, deceptive or unlawful purpose.
Support
- Email: lanceapi2026@hotmail.com
- Or open an issue on the Actor page.
A useful bug report contains:
- the run id (Console → Runs → the run);
- the input, with anything sensitive removed;
- the visible status or error message;
- what you expected and what you got instead.
Please never send: your Apify token, cookies or session data, passwords, access keys, proxy details, or Authorization headers. Nothing in a support conversation needs them, and a message containing them should be treated as compromised.
Related Actors
Once you know what you are interested in, these take it further:
| If you need to... | Recommended Actor |
|---|---|
| inspect everything posted by a discovered account | Instagram Profile Posts & Reels Scraper |
| inspect one discovered profile in depth | Instagram Profile Scraper – Followers & Engagement |
| analyse the audience of a discovered account | Instagram Followers & Following Scraper – Export Lists |
| read the comments on a discovered post | Instagram Comments & Replies Scraper – Posts, Reels |
| see who liked a discovered post | Instagram Likers Scraper – Post & Reel Like |
| download a discovered Reel | Instagram Reels Downloader – Video, Audio & Metadata |
| turn a discovered Reel into text | Instagram Video Transcript Scraper |
| find the sound behind a discovered Reel | Instagram Music & Audio Scraper – Reels by Sound |
Use this Actor first when you have keywords and no accounts yet; switch to the account-level Actors once you know who you are interested in.