# Changelog of Instagram Bio Scraper — Instagram Email, Phone & Profile Stats (`steadyfetch/instagram-profile-details-scraper`) Actor

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

## Changelog

### 1.0.34 — 2026-10-18

- **Gold and higher plans now pay $2.30 per 1,000 profiles (was $3.00); the Apify free plan, Bronze and Silver are unchanged.** The opt-in account history stays a flat $0.003 on every plan, and nothing changes about which rows are charged, the input fields or the output columns.

### 1.0.33 — 2026-10-18

- **The first account reaches your dataset as soon as it is read.** Accounts are written in batches of 25 so a long list spends its time looking accounts up, and that meant nothing appeared until the 25th account had been read. The first one now lands, and is charged, the moment it is delivered; the rest follow in the same batches as before. The rows, their order and what you are charged are unchanged.
- **On the Apify free plan, a lookup turned away by a temporary error no longer uses up one of your 60 daily lookups.** Every attempt at such an account was turned away, nothing was delivered and nothing was charged, but the lookup still counted against the daily allowance, so a few temporary errors could end a run on the free plan early. It now gives the lookup back. A lookup that was answered, a not-found account included, still counts as before.
- **A finished run of 25 or more accounts now says how to refresh them.** Every run reads each account fresh, so the run page's last sentence says to re-run just the accounts you want refreshed, and that each is charged again. The input description says the same, and that "Max accounts" defaults to 100, so a longer list needs it raised to the list's length.
- **A run cut off by an abort or a timeout is now accounted for on our side as it goes**, not only when it finishes. Nothing changes about what is delivered or charged.
- Nothing changes about the price, which rows are charged, the input fields or the output columns.

### 1.0.32 — 2026-10-16

- **The run summary uses plain names.** The run's summary — the last dataset row and the OUTPUT record — now names the count of reads the run made `reads`. The counts themselves, which rows are delivered, the input fields and every other column are unchanged.

### 1.0.31 — 2026-10-14

- **A post link from a profile grid is no longer looked up as an account.** A post or reel link copied from a profile grid is now refused as a post link on an uncharged row, instead of looking up the account that posted it. Nothing changes about the price or the output columns.

### 1.0.30 — 2026-10-11

- **Clearer wording when a run stops at its limit.** Rows a run could not finish now carry the status "limit_reached" and say "This run reached its limit", with how to fetch the rest in a new run. The run's status line says the same in plain words instead of a code. Nothing changes about the price, which rows are charged, the input fields or the other output columns.

### 1.0.29 — 2026-10-08

- **The monthly data allowance behind this actor was raised, so a busy month no longer stops delivery part-way through.** Nothing you are charged changes: the allowance is ours, not yours, and a run that ever meets it still stops honestly and charges nothing for what it did not deliver.
- **Accounts reach your dataset in batches instead of one by one, so a long list spends its time looking accounts up, not writing them — more of a big list fits the run.** The rows, their order and what you are charged — for each account and for its history when you ask for it — are unchanged, and nothing is charged until its rows are in your dataset.

### 1.0.28 — 2026-10-05

- **A story highlight link no longer looks up the wrong account.** A highlight link (instagram.com/stories/highlights/…) carries a number, not the name of the account that posted it, but it was read as the account called "highlights", so that account's profile was looked up and charged. Now it gets one uncharged row saying to paste the handle or profile link of the account you meant. Every handle, account id and profile link is read and charged exactly as before.
- Nothing changes about the price, which accounts are charged, the input fields or the output columns.

### 1.0.27 — 2026-10-05

- **You can now see a real run before you start one.** The top of this page links to a sample dataset: ten public brand accounts from one verified run, one row per account with the contact fields in that same row, unedited, with the run's own summary row last. Nothing changes about the price, which accounts are charged, the input fields or the output columns.

### 1.0.26 — 2026-10-03

