コンテンツへスキップ

開発者ガイド

独自のコンテンツパイプラインからAmapostaに投稿を送信できます。メディアをアップロードし、各チャンネル用の投稿を作成・予約して、公開時に通知を受け取れます。

認証

ダッシュボードの設定の「APIとWebhook」でAPIキーを作成してください。キーはss_live_で始まり、1つのワークスペースに属し、特定のプロジェクトに制限できます。すべてのリクエストでBearerトークンとして送信してください:

キーを確認
curl https://www.amaposta.com/api/v1/me \
  -H "Authorization: Bearer ss_live_…"

応答にはキーの名前、スコープ、ワークスペースが含まれます。各キーには1つ以上のスコープがあります:

スコープ許可する権限
content:readプロジェクト、チャンネル、投稿、メディアを読み取ります。
content:writeメディアのアップロード、投稿の作成・編集を行います。
posts:publishレビューなしで予約・公開します。この権限がない場合、承認が有効なプロジェクトではレビュー受信箱に送られ、それ以外では下書きのままになります。

各キーは毎分120リクエストまで実行できます。超過するとrate_limitedコード付きの429が返ります。少し待って再試行してください。

プロジェクトとチャンネル

投稿はプロジェクトに属します。キーで参照できるプロジェクトを取得し、次にその中のチャンネルを取得してください。投稿先の指定や本文の上書きにはチャンネルIDが必要です。

プロジェクトとチャンネルを取得
curl https://www.amaposta.com/api/v1/projects \
  -H "Authorization: Bearer $AMAPOSTA_KEY"

curl https://www.amaposta.com/api/v1/projects/$PROJECT_ID/channels \
  -H "Authorization: Bearer $AMAPOSTA_KEY"

チャンネルにはprovider(youtube、instagram、tiktok、x、facebook、custom、sandbox)、status(active、expiring、needs_reconnect、error、disabled)、およびsupportsで示す対応投稿形式があります。

メディアをアップロード

画像と動画はプロジェクトごとに1回アップロードすれば、何件の投稿でも利用できます。取り込み方法は3つあります。

直接アップロード

fileという名前のフィールドにmultipart形式のフォームデータとしてファイルを送信してください。数百MBまでのファイルに適しています。

POST /api/v1/projects/{project_id}/media
curl https://www.amaposta.com/api/v1/projects/$PROJECT_ID/media \
  -H "Authorization: Bearer $AMAPOSTA_KEY" \
  -F "[email protected]"

大容量ファイルの署名付きアップロード

アップロードURLを取得し、バイト列をストレージへ直接PUTしてから、完了をAmapostaに通知します。

3つのリクエスト
# 1. Ask for an upload URL. The response is {"media": { … }, "upload_url": "…"}
curl https://www.amaposta.com/api/v1/projects/$PROJECT_ID/media/uploads \
  -H "Authorization: Bearer $AMAPOSTA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filename": "batch-142.mp4", "content_type": "video/mp4", "size_bytes": 48213844}'

# 2. Upload the bytes to upload_url
curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: video/mp4" \
  --upload-file batch-142.mp4

# 3. Finish the upload
curl -X POST https://www.amaposta.com/api/v1/media/$MEDIA_ID/complete \
  -H "Authorization: Bearer $AMAPOSTA_KEY"

URLから取り込む

公開URLを指定すると、Amapostaがバックグラウンドでファイルをダウンロードします。この手順を省略し、投稿作成時にmedia_urlを渡すこともできます。

POST /api/v1/projects/{project_id}/media/import
curl https://www.amaposta.com/api/v1/projects/$PROJECT_ID/media/import \
  -H "Authorization: Bearer $AMAPOSTA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://cdn.example.com/batch-142.mp4"}'

アップロードはすべてメディアオブジェクトを返します。statusはpendingからprocessing、readyへ変わります(失敗時はfailedとなり、errorを含みます)。処理後はwidth、height、duration_msとサムネイルを持ちます。GET /api/v1/media/{id}をポーリングするか、media.readyのWebhookを受信してください。返されるurlは署名付きで12時間有効です。

投稿を作成

POST /api/v1/projects/{project_id}/postsは、各チャンネルへの配信を含む投稿を1件作成します。Amapostaはプラットフォームごとに内容を調整します。タイトルはタイトル欄がある場合のみ送信され、ハッシュタグはYouTubeではタグに、それ以外では本文中のテキストになります。

