Instagram API rate limits and pagination, explained
Two things decide how smoothly a scraping job runs against the herascrape real-time Instagram data API: how you page through list results, and how you stay inside the rate limit. Both follow one convention across all 37 endpoints, so once you have the pattern it works everywhere. This explains cursor pagination, the 50 requests-per-second limit, exactly what you pay for, and how to recover from a 429.
Create one in the dashboard — $1 free on signup, no card. Pass it as the x-api-key header and start calling.
Cursor pagination
Every list endpoint — followers, following, comments, media and the rest — returns one page wrapped in the same envelope:
{
"items": [ /* this page of results */ ],
"next_cursor": "QVFE…opaque…",
"has_more": true
}
items is the page. next_cursor is an opaque token — treat it as a black box, never parse or construct it yourself. has_more tells you whether another page exists. To collect a full list you make the first request with no cursor, then on each response pass next_cursor back as the cursor query parameter, and stop the moment has_more is false.
Here is the loop against the followers endpoint, GET /users/{id}/followers, in Python and Node:
import os, httpx
KEY = os.environ["HERASCRAPE_KEY"]
BASE = "https://api.herascrape.com"
def followers(user_id):
cursor = None
with httpx.Client(headers={"x-api-key": KEY}) as c:
while True:
params = {"cursor": cursor} if cursor else {}
page = c.get(f"{BASE}/users/{user_id}/followers", params=params).json()
yield from page["items"]
if not page["has_more"]:
return
cursor = page["next_cursor"]
for u in followers("528817151"):
print(u["username"], u["pk"])The same three-field envelope and the same loop drive every paginated endpoint, so you can reuse this helper by swapping the path. See the pagination guide for the per-endpoint field reference.
Rate limits
Each API key has a default limit of 50 requests per second, plus a cap on how many requests can be in flight at once (concurrency). Exceed either and the next request comes back as 429 Too Many Requests instead of data.
Fifty per second is plenty for most jobs, and because pagination is sequential — you need one page's cursor before you can fetch the next — a single paging loop never approaches the limit on its own. You hit 429 when you fan out: many accounts scraped in parallel, or several workers sharing one key. If that is your workload, the limit can be raised on request — tell us the throughput you need.
The important part: a 429 is never billed. It costs you nothing but the round trip, so a rejected request is safe to simply retry.
What's billable
Billing is flat and tied to success. Only responses in the 2xx range cost anything, at $0.0004 per request — that is $0.40 per 1,000 successful calls, with no tiers and no per-endpoint pricing.
Everything that is not a 2xx is free. A 429, a 4xx such as a bad user id, a 5xx — none of them are charged. In practice that means your bill tracks the number of pages you actually received, so the cost of a pagination job is simply the page count times $0.0004. Full detail is on the pricing page.
Handling 429
Because a 429 is free and transient, the right response is to wait a moment and retry — with exponential backoff so repeated failures back off progressively rather than hammering the key. Here is a small wrapper that retries on 429 and returns the parsed page:
import os, time, httpx
KEY = os.environ["HERASCRAPE_KEY"]
BASE = "https://api.herascrape.com"
def get_page(client, user_id, cursor=None, max_retries=5):
params = {"cursor": cursor} if cursor else {}
for attempt in range(max_retries):
r = client.get(f"{BASE}/users/{user_id}/followers", params=params)
if r.status_code == 429:
time.sleep(2 ** attempt) # 1s, 2s, 4s, 8s, 16s
continue
r.raise_for_status()
return r.json()
raise RuntimeError("rate limited after retries")
with httpx.Client(headers={"x-api-key": KEY}) as c:
page = get_page(c, "528817151")
print(len(page["items"]), page["has_more"])Drop get_page into the pagination loop above in place of the raw request and the whole job becomes resilient to bursts. The errors reference lists every status code and when to retry.
The depth cap
One thing to expect on very large or business accounts: Instagram itself caps how deep some paginations can go, so for an account with tens of millions of followers the full list may not be reachable to the last entry. You do not need to detect this or handle it specially — the API surfaces it the same way it surfaces the natural end of a list. When there is no more data to return, has_more comes back false and your loop ends. The pattern you already wrote handles the cap and the true end of the list identically.
Create one in the dashboard — $1 free on signup, no card. Pass it as the x-api-key header and start calling.