- **A stop now says which limit really ended the run.** A run whose maximum cost per run left no room for even one account used to say it "stopped at your maximum cost per run", though nothing was charged; the run page and the uncharged rows now say it stopped before any charge and that the maximum leaves no room for one account. And when the actor's own limit for a small ask stops it from looking up more accounts, the run page now says so, instead of giving no reason. Nothing changes about the price, which accounts are charged, the input fields or the output columns.

### 1.0.25 — 2026-09-26

- **A run on the Apify free plan now says which price it was charged at.** When a run is billed at the Apify free plan's price, the run page now says so in one sentence — $6.00 per 1,000 profiles — and names what the same run costs on paid plans: $4.50 on Bronze, $3.60 on Silver and $3.00 from Gold. Runs on paid plans read exactly as before. Nothing changes about the price, which rows are charged, the input fields or the output columns.

### 1.0.24 — 2026-09-26

- **A run cut off before it finishes is now accounted for.** The run now notes its own start in its internal record, so a run that ends without finishing — a hard abort, a crash or a timeout — is still accounted for on our side. Nothing changes about what is delivered, which rows are charged, the price, the input fields or the output columns.

### 1.0.23 — 2026-09-26

- **A large run on a paid Apify plan is no longer cut short by this actor's own per-run data budget, and a run that does reach it now says so truthfully.** On paid plans that budget now grows with the number of accounts you ask for, so a list of several hundred accounts is looked up in one run. When a run does stop at it, each uncharged row now says that a new run with those accounts collects them, instead of saying the stop lasts until the start of next month. Nothing changes about the price, which rows are charged, the input fields or the output columns.

### 1.0.22 — 2026-09-24

- **On the Apify free plan, this actor now has a daily allowance.** An account on the Apify free plan can make up to 60 Instagram lookups on this actor in any 24 hours, and accounts on the free plan share one daily allowance on this actor. Past either, the run keeps every account it already delivered, adds one uncharged row saying how many accounts were not looked up and when the allowance resets (UTC), and says the same on the run page. An account named twice in one run is still delivered from the first lookup. Paid Apify plans carry no daily allowance, and nothing changes for them: not the price, the input fields or the output columns.

### 1.0.21 — 2026-09-24

- **Settings sent wrapped in an extra "input" object are now read.** A run whose input arrives as `{"input": {…}}` — for example from `run_input={"input": {...}}` in the Python client — now reads those settings as if sent directly and adds one uncharged note row naming them. Before, the run stopped with a guidance row and collected nothing. Nothing changes about the price, the input fields or the output columns.
- **The run page no longer says your account list "was run at this actor's own ceiling" when your accounts arrived under another field name.** That line is now said only of a cap that really ran at this actor's limit; the uncharged note row naming the field to use is unchanged.

### 1.0.20 — 2026-09-24

- **A run that Apify restarts after it has already delivered keeps its own record of that delivery.** If the restarted part of a run delivers nothing new, the run's internal record now keeps what the earlier part delivered and charged, with a note of the restart, instead of being rewritten as if nothing had been delivered; one line in the run log says so. Nothing changes about what is delivered, which rows are charged, the price, the input fields or the output columns.

### 1.0.19 — 2026-09-22

- **The run log no longer prints an internal usage summary at the end of a run, and a failed internal bookkeeping write no longer prints where it was being saved.** Nothing changes about what is delivered, which rows are charged, the price, the input fields or the output columns.

### 1.0.18 — 2026-09-22

- **The run log no longer prints internal cost lines, and a row stopped by this actor's own monthly collection allowance no longer quotes a dollar figure.** Those numbers described our own costs with the data source, not anything you pay. The row still says the allowance was reached and that it resets at the start of each month; your rows and charges are unchanged.

### 1.0.17 — 2026-09-22