動画投稿を作成してキューに追加
curl https://www.amaposta.com/api/v1/projects/$PROJECT_ID/posts \
  -H "Authorization: Bearer $AMAPOSTA_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: episode-142" \
  -d @- <<'JSON'
{
  "kind": "video",
  "title": "How we roast a batch in 12 minutes",
  "description": "From green beans to first crack. We filmed one batch start to finish.",
  "hashtags": ["coffee", "behindthescenes"],
  "media_url": "https://cdn.example.com/batch-142.mp4",
  "cover_url": "https://cdn.example.com/batch-142-cover.jpg",
  "overrides": {
    "<x-channel-id>": {
      "description": "Batch 142 is out: green beans to first crack in 12 minutes."
    },
    "<youtube-channel-id>": {
      "options": { "privacy": "unlisted" }
    }
  },
  "schedule": { "mode": "queue" },
  "external_ref": "episode-142",
  "metadata": {
    "pipeline": { "name": "nightly-cut", "run": "2026-10-17" }
  }
}
JSON

フィールド

フィールドできること
kindtext、image、videoのいずれか。必須です。
titleタイトル欄があるプラットフォームで使用します:YouTube、TikTokの写真投稿、Facebookの動画。
descriptionキャプション、説明、または投稿本文。body、caption、textとしても指定できます。
hashtags#の有無を問わないリスト。
media_id, media_urlアップロード済みのメディアID、またはAmapostaがダウンロードする公開URL。画像投稿と動画投稿には必須です。
cover_media_id, cover_urlプラットフォームが対応している場合に使用する動画のカバー画像。
channels投稿先のチャンネルID。省略すると、プロジェクト内でこの投稿形式に対応するすべての有効なチャンネルを使用します。
overridesチャンネルIDをキーとするチャンネル別の変更:title、description、hashtags、およびYouTubeのprivacyなどのプラットフォームoptions。
schedulemodeはat(ISO 8601形式のatを指定)、now、または次の空き投稿枠を使うqueueです。省略すると下書きが作成されます。
external_ref任意の参照値。投稿とCustom APIへの配信でそのまま返されます。
metadata投稿とともに保存する自由形式のJSON。生成コンテンツには"pipeline"を含めてください。ダッシュボードに表示され、Webhookは生成元をpipelineとして通知します。

応答は投稿で、deliveriesにはチャンネルごとに1件の項目が含まれます。

安全な再試行

ネットワークではリクエストが失われる場合があります。投稿作成時にIdempotency-Keyヘッダーを送信すると、同じキーでの再試行は2件目を作成せず、初回の投稿を返します。ジョブIDやエピソードIDなど、自分のシステムで安定した値を使用してください。キーは200文字に切り詰められます。

予約と公開

POST /api/v1/posts/{post_id}/scheduleで下書きの予約、即時公開、キューへの追加、カレンダーからの取り消しができます。

時刻を指定して予約
curl https://www.amaposta.com/api/v1/posts/$POST_ID/schedule \
  -H "Authorization: Bearer $AMAPOSTA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"mode": "at", "at": "2026-10-20T07:00:00Z"}'
  • at指定時刻に公開します。最大2年先まで指定できます。
  • nowすぐに公開します。
  • queue投稿先チャンネルの今後12週間にある次の空き投稿枠を使用します。
  • unschedule投稿を下書きに戻します。

予約前に、投稿エディターと同じチェックを実行します。1つでも失敗すると、予約前に修正してください:YouTubeにはタイトルが必要です。のようなメッセージとともに400を返します。予約にはposts:publishスコープ、または承認が有効なプロジェクトが必要です。後者の場合、投稿はレビュー受信箱で待機します。

投稿と配信の読み取り

プロジェクトの投稿を、status(カンマ区切りのステータス、またはinbox、queue、history)、from/toの期間、limit、offsetで絞り込んで取得します。応答は{"items": […], "total": n}です。

今後の予定と1件の投稿の詳細
curl "https://www.amaposta.com/api/v1/projects/$PROJECT_ID/posts?status=queue" \
  -H "Authorization: Bearer $AMAPOSTA_KEY"

curl https://www.amaposta.com/api/v1/posts/$POST_ID \
  -H "Authorization: Bearer $AMAPOSTA_KEY"

投稿には全体のstatus(draft、in_review、changes_requested、ready、scheduled、publishing、published、partial、failed)と、チャンネルごとに1件の配信があります。

