I built a small CLI that posts images to Instagram from a folder on my Mac. Writing the script took an evening. Getting permission to call the API took longer, and it’s the part every tutorial waves through, so here it is in full.
The goal is two values: an IG_ACCESS_TOKEN (a long-lived Instagram API
token) and an IG_USER_ID (your account’s numeric ID). Everything below is
the path to those.
Your account must be Professional
Personal Instagram accounts can’t publish through the API at all. Instagram app, Settings, Account type and tools, Switch to professional account. Business or Creator, both work.
You need a Meta developer app
Even though you’re not shipping an app. Go to developers.facebook.com, My Apps, Create App, use case Other, type Business.
The Facebook account that owns this app does not need to be linked to your Instagram account or share an email with it. It just owns the app. The Instagram account gets connected separately by logging in with its own credentials. That confused me for an hour.
You have to invite yourself, then accept
The app sits in Development Mode, and a Development Mode app can only touch accounts that hold a role on it. So you add your own Instagram account as an Instagram Tester under App roles, Roles. Then comes the step that’s easy to miss: go into Instagram itself, Edit Profile, Apps and Websites, Tester Invites, and accept. Until you accept, the account shows as pending on the dashboard and token generation doesn’t work.
The upside of this arrangement is that the app can stay in Development Mode forever. It only ever touches my own account, so it never needs App Review.
Two auth paths, and they don’t mix
Nothing on the dashboard tells you these are different products:
- Instagram API with Instagram Login. Add the Instagram product to your
app, choose “API setup with Instagram business login”, add your account, grant
instagram_business_basicandinstagram_business_content_publish, generate the token. No Facebook Page required. Host isgraph.instagram.com. - Facebook Login for Business. The older path. Requires a Facebook Page
actually linked to your Instagram account, and you dig the user ID out via
GET /me/accounts, thenGET /{page-id}?fields=instagram_business_account. Host isgraph.facebook.com.
Most tutorials cover the older Facebook Login path, so that’s what you’ll find when you search. I went with Instagram Login and it was less hassle: no Facebook Page needed, and getting the user ID is a single request.
Generating the access token
On the app dashboard, open the Instagram product and find API setup with Instagram business login. Under “Generate access tokens”, click Add account and log in with your Instagram account (this only works after the tester invite is accepted). Grant at least these two permissions:
instagram_business_basicinstagram_business_content_publish
Then click Generate token next to your account and copy it. This is the
long-lived token, and it’s your IG_ACCESS_TOKEN. You don’t have to build an
OAuth flow for a personal script. The dashboard button is the whole thing.
Finding your user ID
It’s not shown in the dashboard. You ask for it:
curl "https://graph.instagram.com/v23.0/me?fields=user_id,username&access_token=YOUR_TOKEN"
The response contains two IDs. user_id is the professional-account ID that
every publishing call is scoped to. id is
app-scoped and will fail with a permissions error that doesn’t mention IDs at
all. Copy the user_id.
The token expires in about 60 days
There’s a refresh endpoint, a plain GET that hands back a fresh 60-day token:
curl "https://graph.instagram.com/refresh_access_token?grant_type=ig_refresh_token&access_token=YOUR_CURRENT_TOKEN"
Right now I paste the result into .env by hand, which is a time bomb I’ve
scheduled for myself roughly every two months.
One last trap: location IDs
A location tag on a post is a Facebook Page ID, and looking one up through the Pages Search API needs a Facebook Login token, not the Instagram one you just spent an hour getting. I gave up and pulled the numeric ID straight out of a Facebook place page URL.
With the token and user ID in hand, actually publishing is its own story: containers, polling, image constraints, and a Node bug that had nothing to do with Instagram. That’s the next post.