Blog

Why Instagram Publishing Fails Before Upload: An Account and Permission Checklist

Diagnose Instagram publishing failures before media upload by checking the login path, OAuth result, token permissions, account type, and access level.

Kavenio Engineering

Related implementation guides

  1. Instagram Content Publishing API: Accounts, Permissions, and Containers
  2. Build an Instagram API Post With an Image: OAuth to Final Status

An Instagram connection can return to your app with an authorization code, exchange that code successfully, and still be unable to create a media container.

OAuth completed. Publishing eligibility did not.

This checklist is for the failure before Meta starts fetching an image or processing a video. If your application already has a container ID, the account and permissions cleared the first gate. Use the container lifecycle in the Instagram Content Publishing API reference instead.

Prove where the request stopped

Do not start with the media file unless the request reached POST /{ig-id}/media. Classify the last completed step first.

Last completed stepWhat is already provenInspect next
Authorization window openedApp ID and authorization URL were acceptedUser decision, redirect URI, and requested permissions
Callback contains codeThe user approved the requestCode age, one-time use, and token-exchange parameters
Token exchange returned an access tokenThe code and app credentials matchedGranted permissions, token type, and API host
GET /me returned an identityThe token can read its account identityaccount_type and production access level
POST /{ig-id}/media returned a container IDAccount and publishing permission checks passedMedia fetch and container processing

This split matters because changing a JPEG cannot fix an expired authorization code, a personal account, or a token without publishing permission.

Name the login configuration before reading the error

Meta supports two publishing configurations. Their permission names look similar, but the tokens and hosts are not interchangeable.

CheckInstagram LoginFacebook Login for Business
User signs in withInstagram credentialsFacebook credentials
Publishing tokenInstagram User tokenFacebook Page token
API hostgraph.instagram.comgraph.facebook.com
Facebook PageNot requiredRequired and linked to the Instagram account
Minimum publishing permissionsinstagram_business_basic, instagram_business_content_publishinstagram_basic, instagram_content_publish, pages_read_engagement

Meta documents this split in the Instagram Platform overview. Write the selected login type into the connection record. If the system has to infer it later from a token string or account ID, error handling will eventually send a valid token to the wrong host.

Read the OAuth result literally

For Business Login for Instagram, a successful callback contains a code. That code is valid for one hour and can be used once. A canceled flow returns error=access_denied and error_reason=user_denied instead.

Those are different outcomes. Do not turn a user denial into a token-exchange retry.

The exchange request must also use the same redirect_uri used to begin the flow. Meta calls this out explicitly in its Business Login for Instagram guide.

curl -X POST https://api.instagram.com/oauth/access_token \
  -F "client_id=${INSTAGRAM_APP_ID}" \
  -F "client_secret=${INSTAGRAM_APP_SECRET}" \
  -F "grant_type=authorization_code" \
  -F "redirect_uri=${OAUTH_REDIRECT_URI}" \
  -F "code=${AUTHORIZATION_CODE}"

Run this exchange on the server. The request contains the Instagram app secret. If Meta reports that the matching code was not found or was already used, do not retry the same code. Start a new authorization flow and preserve the exact provider error for diagnosis.

Check the permission list before storing the connection

The token response includes the permissions the user granted. For Instagram Login publishing, both of these must be present:

  • instagram_business_basic
  • instagram_business_content_publish

The older business_basic and business_content_publish scope names were deprecated in January 2025. A consent screen that still requests the old names can look correct while producing a token that cannot call current endpoints.

For Facebook Login, do not substitute the Instagram Login names. The required publishing permission is instagram_content_publish, without the business_ segment.

An application-side gate makes this mismatch visible before a scheduled job tries to publish:

type InstagramConnectionCheck = {
  accessLevel: "standard" | "advanced";
  accountType: string;
  apiHost: string;
  grantedPermissions: string[];
  loginType: "instagram" | "facebook";
  servesExternalAccounts: boolean;
};

const requiredPermissions = {
  instagram: ["instagram_business_basic", "instagram_business_content_publish"],
  facebook: [
    "instagram_basic",
    "instagram_content_publish",
    "pages_read_engagement",
  ],
} as const;

