Every entry here is one build on the Builds tab, 1:1, newest first.
- A re-started run no longer collects past the number of rows you asked for. Apify sometimes moves a run to another server part-way through; the run picks up where it left off. To know how many rows it had already handed you, it was counting the ones it had CHARGED for — and a row can be delivered without being charged: a post your account already had and asked to see again, or any row once your maximum cost per run is reached. So it read its own tally low and kept collecting past your "Max rows to return" limit. It now counts the rows you actually have. If your run is never moved, nothing changes: this only ever showed up after a restart.
- A run started with a scoped API token now tells you why it charged for posts you already had. The memory that skips what your account already has is a key-value store in your own Apify account, and a token limited under "Restrict what Actors can access" cannot open one — so the check was silently off and every repeat was collected and charged again, while the run looked ordinary. That run now says so on its status line, on one uncharged note row at the top of the dataset, on every row it charged, and in the run summary, and it names the fix: give the token key-value store Read, Write and Create, or set Actor runs to Full access. A run started from the console, or with a full-access token, is unchanged.
- And a token that can READ that memory but not WRITE it now says so too. That one looked like a perfect run: every post you already had was still recognised and skipped, but nothing this run delivered could be written down — so your NEXT run collected and charged all of it again, with nothing anywhere to say why. The run now names it the moment the first write is refused, on the run page, on its own uncharged row in the dataset, in the run summary and in the run log, and tells you the permission to add: key-value store Write (and Create) under Settings → API & Integrations, or Actor runs set to Full access. A run with a full token is unaffected.
- Nothing else moved: no input field, output column, charged event or price changed.
- An answer this actor has already paid for is never bought a second time. When the data feed replied in a shape this actor cannot read, the run treated it as a passing hiccup: it asked again, and again, up to nine times for one handle — spending part of its own upstream allowance and up to fourteen seconds of your run clock on a reply that was identical every single time. From this build a reply that cost something is asked for once. Only the refusals that cost nothing — a rate limit, a server error, a dropped connection — are still waited out and retried, exactly as before, so a handle lost to a passing rate limit is still rescued. Nothing about what you are charged moves: only delivered posts are charged, everything collected before a refusal is still delivered, and a handle that did not arrive still carries no result fee.
- A post link you have already collected is no longer charged a second time. The run has always
remembered every post it delivered to your account, and handles already skipped the ones you had.
Pasted post links did not: the same link in tomorrow's run was collected again, delivered again
and charged again, even though nothing about it was new. From this build a pasted link is checked
against your account's memory and against the dataset you name in Skip posts in this dataset,
exactly as a handle is — no row, no result fee. Switch Include posts you already have on and
they come back as before, marked
repeat: true and still uncharged. Re-running the same list of
post links now costs only what is genuinely new.
- A brief "slow down" from the data feed no longer costs you a whole handle. Threads answers a
handle's posts in a single request, so when the feed behind this actor came back with a rate
limit, a server error or an unreadable reply, every post you asked for from that handle was lost
within a few seconds. From this build the run waits the refusal out, up to twice per handle, and
asks again; a feed that clears delivers the handle's posts as normal, and only a feed that still
refuses afterwards gives up on it. The waiting is bounded by your run clock — no wait is taken
that the remaining time cannot fit — and it stops immediately at your maximum cost per run. A
verdict about the handle itself (no such account, private, no posts) is never waited on, because
waiting cannot change it.
- A handle that really could not be read is unchanged: the row still says so, and nothing that
did not arrive carries a result fee.
First public build.
- A link in your rows never carries a network address. Every media, thumbnail and profile link
this actor delivers goes through our own URL hygiene first, and the address parameter a signed link
can carry is removed however the link is written — HTML-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.
- A Threads handle —
natgeo, @natgeo or a profile link — returns that profile's posts as rows:
post ID, short code, permalink, handle, the stable numeric author ID, the post text, the date it
was published, the media type, the like, reply, repost, quote and reshare counts, the links and
mentions inside the text, an image link, a video link and the moment that link expires.
- Threads shows the last 15 posts of a profile to a logged-out reader and there is no way to page
further. Ask for more and the run delivers 15 and adds one uncharged row per handle saying the
cap is Threads', not ours. Ask for fewer and you get exactly that many.
- A pasted post link is a first-class input.
threads.com/@handle/post/<code> and the
threads.net form both return that one post, with its view count and its top replies — about
twenty, as Threads serves them — inside the same row, with repliesTotal saying how many exist.
includeProfile adds one profile row per handle: follower count, full name, biography, bio links,
verification and the profile picture.
- An account that hides its counts gets
countsHidden: true and explicit nulls, never a 0 that
would read as a real measurement.
- Date filters compare against the post's own publication time, and posts they drop are never
charged.
- No number you type can refuse your run. The accepted ranges — 1 to 2,000 rows, 1 to 1,000 posts
per handle, 30 to 3,600 seconds — are applied inside the actor, not as a validation gate on the
input form. A value outside a range is moved to the nearest one in it and one uncharged row says
what was read and what was used, instead of the run being rejected before it starts.
- Your limit is the limit:
resultsLimit caps each handle and maxItems caps the run, both exact.
limit, count, max_posts and maxPosts are read as the right one of the two, the smallest
wins, and one uncharged note row says which name was read.