Instagram Graph API(v14.0)のアクセストークンに関する情報がカオスになっているのでなるべく丁寧に整理する

先日仕事で[Instagram Graph API](https://developers.facebook.com/docs/graph-api)を叩く機会がありました。Graph APIで他人のフォロワー数を取得するというものです。

個人的にFacebookのドキュメントって意味不明で苦手意識があり（そもそもFacebookもInstagramもほぼ使っておらず、用語の意味が分からんのです。。）、ちょっとAPI叩きたい時はRapidAPIの非公式APIで済ませていたので、Graph APIを触るのは今回が初めてでした。

そこで「instagram graph api access token」のキーワードでググってみたのですが、出てくる記事には「第一アクセストークン、第二アクセストークン、第三アクセストークン」などといった謎の言葉が飛び交っていて頭がカオスに。おまけに、みなさん丁寧に画面のスクショを貼ってくれてるのですが、FacebookのUIやAPI仕様変更が頻繁なので、現在のスクリーンと違っていて余計に混乱してしまいました。

これはもう急がば回れだろうということでしぶしぶ本家ドキュメントに立ち戻り、ようやく「第〇アクセストークン」の意味が分かってきたので、これからGraph APIを試したい人が、これらのアクセストークンを正しく理解して、欲しい情報をGraph APIから取得できるように解説記事を書いていきたいと思います。

**API version: v14**

**この記事で触れること**

- 無期限のアクセストークンを使ってGraph APIで他人アカウントの[基本情報](https://developers.facebook.com/docs/instagram-api/guides/business-discovery)を取得する方法（バックグランドジョブで使うことを想定）
- ユーザーアクセストークン、ページアクセストークンの種類について


**この記事で触れないこと**

Instagram Graph APIを使用するための事前準備は完了していることを前提に書いてるので、以下の項目については説明しません。

- Facebookページ作成
- Instagramアカウント作成
- InstagramアカウントをFacebookページにリンクさせる
- [Meta for Developers](https://developers.facebook.com)でFacebookビジネスアプリを作成

## リファレンス

Facebookのドキュメントは情報が散乱してて探すのに苦労するので、リファレンスを先にまとめておきます。
例えばGraph APIについてさっき見ていたページどこだっけ？あれ、ない。。と思ったら、よくよくURLを見るとルートが違っていた、なんてことが頻繁にあるので気を付けてください。

Graph API
- https://developers.facebook.com/docs/graph-api

Instagram Graph API
- https://developers.facebook.com/docs/instagram-api

Access Token
- https://developers.facebook.com/docs/facebook-login/guides/access-tokens
- https://developers.facebook.com/docs/pages/access-tokens

Tools
- [Graph API Explorer](https://developers.facebook.com/tools/explorer)
- [Access Token Debugger](https://developers.facebook.com/tools/debug/accesstoken/)

## 前提知識

Instagram Graph APIはFacebookアカウントの認証によってアクセストークンを取得するため、[Facebookログインのページ](https://developers.facebook.com/docs/facebook-login/guides/access-tokens)に書いてあるアクセストークンについて理解する必要があります。

ひとえにアクセストークンと書かれていても、「User Access Token」、「App Access Token」、「Page Access Token」、「Client Token」の4種類があります。どのアクセストークンを使うかによって、叩けるAPIエンドポイントや取得できる情報が異なるようです。本記事では、このうち謎の「第〇アクセストークン」に関連する「User Access Token」、「Page Access Token」の2つを説明します。

### User Access Token

まずは[ドキュメント](https://developers.facebook.com/docs/facebook-login/guides/access-tokens)からの引用です。

> この種類のアクセストークンは、アプリがAPIを呼び出して、特定の利用者のFacebookデータをその利用者の代わりに読み取り、変更、書き込みを行う際に必ず必要になります。ユーザーアクセストークンは、一般的にログインダイアログを通して取得され、アプリがトークンを取得することを利用者が許可する必要があります。

アプリがユーザー（Facebookアカウント）に代わって、APIを通じてユーザーの情報の読み書きを行うために使用するトークンです。OAuthといえば一般にこのイメージですね。

ユーザーアクセストークンには、Short-Term Tokens（以下、「短期ユーザーアクセストークン」）とLong-Term Tokens（以下、「長期ユーザーアクセストークン」）の2種類があり、**短期ユーザーアクセストークンは通常1時間、長期ユーザーアクセストークンは通常60日間の有効期限**となっています。

短期ユーザーアクセストークンは、Facebookログインの実装を通して、または、 [Graph API Explorer](https://developers.facebook.com/tools/explorer)で取得できます。

長期ユーザーアクセストークンを取得するには、`/oauth/access_token`エンドポイントを使う必要があります。（後述）


### Page Access Token

こちらもまずは[ドキュメント](https://developers.facebook.com/docs/facebook-login/guides/access-tokens)からの引用です。

> この種類のアクセストークンはユーザーアクセストークンに似ていますが、Facebookページに属するデータの読み取り、書き込み、変更を行うAPIに対してアクセス許可を提供する点が異なります。ページアクセストークンを取得するには、まずユーザーアクセストークンを取得してから、ページへのアクセス許可または必要なアクセス許可をリクエストする必要があります。ユーザーアクセストークンを取得したら、グラフAPIを介してページアクセストークンを取得します。

どうやら、アプリがFacebookページの操作を代わりに行うために払い出されるトークンのようです。このトークンを取得するためには、まずは対象のFacebookページに対して必要な権限を有しているFaceboookアカウントでUser Access Tokenを取得し、その後別途Page Access Tokenを取得する流れになるようです。

そして重要な点として、Page Access Tokenにも短期と長期の概念があり、**短期ユーザーアクセストークンを使用して払い出されたページアクセストークンは短期ページアクセストークン（有効期限1時間）**、**長期ユーザーアクセストークンを使用して払い出されたページアクセストークンは長期ページアクセストークン（有効期限なし）** になります。これは今まで見てきたドキュメントとは[別のページ](https://developers.facebook.com/docs/pages/access-tokens)に書かれているので非常に分かりにくいです。

そして、今回の目標はバックグランドジョブで使用する有効期限なしのアクセストークンを取得することであったため、長期ページアクセストークンを取得すればいいということになります。

それでは順を追って長期ページアクセストークンを取得していきましょう。

## 1. 短期ユーザーアクセストークンを取得する

これは先述したとおり、Facebookログインの実装を通すか、[Graph API Explorer](https://developers.facebook.com/tools/explorer)で取得することが出来ます。今回はアプリを第三者ユーザーに公開することは意図していないため、Graph API Explorerで取得するのが簡単そうです。

注意点としては、アクセストークンの生成にあたって適切なアクセス許可を設定することです。今回はInstagramのユーザーの基本情報を取得したいので、以下のアクセス許可を最低限与えてやる必要があります。（どのアクセス許可が必要かは目的によって変わります。叩きたいエンドポイントのドキュメントに必要な許可が書かれているので適宜探してください。）

- `pages_show_list`
- `instagram_basic`
- `instagram_manage_insights`

「Generate Access Token」というボタンを押すと、短期ユーザーアクセストークンが払い出されます。（なお、2022年6月9日時点では、生成するトークンの種類の切り替え候補に「ページアクセストークンを取得」とありますが、これを選択して払い出したアクセストークンも実は短期ユーザーアクセストークンのようです。）

生成されたアクセストークンをデバッグするには[Access Token Debugger](https://developers.facebook.com/tools/debug/accesstoken/)が便利です。今回払い出されたトークンを入れてみると、`Type: User`で有効期限が1時間のアクセストークンだということが分かります。（画像は一部表示データをマスクしています）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1201783/6b20af03-f8af-f263-e333-97d287d54e06.png)

## 2. 長期ユーザーアクセストークンを使用する

目的である長期ページアクセストークンを取得するためには、長期ユーザーアクセストークンを先に取得しなければならないためこの手順を踏む必要があります。

[pagesのドキュメント](https://developers.facebook.com/docs/pages/access-tokens)に長期ユーザーアクセストークンの取得方法が書かれているのが解せないですが、`GET /oauth/access_token`エンドポイントを使えばいいそうです。以下の変数名は長ったらしいですが、トークンの種類が分かりやすいようにしてます。

```js
const getLongTermUserAccessToken = async (appID, secret, ShortTermUserAccessToken) => {
  // SEE: https://developers.facebook.com/docs/pages/access-tokens?locale=en_US
  const url = `https://graph.facebook.com/v14.0/oauth/access_token?grant_type=fb_exchange_token&client_id=${appID}&client_secret=${secret}&fb_exchange_token=${ShortTermUserAccessToken}`;
  const res = await fetch(url);
  // 有効期限60日の長期ユーザーアクセストークン
  const { access_token } = await res.json();
  return access_token;
};
```

## 3. 長期ユーザーアクセストークンを使って長期ページアクセストークンを取得する

`GET /me/accounts`エンドポイントを使うと、ユーザーがタスクを実行できる[Pages](https://developers.facebook.com/docs/graph-api/reference/page/)（Facebookページ）リストを取得でき、各Pageには`access_token`フィールドがあります。これがページアクセストークンです。ドキュメントは[こちら](https://developers.facebook.com/docs/instagram-api/getting-started)と[こちら](https://developers.facebook.com/docs/graph-api/reference/user/accounts/)。

jsで書くと以下のような感じです。先述したように、ここで引数に渡す`userAccessToken`が短期なら短期ページアクセストークン、長期なら長期ページアクセストークン（有効期限なし）が返されます。

```js
const getPageBelongsToMe = async (userAccessToken) => {
  // https://developers.facebook.com/docs/graph-api/reference/user/accounts/
  const url = `https://graph.facebook.com/v14.0/me/accounts?fields=access_token,instagram_business_account&access_token=${userAccessToken}`;
  const res = await fetch(url);
  const { data } = await res.json();
  // dataはpagesの配列なので、この中からInstagramアカウントとリンクさせたページを探す
  const page = data[0]
  return page;
};
```

取得した`page.access_token`の値を[Access Token Debugger](https://developers.facebook.com/tools/debug/accesstoken/)で確認してみると、`Type: Page`で`有効期限: 受け取らない`と期待した結果になっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1201783/87a7fac3-1b45-e43d-b7f7-c86cd2344e8e.png)


## Instagram Graph APIを試してみる

長期ページアクセストークンを取得できたので、[ビジネスディスカバリーのAPI](https://developers.facebook.com/docs/instagram-api/guides/business-discovery)を叩いてみましょう。

`GET /{ig-user-id}/business_discovery`

APIを叩くにはInstagramのUserIDが必要です。このUserIDはどこで取得できるかというと、`Page.instagram_business_account`フィールドにあります。

上述の`getPageBelongsToMe`関数ではこのフィールドを取得するようにパラメータを与えているので、返り値の`page.instagram_business_account.id`にInstagramのUserIDが入っているはずです。

これを使用して、ドキュメントと同じくBlue Bottle Coffeeの基本データを取得してみます。

```js

// Meta for Developersのアプリ設定から取得
const APP_ID = "****************"
const APP_SECRET = "*****************"
// Graph API Explorerから生成
const SHORT_TERM_USER_ACCESS_TOKEN = "*********************************************";

const testGraphAPI = async () => {
  const longTermAccessToken = await getLongTermUserAccessToken(
    APP_ID,
    APP_SECRET,
    SHORT_TERM_USER_ACCESS_TOKEN
  );
  const page = await getPageBelongsToMe(longTermAccessToken);
  const {
    instagram_business_account: { id: igUserID },
    access_token: accessToken,
  } = page;
  const url = `https://graph.facebook.com/v14.0/${igUserID}?fields=business_discovery.username(bluebottle){followers_count,media_count}&access_token=${accessToken}`;
  const res = await fetch(url);
  const j = await res.json();
  console.log({ bluebottle: j });
};
// 実行
testGraphAPI();

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1201783/79afc55d-0a70-0239-82aa-e4fabb6871d7.png)

出来ました。

## 最後に

やっぱりFacebookのドキュメントは探しにくいし読みにくかった。

ちなみに、謎だった「第〇アクセストークン」もこれで対応関係が明らかになりました。

- 第一アクセストークン -> 短期ユーザーアクセストークン
- 第二アクセストークン -> 長期ユーザーアクセストークン
- 第三アクセストークン -> 長期ページアクセストークン

なお、Instagram Graph APIはユーザーアクセストークンでも叩けたので、必ずしも長期ページアクセストークンを取得する必要はありません。