All notable changes to this actor are documented here.
- A mistyped link was being reported back to you as a temporary error. A link that is not an
Instagram post or reel URL at all — a profile address pasted by mistake, say — was counted
alongside genuine upstream failures, so the run's closing summary described it as something a
retry might fix. 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.0.5.
A post link that could not be read used to disappear from the results without a word.
If you pasted five links and one of them was a deleted post, a private account's post, or an
account handle instead of a post link, the run carried on with the other four and said nothing
about the fifth in the results. The reason was written to the run log and to the Skipped
Accounts record — but if you call this actor through the API or an AI agent, what you receive
is dataset items and nothing else, so for you it was reported nowhere at all.
Every link you paste now comes back. A link that could not be read gets its own row with
Analysis Status: not_analyzed, the link you pasted in Source, and the reason in plain words
in Why Not Analyzed — 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 worth re-running. These rows are free, on every plan, and capped at 25 per run with the
true total stated in the last one. The run where every link fails already explained itself and
is unchanged.
Three things found while fixing it:
- Links past the 50-per-run ceiling were dropped with only a log line. They now come back as
rows too, and they are not labelled a free-plan limit, because the ceiling applies on every
plan.
- The coverage record counted the links that survived that ceiling, not the ones you
submitted. A 60-link run recorded "50 of 50 read" — full coverage of a job it had not done.
It now records what you submitted, and how many sat past the ceiling.
- Rows that explain a gap were being counted as delivered profiles when a run resumed after
an interruption. That could make the recovery check conclude nothing was missing while a
profile you had paid for was still absent. They are now counted separately and subtracted.
Rows explaining what the free plan held back were never reaching you at all.
They were being written in a shape the dataset's own column types reject, so the whole batch was
refused and the rows vanished — silently, since the failure was only a warning in the log. A
blank cell now takes the form its column actually accepts: "N/A" in a text column, null in a
numeric one. This affected every notice row since the actor was published. A test now checks
every cell of these rows against the published column types on every commit.
Fifteen claims on the published surfaces did not match what the code does. They were found by
auditing the actor across six independent dimensions after it went public — every one had
already shipped. Most were inherited from the six-mode parent this actor was split out of,
which is the pattern worth naming: a spinoff inherits its parent's copy, and copy is not
covered by the parent's tests.
Money and plan
- The free/paid table and the FAQ said Reels analytics require a paid plan. They do not, on any
plan, and this actor's own input schema already said so. A free user with a blank Reels column
— which means Instagram returned no clip data, not that the plan withheld it — was being told
to upgrade for something they already had.
- Nothing public mentioned that a first run is not charged for profiles your own filters
rejected, up to 100. The code has granted this all along; every buying surface denied it.
- The free-plan table said a stopped run gives its slot back. A run that ends by itself does; a
run you abort does not, and that is deliberate — the slot is reserved before the first fetch
so that aborting is not a way around the allowance.
Honesty about coverage — the thing this actor exists for
- The liker sample was described as "roughly a thousand, so on a 3 000-like post you see most of
them". Measured, it is 999–1 896, which on 3 000 likes is a third to two thirds. The section
now also states the measured 72 % private share of a liker sample, which is what decides
how many of those accounts you can actually receive.
- Two surfaces still said comment lists are complete under the cap. Reading also stops when the
source stops answering — the distinction 0.0.2 shipped for the runtime message, now made in
the README and the dataset schema too.
- The coverage message could be silently swallowed: the filter-burn warning claims the same
slot, and one of this README's own published recipes triggers it. Coverage now rides behind
the warning instead of being dropped.
Output that did not match the output
- Both schemas advertised a four-value Quality score. The classifier returns two. An agent
filtering on "Excellent" would have got an empty set forever.
- The dataset schema still called itself "Instagram Profile (Multi-Mode)" and described six
operation modes. This actor has one.
- Offline re-filtering returned rows without Liked Posts, Comments Left and Posts Engaged —
the three columns this actor exists for — because the engagement payload was attached to the
row after the profile was cached, and an offline re-filter can only read the cache. Resume was
never affected, which is why the smoke matrix did not catch it.
- "Columns you did not enable come back as N/A" was false: Phone, Address and the eight Post
captions are omitted entirely when their extraction is off.
extractPosts was listed as a prerequisite for three filters that fetch what they need on
their own; switching it on for them only bought an extra upstream call per profile.
Measurement
- The published filter-burn figure was labelled "across this mode's paid runs". It is across
all runs (67 runs, 4 421 charged profiles, 71.8 %). The by-type split now carries its
population: commenters 86.2 % on 1 491 charged profiles, likers 25.0 % on 4 runs and 20,
which is too thin to plan against and now says so.
- Removed a price literal for an event this actor does not register. It made the portfolio's
price-drift check permanently red here, and a permanently red check is one people stop reading.
- Three pieces of advice named input fields this actor does not have, and one of them named a
cost control under the wrong title. All were reachable. Corrected, and a test now fails the
build if copy naming a field this actor lacks ever returns.
- Removed console and summary strings left behind a condition that can never be false here.
- A partial comment list no longer blames the per-post page cap for something the cap did not
do. Reading can stop for three different reasons — the list ended, we reached our own cap, or
the source stopped answering partway — and the run now records which one and says it. Found by
smoking the very first live matrix: a run that read one page of a ten-page allowance told
the customer their comments "were cut off by the page cap", about a post declaring 31 comments.
The reason was known inside the reader and thrown away one line later.
- The advice follows the cause. A post whose pages stopped arriving is told to re-run; only a
post that genuinely reached the cap is told to split the work. Recommending a workaround for a
limit you never hit is worse than saying nothing, because people act on it.
- A comment page that fails now says so in the run log. That break was completely silent, which
is why the run above left no trace of what actually happened.
ENGAGEMENT_COVERAGE carries a new commentsStoppedBy field (end / cap / error). Older
records without it are read as "cause unknown" and are never back-filled with a guess.
Initial release. Split out of instagram-profile-scraper, whose Mode 6 (Discover by Post
Engagement) this actor now carries on its own.
What it does. Give it Instagram post or reel links; it returns the accounts that liked or
commented on them, each as a full profile — emails, bio links, follower count, engagement rate,
business category, language. One paid event: PROFILE_ANALYZED, $0.01 per profile that came
back readable. Not-found, private and failed retrievals are never charged, and accounts on your
"Exclude Accounts" list are skipped before the charge, not after.
Carried over from the parent at 0.1.131, including the four customer-visible fixes shipped
there on the same day:
- A link that was never a post link is reported as such, and is no longer described as a post
that could not be opened. Those are different mistakes with different fixes, and the run made
zero requests for the first kind.
- A run whose every candidate was already on the caller's exclude list now says so
(
All candidates already excluded, $0 charged) instead of falling through to "we don't know
why this is empty".
- A comment list truncated by the per-post page cap now says so. It previously produced no
message at all, and the input schema's promise that commenters "can be verified complete" has
been replaced by the actual cap.
- The run states the volume it is about to read before the first charge, not after.
What is different from the parent, on purpose:
- One mode, pinned as a constant rather than read from input, so a hand-crafted
operationMode
cannot reach another code path.
- The free plan's monthly run allowance is reserved at the gate, before the first upstream
call, rather than debited when the run finishes. A run that ends by itself without fetching
anything hands its slot back; a run you abort keeps it.
- Three columns of its own —
Liked Posts, Comments Left, Posts Engaged — which are the
product here rather than an extra of one mode among six. Tagged Location and Mutual Follow
belong to modes this actor does not have and are gone.
- The review link is resolved from
APIFY_ACTOR_ID at run time, so it can never point at a
different actor.
- Every message names a field this actor actually has. A test fails the build if copy naming
an input that does not exist here ever comes back.
ENGAGEMENT_COVERAGE is the record to read when you need to know how much of a post the run
could actually see: Instagram serves a post's likers as a single capped sample with no way to
page past it, so on a popular post the run sees a fraction of them and says which fraction.