deliveries[]
"deliveries": [
  {
    "id": "0192b7d4-…",
    "channel_id": "0192b7a1-…",
    "status": "published",
    "url": "https://www.youtube.com/watch?v=…",
    "error": null,
    "published_at": "2026-10-20T07:00:41Z"
  },
  {
    "id": "0192b7d4-…",
    "channel_id": "0192b7a9-…",
    "status": "failed",
    "url": null,
    "error": {
      "code": "endpoint_unavailable",
      "message": "Your endpoint returned 503 Service Unavailable",
      "hint": "Amaposta will retry with backoff."
    },
    "published_at": null
  }
]

配信ステータスはpending、scheduled、publishing、processing、published、failed、cancelledです。DELETE /api/v1/posts/{post_id}で投稿を削除できます。

エラー

エラーはすべて同じ形式です。errorオブジェクトに安定したcode、利用者に表示できるmessageを含み、場合によってはfields(フィールドごとのメッセージ)とhintも含みます。

422 Unprocessable Entity
{
  "error": {
    "code": "validation",
    "message": "Please fix the highlighted fields.",
    "fields": {
      "kind": "Use text, image or video."
    }
  }
}
ステータスコード意味
400bad_requestチェックに失敗した投稿の予約など、指定された処理を実行できません。
401unauthorizedAPIキーが未指定、誤っている、または取り消されています。
402plan_limitワークスペースのプランではこの操作は許可されません。limitで該当する上限を確認できます。
403forbiddenキーにスコープまたはプロジェクトへのアクセス権がありません。
404not_found該当するプロジェクト、投稿、メディア、エンドポイントはありません。
409conflict公開処理中の投稿の編集など、変更が現在の状態と競合しています。
422validation修正が必要なフィールドがあります。fieldsを参照してください。
429rate_limitedこのキーで毎分120リクエストを超えました。
502provider_errorプラットフォームが拒否したか、処理に失敗しました。hintで対処方法を確認できます。

Webhook

設定の「APIとWebhook」でエンドポイントを追加し、必要なイベントを選び、署名シークレットをコピーしてください。イベントごとにAmapostaがURLへJSONをPOSTします。2xx以外の応答、または15秒以内に応答がない場合は間隔を延ばしながら再試行します。失敗が続くエンドポイントは無効になり、管理者に通知されます。

イベント送信するタイミング
post.created投稿が作成されました(ダッシュボードまたはAPI)
post.scheduled投稿が予約されました
post.approved投稿が承認されました
post.publishedチャンネルで投稿が公開されました
post.failedチャンネルへの投稿を公開できませんでした
channel.connectedチャンネルを接続または再接続しました
channel.disconnectedチャンネルの接続が失われました
media.readyアップロードしたファイルの処理が完了しました
media.failedアップロードしたファイルを処理できませんでした
post.published
POST /your/webhook HTTP/1.1
Content-Type: application/json
User-Agent: Amaposta-Webhooks/1.0
X-Amaposta-Event: post.published
X-Amaposta-Event-Id: 0192b7e0-…
X-Amaposta-Signature: t=1792479641,v1=5f2b0c…

{
  "id": "0192b7e0-…",
  "type": "post.published",
  "created_at": "2026-10-20T07:00:41Z",
  "project_id": "0192a3f1-…",
  "data": {
    "post_id": "0192b7c8-…",
    "delivery_id": "0192b7d4-…",
    "channel_id": "0192b7a1-…",
    "provider": "youtube",
    "external_id": "…",
    "url": "https://www.youtube.com/watch?v=…"
  }
}

署名を検証

X-Amaposta-Signatureはt=<unix seconds>,v1=<hex>の形式で、v1は生のリクエスト本文に対するHMAC_SHA256(secret, t + "." + body)です。自分で計算して定数時間で比較し、古いタイムスタンプを拒否してリプレイ攻撃を防いでください。

Node.js
import crypto from "node:crypto";

