# Changelog of Instagram Followers Scraper (`afanasenko/instagram-followers-scraper`) Actor

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

## Changelog

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

- **Every profile row now includes Email, Email Source, Phone, External URL, Category and Address, with no filter needed and at no extra cost.** Before, Email and Phone came only with the Contact Info filter, and the other three not at all. "N/A" means the profile publishes none. The Contact Info filter still keeps only profiles with contact details.

- **When your filters keep only a small share of the profiles analyzed, the run says so first** — how many were kept, which filter rejected the most, and what to change — and the status line shows the kept share.

- **Setting only Keywords now shows the narrow-filter warning before the run, and on the run's status line.**

- **The narrow-filter warning names only the filters you set, in the Input form's words.**

- **A run with no results always ends with an explanation, and returns one row (Source "Run diagnosis") so apps and AI agents get it too.** That row is never charged.

- **On very large runs, "No profiles matched your filters" names the filters that rejected the most profiles across the whole run.**

- **Docs: to continue an interrupted run, click Resurrect; a new run starts from zero.**

- **A run no longer stalls, or stops without explanation, when Apify doesn't give it access to a storage it uses.** If a run can't start, it now says why and what to do in the `USER_MESSAGE` record (Storage tab) and in its status line.

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

- **A followers or following list that could not be read is no longer reported 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, banned or misspelled. It now says the account itself is reachable, that the read did not complete this time, and that this is almost always temporary — a re-run usually works.

### \[3.1.82] - 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.

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

- **A followers or following list that comes back short now says so.** When fewer accounts arrive than the profile 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 a short list 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. When Instagram limits how much of a list is visible on some Verified and Business accounts, the note says that instead.

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

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

- Clarified wording in the documentation and earlier changelog entries. Nothing changes in how the actor runs, what it returns or what it costs.

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

- **Follower and following lists work again.** Since 9 September the follower list could not be read at all, and runs finished with no results and no explanation. Lists are now read through a different route and return data as before. If you ran this actor 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. 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.

### \[3.1.78] - 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.

- **A run that returns nothing now says why in its status line.** Previously an empty run finished green with an empty status box, and the explanation was only in the USER\_MESSAGE record in the Storage tab.

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

- **Hitting the free-plan ceiling no longer ends the run in silence.** Such a run used to stop without writing its summary or its explanation; it now finishes normally and tells you what happened.

### \[3.1.77] - 2026-09-09

When a run cannot finish, it now says so in one plain sentence instead of passing along
raw error text. Input fields, output columns, defaults and pricing are unchanged from 3.1.76.

### \[3.1.76] - 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. On 60 profiles taken from real follower lists, **48% were creators** against 13%
  business and 38% personal — so **Business only** was keeping about an eighth of a follower list
  when nearly two thirds of it was professional.

#### Added

- **Account Type column**, on every 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 in a follower list? Choose **Creator** — the old advice to choose **Personal**
  was wrong.

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

Documentation only. The **Related actors** table now lists *Instagram Email Scraper*, for the
contact details a list of usernames publishes — email, phone, website and business category,
one row per account. No change to this actor's behaviour, input, output or prices.

All notable changes to **Instagram Followers Scraper** (formerly "Instagram Followers & Following Extractor") are documented here.
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

### \[3.1.74] - 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.

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

Maintenance build — no user-facing changes. Input schema, output dataset
columns, KVS records, console output, error messages, defaults, and pricing
are all unchanged from 3.1.72. Saved tasks continue to work identically.

### \[3.1.72] - 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.

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

#### Fixed

- A mistake in the input no longer marks the run as failed. If a target isn't a username, the
  account list is empty, or both list toggles are off, the run now finishes normally and the status
  line says exactly what to change. Nothing is fetched and nothing is charged, as before.
- The status line now names the problem for every kind of input mistake, not just a missing target.
- If a billing call fails mid-run, the run now shuts down in an orderly way instead of stopping
  dead. Everything already collected is saved to the dataset and the run summary, and profiles the
  platform did not actually bill for are no longer counted as charged.