- **When the data source behind this actor goes down, the run now says so and asks you to try again, instead of reporting the account as permanently unavailable.** A source outage answers with a status code that says nothing about the handle you asked for — the kind a service's front door sends while its own servers are unreachable. The run used to treat any answer it did not have on a short list as a final verdict about your account, so during an outage on 22 September a handle that had been served fifteen minutes earlier came back marked as refused for good, with a row telling you not to bother re-running it. It now works the other way round: only the answers the source actually gives ABOUT what you sent — not found, not permitted, malformed — are treated as final, and everything else is read as temporary, retried a few times inside the run, and reported as an uncharged row worth re-running. Nothing you are charged changes, and an account that really does not exist is still the same definitive answer it has always been.

- **An answer the data source has never sent before now counts as an outage rather than as something we paid for.** The run keeps its own record of which of the source's answers cost us money, and until this build that record was a fixed list — so a response nobody had listed, such as a code the source first sent during an outage, was read as one we had bought. Now only the answers the source actually bills count as bought; everything else is booked at nothing and simply tried again. Nothing changes about what is delivered, what you are charged, the price, the input fields or the output columns.

### 1.0.16 — 2026-09-21

- **The store page now opens with what this is and what it costs, instead of putting the price on the fifth line.** The price sat about 1,190 characters down, past the pin block; the opening line now names the actor and both ends of the ladder — $3.00 per 1,000 profiles on Gold and above, $6.00 per 1,000 on the Apify free plan — and the paste instruction, the one-price-per-row promise, the private-account rule and the never-charged rule all follow it unchanged. No price, input field, output column or charged event changes.

### 1.0.15 — 2026-09-20

- **An Instagram “Copy link” address of the `instagram.com/s/…` kind is now recognised as a share link, instead of being read as an account called “s”.** Instagram mints share links under two paths — `/share/…` and `/s/…`, the one it uses for stories and highlights — and only the first was recognised. The second was read as a one-character profile name, so a lookup was spent on an account that does not exist and the answer came back “not found” about a link that really did carry a profile behind its redirect. It now gets the same plain sentence the other share links get: open the link, then paste the handle or the profile URL it lands on. Nothing is read and nothing is charged for it. Real accounts are untouched, including one whose name simply starts with s, and `s` typed on its own is still read as the account you named. No price, input field, column or charged event changed.

### 1.0.14 — 2026-09-19

- **The monthly allowance behind this actor's data reads has been raised again — a busy month no longer stops delivery part-way through.** Nothing you are charged changes: the allowance is ours, not yours, and a run that ever meets it still stops honestly and charges nothing for what it did not deliver.

### 1.0.13 — 2026-09-19

- **The whole "Maximum cost per run" you set is now spent on profiles; before this build the run held back about a twelfth of it.** Your cap pays for profiles and nothing else — the compute, the proxy and the data behind the run are ours, not yours — but the run was still reserving roughly 8% of your cap against those costs before it planned a single profile. So a cap worth ten profiles bought nine, and the tenth was money you had authorised and could not spend. That reserve is gone. A run at its cap still ends the same honest way it always did: it stops itself, tells you what it cut and why, and charges you for nothing it did not deliver. No price, input field, output column or charged event changed.

### 1.0.12 — 2026-09-18

- **The monthly allowance behind this actor's data reads has been raised — a busy month no longer stops delivery part-way through.** Nothing you are charged changes: the allowance is ours, not yours, and a run that ever meets it still stops honestly and charges nothing for what it did not deliver.

### 1.0.11 — 2026-09-18

- **When the run page's one-line summary is too long to fit, the sentences about what you were charged are now the last to go, not the first.** Apify cuts that line at 500 characters, so a long run drops clauses until it fits — and the order it dropped them in was wrong: "N accounts named twice: delivered in full, charged once", the account-history reads that were not charged, and "none of the undelivered rows was charged" were being thrown away to make room for a settings note and a request to open a support issue. Measured across 360 shapes a real run can end in, 75 of them lost a sentence about money or lost the one sentence telling you what to do next, while the line still read as a tidy 500 characters. Everything that costs you nothing to lose now gives way first — the census of undelivered rows folds to a pointer, then the settings note and the support ask, then the stop's own guidance, which the receipt row carries in full — and only under all of that do the money sentences shorten to their plainest form. They never disappear. Nothing about what is collected or charged changed.

