- A search query of three or four words that finds no accounts is now searched again in its two-word parts. Instagram's account search looks for your words in account names, so a phrase that describes a niche — "dog grooming studio", "vegan bakery london" — often finds no one as typed. The run now searches the phrase's two-word parts ("grooming studio", "dog grooming") and keeps only accounts whose name carries at least two of the phrase's words. Those profiles say
Keyword Discovery (broadened): <your query> in the Source column, so you can tell them apart from exact matches. They are analyzed and charged like any other profile, within your usual limits. The extra searches stay inside "Max pages per query / hashtag", happen only when the search answered with no accounts (never when it could not be completed), and the run's message names each query and the searches it became.
- When a query finds no one, the message now names shorter versions to try. If the two-word parts find no one either, the message says what was searched. If "Max pages per query / hashtag" left no room for them, or the query has five or more words, it lists the shorter versions you can search instead.
- A profile whose posts or Reels could not be read is no longer judged without them, or charged for. At a busy moment, when a profile's posts or Reels could not be read in time, the profile went on to your filters without them. A Reels or views filter then rejected it as having no recent Reel, and that rejection was charged. Without such a filter it came back with empty engagement and views columns, also charged. Now such a profile is not delivered and not charged, and
SKIPPED_ACCOUNTS lists it as not read this time.
- A busy moment no longer ends a read after a few seconds. When many runs ask for Instagram data at once, a request used to be given up after about 7 seconds. It now waits its turn, never past the run's own timeout. A dropped connection or a temporary error is asked again before anything is given up.
- Excluded accounts are removed before selecting discovery candidates, so known accounts no longer consume the free plan's five analysis slots. Selection preserves fair source ordering and existing search, page and budget limits.
- Discovery deduplicates equivalent handles before the cap. Free-limit notices count only eligible accounts still available; a run whose available candidates are all excluded explains that outcome without charging for profile analysis.
-
On a paid plan, when a run returns results but your filters kept fewer than 1 in 5 of the 20 or more profiles it analyzed, the dataset now ends with one "Run diagnosis" row: the kept share, the filters that rejected the rest, and how to get more rows — so apps and AI agents that read only the results see it too. It is not a profile and is never charged.
-
On a paid plan, a run that keeps few profiles now says so near the top of the results. Once a run has analyzed 100 or more profiles and your filters have kept fewer than 1 in 10 so far, a free "Run diagnosis" row goes in right away, within the first 20 rows: the numbers so far, the filters rejecting most, and how to get more rows. Apps and AI agents that read only the first page now see it. The last row still gives the final count, and says so if the share recovered. Neither row is charged or counted as a profile.
-
Runs that keep only a small share of the profiles they check now say so in the run's status message, with how to re-check them for free using Enable Offline Mode.
-
Offline Mode: re-filtered rows keep the Source of the search that found the profile instead of "Cached". Profiles saved by earlier versions still show "Cached".
-
Offline Mode runs no longer open with the narrow-filter cost warning — re-filtering is not charged.
-
Runs started at the same moment as another run, or from inside another Actor, no longer stop at startup; a run that still cannot start now says why in the Storage tab instead of pointing at your API token.
-
Two runs started close together no longer undo each other's progress. Each run keeps its own record of the accounts it has analyzed. A run restarted by the platform no longer analyzes — or charges — an account twice, and a run that was still working when you started another one fresh keeps saving its profiles for Offline Mode. Unchecking "Start a Fresh Run" still skips everything analyzed since your last fresh run.
-
A run now stops at its "Maximum cost per run" (or at what is left of your account's credit): nothing past it is analyzed or charged, and the run ends with a message saying what the maximum paid for, how many accounts were left, and how to get the rest.
-
A run that stops on an error before saving any profile now says there are no results, instead of calling the dataset empty.
-
A search that could not be completed is no longer reported as "no profiles found". When the search did not respond for any of your terms, the run now says exactly that: the problem was on our side, it is almost always temporary, your terms were never checked (so there is nothing to change in them), and nothing was charged. Re-run in a few minutes — a Schedule rides this out automatically. Until now such a run told you your terms were too narrow and suggested broadening them.
-
When only some terms could not be searched, the run says which. The run's message lists the terms that were searched and found nobody, and names the ones that could not be searched this time, which a re-run may still find accounts for. The run log and the SKIPPED_ACCOUNTS storage record name them too, and a run that still found accounts from your other terms says in its log which terms added nothing.
-
Search terms with no matching accounts finish sooner. The run stops asking as soon as the search reports there is nothing to find, so an empty term no longer holds the run up. The no-results message and the README no longer suggest raising "Max pages per query / hashtag" for a term that found few or no accounts — the search has nothing further to give for such a term.
-
A free run whose filters reject every profile it checks now returns those profiles instead of an empty dataset. On the free plan a run checks 5 of the profiles your search finds, and a narrow filter can reject all of them. Those profiles now come back as full rows marked filtered_out, and Why Not Analyzed names the filter each one did not pass, the value you set and the account's own value — for example: Did not pass "Follower Count Range: Min" (100,000) — this account has 12,400 followers. When your search had more accounts than the free plan checks, the results also say how many. The run's message says the same, and explains how to re-filter these profiles for free with Offline Mode.
-
These rows are not results and add no charge. A filtered_out row costs nothing of its own, the run still reports that no profile matched your filters, and runs on a paid plan are unchanged.
-
The free-plan note at the start of a run names the limit a free run actually meets. It said a free run can analyze up to 50 profiles, while a free run stops finding accounts at 5 first. It now says that a free run analyzes at most 5.
-
A demo run on the free plan now says it analyzed at most 5 profiles. The note it leaves in the run's Storage tab said the output was capped at 10 — a cap a free demo never reaches, because it stops at 5 first. Demo runs on a paid plan read exactly as before.
-
The welcome box on your first paid run now fits inside its border. Long tips wrap onto the next line instead of running past the right-hand edge.
-
A free run that read profiles now counts as one of the month's free runs, even when nothing was charged. On your first run, profiles your filters reject are not charged, and a run like that was being treated as one that had read nothing — its log even said it had fetched nothing. That includes a run that returned those profiles as filtered_out rows. The log of a free run now ends by saying whether the run counted.
-
Several search terms typed into one row are now searched separately. A comma or a line break inside a "Search Queries" row separates terms, so "fitness, yoga coach" searches for "fitness" and for "yoga coach". A "Search Hashtags" row pasted from a caption, such as "#beagle #corgi #dachshund", is read as three hashtags. A phrase is never split on its spaces: "yoga coach" stays one search. A term repeated in the same field is searched once, and the run log lists every row it split.
-
A search term sent under another field name no longer starts the demo search. An input that left "Search Queries" and "Search Hashtags" empty but put the term under a name like hashtags, searchQuery or query used to run the demo search for "fitness coach" — and charge for it. The run now stops before searching, charges nothing, and says which field name to use instead. The value is not moved across for you: guessing wrong would spend your money on a search you did not ask for.
-
On the free plan, an empty run now says when the free limit shaped it. The free plan checks at most 5 profiles per run, chosen before your filters run, so a narrow filter can reject every one of them. When that is what happened, "No profiles matched your filters" now says so, and says that a paid plan keeps going. Runs on a paid plan read exactly as before.
-
The free-plan limit is now recorded on empty runs too. When the 5-profile limit cut a run short and the run then returned nothing, the run's FREE_LIMITS_APPLIED record left the limit out, as if it had not applied. It now lists it.
-
The free-plan limit message talks about your search terms. When the limit stopped a run that did return profiles, the message said "your seed kept producing matching profiles" — wording from an actor that starts from accounts, not searches. It now says your search terms had more profiles than the free plan checks in one run.
-
Runs whose accounts were already in "Exclude Accounts" now explain themselves. When every account a run found was already excluded, the run now says "You already have all of these" — nothing was fetched and nothing was charged — instead of saying it could not tell why it was empty. When some accounts were excluded and your filters rejected the rest, the run now gives the filter explanation it should have given.
-
The free plan's monthly run allowance is now counted when a run starts, not when it ends. A free run you stop part-way now uses one of the month's free runs, the same as a run that finishes. A run that ends without charging or delivering anything still gives it back, and demo runs and Offline Mode re-filters still don't count.
-
Advice that pointed at the wrong thing is corrected. The "no profiles found" message told you to increase "Max Pages Per Source", a field this actor doesn't have; it now names "Max pages per query / hashtag". The narrow-filter warning told free-plan users to set "Max profiles to process" to 100–200 for a test run, which the free plan doesn't allow; on the free plan it now suggests checking this run's own numbers and re-filtering for free in Offline Mode. The same warning now calls the business-address filter by its current name.
-
The README no longer says Reels analytics need a paid plan, and now lists the free plan's monthly runs. The free-vs-paid table and the FAQ said Reels metrics are blanked on the free plan. They aren't: the free plan returns them exactly like a paid plan. The table now also shows the free plan's 15 runs per calendar month, and that a run you stop part-way uses one of them.
-
The run's own messages now describe this actor, not modes and account handles it doesn't have. The first-paid-run tips point at "Max profiles to process", "Max pages per query / hashtag", "Search Queries" and "Search Hashtags". The demo-run note and the TEST RUN banner in the log say that no search term was given and where to put one, and on the free plan the banner names the limit a demo actually stops at. The run log, the live status page and the free-plan limit record no longer mention modes.
-
The input form and the output column descriptions describe this actor too. The "Exclude Accounts" description no longer mentions a mode this actor doesn't have. The Source column's description lists exactly the values this actor writes, Why Not Analyzed talks about search queries and hashtags, and Account says it holds "N/A" on the notice rows. The README's list of common wrong field names now names them — startUsernames, targetUsernames, profileUrls — instead of pointing at another actor.
-
A run the platform restarts after writing its notice rows still delivers what it owes. The rows that report a free-plan limit, and the row that explains a run with no results, are not profiles. A run restarted after writing them counted them as profiles it had already delivered, so it could skip restoring a profile you were charged for and report more profiles saved than it returned. It now counts only real results.
-
Offline Mode no longer rejects a saved profile over "Min Engagement Rate (%)" just because its posts were never downloaded, when "Calculate Engagement Rate (ER)" is off. A normal run with engagement analysis off doesn't apply that filter to such a profile, but re-filtering saved profiles treated the filter as needing posts and rejected every one of them — so a free re-filter could come back empty where the same filters in a normal run return rows. Those profiles are now judged the way a normal run judges them.
-
The same fix for a run that resumes after a restart. With that combination of settings, results the run had already found, but whose posts hadn't been downloaded, were discarded instead of being restored. They are now restored.
- Clarified wording in the README, the input form and the storage record descriptions. Nothing changes in how the actor runs, what it returns or what it costs.
- The empty-run explanation promised in 0.0.26 now actually reaches the results. In that build the row was written but never emitted — it was placed earlier in the run than the explanation it carries, so the check that guards it was always false. The run's status line worked; the results row did not. Nothing else changed.
-
A brief hiccup no longer makes a real, public account look deleted. When an account can't be looked up on the first try, it is now double-checked through a second, independent route before being reported as unreachable. Accounts that really are deleted, private, or mistyped are still reported exactly as before.
-
Honest wording above the filter section. It used to say filters "save on processing costs". They don't: filters run after each profile is read, so they shape your results, not your bill. The Pricing section has always said this; the input form now agrees with it.
-
A run that returns nothing now says why in the results themselves. Until now the explanation went to the run log, the status page and the USER_MESSAGE record — none of which an API or MCP caller receives. An empty run now also returns one row carrying the same explanation, and the run's status line carries its headline.
-
Clearer wording when nothing could be scraped. The end-of-run note no longer says "Nothing was charged" on runs where some accounts were read and then rejected by your filters — reading is what the charge is for, so the note now says which part was charged and which wasn't.
When a run cannot finish because something outside this actor refuses the request, it now
says so in one plain sentence instead of passing along whatever text came back. Input
fields, output columns, defaults and pricing are unchanged from 0.0.24.
- Creator accounts were being reported as personal, and the account-type filter dropped them.
Instagram has three kinds of account — business, creator and personal — and the first two are
both professional: both can publish a Contact button with an email or a phone number. This actor
had been reading a single business/not-business signal, so every creator fell into the personal
bucket. On 60 accounts taken from this actor's own search results, 53% were creators — the
majority. Business only was discarding all of them, and Personal only was returning them
under the wrong name.
- Account Type column, on every analyzed row:
business, creator or personal, read from
Instagram's own account type. The README has listed this column for some time; it now exists. It
is the last column, so every column you already read keeps its position.
- Two new choices in Filter by Account Type: Creator only, and Professional (business or
creator) for when you want everyone with a Contact button and do not care which kind. Searching
for influencers? Choose Creator — the old advice to choose Personal was wrong.
Documentation only. The Related actors table now lists Instagram Email Scraper, for the
contact details a list of usernames publishes — email, phone, website and business category,
one row per account. No change to this actor's behaviour, input, output or prices.
Documentation only. The Related actors table now lists Instagram Comments Scraper, for
reading what a post's comments say — every comment and reply as a row, with @mentions and
#hashtags in their own columns. No change to this actor's behaviour, input, output or prices.
Maintenance build — no user-facing changes. Input schema, output dataset
columns, KVS records, console output, error messages, defaults, and pricing
are all unchanged from 0.0.20. Saved tasks continue to work identically.
The rows explaining what the free plan held back were never reaching you.
On the free plan the actor adds a row per search term that still had accounts left when the run
stopped, saying how many more there were. Those rows were being written in a shape the dataset's
own column types reject — three columns are declared numeric, and a text placeholder in any one
of them makes the whole batch refused. So they vanished, and quietly: the failure was only a
warning in the run log, while the README and the dataset schema both said the rows were there.
This has been true since the actor was published.
A blank cell now takes the form its column actually accepts: "N/A" in a text column, null in
a numeric one. Nothing about your result rows changes. A test now checks every cell of these
rows against the published column types on every commit — nothing in this actor read its own
dataset schema before, which is how a shipped feature stayed dead with a green test suite.
- Related actors section updated to include Instagram Likes Scraper — paste a post or reel
link and get the accounts that liked or commented on it, enriched the same way this actor
enriches its own results.
- If a billing call fails mid-run, the run now shuts down in an orderly way instead of stopping
dead. Everything already collected is saved to the dataset and the run summary, and profiles the
platform did not actually bill for are no longer counted as charged.
- A failure during start-up — before the first profile is read — no longer ends the run without
explanation. The reason is now written to the log and to the run's status.
- The live status page can no longer take the run down with it if its port is unavailable.
- Runs now get 1 GB of memory by default instead of 512 MB. The busiest run measured used 87% of
the old ceiling, and a run that runs out of memory stops without saving anything.
- A run that returned nothing because of the last-post filter now says what the rejected profiles
actually looked like — "7 of 10 had no posts we could read, and this filter needs posts to judge
an account" — instead of naming the filter and stopping there. The sentence was already written
and could never appear, so every zero-row run caused by that filter gave you the filter's name
and no number to move.
- Correction to the 0.0.13 note on the Business-Address filter: full country names DO match
outside the US, Canada, Mexico and India — Instagram writes the city line as "Berlin, Germany"
or "Lagos, Nigeria", country spelled out in English; in the US, Canada, Mexico and India it
ends in the state ("Miami, Florida"). Abbreviations like "USA" or "UAE" are not what the field
carries.
- The filter's input-form description, the README guidance and the run messages now teach the
shape that works: a comma-separated list of several cities plus the region written out in full,
in Instagram's own spelling, accents included.
- When some profiles did publish an address and your terms simply missed them, the closing
message now says so and suggests the towns-plus-region list instead of staying generic.
- The location filter is now called "Filter by Business Address — professional accounts only",
because that is what it reads. Instagram publishes an address only on professional accounts that
filled it in, so ordinary creators have none at all and this filter rejects every one of them.
Write city names: the field holds a city, so "Miami" can match and "USA" never will.
- A run whose location filter cannot match anything now stops after the first 25 profiles instead
of paying its way through the whole search and reporting an empty result at the end. If none of
those 25 publishes a business address, the run says so, tells you how much of the search it
declined to pay for, and points at the field to clear. The profiles already fetched stay saved,
so you can clear the filter and re-filter them for free with Offline Mode.
- The pre-run warning about narrow filters now also fires on a location filter used on its own.
Until now it only spoke up when location was combined with keywords, so a run narrowed by
location alone went ahead with no warning at all — and those are the runs most likely to fetch
profiles, charge for them and save none of them.
- The expected-yield figure in that warning is now specific to the contact type you picked.
All four choices used to be quoted the same ~10%. They do not behave the same: asking for
BOTH an email and a phone — the strictest of the four — actually keeps about twice as many
profiles as asking for an email alone, and the warning now says so instead of understating it.
Maintenance build — no user-facing changes. Input schema, output dataset
columns, KVS records, console output, error messages, defaults, and pricing
are all unchanged from 0.0.10. Saved tasks continue to work identically.
Maintenance build — no user-facing changes. Input schema, output dataset
columns, KVS records, console output, error messages, defaults, and pricing
are all unchanged from 0.0.9. Saved tasks continue to work identically.
Maintenance build — no user-facing changes. Input schema, output dataset
columns, KVS records, console output, error messages, defaults, and pricing
are all unchanged from 0.0.8. Saved tasks continue to work identically.
Maintenance build — no user-facing changes. Input schema, output dataset
columns, KVS records, console output, error messages, defaults, and pricing
are all unchanged from 0.0.7. Saved tasks continue to work identically.
- Runs no longer fail when you start several at the same time. If you launched two or more runs together, one of them could stop with "Could not read this run's progress record in full" — and there was nothing wrong with it. Each run keeps its own list of the accounts it has already looked at, so it can pick up where it left off; a run starting up would clear that list at the very moment another run was reading it. A run that cannot read the list now waits and tries again twice, re-opens the list so anything it saves afterwards is kept, and — if it has not analysed anything yet — carries on with an empty list instead of stopping. That check exists to protect a resumed run from being charged twice for the same account, and a run that has just started has nothing to be charged twice for. Only runs started alongside other runs were ever affected; a single run on its own was not.
- The language filter and the
Detected Language column now cover 44 languages instead of 13. Korean, Polish, Ukrainian, Dutch, Swedish, Serbian, Thai, Vietnamese, Greek, Hebrew, Persian and 23 others were already being recognised from the bio and captions — and then reported as "N/A", because the column only knew how to name 13 of them. They are now named, and each can be picked in Filter by Profile Language. Nothing changed for the 13 languages that already worked.
- The dataset description no longer says the actor has six search modes. It has one — keyword search and hashtag search — and the description was inherited from a larger actor.
- README now lists the Example tasks — ready-to-run searches you can start in one click, no setup. Five more are published in German, Korean and Portuguese and are linked from the Examples tab.
- Runs started from an Example task page no longer open with "Unknown input fields were ignored". The page carries one marker field of ours in its input, and the check that names mistyped fields was counting it as one of the customer's.
- Two runs of the same search are now recognised as the same search whether they were started from an Example page or from the input form.
- Declared the
analytics_failed_* diagnostics records in the key-value-store schema. They were already being written when a background bookkeeping call could not be delivered, and the schema did not mention them.
- Output sample in the README is now a real row from a real run.
- Declared
Posts in Last 30 Days in the dataset schema; the column was already being written but not documented (inherited from the parent).
Initial release.
- Finds Instagram accounts by keyword search and by hashtag search, then enriches each one with contact details, audience size, engagement rate, Reels metrics, business category and detected language.
- Search terms are read in parallel and merged round-robin, so one broad term cannot consume the whole budget while a narrower one returns nothing.
- 20+ post-fetch filters: follower band, engagement floor, Reels view ratio, language, business category, contact-channel presence, posting cadence, verification.
- Single paid event,
PROFILE_ANALYZED at $0.01 per analyzed account. Accounts that could not be read are not charged.
- An empty input runs a small capped demo search instead of failing, and says so in the log, in
USER_MESSAGE, in RUN_SUMMARY and on the status page.
- Free plan: 5 accounts per run, with a row per search term reporting how many candidates it still had.
- Offline Mode re-applies filters to already-fetched accounts without new requests or charges.
- Resume from checkpoint after an interruption; analyzed accounts are never re-billed.