# Changelog of Instagram Profile Scraper — Emails, Bio Links & Post Likers (`afanasenko/instagram-profile-scraper`) Actor

- **URL**: https://apify.com/afanasenko/instagram-profile-scraper/changelog.md
- **Full Actor documentation**: https://apify.com/afanasenko/instagram-profile-scraper.md

## Changelog

All notable changes to the Instagram Profile Scraper actor are documented here.
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

### \[0.1.149] - 2026-09-24

- **Mode 1: when almost none of the profiles in a run list an email, the run now says why.** An email appears only where the account owner published one. The message names what changes it: the accounts you collect from. The demo run notes the same about the Email column.

- **Mode 1, free plan: the "up to 50 profiles per run" notice now appears only when the limit actually stopped your run.** It names how many profiles you asked for and how many were analyzed.

- **Mode 1, free plan: the free-plan message counts profiles analyzed, not only those your filters kept.**

- **Mode 6 (Post Engagement Discovery): when a post's comments stop loading partway, the run now says so and suggests a re-run instead of blaming the per-post page limit.** Offline Mode re-filtering of these results now keeps the Liked Posts, Comments Left and Posts Engaged columns.

- **Mode 3 (Network Expansion): when nothing is found, a starting profile that doesn't exist, is private or couldn't be read is now named as such instead of being described as fine.**

- **Runs started at the same moment as another run, or from inside another Actor, no longer stop at startup:** if Apify does not give a run access to your saved profiles, it keeps its own copy and finishes the job (that run's profiles can't be re-filtered later in Offline Mode).

- **When a run stops on an unexpected error before saving anything, the explanation it returns — in the Storage tab and as its one result row — now says there are no results instead of calling the dataset empty.**

### \[0.1.148] - 2026-09-23

- **Mode 4: 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.

- **Mode 4: 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.

- **Mode 4: 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 also no longer suggests raising the page limit, which cannot find accounts the search does not have.

- **Mode 1: a target account that does not exist is reported as unreachable.** Until now such a run said the account "opened without an error" and that its follower list came back empty. It now says the account could not be reached and may be deleted, banned or misspelled.

- **Mode 1: a follower or following list that could not be read is reported as a temporary problem, not as a missing account.** When the target account opened fine but its list could not be read, the run used to say the account might be deleted or misspelled. It now says the read did not complete this time and that a re-run usually works. When other targets in the same run do not exist or are private, the message says so as well.

- **Mode 1: a private target account is reported as private.** Its follower and following lists cannot be read, and until now the run called that a temporary problem and suggested re-running, which could not change the result. It now says the account is private.

### \[0.1.147] - 2026-09-11

- **Followers and following lists no longer skip accounts when the list changes while it is being read.** When accounts move around in a smaller list while the run collects it, the run now checks the list again and collects the accounts it would otherwise have missed, instead of delivering a list that is quietly a few people short.

- **The incomplete-list note appears less often.** On lists small enough to be checked again, the `USER_MESSAGE` and `RUN_SUMMARY.partialListReads` note now covers only the accounts that still could not be collected.

### \[0.1.146] - 2026-09-11

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

### \[0.1.145] - 2026-09-10

- **A followers or following list that comes back short now says so.** When fewer accounts arrive than the profile itself declares, the run reports how many of how many were delivered — in the `RUN_SUMMARY` storage record and, unless the run has something more important to tell you, in its message. Until now only a large shortfall was reported; a list that came back a few percent short passed without a word.

- **The note says what actually happened.** A list can change while it is being read, and then some accounts are skipped — running again may return a slightly different set. That case is no longer described as Instagram hiding part of the list; that wording stays for accounts where Instagram limits how much of the list is visible.

- **Clearer reasons when a list cannot be read.** The run log and the skipped-accounts record now describe the problem in plain words.

### \[0.1.144] - 2026-09-10

- **Clarified wording in the README, the changelog, the input form and the storage record descriptions.** Nothing changes in how the actor runs, what it returns or what it costs.

### \[0.1.143] - 2026-09-10

- **Mode 1 (Analyze followers & following) works again.** Since 9 September the follower list could not be read at all, and runs finished with no results and no explanation. Follower and following lists are now read through a different route and return data as before. If you ran Mode 1 on 9 or 10 September and got an empty result, please run it again — you were not charged for those runs.

- **An empty result now tells you whether the list was empty or unreadable.** These are opposite facts and the actor used to report both the same way — as "that account's list came back empty", even when the list had not been read at all. A list that cannot be read is now reported as a temporary problem on our side, not as a fact about the account you asked for.

- **Large accounts now say when Instagram is hiding most of the list.** Instagram serves only the first few dozen followers of very large accounts and then stops, without reporting an error. Runs used to hand that back as if it were the complete list. When far fewer accounts arrive than the profile itself declares, the run now says so and reports how many of how many it actually saw.

### \[0.1.142] - 2026-09-09

- **The empty-run explanation promised in 0.1.141 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.

### \[0.1.141] - 2026-09-09

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

### \[0.1.140] - 2026-09-09

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

### \[0.1.139] - 2026-09-04

#### Fixed

- **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. Two consequences, both now gone. **Business only** silently discarded creators: on a
  measured sample of suggested profiles that was half of the professional accounts in the result
  set. **Personal only** returned creators instead of personal accounts — on that same population,
  where personal accounts are rare, it returned almost nothing else.

#### Added

- **Account Type column**, on every analyzed row: `business`, `creator` or `personal`, read from
  Instagram's own account type. 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. Looking
  for influencers? Choose **Creator** — the old advice to choose **Personal** was wrong.
  **Any**, **Business only** and **Personal only** behave as before, except that **Personal only**
  now means what it says.

### \[0.1.138] - 2026-09-04

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.

### \[0.1.137] - 2026-09-03

#### Fixed

- **A mistyped post link was being reported back to you as a temporary error.** In Mode 6, a link
  that is not an Instagram post or reel URL at all — a profile address pasted by mistake, say —
  was counted alongside genuine temporary read failures, so the run's closing summary described it as
  something that might work on a retry. It never would. Those links no longer inflate the error
  count in that summary; each one still comes back as its own free row naming the exact link and
  why it could not be read, as it has since 0.1.135.

### \[0.1.136] - 2026-09-02

#### Fixed

- **Rows that explain what a run did not deliver were never reaching the results, and had not
  been since 0.1.112.** Every such row was filled with `"N/A"` in each column it does not speak
  about, including six columns that only accept a number — so the results rejected the whole
  batch, the run logged a warning nobody reads, and the rows silently disappeared. This applied
  to both kinds: the free-plan rows that tell you which of your submitted accounts were not
  reached, and the Mode 6 rows added in 0.1.135 for post links that could not be opened. A blank
  cell is now whatever the column accepts — `"N/A"` in a text column, empty in a numeric one —
  and a test now checks every cell of such a row against the published column types, which
  nothing did before.

### \[0.1.135] - 2026-09-02

#### Fixed

- **A post link the run could not open now comes back as a row instead of vanishing.** In Mode 6,
  when some of your links opened and some did not, you got fewer rows than links and nothing in the
  results said which ones were lost — the reason lived only in the run log, which a caller through
  the API or an AI agent never opens. Every link that could not be read now returns its own free row
  naming the link and saying which of three things happened: it was never a post link, so nothing
  was looked up; it is a real post that is deleted or inside a private account; or it hit a
  temporary error. A run where every link fails already explained itself and is unchanged.

- **Links past the 50-per-run ceiling are reported too.** Pasting 60 links used to drop the last ten
  with a line in the log and nothing else, and the run's coverage record then claimed it had read
  everything you asked for. The dropped links now come back as rows, and the coverage record states
  how many links you submitted rather than how many survived the ceiling.

- **A restarted run no longer counts these rows as results.** Rows that report something rather than
  deliver a profile were being counted as delivered when a migrated run reconciled its output, which
  could stop it recovering a profile that had already been paid for and made the run's own summary
  overstate what it saved.

### \[0.1.134] - 2026-09-02

#### Added

- **Mode 5 with an empty form now runs a demo instead of refusing.** Leaving "Location Seeds" blank
  used to end the run with an input error. It now searches a demo city, capped at 10 profiles
  (about $0.10) like every other demo run, and the banner says which field to fill in to search
  your own places. Every other mode already worked this way.

#### Changed

- **Combining Mode 5 with "Filter by Business Address" now says plainly that the two cancel out.**
  The warning used to advise loosening the filter, which cannot help here: location pages are
  tagged by whoever was standing at the place, and a business address exists only on professional
  accounts. Every run that has combined the two returned no rows and still paid for the profiles it
  fetched. The message now says to remove one of the two, and which one fits which goal.

- **The demo banner, the closing line and the run summary now give Mode 5 its own instructions.**
  They previously fell through to "replace the target usernames" — a field Mode 5 does not have.

#### Fixed

- **The run summary now records which place each seed resolved to and why.** 0.1.133 printed this
  in the run log only, so it was invisible to anything reading the run afterwards.

### \[0.1.133] - 2026-09-02

#### Fixed

- **Mode 5 now goes to the city you asked for.** A seed written as `City, Country` was matched by
  looking for the country's letters anywhere inside a place's name — and those letters turn up
  inside ordinary words. `Palm Springs, US` resolved to **Palm Springs Air Museum**,
  `St. Petersburg, US` to **St. Petersburg, Russia**, `Charleston, US` to **Charleston Music Hall**
  and `Melbourne, US` to **Melbourne, Australia**, while the correct city was sitting first in the
  results. A full country name had the opposite problem — every place in Italy matches "Italy", so
  `Milano, Italy` could land on Milano Marittima. Seeds are now ranked by the city you typed first
  and the country second, US state names are understood, and the run log says which rule matched,
  so a surprising pick can be seen rather than guessed at.

