Skip to content

Get Instagram posting credentials

keryx posts Reels through the Instagram API with Instagram Login (graph.instagram.com): no Facebook Page, and own-account posting runs under Standard Access, so there is no App Review. It needs a long-lived access token and your professional account id, and there are two ways to get them.

The short version. With the app set up (§1 to §3 below) let keryx run the OAuth flow and store both:

keryx auth instagram        # opens the browser, stores the token and user_id

Or take a token from the dashboard's Generate token button (§4), check it, and wire it up by hand:

export INSTAGRAM_ACCESS_TOKEN='IGAA…'        # env only, never committed
curl -s "https://graph.instagram.com/me?fields=user_id,username,account_type&access_token=$INSTAGRAM_ACCESS_TOKEN"
# → {"user_id":"178414…","username":"yourhandle","account_type":"MEDIA_CREATOR"}
# ~/.keryx/config.yaml
platforms:
  instagram:
    enabled: true
    user_id: "178414…"

Either way, posting needs the instagram_business_content_publish permission on the app (§7); the dashboard's default token does not carry it.

Arrived here from an error or a pasted URL?

  • graph.instagram.com/me?fields=user_id,username… is the connection check, §5. An error reply means the token has expired or the account is not reachable by the app (§3).
  • www.instagram.com/oauth/authorize?…scope=instagram_business_basic,… is the raw OAuth URL, appendix; keryx auth instagram drives it for you.
  • grant_type=ig_exchange_token swaps a short-lived token for a long-lived one, same appendix. The dashboard's Generate token button skips it.
  • "Unable to add a user with a role on the app's owning business" or "Insufficient developer role": you are an admin of the business that owns the app, so you cannot be its tester. Publish the app instead, §2.

Meta's developer dashboard changes often, and the path has real snags. The steps below are the validated route (captured working end-to-end on 2026-06-17); the gotchas that cost us time are called out inline.

Prerequisites

  1. An Instagram Professional account (Business or Creator). Convert in the Instagram app: Settings → Account type and tools → Switch to professional account.
  2. A Meta developer account + an app at developers.facebook.com.

1. Add the Instagram use case

Meta's dashboard is use-case-based (there is no "Add product"):

  1. App Dashboard → Add use cases (or Use cases in the left nav).
  2. Add "Manage messaging & content on Instagram" (publishes posts via the Instagram API). A pre-existing Facebook Login for Business use case is unrelated — leave it.
  3. Customize the use case → API setup with Instagram login. This shows the Instagram app ID and Instagram app secret (distinct from the Facebook app id/secret).

2. Publish the app (switch to Live) — the key unlock

In development mode, token generation only works for accounts holding an app role (Instagram Tester). If your Instagram account belongs to the same Meta Business that owns the app (i.e. you're a business admin), you cannot add it as a tester — Meta refuses with "Unable to add a user with a role on the app's owning business" — and Add account then fails with "Insufficient developer role". The way out is to take the app Live:

  1. App settings → Basic → set a Privacy Policy URL (mandatory to publish) and a Category → Save.
  2. Left nav → PublishPublish (Development → Live). Own-account Standard Access needs no App Review.

3. Make your account reachable by the app

If the Instagram account and app share a Meta Business (you're admin) — the common case:

  1. Business Settings → switch to the business that owns the app (check Accounts → Apps for it).
  2. Accounts → Instagram accounts → Add → Connect your Instagram account → log in. This claims it into the business.
  3. With the IG account selected, Assign access → add yourself with full control. (The "0 people assigned" state is what blocks the app — you must be assigned.)

If it's a standalone account (no business): instead use App roles → Roles → Instagram testers → invite your username → accept in Instagram (Settings → Apps and websites → Tester invites).

4. Generate the token

Back on API setup with Instagram login → "Generate access tokens":

  1. Add account → log in with your professional IG account → Allow.
  2. Click Generate tokencopy it immediately (it's shown once). It's an IGAA… long-lived token (~60 days).

The default consent covers messaging + basic profile — it does not include publishing. That's enough to validate the connection (next step); adding the publish permission for actual posting is §7.

5. Validate the connection

curl -s "https://graph.instagram.com/me\
?fields=user_id,username,account_type\
&access_token=<TOKEN>"
# → {"user_id":"178414...","username":"yourhandle","account_type":"MEDIA_CREATOR", ...}

A clean JSON response means the token + account work. The user_id is your platforms.instagram.user_id.

6. Configure keryx

export INSTAGRAM_ACCESS_TOKEN='<TOKEN>'   # env only, never committed
# ~/.keryx/config.yaml
platforms:
  instagram:
    enabled: true
    user_id: "178414..."

A dry-run validates wiring without posting:

keryx post instagram --workspace <slug> --dry-run

7. Enable posting (content publish)

The connection token from §4 can't post (messaging/basic scope only). To post Reels:

  1. Use case → Permissions and features → add instagram_business_content_publish. For your own account it works under Standard Access (no App Review).
  2. Regenerate the token (§4) so it carries the publish scope, and update INSTAGRAM_ACCESS_TOKEN.

Reels publishing via this API supports Professional accounts (Business and Creator). If a publish call is rejected on a Creator account, switching the account to Business is the fallback.

Token lifecycle

The long-lived token lasts ~60 days and refreshes headlessly before expiry:

curl -s "https://graph.instagram.com/refresh_access_token\
?grant_type=ig_refresh_token&access_token=<TOKEN>"

keryx auth refresh rotates and persists it with an alert on failure; see scheduled token refresh for running that on a timer. The curl above is the manual equivalent.

Appendix: short-lived → long-lived exchange

If you obtain a short-lived token via the raw OAuth flow rather than the dashboard button, exchange it once:

curl -s "https://graph.instagram.com/access_token\
?grant_type=ig_exchange_token\
&client_secret=<INSTAGRAM_APP_SECRET>\
&access_token=<SHORT_LIVED_TOKEN>"
# → {"access_token":"IGAA...","expires_in":5183944}

The raw OAuth authorize URL (for keryx auth to automate): https://www.instagram.com/oauth/authorize?client_id=<INSTAGRAM_APP_ID>&redirect_uri=<REDIRECT>&response_type=code&scope=instagram_business_basic,instagram_business_content_publish