Troubleshooting · September 2026

Instagram Scraping Blocked: challenge_required and checkpoint_required Explained

Three different block responses that get treated as one bug. What each one means, how to recover the account safely, and how to get accounts out of your public-data pipeline for good.

· 10 minute read

The short answer: challenge_required and checkpoint_required are not scraping errors — they are account state errors. Instagram has flagged the account your client is using and is asking a human to complete a verification step in the official app or browser. No amount of retry logic, header tuning, or endpoint switching clears them, because the problem is not the request. It is the identity behind it.

feedback_required is the same family with a different meaning: the account is rate-limited or restricted for that action, and the restriction lifts on Instagram’s schedule, not yours. This is why the “blocked” bucket needs triage before code changes. Getting it wrong is how people lose accounts.

Do not attempt to automate past a challenge or checkpoint. Do not use third-party “challenge solver”, “checkpoint unlock”, or “bypass” services, and never hand your credentials, session cookies, or 2FA codes to one. Those services are credential-harvesting operations, and automated challenge-solving violates Instagram’s terms. Complete verification yourself, in the official app, as the account owner.

Which response do you actually have?

Response bodyWhat it meansCorrect response
{"message":"challenge_required","status":"fail"} (often with challenge_url)The account must pass a verification challenge before further authenticated requestsStop the scraper. Complete the challenge yourself in the official app.
{"message":"checkpoint_required","status":"fail"}Same gate, surfaced by different client libraries; the account is held at a security checkpointSame: manual verification, then a long cooldown.
{"message":"feedback_required","status":"fail"}The action is temporarily blocked — a restriction, not a verification gateStop the action. Wait hours, not seconds. Reduce volume permanently.
login_required / 401No valid session at allDifferent problem — see login required errors.
429 Too Many RequestsRate or anti-automation limitBack off with jitter — see the Instaloader 429 guide.
Typical payloads
HTTP/1.1 400 Bad Request
{"message":"challenge_required","status":"fail","challenge_url":"/challenge/..."}

HTTP/1.1 401 Unauthorized
{"message":"checkpoint_required","status":"fail"}

HTTP/1.1 400 Bad Request
{"message":"feedback_required","status":"fail"}

Why it happened

1. The account looks automated

Any session-driven automation produces a request pattern that differs from human usage: tight intervals, no reads before writes, many targets in a short window, datacenter IP ranges, a client fingerprint that does not match the declared client. Instagram’s risk systems score the account and act on the score. This is not a bug in your code — it is the expected outcome of pointing an automation rig at an endpoint that is designed to be driven by a human app.

2. The library and the platform have drifted apart

Challenge handling is where open-source clients break first, because the challenge flow itself is a moving target. Recent examples from public issue trackers:

The common thread in these threads is that maintainers do not recommend automated bypass. They recommend manual verification and lower volume — or moving off session-based automation entirely.

3. Shared or reused sessions

If one session is used by several processes, machines, or people — a cron job plus a laptop plus a CI runner — the account suddenly “appears” in multiple places at once. That alone triggers challenges. This is one of the most common self-inflicted causes and the easiest to confirm: look for a second worker before you blame Instagram.

4. It is not your account’s fault at all

Sometimes the target is gone (deleted, renamed, or changed to private), the numeric ID you cached is stale, or you are querying an endpoint your client cannot reach at all. Check for a 404-shaped cause before assuming a block — and note that several Instagram paths return HTTP 200 with "status":"fail", which is why body inspection matters.