- A failure during start-up — before the first profile is read — no longer ends the run without
  explanation. The reason is now written to the log and to the run's status.
- The live status page can no longer take the run down with it if its port is unavailable.
- Reading a very long followers or following list is now capped at 50,000 accounts per run, so one
  read cannot exhaust the run's memory. When a list is longer than that, the log says so instead of
  leaving you to assume you got all of it.

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

#### Fixed

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

### \[3.1.69] - 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 3.1.68. Saved tasks continue to work identically.

### \[3.1.68] - 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 3.1.67. Saved tasks continue to work identically.

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

#### Fixed

- **A run that gets moved to another server no longer re-analyses — and re-charges — the
  accounts it was in the middle of.** The actor kept a record of every account it had already
  analysed, so it could pick up exactly where it left off, but that record was switched off:
  it was being written into the same place as your results, and it would have shown up in
  them. It is now kept privately, out of your results, and switched back on. A moved run
  resumes from the last account instead of the last batch, and you are not billed twice for
  the ones in between.
- **This actor no longer shares storage with Instagram Profile Scraper.** Both used the same
  two internal storage names, so if you ran them at the same time each one would clear the
  other's saved progress. They now have separate names.
- **Starting a fresh run no longer deletes saved profiles another of your runs is still
  using**, and runs started together no longer clear each other's progress records at all —
  each run keeps its own.

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

#### Fixed

- **Runs no longer fail when you start several at the same time.** If you launched two or more
  runs together — of this actor or of Instagram Profile Scraper, which keeps its progress list
  under the same name — one of them could stop with *"Could not read this run's progress record
  in full"*, and there was nothing wrong with it. Each run keeps a list of the accounts it has
  already looked at so it can pick up where it left off, and a run starting up would clear that
  list at the very moment another run was reading it. Three things changed:

  - a run that cannot read the list now waits a moment and tries again, twice, instead of
    giving up the first time;
  - it re-opens the list after the retry, so anything it saves afterwards is kept rather than
    written into thin air;
  - a run that has not analysed anything yet no longer stops at all. That check exists to
    protect a resumed run from being charged twice for the same account; a run that has just
    started has nothing to be charged twice for, so it carries on with an empty list and a note
    in the log.

  Only runs started alongside other runs were ever affected. A single run on its own was not.

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

#### Fixed

- **A failed run no longer prints raw technical failure text.** That text went into the run log and
  into the run's storage records, 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; the reasons you can act on are
  unchanged.

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

#### Changed

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

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

#### Fixed

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

### \[3.1.62] - 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.

### \[3.1.61] - 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 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.
- **Each row of "Target Accounts" is treated as one account.** 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 read the followers of 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 now stops the run with the exact rows
  at fault and instructions for finding the right username, instead of quietly reading someone
  else's audience.
- **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.
- **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.

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

#### Changed

- **The Store listing now says what this actor returns** — emails, bio links, engagement rate,
  business category and language on every profile in a follower or following list — and the price
  line shows the per-profile price instead of a generic label.

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

#### Added

- **A "Live Status" link in the run's Output tab.** The run has always kept a status page up to date while it works, but nothing linked to it — you had to know the record was there. The link opens the page during the run and still opens it after the run has finished.
- The run's **Storage** tab now has a named tab for the accounts-processed count, so the record is labelled instead of appearing as a bare key.

#### Fixed

- The Storage tab no longer advertises an "Internal Cache" tab that never had anything in it — that cache is kept elsewhere and was never a stored record.

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

#### Changed

- The Related Actors table now describes Instagram Profile Scraper accurately: it gained a sixth discovery mode that returns the accounts which liked or commented on a given post, so the row no longer says five.

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

Maintenance build — no user-facing changes.

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

Maintenance build — no user-facing changes. Same input shape, output columns, dataset shape, and pricing.

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

#### Added