- **A location lookup that fails on our side is no longer reported as your mistake.** When the place
  search could not be reached, the run ended with "none of your location seeds could be resolved —
  check the format", which sent people to re-check spelling that was never the problem. That case
  now ends cleanly with its own message saying it is temporary, that nothing was charged, and that a
  numeric place ID skips the lookup entirely.

- **A free-plan run that returns nothing now says whose limit produced that number.** Discovery
  stops at five candidates on the free plan; if your filters then rejected all five, the run said
  only "your filters rejected every one of them". It now states that the five are all the free plan
  hands to the filters, before listing what rejected them.

### \[0.1.132] - 2026-09-01

#### Changed

- Related actors section updated to include **Instagram Likes Scraper** (post likers and
  commenters) and **Instagram Influencer Search** (keyword and hashtag search). The second one
  had been missing since it shipped on 2026-08-11 — a customer landing here saw a family that
  was two members short of the real one.

### \[0.1.131] - 2026-09-01

#### Fixed

- **The example link in Mode 6’s error messages is no longer replaced by a stray placeholder word.**
  Asking for post engagement without a post link answered “Paste a post or reel link … for example”
  followed by a placeholder instead of a link. The run’s status line is passed through a filter that
  keeps internal details out of customer-facing text, and that filter removes every web address it sees — including the example
  we wrote ourselves. The status line now describes the link instead of printing one, and the two
  worked examples stay in the run’s USER\_MESSAGE record, which is not filtered.

### \[0.1.130] - 2026-09-01

#### Added

- **The run now states the size of the job before it charges anything.** Mode 6 prints how many
  likes and comments the posts you gave it declare, and the most accounts it will analyze — and it
  says up front when a post carries more comments than one run reads. The numbers were always on
  hand; nothing said them until the bill arrived.

#### Fixed

- **A profile link pasted into "Post Links" is now told apart from a post that could not be
  opened.** Mode 6 takes links to *posts*; when it was handed an account instead — `@name`, or
  `instagram.com/name` — it stopped and said "None of your post links could be opened", offering
  deletion or a private account as the likely cause. Neither was possible: nothing had been looked
  up. The run now says the entries are not post links, shows which ones, shows what a post link
  looks like, and points at Mode 2 for anyone who wanted a whole account. A link that genuinely was
  opened and lost keeps the deleted/private explanation, and a run carrying both kinds gets both,
  named separately.
- **"You already have all of these" no longer reads as "we don't know why this is empty".** When
  every account a run found was already in your **Exclude Accounts** list, the run ended with the
  unexplained-empty-result message. The accounts had been skipped before being fetched, so the run
  cost nothing — but nothing said so. It now says exactly that, and suggests widening the search or
  clearing the list. This affected every mode, not only Mode 6.
- **A comment list cut short by the per-post page limit now says so.** The run reads up to 10 pages
  of comments per post (about 150). Above that, the commenter list was a prefix and nothing
  reported it, while the input form promised comments were "read in full" and recommended
  *Commenters only* as the way to get a set you could verify. The run now reports the shortfall the
  same way it already reported partial liker lists, naming the posts and the pages read — and it
  stops recommending *Commenters only* on runs where the comment cap was what bound. The input
  form now states the limit instead of promising completeness.

### \[0.1.129] - 2026-08-31

#### Fixed

- **A mistake in your input no longer marks the run as failed.** Pasting something that is not an
  Instagram username, leaving a required field empty, or giving a city and a post link that cannot
  be opened — all of these already cost you nothing, saved the explanation to `USER_MESSAGE` and
  listed the offending entries in `SKIPPED_ACCOUNTS`. They also ended the run in red, as though the
  actor had broken. They now finish normally, with the same sentence on the run's status line where
  you and any tool reading the run can see it without opening the log. Nothing else about these
  runs changes: the same message, the same records, the same $0.
- **A run that cannot be billed now keeps what it already collected.** If the platform refused a
  charge partway through, the run stopped instantly — and took with it every profile still waiting
  to be written, including ones you had already been billed for. It now finishes the profiles in
  flight, saves everything it holds, writes the run summary and then stops, saying so on the run
  page. The count of profiles charged also stops including the one that failed to charge.
- **A run that dies before it starts now says what happened.** Failures during start-up — loading
  the language model, reading your input, opening saved data — ended the run with nothing but a
  generic platform message. They now report the reason on the run page.
- **The live status page can no longer take the run down with it.** If its port was already in use,
  the whole run died. Scraping never needed that page: the run now notes that the page is
  unavailable and carries on.

#### Changed

- **Runs now get 1 GB of memory instead of 512 MB.** Peak usage on the old default reached 95% of
  what was available, and a run that runs out of memory is stopped by the platform without
  finishing its results or explaining itself. This costs you nothing — memory is billed to the
  Actor, not to you.
- **A followers or following list longer than 50,000 accounts is read up to that point and says
  so.** Before, one list could be read without any ceiling at all, which is what pushed memory to
  the edge. No run has ever come close to this number; if yours does, the message names the limit
  and suggests splitting the target across runs.

### \[0.1.128] - 2026-08-27

#### Changed

- The mode table and the "When to use it" list now say, in the words people actually search with,
  what modes 4 and 5 do and what the contact columns hold: Mode 4 is described as an influencer
  search and creator search over the words accounts put in their own bios, Mode 5 as a location
  search by place, lead generation as an Instagram email scraper that returns the addresses
  profiles publish on themselves, and influencer marketing as influencer discovery at scale.
  Nothing about behaviour, pricing, input or output changed — this edit is wording only, and every
  phrase names a column the actor already returns (Email / Email Source / Email from Posts,
  Tagged Location, and the keyword-discovery mode itself).

### \[0.1.127] - 2026-08-26

#### Fixed

- A run that returned nothing because of the contact-details filter now says what the rejected
  profiles actually looked like — "9 of 12 published no public email or phone, which Instagram only
  shows on professional accounts that filled them in" — instead of naming the filter and stopping
  there. The same for the last-post filter: "7 of 10 had no posts we could read, and this filter
  needs posts to judge an account". Both sentences were already written and neither could ever
  appear, so every zero-row run caused by those two filters gave you the filter's name and no
  number to move.

### \[0.1.126] - 2026-08-19

#### Changed

- Three cost-control fields now explain what the cap actually counts. Modes 1, 4 and 5 said only
  that each analyzed profile is one paid event; they did not say that a profile your filters
  reject still uses one slot and one paid event. Mode 3 has said so for months, so the same
  sentence now appears in all four — a run that returns fewer rows than the number you typed is
  no longer a surprise, and setting the cap above the number of rows you want is stated as the fix.
- Mode 1 adds that someone appearing in more than one target's list counts once.
- Mode 5 adds that an account counts once no matter how many of your locations it tagged.
- Mode 4 adds the opposite caution: a keyword search can find fewer candidates than the cap, so
  raising it does not always add rows.

### \[0.1.125] - 2026-08-19

#### Changed

- Correction to the 0.1.124 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.

### \[0.1.124] - 2026-08-19

#### Changed

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

#### Added

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

### \[0.1.123] - 2026-08-18

#### Fixed

- 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 same warning now understands `Location Seeds`. That field replaced the older location
  input in the form, and the warning had never been taught the new name, so a run narrowed by
  location discovery looked unfiltered to it.
- 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.

### \[0.1.122] - 2026-08-17

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.1.121. Saved tasks continue to work identically.

### \[0.1.121] - 2026-08-17

#### Fixed

- A run that stopped on an error now always explains itself. Every diagnosis this actor
  writes for an empty result was reachable only when the run finished normally, so a run
  that failed handed back an empty dataset with nothing said about it. It now reports what
  went wrong, what to try, and exactly what was charged — including when the charge was
  under a cent.
- The most common of those errors is the one you see on a resurrected run whose progress
  record could not be read in full. That message was already written for you; it just
  never reached the run's USER\_MESSAGE record. It does now.

### \[0.1.120] - 2026-08-17

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.1.119. Saved tasks continue to work identically.

### \[0.1.119] - 2026-08-17

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.1.118. Saved tasks continue to work identically.

### \[0.1.118] - 2026-08-17

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.1.117. Saved tasks continue to work identically.

### \[0.1.117] - 2026-08-12

#### Fixed

- **Runs you start at the same time no longer interfere with each other.** Every run keeps a
  record of the accounts it has already analysed, so it can pick up where it left off if the
  server moves it. That record was shared by all of your runs at once, and each starting run
  cleared it — so with several runs going, they overwrote each other's. The visible symptom
  was the failure fixed in 0.1.116, but three quieter things were also happening:

  - a run could **skip accounts you asked for** because another run had already analysed
    them, leaving them out of your results with nothing to show for it;
  - the run summary's **"profiles charged" figure could count profiles another run paid
    for**, so the number did not match the run it was printed for;
  - after a server move, a run could **add another run's results to yours**.

  Each run now keeps its own private record, which no other run can see or clear. Resuming
  after a server move works exactly as before, and so does unchecking **Start a Fresh Run**
  to continue a previous run — that now picks up the last run that actually finished, rather
  than whichever one happened to write last.

#### Changed

- **Starting a fresh run no longer deletes saved profiles that another of your runs is still
  using.** The saved profiles are what **Enable Offline Mode** re-filters for free, and a run
  starting up used to clear them. It now leaves them alone if another run touched them in the
  last few minutes, and simply starts its own collection from scratch — which is what
  **Start a Fresh Run** means. Nothing about what you are charged changes.

### \[0.1.116] - 2026-08-12

#### Fixed

- **Runs no longer fail with "Could not read this run's progress record in full" when several
  of your runs are going at once.** Each run keeps a record of which accounts it has already
  analyzed, so that a run which is interrupted can pick up where it left off instead of charging
  you twice. That record is saved under your account, and a run starting up clears it — so when
  you had many runs going in parallel, one starting run could clear the record while another was
  reading it, and the run that was reading gave up. It failed, it told you to resurrect it, and
  resurrecting could not have helped, because the run had not done anything yet.
  - Reading the record now waits and tries again if it is being replaced, instead of stopping at
    the first attempt.
  - A run that has not analyzed anything yet no longer stops at all — there is nothing it could
    charge you twice for, so it simply starts from an empty record.
  - A run that really is resuming still stops rather than risk charging you twice for the same
    account. That protection is unchanged.
  - Affected roughly 1 run in 130 for anyone running many at a time; single runs were never
    affected.