function diagnoseInstagramConnection(input: InstagramConnectionCheck) {
  const issues: string[] = [];
  const expectedHost =
    input.loginType === "instagram"
      ? "graph.instagram.com"
      : "graph.facebook.com";

  if (!["BUSINESS", "CREATOR"].includes(input.accountType.toUpperCase())) {
    issues.push("Connect an Instagram Business or Creator account.");
  }

  if (input.apiHost !== expectedHost) {
    issues.push(`Use ${expectedHost} for the selected login type.`);
  }

  for (const permission of requiredPermissions[input.loginType]) {
    if (!input.grantedPermissions.includes(permission)) {
      issues.push(`Request ${permission} and reconnect the account.`);
    }
  }

  if (input.servesExternalAccounts && input.accessLevel !== "advanced") {
    issues.push("Obtain Advanced Access before connecting customer accounts.");
  }

  return issues;
}

This check does not prove that Meta will accept a particular post. It proves that the stored connection matches the documented publishing configuration.

Ask the token which account it represents

After exchanging for a long-lived Instagram User token, query /me before marking the connection ready.

curl \
  "https://graph.instagram.com/${API_VERSION}/me?fields=id,username,account_type" \
  -H "Authorization: Bearer ${INSTAGRAM_ACCESS_TOKEN}"

The account must be a professional Business or Creator account. A consumer account is a stop condition, not a warning. Repeating the same request with the same account will not make it eligible.

Kavenio performs this identity check during connection. It normalizes Meta's BUSINESS and CREATOR values and rejects other account types with the message Instagram publishing requires a Business or Creator account. The connection does not reach the posting queue.

A test account working does not prove customer access

Standard Access is enough for professional accounts owned or managed by people who have a role on the app. It is intended for development and testing.

Advanced Access is required when the app serves professional accounts it does not own or manage. Meta requires App Review and Business Verification for that level. The current boundary is documented in the access-level section of the platform overview.

This produces a common false conclusion:

  1. A developer connects the team's Business account.
  2. /me works and a test post publishes.
  3. An unrelated customer cannot complete the same flow.
  4. The team starts changing OAuth code that already works.

The useful comparison is not “test versus production.” It is “account with an app role versus customer account without one.” If only the first group works, inspect access level and review status before changing the token exchange.

Facebook Login adds Page gates

Skip this section for direct Instagram Login. That configuration does not require a Facebook Page.

With Facebook Login for Business, the Instagram professional account must be linked to a Page. The person granting access must be able to perform an admin-equivalent Page task. Meta maps the Content and Full control tasks to the ability to grant instagram_content_publish.

There is another Page-level stop condition: Page Publishing Authorization. Meta's Content Publishing guide states that a linked account cannot publish while required authorization is incomplete. It also says the API does not expose whether a Page currently requires that authorization. This is one of the few cases where the correct diagnostic step is a user-facing instruction to check Page settings, not another API request.

Use the symptom to choose the next check

SymptomMost useful checkCorrect stop or fix
Callback contains access_deniedUser decisionStop retrying; let the user start consent again
Token exchange says the code was used or not foundCode age and reuseStart a new authorization flow
Token works on one Graph host but not the otherStored login typeMatch Instagram token to graph.instagram.com or Page token to graph.facebook.com
/me returns a personal accountaccount_typeConnect a Business or Creator account
Team-owned accounts work but customer accounts do notAccess levelComplete App Review and Business Verification for Advanced Access
Publishing permission is absentGranted permission listRequest the correct scope set and reconnect
Facebook Login cannot find a usable Instagram accountPage link and Page taskLink the professional account and grant Content or Full control
Page-linked publishing remains blockedPage Publishing AuthorizationAsk the Page owner to complete the required authorization

What Kavenio marks as ready

Kavenio's current direct Instagram flow requests instagram_business_basic and instagram_business_content_publish, exchanges the short-lived token for a long-lived token, and reads the professional identity from graph.instagram.com.

Its readiness result separates three pre-upload failures:

  • INSTAGRAM_ACCOUNT_NOT_FOUND: no Business or Creator account is available;
  • INSTAGRAM_ACCOUNT_NOT_SELECTED: the connection has no publishing destination;
  • INSTAGRAM_PERMISSION_MISSING: the account must reconnect with publishing permission.

Only a ready connection reaches the container sequence described in the Kavenio Instagram documentation. At that point, media hosting, format validation, container processing, and publishing limits become relevant. Before that point, they are distractions.

The current rule is simple: prove the login path, code exchange, permission set, professional identity, and access level in that order. Do not send media to a connection that has not passed those checks.

Build social posting into your product

Connect accounts, validate content, publish posts, and receive status events through one posting-focused API