- **Your first run no longer pays for filter mistakes.** On a first run with this Actor, profiles rejected by your filters are not charged until the run finds its first match — up to 100 of them. Once one profile passes, your filters are working and the run bills normally for the rest. Nothing changes on later runs, and no run is ever billed more than before.
- `RUN_SUMMARY` carries a new `filterRejectsWaived` count so you can see exactly how many profiles the guarantee covered. The run log prints the same number.

#### Changed

- **The "no profiles matched your filters" message now tells you what to change.** It names the filter using the exact label from the Input form instead of an internal code, and prints the values it actually saw — for example `5 rejected by "Min Followers" — the profiles we found had 14,320 – 291,699 followers, your minimum is 999,999`. Rejections caused by accounts with no readable posts and by missing public contact details are now explained rather than reported as raw text.
- The charge for a profile is now settled after the filter verdict instead of immediately after retrieval. Every profile that used to be billed is still billed, apart from the first-run case above.
- Input-form wording for **Min Followers**, **Max Followers**, **Min Engagement Rate (%)**, **Posted Reel Within (Days)** and **Contact Info** now states what each filter actually rejects. The follower-band example that suggested 5 000–100 000 has been removed: measured across first runs, that band returned nothing.

#### Fixed

- The Input section of the README claimed filters drop profiles "before they're billed". They do not — a profile has to be retrieved before anything can decide whether it matches. The README now says so plainly and links to a new **What filters do to your bill** section. The pricing table, which was already correct, is unchanged.

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

#### Removed

- Removed the **Also monitor follower changes** input option and the follower-monitoring next step it drove. Measured across the three sibling Actors that carried it, no customer ever used it: 0 clicks from 20 eligible users on the completed screen and 0 adopters of the machine-readable contract among 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.

### \[3.1.52] - 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.

### \[3.1.51] - 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.

#### Fixed

- `Run.output.runSummary` now exposes the existing caller-owned `RUN_SUMMARY` record through Apify's standard output link, matching the two sibling Actors and repairing the missing link in 3.1.50. The record contents, storage key, billing, and dataset rows are unchanged.

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

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

#### Changed

- The paid successful WEB completed action now uses the same one-account copy, `$0.30` starting-observation disclosure, and `paid_sibling_single_account_cross_sell_v2` click contract as the other Instagram source Actors. The existing single-account task destination, eligibility, extraction, source billing, dataset rows, and programmatic `direct_actor_api_v2` handoff are unchanged.

### \[3.1.49] - 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.

### \[3.1.48] - 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.

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

#### Fixed

- Paid MCP value runs now normalize to `mcp` and receive the same machine-executable Follower Tracker handoff as paid API/CLI runs.
- The terminal handoff status pointer now exits from the final post-`RUN_SUMMARY` branch where `relatedWorkflow` exists. The earlier patch had modified a pre-summary free-cap exit instead, which could neither surface a paid handoff nor safely reference that variable. Contract ID, endpoint, payload, `$5` cap, attribution, billing, and dataset rows are unchanged.

### \[3.1.46] - 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.

### \[3.1.45] - 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.

### \[3.1.44] - 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.

### \[3.1.43] - 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.

### \[3.1.42] - 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.

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

#### Fixed

- The existing paid web completed-screen action now opens the same attributed single-export Follower Tracker task as the top README banner (`monitor-followers-after-an-instagram-export`) instead of the older generic unfollower template. The CTA, eligibility, click event, source behavior, billing, and IFT runtime are unchanged; this removes a split destination and preserves task attribution after the click.

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

#### Fixed

- Remote Apify CLI runs now normalize to the existing `cli` origin instead of `unknown`, so eligible paid CLI exports receive the same `RUN_SUMMARY.relatedWorkflow` as API runs. The 3.1.39 owner check exposed this before a qualifying CLI proof; web/API/scheduled classification and run behavior are otherwise unchanged.

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

#### Added