### \[0.1.115] - 2026-08-12

#### Fixed

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

#### Added

- **`Posts in Last 30 Days` is now described in the output schema.** The column has always been
  written; it was the one column with no description, so anything reading the schema to find out
  what the output contains never saw it.

### \[0.1.114] - 2026-08-10

#### Fixed

- **A failed run no longer prints raw technical failure text.** That text went into the run log,
  into `RUN_SUMMARY`, and — on a run that failed outright — onto the run page itself, and on a
  connection problem it carried a server address. None of it was ever useful to you.
  Failures now read as a short, plain message, and the
  reasons you can act on ("profile not found", "this account is private") are unchanged.

### \[0.1.113] - 2026-08-10

#### Fixed

- **Free-plan message names your numbers again.** On a free run that trimmed your target accounts,
  the message now says how many you submitted and how many the free plan processes per run. The
  same run's list-cap wording is back too. Both sentences had stopped appearing in 0.1.112.

#### Changed

- The free-plan limit ids in the run's `FREE_LIMITS_APPLIED` storage record use one shared
  vocabulary across all our actors, so a limit means the same thing wherever you read it. The
  human-readable message on each entry is unchanged.

### \[0.1.112] - 2026-08-10

#### Added

- **Your results now say what the free plan left behind.** After your results, a free run adds rows
  with `not_analyzed` in the new **Analysis Status** column and the reason in **Why Not Analyzed**.
  - **Modes 1 and 2** — one row for each account you submitted that the free plan did not reach.
    Paste 55 usernames on the free plan and you get 50 analyzed profiles and five more rows naming
    the five accounts that were left, so you can see which ones rather than working it out yourself.
  - **Modes 3–6** — one row per starting profile, search query, location or post that still had
    candidates when the free plan stopped the search, with the number: *"73 more accounts were found
    for openai and not analyzed."*
  - Until now those facts lived only in the run log, on the live status page and in the run's
    storage records, so anyone reading results through the API or an AI assistant had no way to tell
    "the free plan stopped here" from "your input was wrong".
  - **They are never charged**, they are capped at 25 rows per run, and **a paid run never produces
    them** — nor does a run started with the demo values.

#### Fixed

- **The free plan's message about target accounts now actually appears.** In Mode 1 the run counted
  how many target accounts you submitted and how many it would process, then built the sentence from
  two numbers it was never given — so *"5 target accounts were submitted — the free plan processes 3
  per run"* has never been shown to anyone. It is shown now.

#### Changed

- **Two columns added to every row: `Analysis Status` and `Why Not Analyzed`.** On an analyzed
  profile they read `analyzed` and `N/A`. They are appended at the end of the row, so every column
  that existed before keeps its position.

### \[0.1.111] - 2026-08-02

#### Added

- **Mode 3 now warns when your starting profiles are far bigger than the profiles you asked for.**
  Instagram suggests accounts of a similar size to the one it is given, so starting from a channel
  with 23 million followers and asking for accounts under 100,000 asks for something the suggestions
  will not contain — one such run came back with candidates between 1.4 and 272 million followers and
  kept none of them. The run now says so **after the search and before it starts charging**, names the
  size of each starting profile, and suggests starting from accounts closer to the size you want. A
  run whose starting profiles are within about 10x of your maximum says nothing.

#### Fixed

- **The free re-filter instructions now say where the setting is.** They named the "Enable Offline
  Mode" checkbox but not the section holding it, and the input form has four sections — so the one
  instruction that saves you from paying twice was hard to follow. It now points at
  "▶️ Run & Storage Options".

### \[0.1.110] - 2026-08-02

#### Fixed

- **Runs on 0.1.109 ended with an error after finishing their work.** The run collected and saved
  everything it should have, then failed on the very last step while preparing its closing message.
  Results already in the dataset were not affected, but the run was marked failed instead of
  finished. Fixed within the hour; 0.1.109 was live for about 20 minutes.

### \[0.1.109] - 2026-08-02

#### Fixed

- **A run that saves only a handful of the profiles it paid for now says so.** Until now only a run
  that returned *nothing* got an explanation — deliver a single row and the run finished quietly, even
  if the other forty-nine profiles it charged for had been thrown out by a filter. Those runs now end
  with a plain summary: how many profiles were analyzed, how many your filters kept, which filter
  rejected the rest and what the numbers on those profiles actually were, plus the steps to re-filter
  the ones you already have without paying for them twice.
- **A narrow follower range is now recognised as the strong filter it is.** Setting both a minimum and
  a maximum close together is the single fastest way to spend a run and keep almost none of it, and
  the up-front warning did not treat it as narrow at all. It now does, and it fires before the run
  starts collecting rather than after the money is spent. A wide range is unaffected — nothing that
  ran quietly before starts warning now.
- **The up-front warning names the filters you actually set.** It used to suggest loosening
  "Contact Info, Language, Location Keywords" whether or not you had set any of them.
- **The stated speed in the documentation was wrong.** It promised 150–300 profiles per minute, which
  almost no run reaches. It now gives a realistic figure for each mode, and every run prints its own
  estimate in the log within the first few seconds.

#### Added

- **Mode 3 and Mode 5 now carry the same up-front note as the other discovery modes**: filters are
  applied after each profile is collected, so a rejected profile costs the same as a saved one. Mode 3
  also explains that suggestions tend to match the size of the profiles you start from — so starting
  from very large accounts and then capping followers low rejects most of what comes back.
- **The run summary now reports whether the discovery crawl had more to find** when it stopped at your
  profile limit, so it is clear when raising the limit would return more.

### \[0.1.108] - 2026-07-30

#### Added

- **Related actors** now lists **Instagram Reel Script Extractor** — the family member for reading what a creator's reels actually say and show (spoken transcript, text burned into the frame, opening hook) rather than who follows them.

### \[0.1.107] - 2026-07-30

#### Fixed

- **A run that returns nothing now always says why.** Some empty runs finished labelled
  "Completed successfully" with no explanation anywhere — most often in Mode 1, when a target
  account opened normally but its follower list came back empty or held nothing but private
  accounts. Those runs now name the reason and what to do about it, and a run that ends with
  no rows can no longer finish without an explanation at all, whatever caused it.
- **Mode 1 no longer runs when there is nothing to collect.** With both "Scrape Followers" and
  "Scrape Following" switched off there is no list to read, and the run used to end empty and
  successful. It now stops immediately and says which of the two to switch on. Nothing is
  fetched and nothing is charged.
- **Mode 1 started over an integration that sends neither option now collects followers**, as
  the input form's own default says it should, instead of collecting nothing.
- **Runs that use a wrong field name are told the right one.** Sending, for example, `query`
  instead of `searchQueries` produced a message saying the field was empty, with no hint that
  something had been sent under a name this actor does not have. The message now lists the
  names that were ignored and the correct name for each, and every mode's message gives the
  exact input key alongside the label shown in the form — which is what matters when the run
  was started from an API or an assistant rather than the form.
- **A run stopped by the monthly free-run limit is now recognisable to software.** It already
  explained itself in the run log and storage, but an unattended integration had nothing to
  read: the run summary now carries a `blocked` entry naming the limit and the date the
  allowance returns, and the run's own status line says the same thing.

### \[0.1.105] - 2026-07-29

#### Changed

- **The documentation now lists campaign audience reports among the things this actor is for.** Mode 6
  turns a post or reel into the accounts that liked or commented on it, and it had example tasks and a
  filter recipe but was missing from the list of use cases at the top — so anyone skimming to decide
  whether the actor fits their job could read past it. Nothing about how the actor runs has changed.

### \[0.1.104] - 2026-07-29

#### Fixed

- **Results are no longer lost when the platform moves a run to another server.** Results used to be
  held in memory and written out in groups, so a run that was moved, aborted or interrupted before a
  group was complete lost everything it had collected since the last write — and because those
  accounts were already marked as done, they were skipped instead of retried when the run continued.
  Results are now written continuously and again immediately before a run is interrupted. If an
  earlier interruption did lose results, a continued run rebuilds them from the profiles it already
  holds, at no extra cost and without fetching anything again.
- **The run summary now describes the whole run, not just the part after an interruption.** Profiles
  saved, filtered, private and skipped counts, the filter breakdown, the skipped-accounts list and
  the analysis time all used to restart from zero when a run was moved to another server, so a
  finished run under-reported everything that happened before the move. The time estimate shown after
  an interruption also counted the accounts that were already finished, and now counts only the ones
  left.
- **Each row of the profile fields is treated as one profile.** Entries containing spaces were split
  on the space, so "Alex Hormozi" was read as two separate accounts — and because short words like
  those often exist as real usernames, a run could quietly analyze accounts that had nothing to do
  with the ones requested. A row is now kept whole, and a leading @ or a full profile link is
  accepted and cleaned up automatically.
- **Entries that are not Instagram usernames are caught before anything is fetched or charged.**
  A person's real name, an email address or a link to a post is now reported with the exact rows at
  fault and instructions for finding the right username. In Modes 1 and 3 the run stops, because one
  wrong starting profile changes every result that follows; in Mode 2 the offending rows are skipped
  and the rest of the list is analyzed as usual.
- **Usernames that do not exist now fail immediately instead of stalling the run.** A missing account
  was retried four times with waits of up to 30 seconds even though the answer could never change,
  which held up other profiles waiting to be analyzed. Lists containing deleted or renamed accounts
  are noticeably faster as a result.
- **A run continuing after an interruption can no longer re-analyze accounts it had already
  finished.** If the record of completed accounts was slow to load, it was treated as empty, and the
  run started over — repeating work and overstating the number of profiles scanned in the run
  summary. The record is now read in full, and a run stops with an explanation rather than guessing.

