「ブログを書こうと思っていたのに、気がつけば数か月」を解消する

ホームページを公開したあと、多くの事業者さんがぶつかる壁があります。それは「更新が続かない」という悩みです。

開業準備や日々の業務に追われ、ブログを書くための時間が取れない。書こうとしても文章にまとまらない。気がつけば最終更新が数か月前のまま…という状態は、決して珍しいことではありません。

一方で、Instagramには毎日のように写真を投稿している、という事業者さんも多いはずです。「すでにInstagramでは発信できているのだから、それをそのままホームページに反映できないだろうか」というご相談を、私たち制作者は本当によくいただきます。

この記事では、Instagramに投稿するだけでホームページの「ギャラリー」や「お知らせ」欄が自動更新される仕組みを、ハッシュタグでの振り分けロジックを中心に、設計から実装、運用までまるごと解説します。

想定している読者は次の3パターンです。

  • 同業のWeb制作者の方で、Instagram連携の実装例を探している方
  • 技術に興味のある事業者さん・社内Web担当者で、自分たちのサイトに導入したい方
  • 「こういう仕組みって作れるんですか?」と検討中で、全体像を理解しておきたい方

コード例も含まれますが、コードブロックの前後で「これは何をしているのか」を必ず日本語でご説明します。技術にあまり詳しくない方も、流れだけを追っていただければ概要はつかめるように書いています。

この記事で分かること

  • Instagram連携の全体像と必要な準備
  • Facebook・Meta for Developersのアカウント作成手順
  • ハッシュタグで投稿を振り分けるロジックの実装方法
  • 画像キャッシュとパフォーマンス対策
  • トークンの自動更新とフォールバック設計
  • クライアントへの引き渡しと運用のコツ

1. システム全体像

解決する課題と、Instagram連携で得られるもの

まず、この仕組みが解決する課題を整理しましょう。

ホームページの更新が滞る最大の理由は「更新作業そのものが手間」だからです。CMS(コンテンツ管理システム。WordPressなど)の管理画面にログインし、見出しを書き、画像をアップロードし、本文を整え、公開ボタンを押す。一連の作業は慣れていても10〜20分はかかります。

ところがInstagramへの投稿は、スマホで写真を撮ってキャプションを入力し、シェアボタンを押すだけ。すでに毎日の習慣として組み込まれている事業者さんも多いはずです。

「Instagramに投稿する」という1つの行動だけで、ホームページのギャラリーやお知らせも自動更新される。これがInstagram連携の最大の価値です。

Instagram投稿が自動表示されるホームページのギャラリーセクション例
Instagram連携で実装されたホームページのギャラリーセクションの例。投稿するだけでホームページにも自動的に画像が並ぶ

全体アーキテクチャ(テキストフロー図)

仕組みの全体像を、テキストの流れ図で表すと次のようになります。

[Instagram投稿] → [GitHub Actions(定期実行)]

                Instagram Graph APIで投稿取得

                ハッシュタグで振り分け

                画像をダウンロード&キャッシュ

                Astroが静的HTMLを生成

                Cloudflare Pagesにデプロイ

ポイントは「ビルド時に投稿データを取り込み、静的なHTMLとして出力する」ところにあります。ユーザーがページを開くたびにInstagramのAPIを叩くわけではないので、表示は爆速ですし、Instagram側に過剰な負荷もかかりません。

なお、ここでは「Astro + Cloudflare Pages」の組み合わせを例にご紹介しますが、Next.js + Vercel、Nuxt + Netlify、Gatsby + Firebaseなど、静的サイトジェネレーターとホスティングを組み合わせた構成であれば、同様の考え方で実装できます。

必要なもの一覧

実装に取りかかる前に揃えておくべきものを整理します。

準備するもの

  • Facebookアカウント(個人または事業用)
  • Instagramビジネスアカウント(または クリエイターアカウント)
  • Meta for Developers のデベロッパー登録
  • Node.js が動く開発環境(v18以上推奨)
  • GitHubリポジトリ(GitHub Actionsを使うため)
  • 静的サイトのホスティング先(Cloudflare Pages / Netlify / Vercel など)

これらを順番に揃えていきます。Web制作の経験がある方であれば、最後の3つはすでにお持ちのはずです。最初の3つ、つまりMeta側の準備が、初めて触る方には一番のハードルになります。

2. 【準備編】Facebookアカウントの作成・準備

Facebookアカウントが必要な理由