- Successful paid API/CLI runs that save at least five profiles now expose one static, PII-free `RUN_SUMMARY.relatedWorkflow` for the ready-made multi-account Instagram Follower Tracker task. The public ID is explicitly marked as a template: clients open `taskUrl` once, then reuse the new task ID Apify creates in their account.
- The API documentation now shows how to retrieve `RUN_SUMMARY` through `defaultKeyValueStoreId` and treats field omission as the eligibility contract. Free, web, scheduled, demo, failed, and smaller runs are unchanged; billing, input, dataset rows, and completed-screen behavior are unchanged.

### \[3.1.38] - 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.

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

#### Added

- Paid web users who finish a non-empty export 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.

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

#### Fixed

- **Timestamp filters work again** (last post / last Reel / posts-per-period). Around 2026-06-25 the post date started arriving under a different name, 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 forms are now accepted for posts and Reels, restoring documented behavior.
- Reel view counts now match their posts reliably, even when post and Reel IDs come back in different formats.

### \[3.1.35] - 2026-07-03

#### Fixed

- **Profiles are now billed only after they are successfully retrieved.** Previously the per-profile charge fired before the fetch, so a discovered profile that turned out private, deleted, or temporarily unreadable could be billed without delivering anything. Now unreadable retrievals are never charged — matching what the README and FAQ always promised. `RUN_SUMMARY` gains an additive `profilesCharged` field reporting the exact number of billed profiles.
- **On-screen cost figures now match the real event price.** An internal constant still used the pre-April per-profile rate, so the live status page's Cost tile and `RUN_SUMMARY.cost.spent` under-reported the run's actual spend by 40%. What Apify bills is unchanged — only the displayed numbers were wrong, and now they're right.
- **Runs that fail on empty input now explain themselves.** An empty "Target Accounts" field (or a legacy required field) writes a plain-language `USER_MESSAGE` (`missing_input_v1`) naming the exact field to fill plus a verified starter value, and the run's status message carries the specific error instead of the generic "critical error" line.
- **The free-plan cap notice is recorded honestly.** `cap_max_profiles_free` (in `FREE_LIMITS_APPLIED`) now appears only when the 100-profile ceiling actually constrained the run — previously it was recorded on virtually every free run, whether the cap mattered or not.
- **README free-plan numbers now match the actor's real behavior**: the free plan allows up to **100 profiles per run** (the old text said 50 in some places and 5 in another) and **multiple target accounts** (the old text claimed 1). Nothing about the actual limits changed — the docs were behind.

#### Added

- **Zero-result completed screen now shows a diagnosis.** When a run ends with 0 saved profiles, the live status page explains what happened (cause + top fix + a verified starter input) instead of a bare "Complete" badge with an upsell. The upgrade link is not shown on an empty delivery.
- **Guard for "no list type selected".** With both "Extract Followers" and "Extract Following" switched off the run used to finish silently with 0 rows; it now stops upfront with a clear message naming the toggles.
- **New `list_harvest_empty_v1` message** when the target account is readable but the fetched list has nothing to analyze — empty list, or every entry private. Explains that private-only fetches are never billed and suggests the fix.
- README: 4 new FAQ entries (no-login/no-cookies, multi-account bulk runs, scheduling, "how do I get more results"), TikTok sibling in the related-actors table, and expectation notes on private targets.

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

#### Added

- **New "Example tasks" section in the README** linking ready-to-run example tasks — one-click, pre-configured use cases (export followers, export following, export followers with emails, find mutual followers) you can run with no setup.

### \[3.1.33] - 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. A matching FAQ entry was added to the README. Previously the run delivered the partial list with no indication anything was missing. Billing, stop reasons, input fields, and output columns are unchanged.

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

#### Fixed

- **The narrow-filters heads-up introduced in 3.1.31 now names this actor's real cost-control field.** The warning suggested validating with a small `maxCount`, but this actor's field is `maxResults` — following the advice as written had no capping effect. The suggested filter examples now also match this actor's input options.
- The heads-up now also accounts for the engagement, posting-recency 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.