### \[0.1.103] - 2026-07-28

#### Fixed

- **Two records the run had been writing without declaring them are now in the actor's storage
  schema**: the account queue an interrupted run resumes from, and the diagnostic record kept when a
  background bookkeeping call cannot be delivered. Both were always written; the schema simply did
  not list them, so anything reading the schema had no way to know they existed.

### \[0.1.102] - 2026-07-28

#### Fixed

- **The "Live status" link now works after a run has finished.** It pointed at the run's own
  container, which the platform shuts down as soon as the run ends — so opening it later showed
  nothing. It now opens the saved copy of the page, which stays available for as long as the run's
  storage does.

### \[0.1.101] - 2026-07-28

#### Added

- **Three ready-to-run examples for Mode 6** — start a run in one click for the accounts that
  liked a post, the accounts that commented on it, or the whole audience that engaged with a
  campaign post. Listed in the Example tasks section of the README and on the Examples tab.

### \[0.1.100] - 2026-07-28

#### Added

- **New Example tasks section in the README** — nine ready-to-run searches you can start in one
  click, from finding coaches and realtors by hashtag to pulling emails for a list of accounts.

### \[0.1.99] - 2026-07-28

#### Added

- **Mode 6 — Discover by Post Engagement.** Paste one or more post or reel links and get back the accounts that liked or commented on them, enriched with the same profile columns as every other mode. These are the warmest names you can get: every row demonstrably engaged with something you chose. Three new columns come with it — `Liked Posts`, `Comments Left`, and `Posts Engaged` — so you can sort by how much someone engaged and put the people who reacted to several of your posts at the top. `Engagement Type` selects likers, commenters, or both; `Max profiles to process` is the mode's own cost ceiling.
- **The run tells you how much of each post it could actually see.** Instagram serves a post's likers as one capped sample and offers no way to page past it, so a high-engagement post returns part of its liker list. The run now writes an `ENGAGEMENT_COVERAGE` record stating, per post, how many likers came back against that post's like count, and says so on screen when any post was capped. Every row you get is real — a capped sample leaves accounts out, it never invents them. Comments are read page by page, so setting `Engagement Type` to "Commenters only" gives you a set you can verify is complete.
- Private accounts that liked or commented are skipped before analysis rather than after. Instagram publishes no profile data for them, so they can never become a row — now they are never fetched and never counted against your profile cap either. On a typical post this is the majority of the liker list.
- Leaving the post-link field empty runs a cheap demo on a well-known public account's newest post, capped at 10 profiles, instead of failing with an input error.

#### Changed

- Post links that cannot be opened are reported individually, with the reason: a deleted or private post, or a link that is not an Instagram post at all. The run continues with whatever links did open, and stops only if none of them did. A malformed link is now identified as malformed instead of being reported as a temporary error.
- The test-run banner and message now describe the field the selected mode actually uses, instead of always naming the target-account field.

### \[0.1.98] - 2026-07-28

Maintenance build — no user-facing changes.

### \[0.1.97] - 2026-07-28

#### Changed

- **The first-run guarantee now ends at the run's first match.** On a first run, profiles rejected by your filters stay unbilled until one profile passes; from that point the run bills normally, still capped at 100 waived profiles. Introduced yesterday in 0.1.96, the guarantee covered every filter rejection on a first run, including runs that returned plenty of rows — where the filters were doing exactly what they were asked to. Runs that return nothing are unaffected: they are still fully covered.
- The "no profiles matched your filters" message explains two more rejection causes in plain language: profiles dropped because they had no readable posts, and profiles dropped for having no public email or phone. It also says "one of the filters named above" when more than one filter is listed.

### \[0.1.96] - 2026-07-27

#### Added

- **Your first run is no longer charged for profiles your filters reject.** Filters are evaluated after a profile is retrieved, so a first-time user setting up a filter combination could pay to find out it matched nothing. On your first run with this Actor, rejected profiles are free — up to 100 of them; past that, billing continues as normal and the run says so. `RUN_SUMMARY` reports the count as `filterRejectsWaived`. Unreadable accounts (not found, private, errored) are still never billed on any run.
- **Every profile you pay for is now saved, including the ones your filters reject.** Previously a profile rejected on follower count, verification, account type, contact details, category, location, or a bio keyword was charged for and then discarded — there was no way to get it back at any price. All retrieved profiles now go to the saved profile cache.

#### Fixed

- **Offline Mode works with one checkbox instead of two.** Turning on **Enable Offline Mode** now keeps your saved profiles even when **Start a Fresh Run** is left at its default. The two settings used to conflict: the fresh-run wipe deleted the cache moments before Offline Mode tried to read it, so the run reported "no cached data" and advised collecting the profiles again.
- **An empty cache and an unreadable one are no longer reported the same way.** Reading the saved profiles is now paged and given a realistic time budget; a storage timeout says so and tells you to retry, instead of claiming there is nothing saved.
- **Offline re-filter runs no longer consume one of the free plan's monthly runs**, and are no longer blocked once that allowance is spent. They make no requests, so recovering from a filter mistake now costs nothing at all.
- **Offline runs no longer let post-based filters through unchecked.** A profile stopped by a profile-level filter never had its posts downloaded, so filters like Min Engagement Rate or Filter by Last Post Date had nothing to judge and silently passed it. Those profiles are now rejected with the reason stated, so an offline re-filter returns the same rows an online run would.

#### Changed

- **The "no profiles matched your filters" report now names the filter as it appears in the input form and gives you a number to move it to** — for example "8 rejected by Follower Count Range — the profiles we found had 340–4,210 followers, your minimum is 5,000". It also spells out the free re-filter path. With three to five filters typically active, "try relaxing one of these filters" was not something you could act on.
- **Input form wording corrected where it was steering runs into empty results.** The follower-range field recommended a specific 5,000–100,000 band that in practice returned nothing; **Filter by Location Keywords** is now described for what it actually reads — the business address and city that only professional accounts fill in — so it is not mistaken for a general country or region filter. **Enable Offline Mode** and **Start a Fresh Run** now describe how they relate.
- README: the pricing section stated that filters run before the paid event and that rejected candidates cost nothing. They do not, and never did — the corrected section explains what filters do to your bill, alongside the first-run guarantee and the free re-filter. New FAQ entry for a run that returned no rows.

### \[0.1.95] - 2026-07-27

#### Removed

- Removed the **Also monitor follower changes** input option and the follower-monitoring next step it drove. Measured over its whole lifetime, no customer ever used it: the completed-screen offer drew 0 clicks from 20 eligible users, the checkbox was switched on in 2 of 305 runs by a single non-paying account, and the machine-readable contract was taken up by none of the 61 paying users who saw it. It started nothing on its own and charged nothing, so nothing that ran before this release is billed differently.
- `RUN_SUMMARY` no longer carries the `relatedWorkflow` and `relatedWorkflowExecution` blocks, and dataset rows no longer carry the `Follower Monitoring Offer` column. Every other `RUN_SUMMARY` field, every dataset column, and every storage record are unchanged.
- A saved task that still sends `startFollowerMonitoring` keeps running normally. The field is accepted and ignored, and it is not reported back as an unrecognized input.

#### Changed

- The review invitation returns to the completed screen of successful paid runs. The removed offer had been taking its place there.

### \[0.1.94] - 2026-07-29

#### Changed

- The existing false-default `startFollowerMonitoring` consent is visible as **Also monitor follower changes** in the Web Input form. A successful paid WEB run may now start the same separately billed, one-child, up-to-10-profile tracker baseline used by API/CLI/MCP; free runs and the default-off path still start nothing and incur no tracker charge.
- The first dataset profile returned to a paid API, CLI, or MCP caller carries one compact `programmatic_dataset_tracker_call_v1` contract. It starts and charges nothing, requires affirmative user confirmation, then provides both a direct MCP `call-actor` payload and an equivalent authenticated Actor API POST for up to 10 lowest-follower saved profiles under a combined `$5` cap. It does not rerun the source Actor; WEB/free/demo dataset rows are unchanged.
- The `$5` combined cap, cheapest-profile ordering, pre-read budget skips, durable receipt, separate attribution, PII-free telemetry, and exact same-token repeat contract are unchanged.

### \[0.1.93] - 2026-07-23

#### Added

- Paid API, CLI, and MCP callers can set the hidden `startFollowerMonitoring=true` input to authorize one separately billed Instagram Follower Tracker baseline for up to 10 successfully saved profiles, ordered from the lowest known follower count. The flag defaults to false, uses the caller's token, caps the combined child run at `$5`, skips profiles that do not fit the remaining tracker budget before list reads, and records the outcome plus an exact same-token `repeatObservation` contract in `RUN_SUMMARY.relatedWorkflowExecution`.
- The private analytics row now records only aggregate auto-start request, eligibility, started, and normalized outcome fields. Child run IDs, URLs, usernames, and tokens remain confined to caller-owned storage, while opt-in and start-failure rates become measurable.
- A durable pre-start receipt prevents a resurrection or ambiguous Actor-start response from launching the same child twice. Auto-start adoption uses its own `api_sibling_autostart` attribution instead of contaminating the manual `api_sibling_handoff` funnel.

#### Unchanged

- Calls without the exact opt-in, plus free, WEB, scheduled, demo, failed, and zero-value runs, never start a child. Source extraction, source billing, dataset rows, and the existing manual `relatedWorkflow` POST fallback are unchanged.

### \[0.1.92] - 2026-07-18

#### Changed

- Paid successful WEB runs now offer one-account follower monitoring instead of routing to the seven-account agency template. The completed screen opens the followers-only `monitor-followers-after-an-instagram-export` task, states the `$0.30` starting observation cost, and records the new `paid_sibling_single_account_cross_sell_v2` click contract. Eligibility, source extraction, source billing, programmatic `direct_actor_api_v2`, and customer dataset rows are unchanged.