### 1.0.10 — 2026-09-16

- **The row that says your own time limit stopped an account now fires in every case it should, not just the last seconds of a run.** The last release added that row — but it worked out which case it was by asking whether the run had already reached its own stopping point, and a read is cut off by your limit well before that. In the gap between the two the row still read as the data source being unavailable and still pointed you at a re-run that would stop in exactly the same place. The run now knows, at the read itself, whether its own limit is what ended it, and says so every time; it also no longer asks again for a read it has no time left to use. A read the source really did refuse still reads exactly as before, and nothing about what is collected or charged changes.

### 1.0.9 — 2026-09-15

- **"Max run seconds" now really stops the run — a read already in progress can no longer carry it past the time you set.** Your time limit was checked before each account was looked up and never again, and the look-up itself had only its own 25-second timer, measured from the moment it started. So a run that still had 21 seconds left could start one more read, that read could run its full 25 seconds, and the run finished past the limit you typed — with the retry ladder behind it, further still. Every read is now given the smaller of its own timer and the time your run actually has left, so it ends at your limit instead of past it; a run with plenty of clock is untouched and still gets the full timer on every read, and a 30-second run still buys a real read and still delivers. **Two more things move with it.** If Apify moves your run to another server part-way through, your time limit now counts from when the run started rather than starting over, so a moved run can no longer take twice the time you asked for. And an account your own time limit cut off mid-read now says exactly that — the uncharged row names the run clock and tells you to raise "Max run seconds" — instead of reporting the source as temporarily unavailable and asking you to re-run, which sold you a second run for a limit you set yourself. A genuine outage still reads as one. **And a look-up that could not finish inside your time limit is no longer started at all** — it is reported as the limit stopping it, uncharged, which is what it always was. A time limit still ends the collecting and never the delivering: accounts already in hand are always written out. No price, no input field, no output column and no charged event changed.

### 1.0.8 — 2026-09-14

- **A run that brings back no accounts now tells you what to do about it — and counts the mistake it never showed.** A run that delivered nothing used to name only the count and a reason code — "Delivered 0 accounts of 1 asked for. Not delivered: 1 not found" — and stop there, with no next step, because the run had ended cleanly and only a run stopped by a limit ever said anything further. Each reason now carries its own plain sentence: a handle Instagram has nothing at tells you to open it in a browser and says a re-run answers the same, a temporary read says to start the run again and that it says nothing about the accounts themselves, a value Instagram refused outright says to check it against the account and send the handle or the whole profile link rather than a fragment, and an answer this actor could not map says it is ours rather than yours and asks you to open an issue. **And a value this actor could not read is now counted on that line at all**: it was named on its own uncharged row but was missing from the run page's summary entirely, so a run whose handles were all unreadable said "Delivered 0 accounts of 1 asked for." and nothing else. It now names the count and tells you which field to put accounts into and what one looks like. The monthly collection allowance is said once rather than in two places, a run that did deliver accounts is unchanged and says nothing extra, and nothing about what is collected or charged changed.

- **What a run costs, and which fields actually go stale, are now on the first screen.** You had to scroll past the whole column table to find that a profile is **$3.00 per 1,000** on Gold and above ($6.00 per 1,000 on the Apify free plan) and that the about-account panel is a flat $0.003 more, only when you ask for it. The re-run advice was further down still: follower, following and post counts move every day on an active account, while the bio, links and contact block change only when the account holder edits them, often not for months — so weekly or monthly is what most buyers want, because it is the counts that go stale, not the contact fields. Both now sit in the opening block beside the actor id and the input. No price, no input field, no output column and no charged event changed.

### 1.0.7 — 2026-09-13

