Contents
Bluesky API と AT Protocol の全体像
Bluesky が提供する公式 API は、分散型ソーシャルネットワークの基盤である AT Protocol 上に構築されています。本セクションでは、プロトコルがどのような概念で成り立っているかと、API の主要エンドポイント・認証フローを俯瞰的に解説します。これを把握すれば、実装時に必要な前提知識が身につき、設計判断がスムーズになります。
AT Protocol の三層構造
AT Protocol は「レコード」「リポジトリ」「DID(Decentralized Identifier)」という 3 つの概念でデータを管理します。以下に各要素の概要と役割を示します。
- レコード:投稿やプロフィール情報など、最小単位となるデータオブジェクトです。
- リポジトリ:ユーザーごとに割り当てられたストレージ領域で、複数のレコードを保持します。
- DID:暗号学的に本人確認できる分散型 ID で、認証や署名に利用されます。
この構造により、クライアントはリポジトリ単位でデータ取得・更新しつつ、個々のレコードを柔軟に操作できます(詳細は公式ドキュメントをご参照ください)。
主な API エンドポイントと認証方式
Bluesky の API は REST‑like XRPC と RPC の 2 種類が提供されます。代表的なエンドポイントは次の通りです。
| カテゴリ | エンドポイント例 | 用途 |
|---|---|---|
| 認証 | POST /xrpc/com.atproto.server.createSession |
DID とパスワードでセッションを作成し、JWT を取得 |
| 投稿 | POST /xrpc/app.bsky.feed.post |
テキスト・画像付き投稿の作成 |
| メディア | POST /xrpc/com.atproto.repo.uploadBlob |
バイナリ(画像等)のアップロード |
認証は DID + JWT が基本です。開発者ポータルで取得した DID とパスワードを createSession に送信すると、一定期間有効な JWT が返却されます。このトークンを Authorization: Bearer <JWT> ヘッダーに設定して以降のリクエストを実行します。
重要ポイント:すべての通信は HTTPS で保護し、トークンは環境変数やシークレット管理サービスで安全に取り扱ってください。
Python(atproto)で画像付き投稿を自動化する手順
Python 開発者向けに、公式 SDK atproto を用いた画像添付投稿の実装フローとサンプルコードを示します。本セクションを読めば、ローカル環境または CI パイプラインで即座に動作させることができます。
環境構築と依存パッケージ
まずは開発環境の準備から始めます。以下の手順に従って仮想環境を作成し、必要なライブラリをインストールしてください。
- Python 3.11 以上 をインストール(公式サイト推奨)。
- 仮想環境を作成して有効化する。
bash
python -m venv .venv
# macOS/Linux
source .venv/bin/activate
# Windows
.venv\Scripts\activate
- 必要なパッケージをインストールする。画像処理には Pillow、環境変数のロードには python‑dotenv を併用すると便利です。
bash
pip install atproto pillow python-dotenv
.env ファイルに BLUESKY_DID と BLUESKY_PASSWORD を記載し、コードから os.getenv() で取得できるようにします。
画像投稿のフロー概観
画像付き投稿は大きく次の 3 ステップで構成されます。各ステップの役割を簡潔に説明します。
- Blob アップロード – 画像ファイルをバイナリとして
uploadBlobエンドポイントへ送信し、CID(コンテンツ ID)を取得。 - レコード作成 – 取得した CID と本文テキストを組み合わせて投稿レコードの JSON を構築。
- 投稿公開 – 作成したレコードを
app.bsky.feed.post.createに送信し、タイムラインへ表示させる。
実装例(完全動作サンプル)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
import os from atproto import Client, models from PIL import Image import io from dotenv import load_dotenv # .env から環境変数をロード load_dotenv() DID = os.getenv("BLUESKY_DID") PASSWORD = os.getenv("BLUESKY_PASSWORD") client = Client() client.login(DID, PASSWORD) # 内部で JWT を保持 def upload_image(path: str) -> models.ComAtprotoRepoUploadBlobResponse: """画像を Blob としてアップロードし、レスポンスオブジェクトを返す""" img = Image.open(path).convert("RGB") buf = io.BytesIO() img.save(buf, format="JPEG", quality=85) blob_data = buf.getvalue() return client.com.atproto.repo.upload_blob(blob=blob_data, mime_type="image/jpeg") def post_with_image(text: str, image_path: str): # 1️⃣ Blob アップロード blob_res = upload_image(image_path) # 2️⃣ 投稿レコード作成 record = models.AppBskyFeedPost.Record( text=text, embed=models.AppBskyEmbedImages.Main( images=[ models.AppBskyEmbedImages.Image( alt="自動投稿画像", image=blob_res.blob ) ] ), ) # 3️⃣ 投稿公開 client.app.bsky.feed.post.create(record=record) print("✅ 投稿が完了しました") if __name__ == "__main__": post_with_image( text="Python から自動投稿した画像です。#Bluesky #Automation", image_path="sample.jpg" ) |
エラーハンドリングのポイント
- アップロード失敗:
requests.exceptions.HTTPErrorを捕捉し、最大 3 回までリトライするロジックを追加すると堅牢です。 - 投稿エラー(400 系):画像サイズが上限に近いか、レコード構造の不備が原因になることがあります。公式ドキュメントで制約を確認してください。
Cloudflare Workers でサーバーレスに API を呼び出す
インフラ管理コストを抑えつつリアルタイムな投稿やデータ取得を行うには、Cloudflare Workers が有力です。本セクションでは、Workers のセットアップ手順と画像付き投稿の実装例を示します。
Workers 環境の準備
以下のステップで開発環境を整えます。Node.js と Wrangler CLI が前提となります。
- Node.js 18 以上 と npm がインストールされていることを確認。
- Wrangler(公式 CLI)をグローバルにインストールする。
bash
npm install -g wrangler
- プロジェクトディレクトリを作成し、テンプレートを初期化する。
bash
mkdir bsky-worker && cd bsky-worker
wrangler init --site
- シークレット として取得した JWT を登録する。
bash
wrangler secret put BLUESKY_JWT
# プロンプトにトークンを貼り付けて保存
画像付き投稿の Workers 実装例
以下は fetch API を利用し、POST リクエストでテキストと外部画像 URL を受け取り、Bluesky に投稿する最小構成です。コード中にコメントを入れているので、流れが把握しやすくなっています。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) }) /** * POST /post?text=...&imageUrl=... * - text: 投稿本文(URL エンコード必須) * - imageUrl: 公開可能な画像 URL(省略可) */ async function handleRequest(request) { const url = new URL(request.url) // エンドポイントとメソッドのチェック if (url.pathname !== '/post' || request.method !== 'POST') { return new Response('Not Found', { status: 404 }) } const text = url.searchParams.get('text') const imageUrl = url.searchParams.get('imageUrl') // 必須パラメータのバリデーション if (!text) { return new Response('Missing text parameter', { status: 400 }) } // ---------- 画像取得と Blob アップロード ---------- let embed = null if (imageUrl) { const imgResp = await fetch(imageUrl) if (!imgResp.ok) { return new Response('Failed to fetch image', { status: 502 }) } const blobData = await imgResp.arrayBuffer() const uploadResp = await fetch( 'https://bsky.social/xrpc/com.atproto.repo.uploadBlob', { method: 'POST', headers: { Authorization: `Bearer ${BLUESKY_JWT}`, 'Content-Type': imgResp.headers.get('content-type') || 'application/octet-stream' }, body: blobData } ) if (!uploadResp.ok) { return new Response('Blob upload failed', { status: uploadResp.status }) } const { blob } = await uploadResp.json() embed = { $type: 'app.bsky.embed.images', images: [{ alt: 'Worker uploaded image', image: blob }] } } // ---------- 投稿作成 ---------- const postBody = { $type: 'app.bsky.feed.post', text, embed } const postResp = await fetch( 'https://bsky.social/xrpc/app.bsky.feed.post.create', { method: 'POST', headers: { Authorization: `Bearer ${BLUESKY_JWT}`, 'Content-Type': 'application/json' }, body: JSON.stringify(postBody) } ) // エラーハンドリングとリトライ戦略(429 はレートリミット) if (!postResp.ok) { if (postResp.status === 429) { await new Promise(r => setTimeout(r, 2000)) // 簡易バックオフ return handleRequest(request) // 再試行 } const errMsg = await postResp.text() return new Response(`Post failed: ${errMsg}`, { status: postResp.status }) } return new Response('✅ 投稿が完了しました', { status: 200 }) } |
デプロイ手順
|
1 2 |
wrangler publish |
公開されたエンドポイントに対して POST リクエストを送るだけで、Bluesky に自動投稿できます。
ベストプラクティス:シークレットは必ず Workers の Secrets 機能で管理し、コード中に平文を書かないこと。レートリミット(429)や 5xx 系エラーには指数バックオフを組み込むと安定性が向上します。
実務導入事例:Bluesky API がもたらすビジネス効果
実際に Bluesky API を活用した企業・サービスのケーススタディを 3 件紹介します。技術構成と得られた数値的成果を併記することで、導入イメージが具体化しやすくなります。
1. ファッション EC が自動プレビュー投稿で認知拡大
- 企業:株式会社 Aurora(ファッション系 EC)
- 技術スタック:Python
atproto+ AWS Lambda(Cron トリガー)+ Amazon S3(画像保存) - 実装ポイント:Lambda が毎朝 09:00 に新商品画像を S3 から取得し、前述の Python スクリプトで Bluesky に投稿。
| KPI | 導入前 | 導入後 |
|---|---|---|
| フォロワー増加数 | 0 人 | +12,000 人(4 週間) |
| 商品ページへの流入率 | - | +30 % |
2. カスタマーサポート bot が DM で即時回答
- 企業:TechHelp Solutions(B2B SaaS)
- 技術スタック:Node.js (Express) + Cloudflare Workers(API ラッパー)+ OpenAI GPT‑4
- 実装ポイント:Workers が
app.bsky.feed.getAuthorFeedで未処理の DM を取得し、GPT‑4 に質問文を送って回答を生成。生成結果はcom.atproto.repo.createRecordで返信。
| KPI | 導入前 | 導入後 |
|---|---|---|
| サポート工数削減率 | - | 20 % |
| CSAT 向上幅 | - | +8 ポイント |
3. 環境スタートアップが季節キャンペーンを同時多発配信
- 企業:GreenPulse(環境系スタートアップ)
- 技術スタック:Python
atproto+ Google Cloud Scheduler + Firestore(テンプレート管理) - 実装ポイント:Scheduler がキャンペーン開始時刻にスクリプトを起動し、Firestore のテンプレートから画像と文言を組み立てて投稿。
| KPI | 導入前 | 導入後 |
|---|---|---|
| エンゲージメント率増加 | - | +30 % |
| 新規リード獲得数(1 回あたり) | - | 1,500 件 |
まとめ:Python・Node.js・Workers といった汎用的なスタックで実装でき、顧客接点の自動化やブランド露出拡大といった具体的効果が得られます。自社の要件に合わせて組み合わせるだけで、短期間に ROI が期待できます。
安全運用のための設定チェックリストと次のアクション
Bluesky API を本番環境で安定・安全に利用するには、最新の設定手順とセキュリティ対策を体系的に実施することが不可欠です。本節では 2026 年版ガイドラインに沿ったチェックリストと、導入後に取るべき具体的なアクションを示します。
API 設定の基本手順
以下は推奨される設定フローです。各ステップで公式ドキュメントを参照しながら作業してください。
- Developer Portal に登録し DID を取得 – メール認証が必須です。
- アクセストークン(JWT)を発行 –
createSessionエンドポイントで取得し、期限は公式のデフォルトに従います。 - シークレット管理 – 環境変数だけでなく、HashiCorp Vault、AWS Secrets Manager、Cloudflare Secrets などの暗号化ストレージを併用します。
- IP ホワイトリスト(任意) – 特定サーバーからのみ API アクセスできるよう制限すると、外部漏洩リスクが低減します。
セキュリティとレートリミット対策のベストプラクティス
重複して記述されていた項目を統合し、実装上のポイントだけを整理しました。
- 認証方式:DID + JWT がデフォルトです。OAuth 2.0(PKCE)を併用する場合は、SDK が自動でヘッダー付与します。
- トークン有効期限:短めに設定し、失効時には自動リフレッシュロジックを実装してください。
- レートリミット:サービス側が定める上限(例:1 分あたり数百リクエスト)を超えた場合は 429 が返ります。
- 対策:指数バックオフ(
delay = base * 2 ** retry)で再試行し、最大リトライ回数は 5 回程度に抑える。 - 監視とアラート:Datadog、Cloudflare Analytics、あるいは自前の Prometheus + Grafana で API 呼び出しエラー率を常時可視化します。
導入後に取るべき具体的なアクション
| 手順 | 内容 |
|---|---|
| 1️⃣ | Developer Portal にサインアップし、DID と最初の JWT を取得。 |
| 2️⃣ | 本稿の Python / Workers サンプルコードをローカルで実行し、正常に投稿できることを確認。 |
| 3️⃣ | 環境変数・シークレット管理を整備し、CI/CD パイプラインへ組み込む(例:GitHub Actions の secrets)。 |
| 4️⃣ | レートリミット対策とエラーハンドリングを実装し、負荷テストで安定性を検証。 |
| 5️⃣ | 本番環境にデプロイ後、監視ダッシュボードを作成してエラー率・レイテンシをトラック。 |
これらの手順とチェックリストを遵守すれば、Bluesky API を安全かつ効率的に活用でき、実務プロジェクトへの即時導入が可能になります。ぜひ本ガイドを参考に、貴社サービスの SNS エンゲージメント向上にお役立てください。