### \[3.1.31] - 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.

### \[3.1.30] - 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 followers / following list reads already use — short hiccups no longer abort the run with zero results. No change to behavior on successful runs; no change to billing.

### \[3.1.29] - 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.

### \[3.1.28] - 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.

### \[3.1.27] - 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, 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 onboarding banner only surfaces when there's nothing more specific to report.

### \[3.1.26] - 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.

### \[3.1.25] - 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.

### \[3.1.24] - 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.

### \[3.1.23] - 2026-05-14

#### Changed

- Expanded the Resume / checkpoint section with a concrete recovery example and explicit billing-on-resume behaviour (already-analyzed profiles not re-billed). No change to input, output, behaviour, or pricing.

### \[3.1.22] - 2026-05-13

#### Changed

- Expanded the legal section with a clearer answer to "Is it legal to scrape Instagram?" — what data is and isn't scraped, operator compliance responsibilities (ToS, GDPR, CCPA, CAN-SPAM), and a note that Instagram schemas/categories/pricing can change without notice. No change to input, output, behaviour, or pricing.

### \[3.1.21] - 2026-05-13

#### Changed

- Tightened README opener and Store description to match Apify Store best practices: removed pricing repetition from the value prop (Store card already shows pricing). No change to input, output, behaviour, or pricing.

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

#### Fixed

- **`Dataset was not found` warning storm on non-LIMITED `clearSavedData: true` runs** — when run outside LIMITED\_PERMISSIONS, 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 — propagated 1:1 from parent `instagram-profile-scraper` 0.1.50. Same input, output, and pricing.

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

#### Changed

- Maintenance build — no user-facing changes.

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

#### Changed

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

### \[3.1.17] - 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 `💡 Tips & Best Practices` section (4 sub-sections × 3 bullets); 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); MCP wrapper added to Related actors. No functional changes — same input, output, dataset shape, and pricing.

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

#### Fixed

- **Test runs that used the default demo seed (`nike`) 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 that the canonical input field for their own seeds is `usernames`. 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.
- **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. `startUsernames`, `targetUsernames`, `profileUrls`, `seedAccounts`, `maxItems`, `maxResults`, `maxProfiles` — are detected and mapped back to this actor's canonical names (`usernames` for seeds, `maxResults` for the cap). Previously a run configured with the wrong field name would still execute but ignore the customer's intended values without any indication.

### \[3.1.15] - 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 3.1.14 retry-pacing speedup. Real runs on a normal day now finish around the printed estimate, not at half of it.

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

#### Changed

- Faster recovery from brief errors. The wait between retry attempts after a server error was reduced from 5s/10s/15s to 2s/4s/6s, so short wobbles no longer add minutes to a run. Retry attempts realigned with the rest of the Instagram actor family (now 3, was 5) — all observed retries succeed well within the first two attempts.
- Higher concurrency ceiling (8 → 16 parallel requests). Until now the actor itself capped every run at 8 concurrent requests; the higher ceiling lets runs finish faster whenever more parallel requests are allowed.
- The retry log line now reads "Pacing requests — next attempt in Ns..." matching the wording used in the parent actor.
- The "Estimated run time" buffer was tightened from 1.5× to 1.25× to reflect the lower pacing overhead.

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

#### Changed

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

### \[3.1.12] - 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 (a deleted, private or misspelled profile, a temporary 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.

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

#### Fixed

- Targets whose followers/following list comes back as not found (deleted, banned, or mistyped username) are now reported as unreachable instead of completing silently with 0 saved profiles. Previously the run could finish with 0 output even though the list fetch had failed — the **Stop Reason: All target accounts unreachable** diagnostic now fires correctly for this case.

### \[3.1.10] - 2026-05-05

#### Fixed