### \[0.1.91] - 2026-07-17

#### Changed

- `RUN_SUMMARY.relatedWorkflow` is now the single-account `direct_actor_api_v2` contract. It selects the saved profile with the lowest known follower count, so a programmatic caller starts with the least expensive available observation instead of receiving up to five separately billed accounts. The direct Actor endpoint, `$5` run cap, attribution, eligibility, billing, and dataset rows are unchanged.

### \[0.1.90] - 2026-07-17

#### Changed

- Superseded by 0.1.91 before the new source was published; this build reused the previous source snapshot.

### \[0.1.89] - 2026-07-17

#### Changed

- The terminal Follower Tracker pointer now tells API/CLI/MCP callers to read `RUN_SUMMARY` from the run's default key-value store before posting `relatedWorkflow.input`. This matches the actual Apify MCP response surface while keeping the contract ID/version, endpoint, payload, `$5` cap, attribution, billing, and dataset rows unchanged.

### \[0.1.88] - 2026-07-17

#### Changed

- Paid MCP value runs now normalize to `mcp` and receive the same machine-executable Follower Tracker handoff as paid API/CLI runs. This opens the contract to the observed agentic cohort without changing its ID, endpoint, payload, `$5` cap, attribution, billing, or dataset rows.

### \[0.1.87] - 2026-07-17

#### Changed

- The machine-visible Follower Tracker title and note now describe one or more saved accounts instead of implying that multiple accounts are required. Contract ID/version, endpoint, payload, `$5` cap, attribution, and billing are unchanged.

### \[0.1.86] - 2026-07-17

#### Fixed

- The REST API instructions now match the live single-profile `direct_actor_api_v1` eligibility rule instead of retaining the former five-profile threshold.

### \[0.1.85] - 2026-07-17

#### Changed

- The direct Follower Tracker API contract now appears after any successful paid API/CLI value run with at least one saved profile, instead of requiring five. Its URL, `$5` safety cap, payload, attribution, billing, and dataset behavior are unchanged; the 5+ account agency segment remains separately measurable.

### \[0.1.84] - 2026-07-17

#### Changed

- Eligible paid API/CLI runs now repeat the direct Follower Tracker pointer in their terminal run `statusMessage`, which is already returned to polling integrations. The executable input remains in `RUN_SUMMARY.relatedWorkflow`; dataset rows, eligibility, billing, and all other runs are unchanged.

### \[0.1.83] - 2026-07-17

#### Added

- Eligible paid API/CLI runs now receive a machine-executable `direct_actor_api_v1` Follower Tracker contract in `RUN_SUMMARY.relatedWorkflow`: a Bearer-authenticated Actor POST URL capped at `$5` plus input populated with up to five successfully saved usernames. The same request can be repeated for later observations without copying a Console task; the existing template link remains as a human fallback.

### \[0.1.82] - 2026-07-17

#### Fixed

- `RUN_SUMMARY.relatedWorkflow` now marks its public example `taskId` explicitly as `taskIdRole: "template"`. The note tells API clients to open `taskUrl`, copy the template into their own account, and reuse the new task ID created by Apify. This prevents clients from trying to run the author's template ID directly; eligibility and destination are unchanged.

### \[0.1.81] - 2026-07-17

#### Changed

- The API documentation now shows how to fetch `RUN_SUMMARY` after a run and consume the optional `relatedWorkflow` added in 0.1.80. The eligibility rules, destination task, runtime, input, output rows, and billing are unchanged.

### \[0.1.80] - 2026-07-17

#### Added

- Successful paid API/CLI runs that save at least five profiles now expose one PII-free `RUN_SUMMARY.relatedWorkflow`: the ready-made multi-client Instagram Follower Tracker task. Programmatic callers can discover the relevant recurring-monitoring workflow without parsing logs or web UI. Free, web, scheduled, demo, failed, and smaller runs are unchanged.

### \[0.1.79] - 2026-07-16

#### Changed

- The one paid completed-screen handoff now opens the focused multi-client Follower Tracker task. Its copy matches the dominant paid workflow here—five or more saved profiles—and the click carries a separate PII-free agency variant for contribution attribution. Free, API, scheduled, failed, and zero-result runs remain unchanged.

### \[0.1.78] - 2026-07-16

#### Changed

- The top README handoff now opens a ready-made Follower Tracker task directly instead of the generic Store page. The dedicated task name keeps this acquisition path measurable without query-string tracking or personal data.

### \[0.1.76] - 2026-07-16

#### Added

- Paid web users who finish a non-empty run now get one focused next step on the completed screen: open the ready-made Instagram Follower Tracker to monitor new followers and unfollowers over time. The link is click-measured without personal data and replaces the review prompt on that success path. Free, API, scheduled, failed, and zero-result runs are unchanged.

### \[0.1.75] - 2026-07-06

#### Fixed

- Reel view matching hardened against ID-type drift (feed posts and Reels now report IDs in different types; matching is type-agnostic). Complements the 0.1.74 timestamp fix.

### \[0.1.74] - 2026-07-06

#### Fixed

- **Timestamp filters work again** (last post / last Reel / posts-per-period). The post timestamp data changed format around 2026-06-25, which made every profile look like it had "no media found to apply last post filter" — any run using `lastPostDays`, `lastReelDays`, `minPostsInPeriod`, or posts-per-month metrics returned 0 results while still charging for the scan. Both formats are now accepted (feed media and clips), restoring documented behavior.

### \[0.1.73] - 2026-07-06

#### Changed

- Every "Upgrade" link in run messages now says what's behind it: paid plans start at $29/mo with $29 in platform credits, and the link opens your plan page in Apify Console.

### \[0.1.72] - 2026-07-06

#### Fixed

- The "Upgrade" link in run summaries now opens your Apify plan page directly (the old link form could land on the wrong billing tab).

### \[0.1.71] - 2026-07-02

#### Fixed

- **Profiles that fail to retrieve are no longer billed.** Not-found, private, and errored retrievals never produced a row, but each retrieval attempt was still charged as a Profile Analyzed event. The charge now fires only after a profile actually comes back readable — matching what the pricing section and FAQ always promised. Profiles that are retrieved and then rejected by your filters are still billed, as documented. `RUN_SUMMARY.profilesCharged` now reports the count of actually-billed profiles.
- **Runs that stop on a missing required field now explain themselves.** A run started without the selected mode's required input (no search query or hashtag in Mode 4, no target/start/list usernames in Modes 1–3, no location seed in Mode 5, or seeds that don't match any Instagram place) previously failed with a log-only error — API and agent callers saw a failure with no stored explanation. These runs now write a `USER_MESSAGE` record naming the empty field, a working starter value, and the fix, and the run's status message carries the specific error instead of a generic "critical error" line.

#### Added