Safe recovery sequence

  1. Stop every process using the account. Kill the scraper, pause the cron job, stop the CI schedule. This is step one because continued traffic converts a temporary checkpoint into a suspension.
  2. Complete the challenge yourself, manually. Sign in through the official Instagram app (preferably on the device the account normally uses) and follow the on-screen verification. Do this once, patiently. Do not script it, do not loop it, and do not attempt it from a datacenter IP.
  3. Confirm the account is healthy. Once verified, use the app normally for a while. If feedback_required is what you saw, there is nothing to “complete” — just wait. Restrictions commonly persist for hours to days.
  4. Wait well past the apparent cooldown. A challenge that clears in the app does not mean the account is trusted again immediately. Restarting automation within minutes is a reliable way to trigger a second challenge.
  5. Change the architecture, not the headers. If the same workload will run again, the account will be flagged again. That is the finding this error is giving you.
  6. Never share session material. If you opened an issue or posted logs, assume any pasted sessionid, csrftoken, or ds_user_id is compromised. Verify the account, change the password, and sign out other sessions. See Instaloader #1702 for how account losses escalate.

Uninstalling and reinstalling the app, clearing stale sessions, and using a stable network are legitimate steps for your own account. Rotating accounts or proxy pools to keep a scraper alive is not troubleshooting — it is evasion, it breaks terms, and it gets accounts banned in batches. Comply with GramScraper’s Acceptable Use Policy and Instagram’s terms.

Code: detect the block and stop cleanly

The most valuable code change is the one that prevents the retry storm. Several of these responses arrive with a 200 or 400, so status-only checks miss them.

Node.js — classify before you act
const BLOCK_MESSAGES = new Set([
  "challenge_required",
  "checkpoint_required",
  "feedback_required",
]);

async function readJson(response) {
  const body = await response.json().catch(() => ({}));
  const message = String(body?.message || "");
  if (BLOCK_MESSAGES.has(message)) {
    // Account-level block: not retryable, requires human verification.
    const error = new Error(`Account blocked upstream: ${message}`);
    error.code = message;
    error.retryable = false;
    throw error;
  }
  return body;
}

Then let the block terminate the job. A crashed job with a clear error is strictly better than a job that quietly hammers a checkpointed account for six hours.

The architectural fix: take the account out of the loop

Every response in this article depends on a signed-in identity. That identity is the thing being flag-scored, restricted, and checkpointed — and it is the thing you do not control. If the data you need is public, the account is an unnecessary dependency in your pipeline: it is the only component that can get your workload blocked, and it is also the only component that can put a real person’s account at risk.

GramScraper removes it. You call a documented REST endpoint with a GramScraper API key. There is no Instagram login, no session to checkpoint, no account to lose. Public profile lookups cost one credit per request.

curl — verified against production
curl --get \
  "https://gramscraper.com/api/instagram/v1/user/by/username" \
  --header "Authorization: Bearer $GRAMSCRAPER_API_KEY" \
  --data-urlencode "username=instagram"
Node.js 18+ — verified against production
const url = new URL(
  "https://gramscraper.com/api/instagram/v1/user/by/username"
);
url.searchParams.set("username", "instagram");

const response = await fetch(url, {
  headers: { Authorization: `Bearer ${process.env.GRAMSCRAPER_API_KEY}` },
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const profile = await response.json();
console.log(profile.username, profile.follower_count); // instagram 686724581

The response is flat public JSON — username, full_name, biography, follower_count, following_count, media_count, is_private, is_verified, profile_pic_url — plus a _meta object with credits consumed and remaining. All endpoint documentation is in the API docs; a fuller walkthrough of the fields is in the profile data API guide.

What changes in your error handling

Verified statuses on the live endpoint:

The useful part is what is absent: there is no challenge_required branch, because there is no account to challenge. Your incident surface drops to ordinary HTTP semantics, and a failed job can no longer cost someone their Instagram account.

Related failure modes

Blocks rarely arrive alone. If your client also returns an authentication error, start with Instagram scraping “login required”. If the failure is rate-shaped rather than account-shaped, read the rate limit and “too many requests” guide and the Instaloader 429 and first-request limit guide. For how these signals are compared at the request level, see useragent mismatch and web_profile_info requests. For the wider picture of why session-based scrapers decay, read why DIY scrapers break.

Get 100 free API credits

Remove the account layer entirely — no Instagram session, no checkpoint, no ban risk. Call documented endpoints instead.

Create a free account →