- "No profiles matched filters" is no longer reported on small runs that fetched zero candidate accounts — it now fires only when at least one candidate was actually evaluated against your filters. Previously, a run with `maxFollowers=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).

### \[3.1.9] - 2026-05-05

#### Changed

- Improved diagnostics when every fetched follower / following account is rejected by the configured filters. Runs now show **Stop Reason: No profiles matched filters** (instead of completing silently with 0 saved profiles) and write a `USER_MESSAGE` storage record listing the top 3 filter rejections so it's easier to see which filter to relax.

### \[3.1.8] - 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.
- Unreachable target accounts (404 / private) are now recorded in the `SKIPPED_ACCOUNTS` storage record with category `not_found` / `private` / `error`. Previously these failures were logged to console but absent from the storage record.
- Removed an unnecessary `Actor.exit()` on the "no public accounts to process" early-return path so the finalize block (analytics, USER\_MESSAGE, SKIPPED\_ACCOUNTS) always runs to completion, matching the parent actor's pattern.

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

#### Changed

- README expanded with a full output sample, $0.01-per-profile pricing math, three filter recipes (nano-influencer outreach / English-speaking creators / B2B partnership shortlist), an FAQ section, and a comparison table vs the other Instagram scrapers in this family.
- 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 7 always-emitted columns + the conditional Email / Phone / Category / Address / Reels / Quality / Post N / contacts-from-posts columns.
- 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).

### \[3.1.5] - 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 `minViewToFollowerRatio` 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.

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

#### Changed

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

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

Maintenance build — no user-facing changes.

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

Maintenance build — no user-facing changes.

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

Maintenance build — no user-facing changes.

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

#### Added

- Demo-input runs (target usernames left at the `nike` example) are now capped at 10 profiles on any plan, making one-click test runs predictable (~$0.10 max). Custom inputs are unaffected — default Max profiles (1000) is restored as soon as you change the seed username.
- TEST RUN banner at start of demo runs explaining how to scrape your own data.
- `RUN_SUMMARY` Key-Value Store record at end of every run — fetch via `Actor.getValue('RUN_SUMMARY')` for run-level stats (status, profiles found/analyzed, cost, free-tier limits applied).
- `FREE_LIMITS_APPLIED` KVS record when free-plan caps were hit on this run, with stable `id` codes for programmatic consumption. Empty/omitted on paid runs.
- `USER_MESSAGE` KVS record on a paid user's 1st and 3rd paid run — short onboarding/check-in tips, also embedded in `RUN_SUMMARY`.
- Live status page + JSON status API exposed via the run container (`/`, `/api/status`, `/api/health`). The Apify Console "Live View" tab now shows real-time progress (saved/filtered/private/errors counters, cost, elapsed time).
- Key-value store schema groups records into named UI tabs ("Run Summary", "User Message", "Free-tier Limits Applied", "Live Status Page", "Internal Checkpoint State", "Internal Cache") so internal checkpoint/cache keys no longer clutter the run's Storage tab.

#### Fixed

- Run finalization is now bounded with 3-second per-step timeouts plus a 60-second cleanup safety net. Under reduced-permission run contexts these calls were stalling silently and runs could reach the platform timeout instead of exiting cleanly. Completed runs now reliably terminate within ~25 seconds of "Run Complete".
- Transient `Dataset was not found` errors during result writes now retry up to three times with exponential backoff and a forced dataset re-open.

#### Changed

- Language detection switched to `cld3-asm` (Google's Compact Language Detector v3, compiled to WebAssembly) for improved short-text accuracy on bios. Existing `profileLanguage` filter behavior is preserved for the major language tags but edge-case classifications may shift.

### \[3.0] - earlier 2026

#### Added

- Initial public release of Instagram Followers & Following Extractor.
- Single-mode actor for extracting followers / following lists from target Instagram profiles.
- Filters: minimum/maximum followers, language, account type, verification, business category, contact info type, last post / last reel days, engagement rate, keyword + keyword location.
- Free plan: 100 profiles per run.
- Pay-per-event pricing: $0.006 per profile analyzed (raised to $0.01 in subsequent build).
