A temporary failure on the rescue route now says "please re-run", and video posts point at their next step.
- A profile served through the fallback route is no longer written off when that route itself hits a temporary wall. For the small class of profiles Instagram's own profile service errors on, the actor resolves the profile over a second route (added in 1.0.11). When that second route was also blocked — a rate wall, an empty answer, a degraded page — the run kept the original "it is on their side, a re-run won't help" verdict, so a profile that would deliver on a re-run was worded as a lost cause. A temporary failure on the rescue route now ships as what it is: uncharged, marked retryable, "please re-run". Profiles the rescue answers definitively — genuinely missing or private — keep the same honest final answer as before, word for word.
- Video posts now point at the next step. The profile row counts the videos among the posts it returned — "2 of these are videos" — and, like the run's status line, says how to turn them into text: pass the run's Dataset ID to
steadyfetch/instagram-reel-transcript-scraper. Runs that deliver no videos say nothing extra.
- The Instagram suite and steadyfetch shelf tables in the README now link every actor that is live on the Store.
Profiles that Instagram's own profile service errors on are now delivered through a fallback route.
- A small class of large public profiles —
natgeo was the reported one — could not be read at all. Instagram's profile service answers these accounts with its own server error instead of the profile, every time, so every run ended in an uncharged "Instagram's side" miss. The actor now recognises that error and resolves the profile over a second route, and the posts are collected and delivered as normal. The same rescue is attempted for profiles the service persistently answers with an empty body.
- On rescued profiles, a few profile-level columns Instagram does not publish on the fallback surface (
postCount, isBusiness, categoryName, and profilePostCount on post rows) are null rather than guessed. Post rows are otherwise complete, charged only when delivered, exactly as before.
- Nothing changes for everyone else: profiles that are genuinely missing, private, or empty keep their honest uncharged answers, and temporary walls still say "please re-run".
A typo in an input field name now gets a helpful pointer instead of sample rows.
- A field name the actor does not recognise is named back to you. If you sent your handles under a field this actor does not have —
handles instead of profiles, say — the run answered with the free sample rows, exactly as if you had asked for a preview, and nothing told you the input had been ignored. You now get one uncharged row that names the field it did not recognise, names the field you meant, and shows the shape to send. Sending only settings, with no profiles at all, gets the same pointer. A run with no input at all still returns the free sample rows, unchanged.
Large runs finish faster — same rows, same prices.
- Posts are now written to your dataset a feed page at a time instead of one row at a time, and each page is billed in one step. A run that walks long profiles spends its time fetching, not on bookkeeping. Nothing about the output changed: same rows, same fields, same order, same prices, and interrupted or moved runs still resume without repeating or double-charging anything.
Review-fix build: a profile that was cut short no longer reports as finished, and hashtags are read in every language.
- A profile the run stopped part-way through stays open. When a run ended a profile early — because it hit your result limit, your "max run seconds", or the actor's own run ceiling — that profile still got its finished summary row. A later run read that row as "already answered" and skipped the profile, so the posts the first run never reached could not be collected at all. Now the profile is left open: raise the limit and run again, and the run picks up exactly the posts you have not received yet. Posts already delivered are never sent or charged a second time.
- Hashtags in every script Instagram accepts. Tags written in Japanese, Chinese, Korean, Thai and other scripts were dropped from the
hashtags column; they are now returned like any other tag. Latin, Cyrillic and Arabic tags are unchanged.
- Back-to-back hashtags all come back. A caption written
#sale#new#drop returned only the first tag; it now returns all three. A URL fragment such as example.com/page#anchor is still not read as a hashtag.
Runs that get moved, restarted or resurrected now pick up exactly where they left off.
- Nothing is ever delivered or charged twice, even if Apify moves your run to another server mid-way. Long runs are occasionally migrated between servers, and a resurrected run restarts the same way. Until now that meant the posts already in your dataset were collected again, pushed again as duplicate rows, and charged again. Every row now carries a hidden key, and a restarted run reads your own dataset first: posts it already delivered are skipped silently, and only the posts you have not received yet are collected.
- Your result limit survives a migration. A run that restarted used to start counting from zero, so a limit of 500 could return up to 1,000 rows and bill for them. The limit is now counted across the whole run, wherever it ran.
- "Max run seconds" now measures your run, not the server it happens to be on. A restarted run inherits what is left of your time limit instead of starting the clock again.
- A charge interrupted half-way is completed, never repeated. If a run is moved in the instant between a row being delivered and being billed, the missing charge is settled once, on the next start, after checking the run's own billing record.
- Every row still states what was actually billed for it, and rows the actor cannot bill now say
charged: false rather than claiming a charge that never happened.
Review-fix build: dedupe under concurrency, charge settlement, honest duplicate wording.
- Billing accuracy. A post published to two profiles you asked for is one row and one charge even when both profiles are being read at the same moment. Until now the two parallel readers could each reach that post before the other's row landed, and the same post could be delivered — and billed — twice.
- If a row fails to reach your dataset, its place in your result limit and its charge slot are both handed back, so a failed write neither eats one of your results nor costs you anything. Once a row has landed it is never re-sent, so nothing can be delivered to you twice.
- Clearer counts: the run summary and the OUTPUT record now say "duplicate sightings skipped (first sighting kept and charged once)" rather than "duplicates merged". The dataset is append-only, so the first row that ships is the one you keep — the old wording promised a merge that could not happen.
Every post and reel from any public Instagram profile, by handle or profile URL.
- Your result limit is exact. The run stops on the row you set, on any page boundary, and the last row names which limit stopped it and what is still pending. Profiles are read in parallel and the cap still holds to the row: ask for one post and you get one post, and one charge.
- One row per post with caption, hashtags, mentions, engagement, video length, media links, tagged accounts, co-authors, location and the paid-partnership flag — at one price, with no second charge for details.
- A profile row per handle: followers, bio, category, link in bio, how many posts came back and how many your filters excluded.
- Profiles that return nothing still get a row saying why, and are never charged — private accounts, wrong handles, empty profiles, and Instagram's own temporary walls are all told apart.
- Every row's
charged column states what was actually billed on that run, so the dataset reconciles against your invoice on its own.
- A post published to two profiles you asked for is one row and one charge.
- If Instagram stops serving a profile part-way through, the profile row says so and is marked worth re-running — a partial answer never reads like a complete one.
- Filters: post type, posted-after and posted-before dates.
- Chaining: pass a Dataset ID (or paste rows) and the actor finds the profiles in it.
- Runs with the input left empty return uncharged sample rows without contacting Instagram.