// rawBody: the request body exactly as received (string or Buffer).
export function verifySignature(rawBody, header, secret, toleranceSeconds = 300) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=", 2)));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false;
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${t}.${rawBody}`)
    .digest();
  const given = Buffer.from(parts.v1 ?? "", "hex");
  return given.length === expected.length && crypto.timingSafeEqual(given, expected);
}
Python
import hashlib
import hmac
import time


def verify_signature(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    t = int(parts.get("t", "0"))
    if abs(time.time() - t) > tolerance:
        return False
    expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts.get("v1", ""))

Custom APIチャンネル

Custom APIチャンネルは、自分で運用するHTTPSエンドポイントに投稿します。CMS、ニュースレターツール、Amapostaがまだ対応していないプラットフォームなどを利用できます。他のチャンネルと同様にカレンダーに表示され、チェック、承認、再試行の対象になります。投稿時には配信ごとに1回のPOSTを送信します。

エンドポイントが受信する内容
POST /amaposta/publish HTTP/1.1
Authorization: Bearer <your API key>
Content-Type: application/json
Idempotency-Key: 0192b7d4-…
X-Amaposta-Delivery: 0192b7d4-…
X-Amaposta-Signature: t=1792479600,v1=9ac41e…

{
  "id": "0192b7d4-…",
  "type": "post.publish",
  "created_at": "2026-10-20T07:00:00Z",
  "content": {
    "kind": "video",
    "title": "How we roast a batch in 12 minutes",
    "text": "From green beans to first crack. We filmed one batch start to finish.",
    "hashtags": ["coffee", "behindthescenes"],
    "media": {
      "url": "https://…",
      "content_type": "video/mp4",
      "size_bytes": 48213844,
      "width": 1080,
      "height": 1920,
      "duration_ms": 58000,
      "filename": "batch-142.mp4"
    },
    "cover": null,
    "options": {}
  },
  "post": {
    "id": "0192b7c8-…",
    "external_ref": "episode-142",
    "scheduled_at": "2026-10-20T07:00:00Z",
    "metadata": {
      "pipeline": { "name": "nightly-cut" }
    }
  },
  "project": { "id": "0192a3f1-…", "name": "Spring launch" },
  "channel": { "id": "0192b7a9-…", "name": "api.example.com" }
}

応答方法

  • 2xx公開済みです。任意で{"id": "…", "url": "…"}を返すと、投稿にリンクが表示されます。
  • 401, 403キーが拒否されました。チャンネルは再接続が必要な状態になり、管理者に通知されます。署名が不正な場合にこれらを使用しないでください。
  • 5xx, 408, 429一時的な問題です。Amapostaが間隔を延ばしながら再試行します。
  • その他の4xxメッセージとともに投稿が失敗状態になり、担当者が修正して「再試行」を押せます。
Expressで受信する
app.post("/amaposta/publish", express.raw({ type: "application/json" }), async (req, res) => {
  const signature = req.get("X-Amaposta-Signature") ?? "";
  if (!verifySignature(req.body, signature, process.env.AMAPOSTA_SIGNING_SECRET)) {
    return res.status(400).json({ message: "Bad signature" });
  }
  const delivery = JSON.parse(req.body);
  const item = await publishToYourSystem(delivery.content, req.get("Idempotency-Key"));
  res.json({ id: item.id, url: item.url });
});

エンドポイントはインターネットから到達可能である必要があり、リダイレクトは追跡されません。チャンネルの接続時、Amapostaは設定されたヘルスチェックURLにGETを送信し、未設定ならエンドポイントにOPTIONSリクエストを送信します。

Sandboxチャンネル

Sandboxチャンネルは実際のチャンネルと同じように動作しますが、Amapostaの外部には何も送信されません。連携の構築からテストまで一通り試せます。投稿は予約され、配信状態に進み、公開または失敗し、同じWebhookを発火します。Sandboxチャンネルはすべてのプランで無料で、チャンネル数の上限には含まれません。

追加時の動作を選んでください:

  • 常に成功すべての投稿が公開されます。
  • 時々失敗実際の障害を待たずに、失敗や再試行を確認できます。
  • 常に失敗post.failedの処理をテストできます。
  • 期限切れの接続アクセス権が失効したときの再接続通知やリンクの表示を確認できます。

自分のアプリでXに接続

Xでは、投稿を含むすべてのAPI呼び出しの料金が、呼び出しを行う開発者アプリの前払いクレジットから差し引かれます。そのため、自分のX開発者アプリを使ってXに接続します。ワークスペースごとに一度設定すれば、好きな数のXアカウントを接続できます。各アカウントでXにログインし、投稿料金はアプリのクレジットから支払われます。

知っておくと便利なこと

  • アプリはワークスペース全体で使えるように保存され、シークレットは暗号化されます。他のXアカウントは、どのプロジェクトからでもワンクリックで接続できます。
  • チャンネルには接続時に使ったアプリが保持されます。クライアント向けの再接続リンクを使う場合も含め、チャンネルを再接続すると同じアプリが使われます。クライアントにアプリが表示されることはありません。
  • アプリを切り替えるには、Xへの接続時に「別のアプリを使う」を選んでください。Client Secretを再生成した場合は、そこで新しい値を入力し、Xチャンネルを再接続してください。
  • Xのコンソールでトークンを発行できるのは、アプリを所有するアカウントだけです。クライアントのアカウントを接続するには、自分のアプリを通じてログインします。自分のアカウントでは、引き続きトークンを手動入力できます。

プラットフォームのルール

各チャンネル形式が対応する内容です。チェックでも同じ上限を使用するため、ほとんどの問題は公開時ではなく予約前にわかります。

プラットフォーム 投稿 タイトル テキスト ハッシュタグ メディア
YouTube 動画 タイトル必須、最大100文字 説明最大5,000文字 タグとして送信、最大60個 MP4, MOV, WebM, AVI, MKV; 最大256 GB; 1秒〜12時間
電話番号が未認証のアカウントは最長15分の動画をアップロードでき、カスタムサムネイルは設定できません。 アップロードごとにAPI割り当てを消費します。Googleの既定の割り当てでは、アプリごとに1日約6回アップロードできます。
Instagram 画像 タイトルなし キャプション最大2,200文字 本文に追加、最大30個 JPEG; 最大8 MB
動画 タイトルなし キャプション最大2,200文字 本文に追加、最大30個 MP4, MOV; 最大300 MB; 3秒〜15分
Instagramのビジネスアカウントまたはクリエイターアカウントが必要です。 Instagramでは、過去24時間にアカウントごとにAPIから50件まで投稿できます。 Instagramではテキストのみの投稿はできません。
TikTok 画像 タイトル最大90文字 説明最大4,000文字 本文に追加 JPEG, WebP; 最大20 MB
動画 タイトルなし キャプション最大2,200文字 本文に追加 MP4, MOV, WebM; 最大4 GB; 3秒〜10分
TikTokの審査が完了していないアプリからの投稿は、作成者のみに表示されます。 TikTokは動画フレームからカバーを選択します。カスタムカバー画像には対応していません。 TikTokではテキストのみの投稿はできません。
X テキスト タイトルなし 投稿必須、最大280文字(リンクは23文字、絵文字は2文字) 本文に追加 なし
画像 タイトルなし 投稿最大280文字(リンクは23文字、絵文字は2文字) 本文に追加 JPEG, PNG, WebP, GIF; 最大5 MB
動画 タイトルなし 投稿最大280文字(リンクは23文字、絵文字は2文字) 本文に追加 MP4, MOV; 最大512 MB; 0.5秒〜2:20分
独自のX開発者アプリを使う場合、投稿はそのアプリの従量課金APIクレジットを使用します。 ハッシュタグは文字数制限に含まれます。
Facebook テキスト タイトルなし 投稿必須、最大63,206文字 本文に追加 なし
画像 タイトルなし キャプション最大63,206文字 本文に追加 JPEG, PNG, GIF, WebP; 最大10 MB
動画 タイトル最大255文字 説明最大63,206文字 本文に追加 MP4, MOV; 最大1 GB; 1秒〜20分
ページのみに公開します。API経由で個人プロフィールに投稿することはできません。
Custom API テキスト タイトル長さ制限なし 説明長さ制限なし タグとして送信 なし
画像 タイトル長さ制限なし 説明長さ制限なし タグとして送信 JPEG, PNG, WebP, GIF
動画 タイトル長さ制限なし 説明長さ制限なし タグとして送信 MP4, MOV, WebM
各リクエストにはHMAC-SHA256ヘッダーで署名が付き、Amapostaからの送信であることを検証できます。 2xxと任意のJSON {"id": "…", "url": "…"}で応答してください。408、429、5xxの場合は間隔を延ばしながら再試行します。401/403ではチャンネルが再接続必要となり、他の4xxでは配信が失敗となるため、修正して再試行できます。
Sandbox テキスト タイトル最大200文字 説明最大5,000文字 本文に追加、最大30個 なし
画像 タイトル最大200文字 説明最大5,000文字 本文に追加、最大30個 JPEG, PNG, WebP, GIF
動画 タイトル最大200文字 説明最大5,000文字 本文に追加、最大30個 MP4, MOV, WebM
公開には少し時間がかかるため、状態の変化をリアルタイムで確認できます。