Instagram Graph APIを使う手順とメモ。以前、2020年頃に少し使い方のメモに関する記事を書いたが、「始め方」の部分が少し違っていたりしたのと、当時と比べて投稿系の処理がAPIで可能になっていることもあり、改めてここで整理しておく。
アクセストークンの取得方法
大体以下のドキュメントに載っているがわかりづらいのでスクショ多めで手順を載せることにする。
https://developers.facebook.com/docs/instagram-platform/instagram-api-with-instagram-login/create-a-meta-app-with-instagram
なお、どうもAPIを使用するうえで前提となる認証の方式に「Instagram Login」と「Facebook Login」の2種類が存在するようで、「Instagram Login」の場合と「Facebook Login」の場合とで利用できるデータの種類やコールするAPIのホスト等が異なるようだ。以下に挙げる手順は「Instagram Login」に該当する。詳しくは以下で解説されている。
https://developers.facebook.com/docs/instagram-platform/overview
手順
Metaアプリを作成してからテストユーザーを設定するまでの流れは、Threadsの流れと大分近い。そちらは以下のGitHubリポジトリにまとめてあるので興味があれば。
https://github.com/ricemountainer/sample-threads-oauth-token
Meta Developersのサイトで「アプリを作成」
作成したアプリのダッシュボード左メニューから「商品を追加」→「Instagram」を選択
一番下の「Instagramテスター」を選択し、ユーザー名(@の後ろ)を入力してsuggestされたユーザーを選択。自分のユーザー名を入力・選択してください。以下は私の個人ユーザーricemountainerを入力・選択してみた例です。
するとさっき「メンバーを追加」したリストにユーザーが追加される。追加直後は以下の★マークの部分に「承認待ち」というマークが出る。
↑の「...アプリとウェブサイトセクションから招待を管理できます」のリンク部分をクリックする。と、Instagramの「アプリとウェブサイト」ページに遷移するので、そこで「承認」をクリックする(スクショ忘れ)なお、これはPCのブラウザでないと承認操作できない(Instagramのスマホアプリ(iPhone)では「アプリとウェブサイト」の許可セクションにこの「Instagramテスター」というメニュー自体が存在していなかった)無事に承認が完了すると↑の★部分の「承認待ち」の部分の表示が消える。
左メニューの「Instagram」>「InstagramログインによるAPI設定」を選択
「1. アクセストークンを生成する」の項に緑のチェックマークがつき、先ほど追加したユーザーが表示されていることが確認できる。その後「トークンを生成」リンクをクリック。
モーダルが開いてアクセストークンが表示される。忘れずにコピーして保存しておく。
以後、基本的にはこのトークンを使ってAPIをコール可能である。Instagram Graph APIはアクセストークンをaccess_token=xxx
の形でGETパラメータに付与してリクエストする。このアクセストークンは(発行までの過程で明示的に指定してないが)Instagram Graph APIコールに必要なPermissionを暗黙的に一通りそろえてくれている。アクセストークンデバッガーで調査すると保有しているPermissionは以下の通り。
instagram_business_basic, instagram_business_manage_messages, instagram_business_content_publish, instagram_business_manage_insights, instagram_business_manage_comments
ただしこのアクセストークンには期限があり、未来永劫使えるわけではない。これは後述する。
Instagram Graph APIコール:参照系
例えば/me
エンドポイントに対しては
$ curl https://graph.instagram.com/v22.0/me?access_token=...
{"id":"123"}
となる。
ここで取得したid
値を使ってそのユーザー(自分)のメディア一覧を取得可能で、上の例123
だと
$ curl https://graph.instagram.com/v22.0/123/media?access_token=...
{"data":[{"id":"2345..."},...]},"paging":{"cursors":{"before":"xxx...","after":"yyy..."},"next":"https:\/\/graph.instagram.com\/v22.0\/..."}}
となる。data
フィールドのid
の配列がメディアIDの一覧がになっており、単一の投稿の情報取得はこのIDを使って以下のように取得可能である。
$ curl "https://graph.instagram.com/v22.0/2345?fields=caption,like_count&access_token=..."
{"caption":"20250324 drawing\n\n\u30c9\u30e9...","like_count":5,"id":"2345"}
ここでfields
パラメータはレスポンスに含める投稿のメタ情報を指定する。指定できるフィールドパラメータは以下参照。
https://developers.facebook.com/docs/instagram-platform/reference/instagram-media#-----
ちなみにcaptionに日本語文字を含む場合、レスポンスは上のようにいわゆるnative2ascii
された値で返ってくる。受信したデータを日本語として扱いたいワークロードがある場合は、レスポンスから取り出した後意図的な変換が必要になると思われるので注意が必要だ。
Instagram Graph APIコール:投稿系
基本的には、
- (0) Instagramに投稿したい画像や動画をPublic Access可能なサーバー上にUPLOADしておく
- (1) APIコール時に1.のURLを指定してアップロード
- (2) 上記2.の処理が完了するまで待機
- (3) 上記2.の処理が完了したら公開指示
って流れでようやく「公開」に至る。これはThreadsも同じ流れである。(0)はAPIの範疇ではないのでここでは割愛する。適当なS3バケットつくる等してアップロードなりしていただければ。。
なお、APIの認証方式がFacebookログインで、かつ投稿するのが動画である場合に限定だが、ローカルから直接アップロードする口が用意されているらしい。以下に記述がある。
https://developers.facebook.com/docs/instagram-platform/instagram-api-with-facebook-login/content-publishing#----------------
冒頭書いた通りこの記事はInstagramログインをAPIの認証方式とする手順や方法を解説するものになっており、個人的にもFacebookログインのほうは試したことがないので不明。参考記述として載せておく。
単一画像投稿
恐らく一番シンプル。
(1) POST /(ig-user-id)/media
まず/(ig-user-id)/media
にPOSTを投げて投稿指示をする。(ig-user-id)
は↑で/me
エンドポイントのレスポンスから取得したIDである(この例では123
)。
$ curl -X POST "https://graph.instagram.com/v22.0/123/media?image_url=https://hoge.com/huga.jpg&caption=20250329%20test%0Atest%0A%23manga&access_token=..."
{"id":"34567"}
以下がポイント。
-
123
はユーザーIDである。↑の/me
のレスポンスで取得したID。 -
image_url
には上述の通り、Public Access可能な画像のURLを指定する。この例ではhttps://hoge.com/huga.jpg
である。ちなみに拡張子はjpg
しか許可されていない。らしい。 -
caption
はURLエンコードされている必要がある。例えば改行は%0A
、空白は%20
となる。 - レスポンスの
{"id":"34567"}
はIGコンテナという、画像や動画のアップロードの進捗状況を管理するオブジェクトのIDを示している。後述で使用する。 - このときに
media_type=STORIES
を付与しておくとストーリーへの単一画像投稿になる。
(2) GET /(ig-container-id)
ドキュメントにも記載があるが、画像や動画のアップロードは非同期なので、完全に完了するまで待たなければならない。これの進捗状況を確認する場合、POST /(ig-user-id)/media
のレスポンスのIGコンテナIDを使って、fields=status_code
を含む形で、/(ig-container-id)
のエンドポイントに以下のように問い合わせる。まだアップロード中だと以下のようなレスポンスになる。
$ curl "https://graph.instagram.com/v22.0/34567?fields=status,status_code&access_token=..."
{"status":"IN_PROGRESS","status_code":"IN_PROGRESS","id":"34567"}
完了してると以下のようなレスポンスになる。
{"status":"FINISHED","status_code":"FINISHED","id":"34567"}
"status_code":"FINISHED"
なら完了しているらしい。ドキュメントは以下。
https://developers.facebook.com/docs/instagram-platform/instagram-graph-api/reference/ig-container
(3) POST /(ig-user-id)/media_publish
IGコンテナのstatus_code
が"FINISHED"であることを確認後、 /(ig-user-id)/media_publish
にcreation_id=(ig-container-id)
の形でGETパラメータを付与してPOSTを投げる。
$ curl -X POST "https://graph.instagram.com/v22.0/123/media_publish?creation_id=34567&access_token=..."
{"id":"34567"}
このレスポンスはMedia IDであり、GET /media
のレスポンスで返却されるID値と同質のものとなる。これにより実際に投稿されたのが以下。
https://www.instagram.com/p/DHv-EaeBpKl/
リール投稿
基本的に↑の「単一画像投稿」と同じ。ただ最初に指定する際のパラメータが多いのでちょっと大変。とりあえず試した範囲での情報を載せる。ドキュメントは以下。
https://developers.facebook.com/docs/instagram-platform/instagram-graph-api/reference/ig-user/media#--------
(1) POST /(ig-user-id)/media
/(ig-user-id)/media
にmedia_type=REELS
を指定して色々パラメータ付与してPOSTする。以下のような感じ
$ curl -X POST "https://graph.instagram.com/v22.0/123/media?media_type=REELS&video_url=https%3A%2F%2Fhoge.com%2F001_test.mp4&caption=20250401+test%0D%0Areel+test%3B%0D%0ACollected+some+mangapanels+from+my+original+manga+%22RESIGN+THREAT%22%21%0D%0Ahttps%3A%2F%2Fresign-threat.com%2Fstory%2Flatest%0D%0A%23manga+%23originalmanga+%23mangaart+%23mangapanel&share_to_feed=true&cover_url=https%3A%2F%2Fhoge.com%2Fcover.jpg&audio_name=testaudio&access_token=..."
{"id":"5678"}
ポイントとしては、、、
-
media_type=REELS
は必須。 -
video_url
にはリールに投稿する動画のURLを指定する。上の単一画像投稿と同じでPublic Access可能である必要がある。この例だとhttps://hoge.com/001_test.mp4
でURLエンコードしている。が、そういえば単一画像投稿のときにはURLエンコードしなかったので、このパラメータのURLエンコードはもしかしたら必須ではないかもしれない。 -
share_to_feed
は「フィードに投稿するかどうか」で上の例だとtrue
(フィードに投稿する)を指定している。 -
cover_url
はリール動画の「カバー」の画像URLを指定する。video_url
と同じくPublic Access可能な場所に配置されている必要がある。 -
audio_name
はAudio名を指定する。これはリール投稿に表示される音源の名称をここで指定できる。この場合"testaudio"という文字列を指定している。詳細後述。 -
video_url
に指定する動画と、cover_url
に指定するカバー画像には、アスペクト比やコーデック等に関して厳密な仕様が存在する。これを逸脱したデータを指定した場合にどういう動きをするのか不明。詳細は以下を参照。
https://developers.facebook.com/docs/instagram-platform/instagram-graph-api/reference/ig-user/media#reels-specs
ドキュメントによれば他にもいくつかパラメータはあるが、とりあえず上に挙げた分だけ指定すればリールの投稿には成功した。
(2) GET /(ig-container-id)
上の単一画像投稿取得と同じなので割愛
(3) POST /(ig-user-id)/media_publish
上の単一画像投稿取得と同じなので割愛
実際に投稿されたのが以下
https://www.instagram.com/p/DH5VJgtphzk/
赤い矢印でマーク付けた部分"testaudio"と書いてあるのが、(1) POST /(ig-user-id)/media
でaudio_name
パラメータで指定した値だ。「この動画の音源はこういう名前の曲ですよ」という表示に使用できるということのようだ。なおこのテスト投稿の動画の音源はBulldozerというフリー音源である。
https://www.dova-s.jp/bgm/play12520.html
また、share_to_feed
をtrue
に指定したので、「投稿(フィード)」と「リール」の両方のタブに表示される。
「投稿」タブ
「リール」タブ
カルーセル投稿
複数の画像・複数の動画、あるいはそれらを組み合わせた投稿。対象となる画像や動画を事前にすべてUPLOADしておいて、それらのコンテナIDを配列で指定して公開させる。カルーセルの場合のみ、画像や動画のUPLOADのあと、/media_publish
の前に、個別に「カルーセルそれ自体の投稿」という独自の手順が必要になる。詳細は以下。
https://developers.facebook.com/docs/instagram-platform/instagram-api-with-facebook-login/content-publishing#--------3
(1) POST /(ig-user-id)/media
カルーセル投稿の場合はこの手順が複数回必要になる。(投稿に載せる画像や動画の数分この手順を繰り返す。)個々のリクエスト内容は基本的には「単一画像投稿」と同じ。以下のような感じでリクエストを投げる。とりあえず簡単に画像2枚のカルーセル投稿を作ることを試みる。
$ curl -X POST "https://graph.instagram.com/123/media?is_carousel_item=true&image_url=https://hoge.com/huga-1.jpg&caption=20250402%20test%0Acarousel%20test1&access_token=..."
{"id":"9991"}
$ curl -X POST "https://graph.instagram.com/123/media?is_carousel_item=true&image_url=https://hoge.com/huga-2.jpg&caption=20250402%20test%0Acarousel%20test2&access_token=..."
{"id":"9992"}
ポイントとしては、、、
- カルーセル投稿に載せる画像や動画に関しては、個々の
/media
リクエストに際してis_carousel_item=true
を付ける必要がある。 - 個々のリクエストに
caption
は入れてもエラーにはならないが(上の例はその検証のためにわざと入れてある)、次の手順で入力するcaption
が優先されるのでこの段階でつける意味はない。
(2) GET /(ig-container-id)
上でリクエストした個々の画像や動画に関してアップロードの進捗状況をチェックし"status_code":"FINISHED"
であることを確認次第次に進む。対象が複数になっただけで基本的には上と同じなので割愛する。
(3-0) POST /(ig-user-id)/media (CAROUSEL)
カルーセル投稿の場合は/media_publish
の前にこの手間が一つ必要になる。(1)でアップロードした画像や動画をまとめた投稿の元となる情報を登録する。以下のような感じ。
$ curl -X POST "https://graph.instagram.com/123/media?media_type=CAROUSEL&children=9991%2C9992&caption=20250402%20test%0Acarousel%20test&access_token=..."
{"id":"9993"}
ポイントとしては、、、
-
media_type=CAROUSEL
を付ける。必須。 -
children
に(1)でアップロードした画像や動画のコンテナIDを指定する。この際それぞれをカンマ(,)区切りで繋げる形で指定する。上の例はchildren=9991,9992
である。なお、カンマは%2C
でURLエンコードしなければならないっぽい。「っぽい」というのは上のドキュメントの記述に倣ってそうしているのが理由というだけで、カンマ自体は別にURLエンコード必須というわけでもないはずだからである(実際、アップロードの進捗状況を確認する際のGET /(ig-container-id)
ではfields=status,status_code
をURLエンコードなしで指定できている)なのでURLエンコードが絶対必須かどうかは不明。 - 投稿のキャプションとしてはここで付与したパラメータが使用される。(1)で個々の画像や動画のアップロードの際に付与した
caption
は使用されない。したがってここでcaption
を付けない場合はキャプションなしのカルーセル投稿が出来上がることになる。
(3) POST /(ig-user-id)/media_publish
あとは同じなので割愛する。
実際に投稿されたのが以下
https://www.instagram.com/p/DH76LREBADo/
画像2枚つづりのカルーセル投稿が作成されていることが確認できる。
アクセストークンの期限
↑の手順で取得したアクセストークンは約60日で期限切れになる。これはThreadsも同様。アクセストークンデバッガーで有効期限が確認できる。
有効期限が切れる前に/refresh_access_token
にGETを投げることでリフレッシュできる
https://developers.facebook.com/docs/instagram-platform/reference/refresh_access_token
$ curl "https://graph.instagram.com/refresh_access_token?grant_type=ig_refresh_token&access_token=...
{"access_token":"IG...","token_type":"bearer","expires_in":5184000,"permissions":"instagram_business_basic,instagram_business_manage_messages,instagram_business_content_publish,instagram_business_manage_insights,instagram_business_manage_comments"}
ただし、リフレッシュした場合も、リフレッシュに用いた旧アクセストークンはまだ有効である。(そのアクセストークン使ってAPI呼べる)よって、これは「リフレッシュ」っていうより「有効なトークンの追加生成」に近い。つまり、実行すればするほど、有効なトークンがどんどん増えるということになる。個人的にはリフレッシュしたタイミングで昔のトークンは破棄されてほしいのだが、そういう気の利いたことはしてくれないらしい。そのうえ、任意のタイミングで破棄(Revoke)するエンドポイントも提供されていないため、基本的に60日経過するまで待つ以外にトークンを失効させる手段が(おそらく)存在しない。これはThreadsも同様である。なのであまり多用しない方が良い。
graph.facebook.comとgraph.instagram.comの違い、アクセストークンの扱い
Instagram Graph APIはgraph.facebook.com
(Facebookログインを使用)とgraph.instagram.com
(Instagramログインを使用)の2つにホストされている。冒頭書いた通り、この手順は後者graph.instagram.com
に対してリクエストを送信する場合の手順や方法になっており、前者graph.facebook.com
ではない。両者はAPIを実行するにあたっての認証の方式が異なっており、両者で出来ることも異なる。(っていうことらしい)以下のドキュメントにその辺が記述されている。
https://developers.facebook.com/docs/instagram-platform/overview
2025年4月現在のできること・できないことのマトリクスのスクリーンショット。
ドキュメントのリンクが両者を横断して飛び回っているのでわかりづらいが、どうもgraph.facebook.com
のほうはアクセストークンをGETパラメータではなくAuthorization
ヘッダに設定して使うらしい。以下のドキュメントにその辺の片鱗が見える。(ただ、やったことないので本当に全部そうなのかわからない)
https://developers.facebook.com/docs/instagram-platform/instagram-api-with-facebook-login/content-publishing#----1------------------------------------------
Instagram ログインで生成したアクセストークンをgraph.facebook.com
に設定してもエラーになり実行できない。
$ curl "https://graph.facebook.com/123/media?access_token=..."
{"error":{"message":"Invalid OAuth access token - Cannot parse access token","type":"OAuthException","code":190,"fbtrace_id":"Ax..."}}
これはAuthorization
ヘッダに付けた場合も同じ。
$ curl -H "Authorization: OAuth ..." "https://graph.facebook.com/123/media"
{"error":{"message":"Invalid OAuth access token - Cannot parse access token","type":"OAuthException","code":190,"fbtrace_id":"Ai..."}}
一方、graph.instagram.com
に対しては、GETパラメータではなくAuthorization
ヘッダに付けた場合もなぜか動く。
$ curl -H "Authorization: OAuth ..." "https://graph.instagram.com/123/media"
{"data":[{"id":"2345"},{"id":"3456"},...
個人的には「アクセストークンをGETパラメータに付ける」というのがどうにも気持ち悪いので、Authorization
ヘッダに付けても動くんならそっちのほうがスッキリしていいんだが、ドキュメントにはその辺の明記がないので、実際想定内の挙動かわからない。