「Instagramの投稿を取得するのに、なぜFacebook?」と疑問に思う方も多いはずです。これは、InstagramがMeta社(旧Facebook社)の傘下にあり、API(プログラムからデータを取り出す仕組み)がMeta共通のものとして提供されているためです。

具体的には、Instagram Graph API という名前の仕組みを使いますが、その利用申請や認証はFacebook Developers(Meta for Developers)のプラットフォーム上で行います。そのため、入り口としてFacebookアカウントが必要になります。

新規作成手順

すでにFacebookアカウントをお持ちの方は、この節は飛ばしてください。

新規に作成する場合の手順は、おおよそ次の流れです。

  1. facebook.com にアクセスし「新規アカウントを作成」を選ぶ
  2. 氏名・メールアドレス・パスワード・生年月日・性別を入力
  3. 入力したメールアドレスに届くコードで本人確認
  4. 二段階認証(SMSまたは認証アプリ)を必ず設定
  5. プロフィール写真と基本情報を最低限入力
Facebookアカウント新規登録フォーム
Facebookの新規アカウント作成画面(氏名・生年月日・連絡先・パスワードを入力)

セキュリティ設定、特に二段階認証は必ず有効にしてください。Meta for Developersにログインするためのアカウントが乗っ取られると、本番運用中のホームページにも影響が出ます。

既存アカウントを使う場合の注意点

すでに個人のFacebookアカウントをお持ちの方は、それをそのまま使うこともできます。ただし、以下の点に気をつけてください。

既存アカウント利用時の注意点

  • アカウントの実名・本人確認が完了していること(実名でないとAPI申請で弾かれることがある)
  • 長期間ログインしていないアカウントは制限がかかっている場合がある
  • ビジネス用と個人用を完全に分けたい場合は、新規にビジネス用Facebookアカウントを作る選択肢もある
  • Facebookページ(事業用のページ)の管理者権限が必要になるため、権限関係を整理しておく

クライアントワークの場合、私たちは「クライアントご自身のFacebookアカウントで設定する」ことをおすすめしています。制作者側のアカウントで設定すると、契約終了後の引き継ぎが面倒になるためです。

3. 【準備編】Meta for Developersへの登録

Meta for Developersとは

Meta for Developers は、Meta社が提供する各種API(Facebook、Instagram、Messengerなど)を利用するためのプラットフォームです。Instagram連携のための「アプリ」をここで作成し、APIを叩くための鍵となるアクセストークンを発行してもらいます。

「アプリを作る」と聞くと身構えてしまいますが、ここでいうアプリは、スマホにダウンロードするアプリのことではありません。「Instagram APIを利用するための申請窓口」のようなものだとお考えください。

デベロッパーアカウント作成手順

手順は次の通りです。

  1. developers.facebook.com にFacebookアカウントでログイン
  2. 「スタートガイド」または「Get Started」をクリック
  3. デベロッパーポリシーへの同意
  4. 電話番号でのSMS認証
  5. 「アプリを作成」ボタンが押せるようになれば完了

このあたりはMeta側のUIが頻繁に変わるため、画面と完全に一致しない場合があります。「次へ」「同意する」を順番に押していけば迷うことはありません。

アプリの作成手順

デベロッパーアカウントができたら、Instagram連携用のアプリを作成します。

  1. ダッシュボードで「アプリを作成」をクリック
  2. アプリのタイプは必ず「ビジネス」を選ぶ(Instagram Graph APIの利用にはビジネスタイプが必須です)
  3. アプリ名・連絡先メールアドレス・ビジネスポートフォリオを入力
  4. 「アプリを作成」をクリック
Meta for Developers のアプリ一覧画面
Meta for Developers のアプリ一覧。作成したアプリはここから管理する(アプリIDが各種APIで必要になる)

アプリ名は「会社名 + Webサイト連携」のような分かりやすい名前にしておくと、後から見たときに混乱しません。

プロダクトに「Instagram Graph API」を追加する設定

アプリを作成すると、アプリの管理画面が開きます。ここで「プロダクト」と呼ばれる機能を追加していきます。

  1. 左サイドバー「プロダクトを追加」を選ぶ
  2. 一覧から「Instagram Graph API」を見つけて「設定」をクリック(Instagram Basic Display APIは2024年12月に廃止されたので使えません)
  3. 必要な権限スコープを確認

利用する権限(パーミッション)は、主に次の2つです。

  • instagram_basic — アカウント基本情報と投稿一覧の取得
  • pages_show_list — Facebookページの一覧取得(Instagramと紐付けたページを取得するため)

