# Instagram API Access Token: Get, Exchange, Refresh

How to get an Instagram API access token, exchange it for a 60-day token, refresh it on schedule, and fix error 190 for Instagram Login and Facebook Login.

Canonical page: https://www.tryadeli.com/blog/instagram-api-access-token
Last updated: 2026-10-09

By Mika Reyes, Co-founder, Adeli. Published 2026-10-09.

## The short answer

An Instagram API access token comes from Meta's OAuth flow. With Instagram Login you get a 1-hour token, exchange it for a 60-day long-lived token on graph.instagram.com, and refresh it before day 60. With Facebook Login you publish with a Page access token. Adeli holds the Meta app and refreshes every connected account's token for you.

An Instagram API access token is the credential Meta issues when an Instagram professional account authorizes your app, and **every call to the [Instagram API](https://www.tryadeli.com/blog/instagram-api-guide) needs one**. How you get it depends on the login. With Instagram Login, you trade an authorization code for a 1-hour token, exchange that for a **60-day token**, and refresh it before it lapses. With Facebook Login, you publish with a Page access token taken from a long-lived Facebook User token.

Below are both flows with the exact endpoints, then a refresh schedule and the errors you hit when a token dies.

## What is an Instagram API access token?

**It's a string that authorizes your app to act for one Instagram account.** Each connected account has its own token, and that token only carries the permissions the user granted. There's no app-wide Instagram key: your Meta app has an App ID and App Secret, and the tokens come from each user's login.

**Tokens are scoped to a login type.** A token from Instagram Login works on `graph.instagram.com`. A token from Facebook Login works on `graph.facebook.com`. Send a token to the other host and the request fails, so check the host first when a token that worked in testing breaks in production.

## Instagram Login vs. Facebook Login tokens

**Pick the column that matches how your users connect.** Instagram Login needs no Facebook Page. Facebook Login needs the Instagram account linked to a Page the user manages.

| | Instagram Login | Facebook Login |
|---|---|---|
| **Token you call the API with** | Instagram User access token | Facebook Page access token (for publishing) |
| **API host** | `graph.instagram.com` | `graph.facebook.com` |
| **First token** | Short-lived, **1 hour** | Short-lived Facebook User token |
| **Long-lived token** | **60 days**, via `ig_exchange_token` | User token **about 60 days**, via `fb_exchange_token` |
| **Refresh** | `ig_refresh_token`, adds 60 days | Long-lived Page token has **no expiration date** |
| **Base permission** | `instagram_business_basic` | `instagram_basic` plus Page permissions |
| **Facebook Page needed** | No | Yes |

Sources: Meta's [Instagram Platform overview](https://developers.facebook.com/docs/instagram-platform/overview), [Business Login for Instagram](https://developers.facebook.com/docs/instagram-platform/instagram-api-with-instagram-login/business-login), and [long-lived tokens for Facebook Login](https://developers.facebook.com/docs/facebook-login/guides/access-tokens/get-long-lived).

**Both logins serve professional accounts only.** Meta says app users "must have an Instagram professional account", meaning Business or Creator. The two logins use different scopes, and our [Instagram API permissions guide](https://www.tryadeli.com/blog/instagram-api-permissions) lists every one.

## How to get a short-lived token

**With Instagram Login, send the user to authorize, catch the code, and POST it back.** The code is valid for **1 hour and can be used once**, per Meta's [Business Login docs](https://developers.facebook.com/docs/instagram-platform/instagram-api-with-instagram-login/business-login).

1. **Send the user to the authorization URL.** Use `https://www.instagram.com/oauth/authorize` with `client_id`, `redirect_uri`, `response_type=code`, and `scope`. Add `state` to guard against CSRF.
2. **Read the code from your redirect URI.** Meta appends `#_` to the end of the redirect. It isn't part of the code, so strip it.
3. **Exchange the code for a token.** POST it to `api.instagram.com` from your server:

```bash
curl -X POST "https://api.instagram.com/oauth/access_token" \
  -F "client_id=<APP_ID>" \
  -F "client_secret=<APP_SECRET>" \
  -F "grant_type=authorization_code" \
  -F "redirect_uri=<REDIRECT_URI>" \
  -F "code=<AUTH_CODE>"
```

The response holds `access_token`, `user_id`, and `permissions`. That token is **short-lived and valid for 1 hour**, according to Meta's [get started guide](https://developers.facebook.com/docs/instagram-platform/instagram-api-with-instagram-login/get-started). A reused or malformed code returns `OAuthException` with the message "Matching code was not found or was already used".

**For a test on your own account**, you can skip the flow and generate a token in the App Dashboard on the Instagram API setup page. Meta says dashboard tokens are **long-lived, valid for 60 days**.

With **Facebook Login**, you run the standard Facebook Login flow instead and get a short-lived Facebook User token. The next section turns it into a Page token.

## How to exchange it for a long-lived token

**Call the exchange endpoint from your server within the hour.** It takes your App Secret, so Meta says it must run in **server-side code, never client-side**.

**Instagram Login.** A GET to `graph.instagram.com/access_token` with `grant_type=ig_exchange_token` ([reference](https://developers.facebook.com/docs/instagram-platform/reference/access_token)):

```bash
curl -G "https://graph.instagram.com/access_token" \
  --data-urlencode "grant_type=ig_exchange_token" \
  --data-urlencode "client_secret=<APP_SECRET>" \
  --data-urlencode "access_token=<SHORT_LIVED_TOKEN>"
```

You get back `access_token`, `token_type` (`bearer`), and `expires_in` in seconds. The new token lasts **60 days**. Store `expires_in` with it, because your refresh job needs the date.

**Facebook Login.** A GET to `oauth/access_token` with `grant_type=fb_exchange_token`, your App ID, App Secret, and the short-lived User token ([guide](https://developers.facebook.com/docs/facebook-login/guides/access-tokens/get-long-lived)):

```bash
curl -G "https://graph.facebook.com/oauth/access_token" \
  --data-urlencode "grant_type=fb_exchange_token" \
  --data-urlencode "client_id=<APP_ID>" \
  --data-urlencode "client_secret=<APP_SECRET>" \
  --data-urlencode "fb_exchange_token=<SHORT_LIVED_USER_TOKEN>"
```

That long-lived User token **generally lasts about 60 days**. Then call `GET /<APP_SCOPED_USER_ID>/accounts` with it, and each Page in the `data` array comes with its own `access_token`. **Page tokens generated from a long-lived User token have no expiration date**, so that's the token to store for publishing, which Meta's [content publishing docs](https://developers.facebook.com/docs/instagram-platform/content-publishing) say needs a Page access token.

## How to refresh an Instagram access token

**Call `refresh_access_token` before day 60, and each refresh resets the clock to 60 days.** This applies to Instagram Login tokens. Meta's [refresh reference](https://developers.facebook.com/docs/instagram-platform/reference/refresh_access_token) sets three conditions:

- **At least 24 hours old.** A token issued today can't be refreshed until tomorrow.
- **Not expired.** An expired token can't be refreshed at all.
- **`instagram_business_basic` granted.** The user must have approved the base permission.

```bash
curl -G "https://graph.instagram.com/refresh_access_token" \
  --data-urlencode "grant_type=ig_refresh_token" \
  --data-urlencode "access_token=<LONG_LIVED_TOKEN>"
```

The response is a new `access_token` with a fresh `expires_in`. Save it over the old one. **Tokens that go 60 days without a refresh expire and can't be refreshed**, per the [Business Login docs](https://developers.facebook.com/docs/instagram-platform/instagram-api-with-instagram-login/business-login), so the user has to log in again.

**A refresh schedule that keeps tokens alive:**

| When | What your job does |
|---|---|
| **Day 0** | Store the token and its expiry from `expires_in` |
| **Day 1 or later** | Token becomes eligible for refresh |
| **Around day 45** | Refresh, and store the new token and expiry |
| **Refresh fails** | Retry, then flag the account for reconnection before day 60 |
| **Error 190 at any point** | Mark the account disconnected and ask the user to log in again |

Refreshing two weeks early leaves room for retries when a job fails or the queue backs up. With hundreds of connected accounts, run it daily over every account whose expiry falls inside that window.

## How to check if a token is valid

**For Instagram Login, call `/me`. For Facebook Login, call `debug_token`.**

**Instagram Login.** A successful call to `/me` with `fields=user_id,username` shows the token works and returns the account's ID and username ([get started guide](https://developers.facebook.com/docs/instagram-platform/instagram-api-with-instagram-login/get-started)):

```bash
curl -G "https://graph.instagram.com/v25.0/me" \
  --data-urlencode "fields=user_id,username" \
  --data-urlencode "access_token=<ACCESS_TOKEN>"
```

**Facebook Login.** The [`debug_token` endpoint](https://developers.facebook.com/docs/graph-api/reference/debug_token) returns metadata about a token: `is_valid`, `expires_at`, `data_access_expires_at`, and `scopes`. You call it with an **app access token** or an app developer's user token for the same app:

```bash
curl -G "https://graph.facebook.com/debug_token" \
  --data-urlencode "input_token=<TOKEN_TO_CHECK>" \
  --data-urlencode "access_token=<APP_ACCESS_TOKEN>"
```

Meta's `debug_token` reference doesn't mention Instagram Login tokens, so don't build your Instagram Login checks on it.

## Why did my Instagram token stop working?

**Check the error code first.** It usually names the cause, and these codes come from Meta's [Graph API error handling](https://developers.facebook.com/docs/graph-api/guides/error-handling) codes.

| Symptom | Likely cause | Fix |
|---|---|---|
| **Error 190, subcode 463 or 467** | Token expired, was revoked, or is invalid | Send the user back through login |
| **Error 190, subcode 460** | The user changed their password (Facebook Login) | Have the user log in to your app again |
| **Error 190, subcode 458** | The user removed your app | Reauthenticate the user |
| **Error 10 or 200–299** | A permission was never granted or was removed | Request the missing scope again |
| **Calls fail for other people's accounts** | Your app only has Standard Access | Get Advanced Access through App Review |

A few more catches:

- **Personal accounts.** Neither login supports them. The user has to switch to a Business or Creator account first.
- **Standard Access.** Meta's [overview](https://developers.facebook.com/docs/instagram-platform/overview) says Standard Access is for people with a role on your app. Accounts you don't own or manage need **Advanced Access, App Review, and Business Verification**. Our [Meta app review guide](https://www.tryadeli.com/blog/meta-app-review-time) covers the timeline.
- **Old scope names.** Meta deprecated the `business_*` scopes on **January 27, 2025**. Apps still requesting them can't call the Instagram endpoints until they switch to the `instagram_business_*` names.
- **Wrong host.** An Instagram User token sent to `graph.facebook.com`, or a Page token sent to `graph.instagram.com`, fails.
- **Leaked tokens.** Keep tokens and your App Secret out of client code, URLs you log, and public repos. Treat a leaked token as compromised and reconnect the account.

## Is there a faster way to manage Instagram tokens?

**Yes, by letting a unified API hold them.** [Adeli](https://www.tryadeli.com/product/instagram) holds the Meta developer app, carried Meta's review, and **refreshes every connected account's tokens**. You hold **one API key** and send it as a bearer header, as the [authentication docs](https://www.tryadeli.com/docs/authentication) describe. There's no refresh job to run and no 60-day clock to watch.

Each of your users connects their own Instagram account, and [Adeli's posting API](https://www.tryadeli.com/social-media-posting-api) publishes feed posts, Reels, Stories, and carousels from the same endpoint as every other network. You get a **per-platform status** on every publish and a **webhook when anything changes**. Your **first 3 connected accounts are free**, and there are no per-post fees on Instagram.

## Frequently asked questions

### How long does an Instagram access token last?

With Instagram Login, the token from the login flow lasts 1 hour and the long-lived token lasts 60 days. Tokens generated in the App Dashboard are also long-lived, at 60 days. With Facebook Login, a long-lived User token lasts about 60 days, and a long-lived Page token has no expiration date.

### Can an expired Instagram token be refreshed?

No. Meta's refresh endpoint only accepts a long-lived token that is at least 24 hours old and still valid. Once a token expires, or goes 60 days without a refresh, the user has to log in to your app again to produce a new one.

### Do Facebook Page access tokens expire?

A long-lived Page access token has no expiration date, according to Meta. It can still be invalidated, for example when the user loses their role on the Page or revokes your app, so check for error 190 and send the user back through login when it happens.

## Related Adeli pages

- [Instagram API for Developers](https://www.tryadeli.com/product/instagram.md): Publish, schedule, and read analytics for Instagram through one API. Adeli owns the Instagram developer app, token refresh, and Meta review. Free to start.
- [Social Media Posting & Scheduling API](https://www.tryadeli.com/social-media-posting-api.md): Adeli's social media posting and scheduling API publishes to Instagram, TikTok, YouTube, Facebook, X, and Bluesky in one request, now or at a set time. Free to start.
- [All posts on the Adeli blog](https://www.tryadeli.com/blog.md)

## About the author

Mika Reyes is the CEO behind Adeli and a tech and AI content creator. She was previously co-founder and CEO of Parallax, which was acquired by Phantom, and a product lead at LinkedIn. She is a Forbes 30 Under 30 honoree.