- **If you send your accounts under a different field name, this actor now reads them instead of telling you it found nothing.** `username`, `handle`, `handles`, `account`, `accounts`, `profile`, `profiles`, `profileUrl`, `profileUrls`, `url`, `urls`, `startUrl` and `startUrls` are all read as "Instagram accounts", as a single value or as a list, and each takes the same path a value in the declared field already took — a handle, an `@handle` or a profile link all work, and a Start-URLs entry written as `{"url": "…"}` is opened out to the link inside it. The same account sent in both fields is still one unit. The run gets one uncharged note row saying which field the accounts arrived in and which field to use next time, your run log says the same, and if a value cannot be read the row now names the field you actually set as well as the one this actor reads. `usernames` is still the declared field and nothing about it changed. No price, no charged event, no input field and no output column changes.

### 1.0.6 — 2026-09-13

- **Your result limit now holds even when Apify moves the run to another server part-way through.** This actor already picks up exactly where it left off after such a move, and it works out how much of your limit is left from the accounts it has already delivered — but it was counting only the accounts that carried a fee. An account this run has already read — the same account named twice, by handle and by id — comes back delivered and uncharged, and so does every row on a run that cannot be billed at all, so those were invisible to the count: the run believed more of your limit was unspent than really was, and could hand you more accounts than you asked for. It now counts every one already delivered to you, whether or not it carried a fee. Nothing about what you are charged moves — a repeat is still delivered without a result fee, and only delivered accounts, and the account history where you asked for it, are charged.

### 1.0.5 — 2026-09-13

- **A real sample row on the page, and a title that carries the other thing buyers search for.** The listing is now *Instagram Bio Scraper — Instagram Email, Phone & Profile Stats* (the contact fields were in the title behind a comma, which is not how store search reads a phrase), the first line of the page is that same title instead of a different name, and a new **What a row looks like** section shows one real delivered row — every field, contact fields included — before you spend anything. No price, no input, no output column and no charged event changed.

- **Input schema: the description now opens with the call that works, what one account costs (and what the about-account record adds), the never-charged list and the cap — the screen an AI agent reads; `usernames` leads with the value shape and says an account that cannot be read is never charged.** Nothing else moved: no price, no charged event, no input field, no output column changed.

- **A read that will never work is no longer reported as one that might.** Two different answers used to arrive as `vendor_unavailable`, the row that says "this is temporary — please re-run": a value Instagram refuses to look up at all, and an answer that arrives in a shape this actor cannot read yet. Neither changes on a re-run, so that row was selling you a second run for exactly the same answer. Each now has its own uncharged row — `source_refused` and `unsupported_shape` — saying what is actually true, what to check, and that a re-run will not change it; neither is ever proof that the account is gone. The account-history record says the same thing when Instagram will not serve it for an account. A genuine outage still ships as `vendor_unavailable`, still asks for a re-run, and still carries no result fee. No price, no input and no delivered column changes.

- **A run may now wait longer for a slow source before it gives up.** How long a run can keep waiting is sized against what the profiles it is still owed are worth, and this build lets that sizing draw on half of the run's own margin instead of a quarter — so a page that is answering slowly gets about twice as long to come back, and a delivery cut short on a slow page is rarer. Everything that bounds the waiting is unchanged: your run-time limit, your result limit and your cost cap still end it on the spot, no run ever waits less than it did before, and a small ask waits exactly as long as it always has. No input, no output column, no charged event and no price changes.

### 1.0.4 — 2026-09-12

- **The time a run waits out a quiet source is now sized to what you asked for, instead of being the same five and a half seconds for every run.** A profile that hit a quiet stretch was retried twice — a short wait, then a longer one — and then reported as unreadable, whether you had asked for three profiles on a two-minute run or five hundred on an hour-long one. A big ask on a long run now keeps waiting and asking again, each wait longer than the last, for as long as the profiles it is still owed are worth the run time it costs, and that extra patience is a pool the whole run shares so one difficult profile cannot use it all. A small ask waits exactly as long as it did before, so nothing gets slower or dearer; no run ever waits less than it used to; and no run waits past your own run-time limit, your result limit or your cost cap, all of which still end the waiting on the spot. A profile that still cannot be read carries no result fee and the row still says a re-run is the fix.