アプリのモードに注意

  • 作成直後のアプリは「開発モード」になっており、本人とテストユーザーしかアクセスできない
  • 自分のアカウントの投稿を自分のサイトで使うだけなら、開発モードのままでも問題ない
  • 他人のInstagramアカウントを連携させる場合は「ライブモード」への切り替えとアプリレビューが必要
  • クライアントワークでは「クライアント自身のFacebook・アプリ」で構築するのが最もシンプル

4. 【準備編】Instagramビジネスアカウント連携

個人アカウントをビジネスアカウントに切り替える

Instagram Graph APIで投稿を取得するには、対象のInstagramアカウントが「ビジネスアカウント」または「クリエイターアカウント」である必要があります。個人アカウントのままでは利用できません。

切り替え手順は次の通りです。

  1. Instagramアプリでプロフィール画面を開く
  2. 右上のメニューから「設定とプライバシー」
  3. 「アカウントの種類とツール」→「プロアカウントに切り替える」
  4. カテゴリを選択(業種に近いものを選ぶ)
  5. 「ビジネス」または「クリエイター」を選ぶ

ビジネスとクリエイターの違いは大きくありません。どちらでもInstagram Graph APIは使えますので、業種に合う方を選んでください。

Instagram APIの2つの認証経路を理解する

Instagram Graph APIには、現在2つの認証経路が用意されています。記事を読み進める前に、自分がどちらを使うのかを意識しておくと混乱せずに済みます。

Instagram APIの2つの経路

  • Instagram API with Instagram Login:エンドポイントは graph.instagram.com。Facebookページとの紐付けは不要。新規プロジェクトはこちらが推奨
  • Instagram API with Facebook Login:エンドポイントは graph.facebook.comCMS連携や広告APIと合わせて使う場合はこちら。Facebookページとの紐付けが必須

本記事では、構成がシンプルで、Facebookページを別途用意する必要がない「Instagram Login経路」を採用します。コード例の graph.instagram.com はこの経路のエンドポイントです。Facebook Login経路を選ぶ場合は、エンドポイントや必要権限が変わるので、公式ドキュメントを別途参照してください。

Meta(旧Facebook)アカウントとInstagramの紐付け

Instagram Login経路でも、Meta for Developers にログインするための Meta(Facebook)アカウントとInstagramビジネスアカウントの紐付けは必要です。これは「Facebookページ」とは別の概念で、いわば「個人レベルのMeta IDとIGアカウントの結びつけ」です。

  1. スマホのInstagramアプリで「設定とアクティビティ」→「アカウントセンター」を開く
  2. 「アカウント」→「アカウントを追加」を選ぶ
Metaアカウントセンターの追加するアカウント選択画面
Metaアカウントセンターの「追加するアカウントを選択してください」ダイアログ。ここでFacebookアカウントを追加して、Instagramビジネスアカウントと紐付ける
  1. 「Facebookアカウントを追加」を選び、ログインして紐付ける

この紐付けが完了していないと、Meta for Developersのアプリ管理画面で対象のInstagramビジネスアカウントが選べず、トークン取得ができません。

Facebookページを作っておく(任意・推奨)

Instagram Login経路では「Facebookページの紐付け」は必須ではありませんが、次のようなケースを想定するなら事業用のFacebookページも作っておくことを強くおすすめします。

Facebookページがあると便利なケース

  • 将来的にFacebook広告(Meta広告)を出稿する可能性がある
  • Meta Business Suite で複数アカウントをまとめて管理したい
  • Facebook Login経路にあとから切り替える可能性がある
  • 事業の公式情報を発信する場所をInstagram以外にも持っておきたい

Facebookページの作成手順は次の通りです。

  1. Facebookにログインし、左メニューの「ページ」→「ページを作成」を選ぶ
  2. ページ名・カテゴリ・自己紹介を入力
  3. 「Facebookページを作成」をクリック
Facebookページ作成画面
Facebookページ作成画面。ページ名・カテゴリ・自己紹介を入力すればプレビューが右側に表示される
Facebookのプロフィール選択画面に新しく作成したページが表示されている
ページ作成後、Facebookの「プロフィールを選択」画面に新しく作ったページが追加されていれば作成完了。事業用ページに切り替えて運用していく

なお、FacebookページをInstagramビジネスアカウントと紐付けたい場合は、Facebookページの「設定」→「リンク済みアカウント」→「Instagram」から行います。アカウントセンター(前のセクション)とは別経路の操作です。