- **Discovery runs whose entire analyzed sample was unreachable now say so.** A discovery run (Modes 3–5) that found candidates but whose analyzed candidates all turned out private, deleted, or temporarily unreadable previously finished with "User profile limit reached" and no explanation. It now writes a `USER_MESSAGE` naming the breakdown and the fix (raise the mode's max-profiles cap, or re-run), with a stop reason of "All discovered candidates unreachable". These runs are not billed.
- **Zero-result runs now get a recovery panel on the completed screen.** When a run finishes with 0 profiles saved, the completed screen shows the diagnosis — what happened, the top fix, and a known-good starter input — instead of "Analysis Complete!" with an upgrade button. The panel distinguishes filtered-everything, unreachable targets, empty discovery, and missing input.
- Input form: Mode 1 states it works on public accounts with visible lists only, and the Mode 4 section explains that filters run after candidates are fetched — start loose, check the yield, then tighten.
- README: four new FAQ entries (login/cookies, bulk analysis, schedules, "how do I get more results per run"), honest expectation lines for Modes 1 and 4 in the mode table, and a TikTok sibling row in the related-actors table.

### \[0.1.69] - 2026-07-02

#### Added

- **Free-plan cap notices are no longer dropped when another notice wins the run's message slot.** When a free run was clipped by a plan cap and still delivered rows, but the end-of-run message slot was already taken by the unknown-input-fields or narrow-filters notice, the cap facts now ride behind that notice as a compact `★` block — the run's real numbers plus the upgrade link. Behind the narrow-filters notice the wording is decoupled ("a paid plan lifts the cap, and pairing it with looser filters is where it pays off") so the two notices never contradict. Demo runs and zero-result diagnostics are unchanged. The `USER_MESSAGE` record gains an additive `capTailId` field naming the cap notice that rode along.

### \[0.1.68] - 2026-07-02

#### Added

- **The discovery-cap message now names your filters when they trimmed the harvest.** On a free discovery run (Modes 3/4/5) where filters rejected part of the capped catch, the end-of-run message says "your filters kept N of the 5" instead of only quoting the cap — and points at the free re-filter path ("Enable Offline Mode" re-applies filters to this run's cached results at no cost).
- **`RUN_SUMMARY` gains `discoveredBeforeFilters` and `discoveryCapped`** (additive, discovery modes only): how many unique candidates discovery collected before filters ran, and whether the free discovery cap actually held back more matches this run.

#### Changed

- **The free-plan box on the completed status screen now quotes the cap that actually applied.** Discovery runs (Modes 3/4/5) show "Free plan: 5 discovered profiles per run" instead of the list-mode "50 profiles per run". List modes are unchanged.

#### Fixed

- **`FREE_LIMITS_APPLIED` discovery-cap records (`discovery_cap_5_mode3/4/5`) now appear only when the cap truly withheld results** — a further unique candidate existed beyond the cap, the free cap (not your own `maxCount*` setting) was the binding constraint, and the run actually delivered rows. Previously the record was written whenever discovery reached 5 candidates, including runs where filters rejected everything or where exactly 5 candidates existed in total.

Same input shape, output columns, and pricing.

### \[0.1.67] - 2026-06-22

#### Added

- **New "Example tasks" section in the README** linking ready-to-run example tasks — one-click, pre-configured use cases (analyze profiles, find similar accounts, export & analyze followers, scrape emails, find influencers by niche) you can run with no setup.

### \[0.1.66] - 2026-06-12

#### Added

- **Partial list reads are no longer silent.** When a Verified or Business target's followers/following read is cut off deep in the list by a persistent failure (after the full per-page retry ladder), the run now says so plainly: a `USER_MESSAGE` reports "delivered ~N of the ~M the profile declares" and explains that Instagram limits how much of such accounts' lists is visible (the Instagram app shows the same note on those profiles), and `RUN_SUMMARY` gains an additive `partialListReads` field with the exact per-list numbers. Previously the run delivered the partial list with no indication anything was missing. Billing, stop reasons, input fields, and output columns are unchanged.

### \[0.1.65] - 2026-06-11

#### Added

- **Free plan now gets the full data quality.** Reels analytics (Median Views, Views/Followers Ratio, Last Reel) and every filter — engagement rate, posting frequency, Reel recency, median views, view/follower ratio, post extraction — now work on the free plan exactly like on paid. The old paid-only gating silently degraded free output and ignored configured filters; both behaviors are gone.
- **Free-plan runs now report exactly what was held back.** When a free ceiling fires, the run writes a `USER_MESSAGE` with the real numbers for that exact input: how large your targets' follower/following lists really are versus the 50 analyzed, how many profiles of your Mode-2 list weren't processed, or that discovery stopped at the 5-profile cap. Previously these limits applied with little or no explanation.

#### Changed

- **Free plan now includes 15 runs per calendar month** (resets on the 1st; demo/test runs don't count). Runs over the allowance complete successfully with a clear message and charge nothing. Paid plans are unlimited, as before. Per-run free caps (50 profiles Modes 1/2, 5 profiles Modes 3/4/5, 3 targets) are unchanged.

Same input shape, output columns, and pricing.

### \[0.1.64] - 2026-06-10

#### Fixed

- **The narrow-filters heads-up now names the cost-control field for your mode.** The warning introduced in 0.1.63 always suggested validating with a small `maxCount`, but only Mode 1 reads that field — Modes 2–5 read `maxCountList` / `maxCountExpansion` / `maxCountDiscovery` / `maxCountLocation`. The suggestion now always matches the mode you are running, so the small validation run is really capped.
- On free plans the heads-up no longer counts filter options that the free plan does not apply (engagement, recency, views and view/follower-ratio filters) — predictions match what the run will actually do.
- With **Filter Combination = OR**, combining Keywords with a Business Category no longer over-states the expected filtering — under OR the two widen the match instead of narrowing it.

#### Changed

- The heads-up now also accounts for the engagement, posting-recency, views and follower-range filters when estimating how much of a fetched list will be kept. Runs using only those filters previously produced no prediction at all.

### \[0.1.63] - 2026-06-10

#### Added

- **Heads-up before launching a run with very narrow filters.** Some filter combinations (for example **Contact Info** combined with **Profile Language**, or **Keywords** combined with **Location Keywords**) save only a small fraction of fetched profiles — billing is per fetched profile, not per saved profile, so a big run with such filters can be much pricier per saved result than expected. The actor now shows a pre-run warning estimating the expected save-rate, suggests validating yield with a small `maxCount` first, and writes the prediction to a new `FILTER_BURN_PREDICTION` record in the run's Storage tab. No change to billing, run behaviour, or input schema.

#### Fixed

- Runs that include the **Exclude Accounts** field (cross-run dedup, available for integrators) no longer trigger a spurious "Unknown input fields were ignored" notice. The field was honoured all along, but was missing from the actor's known-fields list and so was incorrectly reported as unrecognised. No change to behaviour or billing.

### \[0.1.62]

- Superseded by 0.1.63 four minutes later; no customer ran it. See 0.1.63 for the changes.

### \[0.1.61] - 2026-06-05

#### Fixed

- **More patient when Instagram briefly returns a server error on the first account lookup.** The account-resolution step now uses the same long retry window the follower / following list reads already use — short Instagram hiccups no longer abort the run with "Temporary Unavailable" / zero results. No change to behavior on successful runs; no change to billing.

### \[0.1.60] - 2026-06-03

#### Fixed

- Profile-category filters (**Filter for Influencers Only** and **Filter by Specific Business Category**) now match category names regardless of capitalisation. Previously, a profile whose category came back as `"Digital creator"` was treated as different from `"Digital Creator"` — the same string with one letter cased differently — and was incorrectly excluded. On creator-heavy queries this was rejecting up to ~45% of analysed profiles. No input change needed; existing filter settings are strictly more inclusive after the fix.

### \[0.1.59] - 2026-05-29

#### Added

- A short callout near the top of the README announcing the new **Instagram Follower Tracker** — a sibling Actor (same author) that reports who started following and who unfollowed any public account over time. Links to its Store page for anyone who wants change-over-time monitoring rather than a one-time scrape.

### \[0.1.58] - 2026-05-28

#### Fixed

- Runs started via API, MCP, or a schedule now finish as soon as the results are ready, instead of holding a 20-second "completed" screen at the end. That hold only ever helped the in-browser live view; for synchronous API and agent/MCP integrations it was dead time that could push a long run past the caller's own timeout — so the caller could receive nothing even though the run finished and was billed. Runs you start from the web console keep the completed screen unchanged.

### \[0.1.57] - 2026-05-26

#### Fixed

- Runs that end with every input account skipped now always say why. The previously silent case — some accounts 404'd, some are private, some filter-rejected, and some hit a temporary read error in the same run — now ends with a clear "Nothing was scraped this run" note that lists each category. A separate case — every target reachable but every read hit a temporary rate-limit — now says "Couldn't finish reading, please re-run" instead of finishing blank.
- Paid-plan welcome / check-in messages no longer overshadow real run results. The end-of-run banner now reflects the actual outcome (saved rows, all-filtered, all-unreachable, etc.) and the onboarding banner only surfaces when there's nothing more specific to report.

### \[0.1.56] - 2026-05-25

#### Fixed

- Large follower / following lists now finish reliably even if a single page briefly hiccups partway through. Before, one temporary blip while reading a long list could stop the read early and return a truncated list; the read now waits and retries that page before moving on, so big accounts come back complete.

### \[0.1.55] - 2026-05-22

#### Changed

- Added the **Instagram Profile MCP Server** (connect Claude / Cursor / ChatGPT to live Instagram data via MCP tools) to the Related Actors section. No change to input, output, behaviour, or pricing.

### \[0.1.54] - 2026-05-22

#### Changed

- Added the new **Instagram Follower Tracker** (track who follows / unfollows a public account over time) to the Related Actors section. No change to input, output, behaviour, or pricing.

### \[0.1.51] - 2026-05-21

#### Added

- **`API_USAGE` storage record + `apiUsage` block in `RUN_SUMMARY`** — every run now reports its request count, split into data fetches vs searches. Tools that integrate this actor can read either record from the run's Key-Value Store to see how much work each run performed. Same input, output, and pricing.

### \[0.1.50] - 2026-05-12

#### Fixed

- **`Dataset was not found` warning storm on `clearSavedData: true` runs** — every fresh-data run was silently logging hundreds of `[finalize] checkpointDataset.pushData failed: Dataset was not found` and matching `detailedCacheDataset` warnings during the account-processing loop. Cause: the drop-then-reopen sequence wrapped the reopen in a 3-second timeout and silently kept the stale dataset reference when the timeout fired, so every subsequent `pushData` targeted a just-dropped backend store. Replaced the racey block with a retry-until-writable helper (6 attempts, 1.5–10 s backoff) that aborts the run loudly if it can't recreate the checkpoint and detailed-cache datasets — which is the right behavior, since silently losing all checkpoints breaks resurrect. Same input, output, and pricing.

### \[0.1.49] - 2026-05-11

#### Changed

- **README** — Trimmed volume from 296 to 269 lines based on usage analytics. Removed Offline-mode documentation from 3 places (the feature stays in the Advanced Run & Storage input section for the few users who need it). Removed the `Tips & Best Practices` section (most-useful tips already covered by the Pricing section's filter-gating sentence and the FAQ throughput note). Moved the Mode-specific-columns reference from a dedicated table inside Output sample to a one-line footnote under the modes table in What it does. Same input, output, and pricing.

### \[0.1.48] - 2026-05-11

#### Changed

- **README** — Dropped the `Choose your mode` and `Offline mode` H2 sections (kept the mode-selection safety note + a 5-row mode picker table at the top of `What it does`, and folded the offline re-filter pattern into `Filter recipes` as a 4th recipe). Same input, output, and pricing — README content unchanged, only structure reorganised.

### \[0.1.47] - 2026-05-11

#### Changed

- **README** — Reverted to the 0.1.43 structure after the experimental restructure in 0.1.44–0.1.46 caused a regression on Apify Store's structure check. Kept one single mode-selection safety note at the top of `Choose your mode` so first-time users still see the warning about filling the wrong mode's input. Same input, output, and pricing.

### \[0.1.46] - 2026-05-11

#### Fixed

- **README** — Corrected the "pick your mode first" warnings: removed the inaccurate claim that the Apify Console hides input sections of the other modes after you pick one. It doesn't — all mode sections stay visible regardless of `operationMode` choice. Warnings now state this plainly so users know they have to fill only the section that matches their selected mode. Also rewrote the example in the callout for clearer cause-and-effect (Mode 3 selected → Mode 4 field filled → Mode 3 runs without seeds → zero rows).

### \[0.1.45] - 2026-05-11

#### Changed

- **README** — Surface the "pick your mode first" warning in 4 places (callout under the mode table, Quick start Step 1, Input highlight bullet, dedicated FAQ entry) so users no longer waste a first run by filling input for one mode while `operationMode` is set to another.

### \[0.1.44] - 2026-05-11

#### Changed

- **README** — Reshaped to mirror TikTok's structure (16 sections, 4 sub-sections under Tips). Folded the 5-mode picker into a table inside `What it does`, dropped the standalone `Offline mode` section (kept as a recipe inside `Filter recipes`), expanded the FAQ from 6 to 12 questions, added 3 quick ways to give feedback under Support, and added a brief note on the legal precedent for scraping public data. Same input, output, and pricing.

### \[0.1.43] - 2026-05-11

#### Changed

- **README** — Removed pricing claim from the opening tagline. The opener is now a pure value-prop sentence; pricing details remain in the dedicated `💰 How much will scraping Instagram cost?` section further down. Same input, output, and pricing.

### \[0.1.42] - 2026-05-11

#### Changed

- **README** — Replaced the five truncated per-mode samples with one complete real-world dataset row (37 fields shown) so users see the full output shape directly in the README without leaving the page. Added a small `Mode-specific columns` table explaining when `Source`, `Mutual Follow`, `Tagged Location`, and `Matched By` appear. Same input, output, and pricing.

### \[0.1.41] - 2026-05-11

#### Changed

- **README** — Output sample now covers all 5 modes (added Mode 2 and Mode 4 examples alongside Modes 1, 3, 5). Tightened JSON field selection per sample and trimmed Filter recipes to keep length in check. Same input, output, and pricing.

### \[0.1.40] - 2026-05-11

#### Changed

- **README** — tightened structure for better discoverability on Apify Store and clearer reading for both first-time users and AI agents. Compacted the 5-mode breakouts, added a `When to use it` use-cases section, moved `Tips & Best Practices` next to FAQ, and reworded the Pricing and Disclaimer headings to match common search queries. Same input, output, and pricing.

### \[0.1.39] - 2026-05-11

#### Changed

- Maintenance build — no user-facing changes.

### \[0.1.38] - 2026-05-10

#### Changed

- **README** — enriched for niche-discovery use cases. Added a Discovery workflows paragraph framing the actor as an end-to-end multi-channel toolchain, per-mode `Strategy:` hints inside the Choose-your-mode breakouts, a Multi-channel union pattern follow-up under Filter recipes, and 4 new FAQ entries (cold-start mode selection, why Mode 3 returns 0 with a low follower cap, narrowing a wide niche between sweeps via the bio keyword whitelist, server-side filter charging order). Plus a Cost-optimization bullet on aggressive pre-event filtering. Same input, output, and pricing.

### \[0.1.37] - 2026-05-10

#### Changed

- Maintenance build — no user-facing changes.

### \[0.1.35] - 2026-05-08

#### Changed

- Maintenance build — no user-facing changes.

### \[0.1.34] - 2026-05-07

#### Changed

- **README** — refreshed structure for clarity (restored prior section layout). Same input, output, dataset shape, and pricing.

### \[0.1.33] - 2026-05-07

#### Changed

- **README** — aligned to Apify quality template. Hero rewritten with value-first prose and a "beyond what Instagram's official Graph API offers" comparison; numbered 3-step Quick start; new `🛟 Support & feedback` section pointing to Apify Store reviews / bookmark / Issues tab; promoted disclaimer to dedicated `⚖️ Is it legal to scrape Instagram?` H2 with link to Apify's web-scraping legality blog; FAQ expanded to 12 Q\&As (including legality, ban risk, data freshness, per-run cost cap); 5-mode descriptions condensed to 1 line each in a table; tightened Tips & Best Practices to 4 sub-sections × 3 bullets; output sample retains 3 mode examples but each trimmed. No functional changes — same input, output, dataset shape, and pricing.

### \[0.1.32] - 2026-05-07

#### Changed

- Top-level **input-schema description** reformatted to the original 3-bullet layout — each step on its own line, bold labels, three operation buckets (Followers/Following | Specific List | Network Expansion), and a link to the README's Tips & Best Practices anchor for cost / performance / data-quality guidance.

#### Fixed

- README — removed unverified "Run during off-peak hours / Avoid Instagram peak usage times" claims from the Tips & Best Practices Performance section. The actor's runtime isn't tied to time-of-day; the only true scaling guidance is around batch size and expected duration for large extractions, which now lives under a renamed **Sizing & Pacing** sub-block.

### \[0.1.31] - 2026-05-07

#### Changed

- Top-level **input-schema description** rewritten to mirror the README's new "How to use it" decision flow (Choose Operation Mode → enter handle/seed → set `maxCount*` cap → tune ⚙️ Data Extraction + 🔬 Advanced Filtering). Customers and AI agents reading the schema now see the same 3-step framing as the README, instead of a flat enumeration of mode names. Length 339 → 415 chars (in band, ≤500). No code or input-field changes.

### \[0.1.30] - 2026-05-07

#### Changed

- README — restored the **🧭 How to use it (in one screen)** decision flow (Choose Operation Mode → Inside Selected Mode → Tuning) right above the per-mode deep dives, and the full **💡 Tips & Best Practices** section before the Disclaimer (4 sub-sections: Getting Maximum Results, Cost Optimization, Performance, Data Quality). Both blocks were present in earlier versions and got dropped during the 0.1.20 LLM-discoverability restructure; customer feedback was that the procedural Quick start alone wasn't enough to figure out which mode + which fields apply. No code changes.

### \[0.1.29] - 2026-05-06

#### Fixed

- **Test runs that used the default demo seed now also write a `USER_MESSAGE` storage record** (in addition to the existing console banner). API-origin runs that never open the run console will now see — wherever they read run output — that the run used the example seed, the output was capped at 10 profiles for cheap testing, and which canonical input field they should set for their own seeds. Previously the only signal was the console banner, so customers who started the actor programmatically with the example seed could end up with 10 unrelated test profiles and assume that's what the actor returned for their input.
- **Runs that returned 0 profiles because the discovery search had no candidates** now show a distinct **Stop Reason** instead of completing identically to a successful run, and write a `USER_MESSAGE` storage record explaining what happened with concrete next steps. Three flavors per discovery mode: Network Expansion (Mode 3) → "No related profiles found for seed account(s)" with `zero_discovery_v1` message; Keyword Discovery (Mode 4) → "No profiles found for search terms"; Location Discovery (Mode 5) → "No profiles found for locations". Each affected seed / search term / location is also recorded in `SKIPPED_ACCOUNTS` under a new category `no_related_profiles`. This is the most common failure mode for niche / non-English / very-small-niche inputs.
- **Unknown input field names now surface a warning in the run log and a `USER_MESSAGE` storage record** instead of being silently dropped. Common typos and field names imported from sibling actors — e.g. `profileUrls`, `seedAccounts`, `usernames`, `accounts`, `maxItems`, `maxResults`, `maxProfiles` — are detected and mapped back to their canonical names (`startUsernames`, `maxCount`). Previously a run configured with the wrong field name would still execute but ignore the customer's intended values without any indication.

### \[0.1.28] - 2026-05-06

#### Changed

- The "Estimated run time" log line is now closer to typical real-world durations. The previous estimate baked in extra buffer for slow-day retry overhead that's no longer needed after the 0.1.27 retry-pacing speedup. Real runs on a normal day now finish around the printed estimate, not at half of it. (On a slower day the run can still take longer than the estimate — live progress is in the "Status:" heartbeat printed every 60s.)

### \[0.1.27] - 2026-05-06

#### Changed

- Faster recovery from transient errors. The retry-pacing wait between attempts on server errors was reduced from 5s/10s/15s to 2s/4s/6s, so brief wobbles no longer add minutes to a run. Successful retry rate is unchanged — observed runs already recovered within the first two attempts.
- Higher concurrency ceiling (8 → 16 parallel requests). Until now, the actor capped itself at 8 concurrent requests even when a higher rate was safe; the new ceiling lets those runs finish faster immediately. The actor still reads the safe request rate on startup and stays within it — this just removes our internal cap as the bottleneck.
- The "Estimated run time" buffer was tightened from 1.5× to 1.25× to reflect the lower pacing overhead.

### \[0.1.26] - 2026-05-06

#### Changed

- **Phone-number extraction is now off by default.** Most lead-gen workflows rely on email and website, and phone-number availability on Instagram profiles is low — keeping it on by default made each profile take a little longer for little benefit. Toggle "Extract Phone Number" on in the input form if you need phones. Users who already had it enabled keep getting phone numbers as before.
- The "Estimated run time" log line now factors in a 1.5× buffer for retries, so the printed ETA matches real-world durations more closely on long runs.

### \[0.1.25] - 2026-05-06

#### Fixed

- **No charge when a followers/following fetch returns no list.** If the request for a target's followers or following list errors out and produces zero accounts (404 from a deleted/private/misspelled profile, transient timeout, etc.), the per-fetch paid event is no longer charged. Previously the run could charge for the attempt even when no data came back. Successful fetches that return any accounts are charged as before.

#### Changed

- The "All target accounts unreachable" diagnostic now spells out all common causes for a 404 — **the account may be private, deleted, banned, or the username is misspelled** — instead of only "deleted or username changed", so users have a clearer first-pass checklist when a target fails.

### \[0.1.24] - 2026-05-06

#### Fixed

- Targets in Mode "Analyze Followers / Following" (Mode 1) whose followers/following list cannot be found (deleted, banned, or mistyped username) are now reported as unreachable instead of completing silently with 0 profiles. Previously the run could finish with a "Welcome" banner and zero output even though the list fetch had failed — the **Stop Reason: All target accounts unreachable** diagnostic added in 0.1.21 now fires correctly for this case.

### \[0.1.23] - 2026-05-05

#### Fixed

- "No profiles matched filters" (added in 0.1.22) is no longer reported on small runs that fetched zero candidate profiles — it now fires only when at least one candidate was actually evaluated against your filters. Previously, a run with `maxCount=1` could trip this diagnostic incorrectly when the fetch returned no candidates at all (a different root cause that should surface as "Completed successfully" with 0 saved, not as filter rejection).

### \[0.1.22] - 2026-05-05

#### Changed

- Improved diagnostics when every candidate profile in a run is rejected by the configured filters. Runs now show **Stop Reason: No profiles matched filters** (instead of "Completed successfully" or the misleading "User profile limit reached"), and write a `USER_MESSAGE` storage record listing the top 3 filter rejections so it's easier to see which filter to relax. Applies to all 5 modes: analyzeFollowersFollowing, analyzeSpecificAccounts, networkExpansion, keywordDiscovery, locationDiscovery.

### \[0.1.21] - 2026-05-05

#### Changed

- Improved diagnostics when every target account in a run is deleted, renamed, or private. Runs now show **Stop Reason: All target accounts unreachable** (instead of "Completed successfully") and write a `USER_MESSAGE` storage record explaining what happened, so paid runs against accounts that no longer exist surface a clear message rather than completing silently with 0 profiles.
- Network Expansion (Mode 3) and Followers/Following (Mode 1) modes now record unreachable seed/target accounts in the `SKIPPED_ACCOUNTS` storage record, with category `not_found` / `private` / `error`. Previously only Specific Accounts (Mode 2) populated this record on per-account failures.

### \[0.1.20] - 2026-05-04

#### Changed

- README restructured to the LLM-discoverability template (16 sections, ~410 lines). Added an Apify status badge, an output sample inside the first 25 % of the page, a Free-vs-paid table, dedicated Resume / Storage Records / Programmatic-API sections, and a 3-row cross-link table to the two specialized Instagram actors.
- Every output column now has a per-field description, type, and example available in the Apify Console's dataset view and to clients that read the actor schema programmatically — covers all 38 emit-able columns across the 5 operation modes plus a description-level note for the dynamic `Posts in Last N Days` column.
- Input field help text rewritten throughout — each field now leads with what it does and any prerequisites (e.g. "Free plan ceiling: 50 profiles", "Required field"), so it's clearer what to set before running.
- SEO title / description tightened on the Apify Store page (no functional change).

### \[0.1.19] - 2026-05-03

#### Fixed

- "Last Post Within (Days)" now reflects the actual most-recent post date. Profiles with pinned posts (Instagram pins up to 3 to the top of the grid regardless of age) were reporting the pinned post's age instead of the latest activity, sometimes by hundreds of days. Affects both the column value and the `lastPostDays` filter.
- "Median Views" and "Views/Followers Ratio" populate correctly for accounts that post Reels. Previously, these came back as 0 / 0.00% on most profiles because the calculation was reading from the feed-posts list (photos / carousels — no view counts) instead of the Reels list. Fixes the false-zero on the column and the corresponding `viewFollowerRatioMin`/`viewFollowerRatioMax` filter.
- Accounts with no Reels at all now show "Median Views: N/A" instead of "0" — the prior 0 was misleading and made these profiles look like dead Reels accounts when really there were no Reels to measure.

#### Added

- New `SKIPPED_ACCOUNTS` Storage record listing every username skipped during the run, with reason and category (`filter`, `not_found`, `private`, `error`). Open the Storage tab → `SKIPPED_ACCOUNTS` to see exactly which usernames hit which filter, and which ones errored (re-run those — most errors are transient). The Log also prints up to 3 examples per category at the end of the run.

### \[0.1.18] - 2026-05-01

#### Changed

- Documentation: added a "Other Instagram Tools" section to the README, with one-line descriptions of the sibling Instagram actors.

### \[0.1.17] - 2026-04-30

#### Fixed

- TEST RUN banner cost estimate is now mode-aware: Mode 1 (Analyze Followers/Following) correctly reports the higher max (includes per-list cost in addition to per-profile cost). Mode 3 still reads ~$0.10 max as before.

### \[0.1.16] - 2026-04-30

#### Changed

- "Max profiles" (Mode 1) default is now 0 (unlimited) — matches the other modes. Free plan still caps at 50 profiles automatically; paid plan runs unlimited unless you set a value.

#### Fixed

- TEST RUN banner showed an outdated "50, free-plan limit" message. Now reflects the actual cap behavior on each plan.

#### Added

- Demo-input runs (target/start usernames left at example values) are now capped at 10 profiles on any plan, making one-click test runs predictable (~$0.10 max). Custom seed inputs are unaffected.

### \[0.1.15] - 2026-04-30

#### Fixed

- Followup to 0.1.14: extended the 3-second-timeout bounding to the run-startup and main-loop storage paths too. Under reduced-permission contexts, the saved-data clear (`setValue` / `dataset.drop` / `openDataset`), checkpoint resume (`dataset.getData` / `getValue`), per-batch checkpoint save, per-account checkpoint write, and detailed-cache write were also at risk of stalling silently. With these calls bounded, runs proceed even when the underlying storage operations are unavailable; storage writes degrade to no-ops rather than blocking the run.

### \[0.1.14] - 2026-04-30

#### Fixed

- Bound every async finalization step (run-summary write, run-metadata fetch, first-run timestamp lookup, analytics POST, status-page write) with a 3-second timeout each, plus a 60-second cleanup safety net. Under reduced-permission run contexts these calls were stalling silently and the run could reach the platform timeout instead of exiting cleanly. Completed runs now reliably terminate within ~25 seconds of "Run Complete" via the existing 20-second completed-screen window.

### \[0.1.13] - 2026-04-28

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.1.12. Saved tasks continue to work
identically.

### \[0.1.12] - 2026-04-28

#### Added

- Free-plan limits are now surfaced to API consumers, not just to
  the Console Log. When a free-plan run hits any cap (profile /
  target / list / discovery) or has a paid-only filter set
  (`minMedianViews`, `viewFollowerRatioMin`/`Max`, `extractPosts`,
  `minEngagementRate`, `minPostsInPeriod`, `lastReelDays`,
  `analyzeQuality`), the run records what was applied or ignored in
  two new places:
  1. A `freeLimitsApplied` array on the existing `RUN_SUMMARY` KVS
     record.
  2. A standalone `FREE_LIMITS_APPLIED` KVS record (one known key,
     fetched programmatically with
     `Actor.getValue('FREE_LIMITS_APPLIED')`).
     Each entry is `{ id, message }` — `id` is a stable enum (e.g.
     `list_cap_50_mode2`, `filter_ignored_minMedianViews`) so consumers
     can switch on it without parsing prose. Empty / omitted on paid
     runs.

#### Changed

- Paid-only filters that were previously ignored silently on free
  plan now produce a single end-of-validation notice naming exactly
  which options were dropped (`lastReelDays`, `minMedianViews`,
  `viewFollowerRatio`, `extractPosts`, `minPostsInPeriod`,
  `minEngagementRate`, `analyzeQuality`). The same notice gets
  surfaced via the new `FREE_LIMITS_APPLIED` record above.

### \[0.1.11] - 2026-04-28

#### Added

- Welcome message for paying users on their 1st paid run, and a
  short check-in note on their 3rd paid run. Apify does not let
  developers email a specific user on their own schedule, so the
  message is delivered in-actor instead. It surfaces in three places
  on the same run:
  1. A boxed banner near the top of the Log tab.
  2. A short pointer line at the end of the Log tab.
  3. A standalone `USER_MESSAGE` record in the run's Storage tab —
     fetch it programmatically with `Actor.getValue('USER_MESSAGE')`
     (also embedded as `userMessage` inside `RUN_SUMMARY` for
     consumers that already read that record).
     Test runs (default demo input) and free-plan runs are not affected.

### \[0.1.10] - 2026-04-28

#### Added

- Free-plan warning when a Mode 2 (Analyze a Specific List) input
  contains more than 50 profiles. Previously the actor silently
  truncated to 50, leaving free users puzzled why their 200+ list
  surfaced only the first batch. Now: explicit "your list has N
  profiles, free plan processes 50, upgrade to process all N" line
  before the existing truncation message.

#### Changed

- Mode 4 (Keyword Discovery) input-error message rewritten to be
  actionable: names the mode, points to the exact Input fields
  ("Search Queries" / "Search Hashtags"), and gives example values.
- `searchQueries` and `searchHashtags` field descriptions now lead
  with **Required for Mode 4** so users see the requirement before
  hitting Run.

#### Fixed

- Array inputs accept JSON-stringified arrays as a fallback
  (`"[\"AI\",\"Tech\"]"` is now parsed as `["AI","Tech"]`). Affects
  `targetUsernames`, `specificUsernamesList`, `startUsernames`,
  `searchQueries`, `searchHashtags`, `keywords`, `locationSeeds`.
  Previously a JSON-string in any of these fields broke
  comma-splitting and produced phantom tokens like `["AI"`.

### \[0.1.8] - 2026-04-28

#### Fixed

- Apify daily auto-test now passes again. Phase 1 (0.1.7) emptied
  `targetUsernames.default`, which caused the scheduled health-check run
  to throw `Input Error` and risked the "under maintenance" badge after
  three consecutive failures.

#### Changed

- `targetUsernames.default` set to `["natgeo"]` — runnable, neutral
  example (replaces the unprime in 0.1.7).
- `maxCount.default` lowered from `0` (unlimited) to `50` to cap the
  default Try cost on paid plans. Free-plan behavior is unchanged.

#### Added

- TEST RUN banner printed at the start and end of any run that uses
  the schema defaults verbatim. The banner explains exactly which
  fields to edit (Target Accounts, Max profiles) to scrape your own
  data.
- `testRunMode` boolean and `testRunHint` string in the `RUN_SUMMARY`
  KVS record (surfaced via the `runSummary` output link), so API
  consumers can distinguish demo runs from real ones.

### \[0.1.7] - 2026-04-28

#### Changed

- Removed the pre-run "Estimated cost for fetching lists" log line —
  the actor no longer surfaces a cost guess before work starts. Real
  spend is still reported in the post-run summary.

#### Fixed

- Mode 1 (`Analyze Followers/Following`) no longer pre-fills the
  Target Accounts field with the developer's own handle. Default is
  empty, with a `natgeo` placeholde