All notable changes to this actor are listed here, newest first. The heading number is the
build number on the Builds tab, so the two always read the same.

### 1.0.3 — 2026-09-12

- **One account named twice is now looked up once and charged once.** An Instagram account can be given
  to this actor two ways — by its handle (`bluebottle`) and by its numeric account id (`354032059`) — and
  the two cannot be matched up without asking Instagram, so a list holding both forms of the same account
  read it twice from the licensed data feed and charged you twice for it. Now the first read teaches the
  run both names, whichever order they arrive in: the account is read once, charged once, and the second
  entry still gets its own complete row, carrying the full account and saying in plain words that it
  repeats an earlier one and carried no result fee. Your result limit, the columns, the input and every
  price are unchanged.

### 1.0.2 — 2026-09-12

- **A link in your rows can never carry a network address, whatever form it arrives in.** Every link
  this actor delivers already went through our own URL hygiene before reaching your dataset, and that
  pass had a gap: where an upstream body writes its links with HTML-escaped separators (`&amp;` in
  place of `&`), the address parameter was not recognised by name and could survive into a row. It is
  now removed however the link is written — escaped, plain or legacy separators, upper or lower case,
  and inside a link that wraps another link. Nothing else in a link moves: the expiry, the signature
  and every routing parameter are delivered exactly as they were served, so the links keep working
  exactly as before. No column, no input, no price and no charge changes.
- **This actor has a new title on the store: Instagram Profile Details Scraper — Instagram Email, Bio, Stats.** Same actor, same id, same URL, same input, same output, same price — only the words on the listing changed, so nothing you have saved, scheduled or wired into an integration needs touching. The title now carries the phrase buyers actually search for, which is the only reason it moved.
- **A limit typed above what this actor can do no longer refuses the run.** "Max accounts" and "Max run seconds" carried
  hard ceilings in the input form itself, which meant the platform rejected the whole run before it started — no rows, no
  run page, nothing to act on — and an automation or an AI agent guessing a round number got an error instead of your
  data. Both ceilings are now the actor's own: ask for 50,000 accounts and the run looks up 5,000, ask for a day and it
  runs for an hour, ask for 5 seconds and it takes 30, and one uncharged row says what was asked and what was used. The
  ceilings themselves are unchanged, and so is every price.
- Nothing else moved: no output column, event or price changed.

### 1.0.0 — first public build

The whole Instagram account record for a handle, a profile link or a numeric account id.

- **One row per account, one price.** Followers, following and post counts, bio and link list,
  website, category, verification, badges, pictures — and the email, phone and address the
  account publishes on its own business profile. There is no second charge for the contact
  fields and no start fee.
- **A contact field the account holder has not published comes back empty, never guessed.**
  Nothing in this actor infers, enriches or looks a contact detail up anywhere else.
- **A private account is delivered, not refused.** Instagram publishes the account itself even
  when its posts are private, so the row carries the counts, the bio, the category and the
  picture, with `isPrivate: true` and a sentence saying what is and is not public about it. It
  is charged like any other delivered account.
- **The account history is opt-in and separately priced.** Switch on "Also get the account
  history" and each delivered account also carries the country Instagram shows for it, the
  month it was created and any former usernames, as one flat `about-account` event — billed
  only when that record actually came back.
- **Your result limit is exact**, and it is claimed before any work starts, so no path can
  overshoot it. An account that could not be delivered does not use up a slot.
- **Honest uncharged rows**: `account_not_found`, `stopped_at_limit`, `vendor_unavailable`,
  `vendor_budget` and `user_input`, each naming its own cause and its own fix. A run that
  delivers nothing but those still finishes successfully.
- **A receipt row closes every run**: delivered, asked for, account-history records delivered
  and unreadable, what stopped the run, and the charged totals for both events.
- A bare Start returns built-in sample rows so the output shape can be read before anything is
  looked up — nothing is read from Instagram on that path and there is no result fee.