アクセストークン取得(短期 → 長期)

ここからが少し技術寄りの作業です。アクセストークンは、APIを叩くときに「自分はこのアカウントの持ち主ですよ」と証明するための鍵のような文字列です。

短期トークンは、Meta for Developers のアプリ管理画面にある「Instagram API Setup(または Instagram → API setup with Instagram business login)」のページから取得します。対象のInstagramアカウントを選び、「Generate Token」ボタンを押すと、OAuth認可画面が開き、許可を与えると短期トークンが発行されます。

最初に発行されるトークンは「短期トークン」と呼ばれ、有効期限が約1時間しかありません。これを「長期トークン」(有効期限約60日)に変換して使います。

長期トークンへの変換は、次のAPIを叩くだけです。

curl -X GET \
  "https://graph.instagram.com/access_token \
  ?grant_type=ig_exchange_token \
  &client_secret=APP_SECRET \
  &access_token=SHORT_LIVED_TOKEN"

これは「短期トークンを長期トークンに交換してください」というリクエストを送る命令です。レスポンスとして新しいトークン文字列が返ってきます。

返ってきた長期トークンを、後述のGitHub Secretsや .env ファイルに保存して使います。

トークンの取り扱い

  • アクセストークンは「パスワード」と同等の機密情報。コミットやSlack共有は厳禁
  • 必ず .gitignore に `.env` を入れて、リポジトリに含まれないようにする
  • 誤って公開してしまった場合は即座に再発行する
  • 長期トークンも60日で失効するため、自動更新の仕組みは必須(第7章で解説)

5. 【実装編】投稿取得とハッシュタグ振り分け

/me/media エンドポイントの叩き方

長期トークンが手に入ったら、いよいよ投稿を取得します。使うエンドポイントは /me/media です。

// Instagram Graph API から投稿一覧を取得
const fetchMedia = async (token) => {
  const fields = [
    'id',
    'caption',
    'media_type',
    'media_url',
    'permalink',
    'timestamp',
  ].join(',');

  const url = `https://graph.instagram.com/v25.0/me/media?fields=${fields}&access_token=${token}&limit=50`;

  const res = await fetch(url);
  if (!res.ok) {
    throw new Error(`Instagram API error: ${res.status}`);
  }
  const json = await res.json();
  return json.data;
};

このコードがやっているのは、ただ1つ。「自分(me)の投稿(media)を50件、指定したフィールドだけ取ってきてください」というリクエストを送っているだけです。

fields パラメータで、必要な情報を指定します。

  • id — 投稿の一意なID
  • caption — 投稿のキャプション(テキスト本文)
  • media_typeIMAGE / VIDEO / CAROUSEL_ALBUM(複数画像)の区別
  • media_url — 画像または動画のURL
  • permalink — Instagram上の投稿ページのURL
  • timestamp — 投稿日時

キャプションからハッシュタグを正規表現で抽出

取得した投稿のキャプションには、ハッシュタグが混ざっています。これを正規表現(文字列のパターンマッチ)で抜き出します。

// キャプションからハッシュタグを抽出する
const extractHashtags = (caption) => {
  if (!caption) return [];
  // # の後ろに続く英数字・日本語・アンダースコアにマッチ
  const regex = /#([\p{L}\p{N}_]+)/gu;
  const matches = caption.matchAll(regex);
  return Array.from(matches, (m) => m[1]);
};

この関数は、キャプションを受け取って、含まれるハッシュタグの名前(#を除いた部分)の配列を返します。たとえば「今日の作業風景です #site_gallery #spring」というキャプションからは ['site_gallery', 'spring'] が返ってきます。

この正規表現は u フラグを付けてUnicode対応にしています。\p{L} は「すべての言語の文字」(日本語含む)、\p{N} は「数字」、_ はアンダースコアにマッチします。これで「#site_gallery」「#2026春」「#今日の作業」などのハッシュタグをすべて拾えます。

振り分けロジックの実装

抽出したハッシュタグをもとに、投稿を「ギャラリー用」「お知らせ用」に振り分けます。

ここでは例として、#site_gallery が付いた投稿はギャラリーへ、#site_news が付いた投稿はお知らせへ、どちらも付いていない投稿はデフォルトでギャラリーへ、というルールを実装します。

// 投稿を振り分け先ごとに分類する
const categorize = (posts) => {
  const gallery = [];
  const news = [];

  for (const post of posts) {
    const tags = extractHashtags(post.caption);

    // 優先度順にマッチング(news > gallery)
    if (tags.includes('site_news')) {
      news.push(post);
    } else if (tags.includes('site_gallery')) {
      gallery.push(post);
    } else {
      // タグなしはデフォルトでギャラリー
      gallery.push(post);
    }
  }

  return { gallery, news };
};

ポイントは「優先度を決めておく」ことです。1つの投稿に両方のタグが付いていた場合に、どちらに分類するかをルール化しておかないと、重複表示などの不具合が起きます。

また「どのタグも付いていない投稿」をどう扱うかも事前に決めておくべきです。上記の例ではギャラリーをデフォルトとしていますが、表示しないという選択肢もあります。

表示用キャプションの整形(タグ除去)

振り分けに使ったハッシュタグは、表示するキャプションには不要です。#site_gallery という文字列が画面に出てしまうと、ユーザーには何のことか分かりません。

そこで、表示用にキャプションを整形します。

// 振り分け用ハッシュタグをキャプションから除去
const formatCaption = (caption) => {
  if (!caption) return '';
  return caption
    .replace(/#site_(gallery|news)/g, '') // 振り分け用タグを削除
    .replace(/\n{3,}/g, '\n\n')           // 連続改行を2行までに
    .trim();                              // 前後の空白を削除
};

これで、振り分け用タグは消えますが、それ以外のハッシュタグ(#spring など)はそのまま残ります。完全にすべてのハッシュタグを消したい場合は、正規表現を /#[\w\p{L}]+/gu に変えれば対応できます。

ハッシュタグ設計のコツ

  • 振り分け用タグは「事業者名 + 用途」の組み合わせで一意にする(例: `#shop_gallery`)
  • 一般的なタグ(#花、#今日のごはん等)と被るとSEO的にも混乱するため避ける
  • 運用ルールを1枚紙にまとめてクライアントに渡しておく
  • 振り分けタグは2〜3種類に絞ると運用が破綻しにくい

6. 【実装編】画像キャッシュとパフォーマンス

Instagram APIの一時URL問題

Instagram Graph APIから返ってくる media_url は、実は永続的なURLではありません。Meta公式は具体的な失効時間を明示していませんが、実装者の報告や開発者フォーラムの議論では「数時間から数日で失効する一時URL」として広く知られています。

このため、毎回ビルドのたびに「APIから取った画像URLをそのままHTMLに埋め込む」と、しばらく経つと画像が表示されなくなる、という問題が起きます。

解決策は「ビルド時に画像を自分のサーバー(またはホスティング先)にダウンロードして、永続的なファイルとして保存する」ことです。

投稿IDベースのキャッシュ戦略

ただし、ビルドのたびに全画像を毎回ダウンロードするのも非効率です。同じ画像を何度もダウンロードするのは、Instagram側にもこちら側にも負担です。

そこで「投稿IDをキーにしたキャッシュ」を使います。流れは次の通りです。

ビルド開始

Instagram APIから投稿一覧を取得

各投稿IDでキャッシュフォルダを確認
  ├─ キャッシュあり → ダウンロードスキップ
  └─ キャッシュなし → 画像をダウンロードして保存

画像最適化([WebP](/glossary#webp)変換・リサイズ)

HTMLに埋め込み

実装イメージは次のようになります。

import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';

const CACHE_DIR = 'src/data/instagram/cache';

// 投稿IDをキーに、画像をダウンロード(既存ならスキップ)
const cacheImage = async (post) => {
  const cachePath = join(CACHE_DIR, `${post.id}.jpg`);

  if (existsSync(cachePath)) {
    // すでにキャッシュ済み
    return cachePath;
  }

  // 新規ダウンロード
  const res = await fetch(post.media_url);
  const buffer = Buffer.from(await res.arrayBuffer());

  mkdirSync(CACHE_DIR, { recursive: true });
  writeFileSync(cachePath, buffer);

  return cachePath;
};

この関数は「キャッシュに同じIDのファイルがあればダウンロードをスキップし、なければ新規にダウンロードして保存する」というシンプルな動きです。

CAROUSEL(複数画像投稿)の子画像取得

Instagramには「複数画像を1つの投稿にまとめる」CAROUSEL_ALBUM という形式があります。これを扱うには、追加でAPIを叩いて子画像の情報を取得する必要があります。

// CAROUSEL_ALBUMの子画像を取得
const fetchChildren = async (postId, token) => {
  const url = `https://graph.instagram.com/v25.0/${postId}/children?fields=id,media_type,media_url&access_token=${token}`;
  const res = await fetch(url);
  const json = await res.json();
  return json.data;
};

// 取得時に media_type を見て分岐
const processPost = async (post, token) => {
  if (post.media_type === 'CAROUSEL_ALBUM') {
    post.children = await fetchChildren(post.id, token);
  }
  return post;
};

カルーセル投稿は、ライトボックス(クリックで拡大表示するモーダル)で2枚目以降を表示するUIと組み合わせると効果的です。グリッドには1枚目だけ表示し、クリックで全画像をスライド表示する、というのが定番のパターンです。

GitHub Actionsでのキャッシュ永続化

ビルドをローカルで動かしている間は問題ありませんが、GitHub Actions(自動ビルドサービス)でビルドする場合、ビルドごとに環境がまっさらにリセットされます。つまり、せっかくキャッシュした画像も、次のビルドでは消えてしまうのです。

これを防ぐために、GitHub Actionsの actions/cache という仕組みを使います。

name: Build and Deploy

on:
  schedule:
    - cron: '0 21 * * *'   # 毎日 06:00 JST(UTCで21:00)
  workflow_dispatch:       # 手動実行も可能

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Restore Instagram cache
        uses: actions/cache@v4
        with:
          path: src/data/instagram/cache
          # github.run_id は実行ごとにユニーク。restore-keys で直近のキャッシュを復元する
          key: instagram-cache-${{ github.run_id }}
          restore-keys: |
            instagram-cache-

      - name: Install dependencies
        run: npm ci

      - name: Build site
        env:
          IG_ACCESS_TOKEN: ${{ secrets.IG_ACCESS_TOKEN }}
        run: npm run build

      - name: Deploy
        run: npx wrangler pages deploy dist
        env:
          CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}

ポイントは actions/cache ステップです。path で指定したフォルダの中身を、ビルド間で永続化してくれます。これにより、次のような動きになります。

  • 初回ビルド: 全投稿の画像をダウンロード
  • 2回目以降: 新規投稿の画像だけダウンロード(既存はキャッシュから復元)

ビルド時間も短縮され、APIへのアクセスも最小限に抑えられます。

キャッシュ運用の注意点

  • GitHub Actionsのキャッシュ容量はリポジトリあたり10GB。古いキャッシュから自動削除されるため通常は問題ないが、運用が長期化したら定期的に確認する
  • github.run_idは実行ごとにユニーク。毎回新キャッシュが作られるため、restore-keysで「直近のキャッシュ」を復元する設計になっている
  • キャッシュキーを${{ hashFiles('src/data/instagram/posts.json') }}のように投稿一覧のハッシュにすると、投稿更新時のみ新キャッシュ作成という設計も可能

キャッシュ運用のメリット

  • Instagram APIの一時URL問題を完全に回避できる
  • ビルド時間を大幅に短縮できる(新規分のみダウンロード)
  • APIアクセスが減るためレート制限のリスクも下がる
  • 削除された投稿のキャッシュは別途クリーンアップ処理を入れると良い

7. 【運用編】トークン管理と自動更新

短期/長期トークンの違い

第4章でも少し触れましたが、Instagram Graph APIのトークンには大きく2種類あります。

種類有効期限用途
短期トークン約1時間初回取得時に発行
長期トークン約60日本番運用で使用

本番運用では必ず長期トークンを使いますが、それでも60日で切れてしまいます。手動で60日ごとに更新するのは現実的ではないため、自動更新の仕組みを用意します。

リフレッシュAPIによる月1回自動更新

Instagram Graph APIには「現在の長期トークンを使って、新しい長期トークンに更新する」リフレッシュAPIが用意されています。

GET https://graph.instagram.com/refresh_access_token
  ?grant_type=ig_refresh_token
  &access_token={現在の長期トークン}

これを月1回、GitHub Actionsのcronで叩いて、新しいトークンをGitHub Secretsに書き戻します。

name: Refresh Instagram Token

on:
  schedule:
    - cron: '0 0 1 * *'   # 毎月1日 00:00 UTC

jobs:
  refresh:
    runs-on: ubuntu-latest
    steps:
      - name: Refresh long-lived token
        run: |
          NEW_TOKEN=$(curl -s "https://graph.instagram.com/refresh_access_token?grant_type=ig_refresh_token&access_token=${{ secrets.IG_ACCESS_TOKEN }}" | jq -r '.access_token')
          gh secret set IG_ACCESS_TOKEN --body "$NEW_TOKEN"
        env:
          GH_TOKEN: ${{ secrets.GH_PAT }}

この処理は「リフレッシュAPIを叩き、返ってきた新しいトークンを IG_ACCESS_TOKEN というSecretに上書き保存する」というものです。

新しいトークンが手に入ると、その時点からまた60日有効になります。月1回更新していれば、トークンが切れて運用が止まる事態は避けられます。

トークン失効時のフォールバック設計

それでも、まれにトークンが失効してしまうことがあります。たとえばパスワードを変えた、二段階認証を新しい端末に切り替えた、Meta側の仕様変更があった、などのケースです。

そんなときに、サイト全体が真っ白になってしまうと致命的です。そこで「前回のキャッシュからビルドを継続する」フォールバック設計を入れておきます。

// API取得に失敗したら、前回のキャッシュからビルド継続
const fetchPostsWithFallback = async (token) => {
  try {
    const posts = await fetchMedia(token);
    // 成功したらキャッシュにも保存
    writeFileSync('src/data/instagram/posts.json', JSON.stringify(posts));
    return posts;
  } catch (err) {
    console.warn('Instagram API failed, falling back to cache:', err.message);
    // 前回キャッシュから読み込む
    const cached = readFileSync('src/data/instagram/posts.json', 'utf-8');
    return JSON.parse(cached);
  }
};

このコードがやっているのは、「APIが叩けなかったら前回保存しておいたJSONを読み込む」という単純な分岐です。これだけで、トークン失効時もサイトは表示され続けます。

合わせて、Slackやメールに「APIが失敗した」という通知を送る処理も入れておくと、管理者がすぐに気づけます。

投稿0件時の表示パターン

開業直後でまだInstagramの投稿が1件もない、あるいは振り分けタグが付いた投稿がまだない、というケースもあります。

このとき空っぽのギャラリーが出てしまうと寂しいので、専用のメッセージを表示するようにしておきます。

---
const { posts } = Astro.props;
---

{posts.length === 0 ? (
  <div class="empty-state">
    <p>準備中です。最新の写真はInstagramでご覧いただけます。</p>
    <a href="https://instagram.com/account" target="_blank" rel="noopener">
      Instagramを見る
    </a>
  </div>
) : (
  <div class="gallery-grid">
    {posts.map((post) => <GalleryItem post={post} />)}
  </div>
)}

「投稿が増えるまでの間も、ホームページがちゃんと機能している」状態を保てるかどうかは、クライアント満足度に直結する大事なポイントです。

運用面で見落としがちなこと

  • 長期トークンも60日で切れる。自動更新は必ず実装する
  • トークン失効時のフォールバックがないと、サイト全体が壊れる可能性
  • Meta側の仕様変更(APIのバージョンアップなど)に追従する運用が必要
  • クライアントがInstagramのパスワードを変えた瞬間に動かなくなる場合がある

8. クライアントへの引き渡し・運用方針

引き渡し時に伝えるべきこと

技術的な仕組みができあがっても、クライアントに「何をどうすればいいか」がきちんと伝わっていないと、運用が始まりません。

引き渡し時には、最低限次の項目を文書化して渡しています。

  • どのハッシュタグを付けると、どのページに表示されるか
  • 投稿のキャプションの書き方(特にハッシュタグの位置)
  • 投稿してから実際にホームページに反映されるまでの時間(毎日◯時の自動更新であれば最大24時間)
  • 緊急で即時反映したいときの手順(GitHubの管理画面から手動でビルドを走らせる方法)
  • 表示したくない投稿が出てしまった場合の対処(Instagram側で削除すれば次のビルドで消える)

投稿時の運用ルール(ハッシュタグの付け方)

ハッシュタグの運用ルールは、クライアントが日々の投稿で迷わないよう、具体例つきで伝えます。

例として渡している説明文の抜粋です(汎用化したサンプル)。

Instagramに投稿するとき、キャプションの最後に次のいずれかのハッシュタグを付けてください。

  • 写真をギャラリーに載せたい場合: #shop_gallery
  • お知らせとして掲載したい場合: #shop_news

どちらも付けない投稿は、自動的にギャラリーに表示されます。 両方付けた場合は、お知らせとして優先的に表示されます。

一般的なハッシュタグ(#spring#日常 など)は、付けても付けなくても表示には影響しません。

口頭での説明だけでなく、必ず1枚紙(PDFやNotionページ)にしてお渡しすることを強くおすすめします。3か月後にはルールを忘れてしまうのが普通だからです。

トラブル時の連絡フロー

「ホームページに新しい投稿が出ない」「画像が表示されない」などのトラブルが起きたとき、誰に・どう連絡するかも事前に決めておきます。

  • 第一連絡先: 制作者(メール / LINE / チャットツール)
  • 確認すべきポイント: Instagram側で投稿が公開状態か / ハッシュタグが正しく付いているか / 投稿から24時間以上経過しているか
  • 緊急時の対応: クライアント側ではGitHub Actionsを手動実行できる権限を持っておく

このあたりを最初に決めておくと、お互いに安心して運用できます。

9. この仕組みの向き不向き

向いている事業者

Instagram連携によるホームページ自動更新は、すべての事業者に最適とは限りません。向いているのは次のような事業者です。

向いている事業者の特徴

  • すでにInstagramへの投稿が習慣化している
  • ブログや長文を書くのが苦手 / 時間が取れない
  • 写真・画像が事業の魅力を伝える中心的な要素になっている
  • 飲食・小売・サロン・教室・ハンドメイドなど、ビジュアル訴求の業種
  • 新作・新メニュー・営業情報などをこまめに発信したい

「Instagramは続いているけど、ホームページの更新はまったくできていない」という事業者さんには、まさにこの仕組みがぴったりです。

向かない事業者

一方で、次のような事業者さんには別の選択肢の方が向いています。

向かない事業者の特徴

  • 長文のコラム・解説記事を主軸にコンテンツマーケティングをしたい
  • SEOで検索流入を本格的に狙いたい(Instagram投稿はSEOには直接効きにくい)
  • 士業・コンサル業など、テキストの専門性で信頼を獲得する業種
  • そもそもInstagramを使っていない / 使う予定もない
  • 投稿内容を厳密に校閲・承認するフローが必要な業種

検索からの集客を狙いたい事業者さんには、ブログを書く仕組み(CMS)の方が向いています。Instagramの投稿は、Google検索の結果には基本的に出てこないためです。

他の選択肢との比較

Instagramの投稿をホームページに表示する方法は、今回紹介した「API連携によるビルド時取得」以外にもいくつかあります。

  • 公式埋め込みコード — Instagramの個別投稿ページから「埋め込みコード」をコピペする最もシンプルな方法。ただし1投稿ずつ手動。
  • WordPressプラグイン — Smash Balloon Social Photo Feed などのプラグインを使う。WordPressサイトには手軽だが、表示速度・カスタマイズ性に制約あり。
  • サードパーティサービス — Curator.io、EmbedSocial などの有料SaaS。月額料金がかかるが導入は楽。
  • 今回の方法(自前実装) — 自由度・速度・コスト効率は最高。ただし初期構築の難易度は一番高い。

クライアントの要件・予算・運用体制に応じて、どの方式が最適かを見極めるのが制作者の腕の見せどころでもあります。

まとめ

Instagram投稿を自動でホームページに反映する仕組みは、「更新が続かない」という長年の悩みに対する、シンプルで強力な答えの1つです。

この記事でご紹介したポイントを、最後にもう一度整理します。

  • Instagram Graph APIを使うには、Facebookアカウント・Meta for Developers・Instagramビジネスアカウントの3点セットが必要
  • 取得した投稿はキャプションのハッシュタグで振り分け、優先度ルールとデフォルト動作を必ず決めておく
  • 画像は投稿IDをキーにキャッシュし、Instagram APIの一時URL問題を回避する
  • GitHub Actionsの actions/cache でビルド間のキャッシュを永続化する
  • 長期トークンは60日で切れる。リフレッシュAPIによる月1回自動更新を必ず実装する
  • トークン失効時は前回キャッシュからビルドを継続させて、サイトが壊れない設計にする
  • クライアント引き渡し時には、ハッシュタグの運用ルールを必ず1枚紙にまとめる

技術的な仕組みは複雑に見えますが、根本にあるのは 「クライアントがInstagramに投稿するだけで、ホームページが勝手に更新される」 というとてもシンプルな価値です。

NAGI-WEB制作では、ご依頼内容に応じてこうしたInstagram連携の実装もご相談を承っています。「自分の事業の場合、Instagram連携は向いているのか?」「実装するとどれくらい費用がかかるのか?」といった検討段階のご相談だけでも歓迎です。

ご相談・お見積りは無料で、無理な勧誘は一切いたしません。お気軽にお問い合わせください。