コミュニティ Open API
サイト単位の API です。1 つのキーが 1 つのコミュニティに紐づき、リクエストで指定したメンバーとして動作します。クリエイターダッシュボードの API でもメンバー API でもなく、ここにログインセッションはありません。
始める前に
どのキーも動くには 2 つの条件が必要で、そのどちらも自分のコードでは解決できません。
1. コミュニティのプランに API アクセスが含まれていること
これを決めるのは連携する側ではなくコミュニティオーナーです。プランには API アクセス機能が必要で、なければすべての呼び出しが 22203 を返します。さらに日次割り当てが 0 でないことも必要で、0 ならすべての呼び出しが 22204 を返します。
以前に作成されたコミュニティは、プランに他の高度な機能が含まれていても、API アクセスが既定で無効になっています。発行したばかりのキーがすべての呼び出しで 22203 を返すなら、コードを読み直すのではなくオーナーに管理画面を確認してもらってください。
エラーを集めて突き止める必要はありません。/capabilities/get が直接答えますし、その 2 つの状態でも動きます。
2. コミュニティ管理画面でキーを作る
コミュニティ管理画面 → インテグレーション → API Keys → 作成。キーは mfk_ に 64 桁の 16 進数が続く形式で、全体が表示されるのは作成時の 1 回だけ。それ以降は先頭のプレフィックスしか見えません。
作成時に選んだスコープが、そのキーで呼べるエンドポイントを決めます。後から編集はできません。変更するには新しいキーを作り、古いものを失効させてください。
任意で、キーを IP 許可リスト(単一アドレスまたは CIDR)に限定できます。それ以外からのリクエストは 10007 で失敗します——無効なキーと同じコードなので、昨日まで動いていたキーが別のホストから動かなくなったときに思い出す価値があります。
スコープ
| スコープ | 対象範囲 |
|---|---|
| site.read | コミュニティのプロフィールと設定 |
| content.read | 投稿の読み取り、フィード、検索、返信ツリー |
| content.write | 投稿の作成・編集・削除 |
| media.read | 保存済みメディアの署名付き URL |
| media.write | メディアアップロード用トークン——後方互換のため署名付き URL の読み取りも可能 |
| spaces.read | スペースの一覧と詳細 |
| members.read | メンバーの読み取りとメール検索 |
* の意味は 2026 年 9 月に変わりました
以前は「後から追加されるものも含めたすべて」を意味していました。つまりプラットフォームが新機能を出すたび、過去に発行されたすべてのキーが黙って広がっていたということです。現在は、新しいキーは作成時点の具体的な一覧を保存します。そして残存する * は、それが発行された時点で存在した 4 つのスコープ、すなわち site.read・content.read・content.write・media.write だけを指します。
したがって古いキーでは新しいエンドポイント——スペース、メンバー、署名付き URL の読み取り——を呼べません。必要なスコープを選んで新しいキーを発行してください。これは意図的な仕様です。プラットフォームが新機能を出したからといって、知らないうちにメンバー名簿が渡されてよいはずがありません。
認証
2 つのヘッダーのどちらかでキーを送ります。両者は同等で、同時に送った場合は X-API-Key が優先されます。コミュニティはキーから導かれるので、サイト id を渡すことは一切ありません。
X-API-Key: mfk_xxx
Authorization: Bearer mfk_xxx
キーはそれ自体が資格情報です——サーバー側に保管してください。ブラウザのコードにもモバイルアプリのバンドルにも置いてはいけません。読み取れる人は誰でも、あなたのメンバーとして投稿できてしまいます。
共通の約束
以下は下記のすべてのエンドポイントに当てはまります。
- すべてのエンドポイントは POST です。GET・PUT・DELETE はありません——読み取りも POST です。
- Content-Type: application/json を送ってください。パラメーターを取らないエンドポイントでも、空の JSON ボディが必要です。
- すべての id は "kZ3mQ9x" のような hashid 文字列です——コミュニティ・ユーザー・投稿・スペース・メディアいずれも同様。数値 id は拒否されます。
- タイムスタンプは RFC3339 で返ります。例: 2026-08-18T10:00:00Z。
- 金額は最小通貨単位(セント)の整数です。
- 任意の Accept-Language ヘッダー(zh, en, ja, ko, es, fr, de, pt)でエラーメッセージが各言語になります。
- コミュニティは API キーから導かれます。パスにもボディにもサイト id を送らないでください。
1 つだけ例外があります。/post/create と /post/update の scheduled_at は文字列ではなく、秒単位の i64 Unix タイムスタンプです。下の「予約投稿」を参照してください。
成功も失敗も同じエンベロープ
{
"code": 0,
"msg": "success",
"data": { }
}エンドポイント
すべてのエンドポイントは /site_open_api/v1 配下にあり、JSON ボディの POST を使い、キーに記載のスコープを必要とします。
キーのスコープ、API アクセスの有無、割り当ての使用量・残量・リセット時刻を返します。プランのゲートや日次割り当てが他のすべてを塞いでいるときにも答える唯一のエンドポイントで、それ自体は割り当てを消費しません。quota_limit が -1 なら無制限。3 つの割り当てフィールドは揃って現れ揃って消えるので、引き算する前にフィールドの存在を確かめてください。
コミュニティのプロフィール、設定、メタデータ
id で 1 件の投稿を取得
必須: id
一度に最大 50 件。見つからなかった id はバッチ全体を失敗させず missing_ids に入ります
必須: ids
投稿を検索——フィールド名は keyword ではなく query です
必須: query
注目の投稿。スペースで絞り込むことも可能
ある投稿配下のすべての返信
必須: post_id
第 1 階層の返信のみ
必須: post_id
ある返信の下のサブツリー
必須: post_id
ある返信の祖先チェーン
必須: post_id
コミュニティフィード、新しい順(文字列カーソル)。since_id はそれより新しい投稿だけを取得します——編集や削除は見えないので、タイムラインの追記には使えても同期には使えません
スコア順のコミュニティフィード(浮動小数点カーソル)
注目フィード(文字列カーソル)
1 つのスペースのフィード。並び替え・検索・Q&A フィルターは任意(文字列カーソル)
必須: space_id
1 つのスペースのフィード、スコア順(浮動小数点カーソル)——互換のために残されている旧来の入口です
必須: space_id
メンバーとして投稿します。本文、スペース、タイトル、返信・引用先、メディア、投票、音声、予約はいずれも任意
必須: author_user_id, idempotency_key
投稿を編集。version を渡せば楽観ロックになります
必須: actor_user_id, idempotency_key, post_id
投稿を削除
必須: actor_user_id, idempotency_key, post_id
その視点で見えるスペース——viewer_user_id を省略すると匿名視点になり、公開されているものだけが返ります
id で 1 つのスペースを取得
必須: id
id で 1 人のメンバーを取得
必須: id
完全一致のメールアドレスからメンバーを特定
必須: email
メディアアップロードサービス用の短命トークン
必須: author_user_id
非公開メディアの署名付き URL(期限切れになります——保存せず、都度署名し直してください)
必須: author_user_id, items, access_level
curl -X POST https://api.mateflow.com/site_open_api/v1/capabilities/get \
-H "X-API-Key: mfk_xxx" -H "Content-Type: application/json" -d '{}'
# → data: {
# "api_version": "v1",
# "site_id": "kZ3mQ9x",
# "scopes": ["site.read", "content.read"],
# "api_access": true,
# "quota_limit": 5000, // -1 means unlimited
# "quota_used": 128,
# "quota_reset_at": "2026-09-15T00:00:00Z"
# }- id のリストを取る読み取りは 50 件が上限で、ページネーションのある全エンドポイントでも limit の上限は 50 です。
- /space/feed/list のカーソルのエンコードは sort に従うため、sort を変えたらページネーションをやり直す必要があります。Q&A スペースは sort を無視し、代わりに qa_sort で並びます。新規の連携では /space/feed/top ではなく /space/feed/list に sort=top を渡してください。
- メンバーのレスポンスは絞り込まれた投影です。id、username、display_name、avatar_url、status、role、created_at のみ。メールで検索した場合でも、メールアドレスは返りません——そのアドレスは既に手元にあるはずですし、全員分を返せば /member/get は連絡先のエクスポートになってしまいます。検索は完全一致のみで、見つからないのはエラーではなく正常な成功なので、ヒットとミスはステータスコードでも所要時間でも区別できません。レート制限もこの API で最も厳しいものです。
メンバーとして操作する
この API にログインセッションはないため、「誰が操作するか」「誰の視点で結果を返すか」は常にリクエストボディで明示します。3 つともメンバーの hashid を取ります。
author_user_id新しい投稿の作者となるメンバー。すでにこのコミュニティに所属している必要があり、そうでなければ 20303 になります。
actor_user_id編集や削除を実行する人。権限はこのメンバーを基準に判定され、作者でも管理者でもなければ 10004 になります。
viewer_user_id読み取り時は任意。そのメンバーから見えるとおりに結果を返します(非公開スペース、いいねやブックマークの状態など)。省略すると匿名で、公開分のみの視点になります。
ページネーション
- フィードとリストは項目と next_cursor を返します。next_cursor をそのまま送り返せば次のページを取得でき、next_cursor が空なら末尾に達したという意味です。
- limit の上限はどこでも 50 です。
- /space/feed/list のカーソルには現在の sort が埋め込まれています。一覧の途中で sort を変えるとカーソルは無効になります——最初のページからやり直してください。
注意: /feed/top と /space/feed/top のカーソルは文字列ではなく浮動小数点のスコアです。両方を 1 つの文字列変数で受けると、ページ送りが静かに壊れます。
冪等性
すべての書き込みはリクエストボディに idempotency_key を取ります。ここにある冪等性の層はちょうど 1 つです——プラットフォームのヘッダーベースの仕組みをご存じなら、下の注記をご覧ください。
- idempotency_key はすべての書き込みで必須、190 文字までです。同じものを再利用しても 2 つ目の投稿が作られることはありません。
- そのスコープはコミュニティとリクエスト種別とキーの組み合わせで、どの API キーを使ったかには依存しません。同じコミュニティの別々の 2 つのキーが同じ idempotency_key を同じエンドポイントに送った場合、2 回目は 1 回目の結果を返します。
- 同じキーで異なるボディを送ると、成功したリトライとして黙って通るのではなく、安定した競合エラーが返ります。
- リクエストがタイムアウトしたら同じキーで再試行してください——決して新しいキーを生成しないこと。
プラットフォーム共通の Idempotency-Key リクエストヘッダー——24 時間のレスポンスキャッシュを持つあの層——は /site_open_api/** には適用されません。あの層は API キー認証の手前にあるため、キャッシュヒットするとキーの検証そのものが飛ばされてしまうからです。ここでこのヘッダーを送っても何も起きません。
予約投稿
予約は、入力形式も挙動も他と異なる唯一の箇所です。
- scheduled_at は秒単位の i64 Unix タイムスタンプです——他のすべての時刻が使う RFC3339 文字列ではありません。
- /post/update で scheduled_at を省略すると、既存の予約はそのまま残ります。クリアではありません。
0 以下の値を渡すと予約を取り消して投稿を下書きに戻します——ただし下書き状態を持つのはブログ投稿だけです。通常の投稿に同じ呼び出しを行うと 10005 と schedule_cancel_unsupported が返ります。内部の処理が「今すぐ公開」であるためで、受け付ければ内容を予定より早く公開し、フィードと通知を発火させ、取り消す手立てがなくなってしまいます。
メディアのアップロード
ファイルは API ホストではなくメディアサービスに送ります。3 ステップです。
- /media/get_token で API キーを短命のアップロードトークンと交換します。
- ファイルを multipart でメディアサービスに POST します。トークンは文字どおり "token" という名前のヘッダーに入れ、file_cate は Media・Avatar・Header・Audio・File のいずれかにします。
- 投稿を作成するとき、返ってきたメディア id を media_ids に渡します。
破壊的変更: 2026 年 9 月
スコープを明示せずに発行したトークンは、現在アップロード権限のみを持ちます。以前はアップロード・読み取り・削除をまとめて持っていました。そのトークンで読み取りや削除をしていた場合は、read または delete のスコープを明示的に渡してください。また、猶予期間中のコミュニティ——トライアル終了後にカード未登録——はプランのゲートによりアップロードトークンを拒否されます。読み取りと削除のトークンは影響を受けません。
# 1. Exchange the API key for a short-lived upload token.
# Since 2026-09 a token with no explicit scope is upload-only.
curl -X POST https://api.mateflow.com/site_open_api/v1/media/get_token \
-H "X-API-Key: mfk_xxx" -H "Content-Type: application/json" \
-d '{"author_user_id": "kZ3mQ9x", "scope": "upload"}'
# 2. Upload the file to the media service (token goes in a "token" header)
curl -X POST https://media.mateflow.com/api/v1/media/upload \
-H "token: <token from step 1>" \
-F "file_cate=Media" -F "file=@photo.jpg"
# 3. Attach the media id when creating the post
curl -X POST https://api.mateflow.com/site_open_api/v1/post/create \
-H "X-API-Key: mfk_xxx" -H "Content-Type: application/json" \
-d '{"author_user_id":"kZ3mQ9x","idempotency_key":"6f1c...","body":"Hi","media_ids":["m8Yq2Lp"]}'非公開メディアは /media/get_signed_urls で読み取ります。items と access_level(1 公開 / 2 半公開 / 3 非公開)を取り、期限付きの URL を返します。保存せずに必要なときに取得してください。さもないと 403 を返し始めます。
投稿にメディアを添付する
- media_id と alt の組のリストである medias か、id だけのリストである media_ids のどちらかを送ってください。両方を送ると 10005 が返ります。
- alt は任意です。空文字列は既存の代替テキストを消去し、フィールドを省略すると元のまま残ります。
エラーコード
成功も失敗も同じエンベロープを共有し、HTTP ステータスはビジネスコードに連動します。ステータスではなく code で分岐してください。
| コード | HTTP | 意味 |
|---|---|---|
| 0 | 200 | 成功 |
| 10005 | 400 | パラメーターが不正——msg に該当フィールド名が入ります |
| 10007 | 401 | キーが無効、失効済み、期限切れ、または呼び出し元 IP が許可リストにない |
| 21304 | 403 | このエンドポイントに必要なスコープがキーにない。スコープは作成時に固定されます |
| 22203 | 403 | プランに API アクセスが含まれていない——data.required_plan を参照 |
| 22204 | 403 | 日次割り当てを使い切った——data.current と data.limit を参照 |
| 20303 | 404 | author_user_id / viewer_user_id がこのコミュニティのメンバーではない |
| 10003 | 404 | 対象のレコードが存在しない |
| 10004 | 403 | 操作者にこのリソースへの権限がない |
| 10202 | 429 | レート制限——バックオフし Retry-After ヘッダーに従ってください |
| 10001 | 500 | サーバーエラー——再試行して問題ありません |
落とし穴に注意: 22203 と 22204 は 429 ではなく 403 です。レート制限は 10202 だけ。HTTP ステータスではなく code で判定してください——この API で最もよくある読み違いです。
エラーレスポンスには request_id が含まれます。問題を報告するときは添えてください——ログであなたの呼び出しを特定する手がかりです。
プラン関連のエラーには構造化メタデータが付きます
どちらのプランエラーも、コミュニティオーナーに何をすべきか伝えるだけの情報を返します。「何かがうまくいきませんでした」ではなく、実際に役立つメッセージを出せます。
// The plan does not include API access
{
"code": 22203,
"msg": "...",
"request_id": "00de5640-8ae5-4a71-aef8-2f2cab0ce8a7",
"data": {
"error_code": "feature_not_available",
"feature": "api_access",
"required_plan": "Growth"
}
}
// The community is out of daily quota
{
"code": 22204,
"data": {
"error_code": "quota_exceeded",
"resource": "api_requests_per_day",
"current": "5001",
"limit": "5000",
"required_plan": "Business"
}
}割り当てとレート制限
3 つの別々の仕組みがあります。これらを混同することが、実際にはプラン割り当てを使い切っただけの連携が「レート制限された」と報告される原因です。
| 層 | 数える対象 | 超過時 |
|---|---|---|
| プランの日次割り当て | 丸一日、全エンドポイント、コミュニティ単位 | 22204 / HTTP 403 |
| キー単位のバースト予算 | 10 秒と 60 秒のウィンドウ、キー単位、エンドポイント種別ごとの段階 | 10202 / HTTP 429 |
| 一般的な IP 制限 | 送信元 IP 単位、10 秒あたり 60 回・1 分あたり 300 回 | 10202 / HTTP 429 |
日次割り当ては認証を通過したリクエストを数え、UTC 0 時にリセットされ、実際に効いている上限を data.limit で返します——そのコミュニティのために個別に引き上げられた枠も含みます。カウンターのバックエンドに不調があった場合は拒否ではなく通過させるので、たまに数え漏れても割り当てが機能しなくなったわけではありません。
キー単位のバースト予算(2026 年 9 月に追加)
以前は IP 単位のバケツしかなく、1 つの出口アドレスの背後にいる複数の顧客が互いに枠を奪い合う一方、多数のアドレスに分散した呼び出し元はほとんど制約を受けませんでした。今は予算がキー単位で段階付けされます——キーこそコミュニティオーナーが発行し、ローテーションでき、責任を持つ対象だからです。
| 段階 | 対象 | 10 秒あたり | 60 秒あたり |
|---|---|---|---|
| 読み取り | フィード、投稿、返信、スペース、イントロスペクション | 120 | 600 |
| メディア | get_token と get_signed_urls | 30 | 120 |
| 書き込み | 投稿の作成・更新・削除 | 20 | 120 |
| 検索 | post/search | 20 | 60 |
| メンバー検索 | member/lookup | 5 | 10 |
member/lookup が格段に厳しいのは、容量ではなくセキュリティ上の理由です。ほかの制約は「1 回の回答がどれだけ明かすか」を制限しますが、これだけは「何回聞けるか」を制限します——検索を名簿の収集に変えるのは、まさにその一歩だからです。設計どおりの使い方、つまり既に手元にあるアドレスを人間のペースで解決する用途なら、1 分に 10 回で十分すぎます。
- 超過すると HTTP 429 と code 10202 が返り、Retry-After と X-RateLimit-Limit・X-RateLimit-Remaining・X-RateLimit-Reset ヘッダーが付きます。
- これらの数値は実測値ではなく出発点として扱ってください——観測されたトラフィックに応じて調整されます。リトライのロジックに直書きせず、429 と Retry-After に従ってください。レート制限の層は既定で無効で、有効化の前にしばらく観測モードで動きます。
- いずれにせよ 429 のバックオフは実装し、読み取りのトラフィックはおおむね毎秒 10 リクエスト以下に保ってください。
Webhook を受け取る
コミュニティオーナーが Webhook エンドポイントを設定している、あるいは Slack・Discord・Zapier を接続している場合、配信には意味の異なる 2 組の id が含まれます。
| フィールド | 識別するもの | リトライをまたぐと |
|---|---|---|
| event_id | ビジネス上のイベント | 変わらない |
| occurred_at | イベントが起きた時刻 | 変わらない |
| delivery_id | この配信の試行 | 毎回変わる |
| timestamp | この試行が送信された時刻 | 毎回変わる |
重複排除は event_id で行ってください。処理には成功したが応答が遅すぎた配信は、新しい delivery_id と同じ event_id で再び届きます。1 つのイベントがカスタム Webhook と Zap の両方に送られる場合も、双方が同じ event_id を見るので突き合わせられます。
配信は at-least-once で、順序が入れ替わることがあります。重複も、古いイベントの遅着もどちらも正常です。受信側が冪等である必要があります。
event_id は存在しないことがあります。このフィールドが導入される前にキューに入ったジョブは持っておらず、配信は 0 を送るのではなくフィールドごと省略します——0 を送れば、古いジョブがすべて同じイベントに見えてしまうからです。欠けている場合も受け入れ、delivery_id によるベストエフォートの重複排除にフォールバックしてください。
トラブルシューティング
実際に寄せられる症状と、たいていの原因。
| 症状 | たいていは |
|---|---|
| 10007 invalid API key。ただしキーは管理画面からコピーしたばかり | キーの前後に空白か改行が入っている。あるいはキーが失効している。あるいは IP 許可リストが設定されていて送信元アドレスが載っていない |
| 22203 feature_not_available | プランの API アクセスが無効。オーナーが変更した後、プランのキャッシュが切れるまで最大 10 分かかります |
| 22204 quota_exceeded で limit が 0 | プランの日次リクエスト割り当てが 0、つまり利用不可という意味です——「未設定だから無制限」ではありません |
| 動くはずのエンドポイントで 21304 | キーにそのスコープがない。スコープは作成時に固定なので、対処は新しいキーを作ることです |
| 「すべての権限」(*) を持つキーで 21304 | 残存する * は後から追加されたスコープ——media.read、spaces.read、members.read——を含みません。それらを選んで新しいキーを発行してください |
| 10202 と HTTP 429 | レート制限です。Retry-After に従ってバックオフしてください。member/lookup で起きたなら、そのバケツは 1 分に 10 回だと思い出してください |
| 10005 と schedule_cancel_unsupported | 通常の投稿に 0 以下の scheduled_at を渡しました。戻れる下書き状態を持つのはブログ投稿だけです |
| 読み取りや削除に使えていたメディアトークンが使えなくなった | 2026 年 9 月以降、スコープのないトークンはアップロード専用です。read または delete のスコープを渡してください |
| /member/lookup が 200 と found false を返す | これはエラーではなく正常な「見つからなかった」です。ヒットもミスも 200 なので、found で分岐してください |
| query に言及する 10005 invalid params | 検索のフィールドは query です。keyword は有効なフィールドではありません |
| 20303 user not found | その id がこのコミュニティのメンバーでない、または hashid ではなく数値 id を送っています |
| ページ送りが進まなくなる | /feed/top と /space/feed/top のカーソルは浮動小数点です。文字列としてシリアライズすると一致しなくなります |
| しばらくするとメディア URL が 403 を返し始める | 署名付き URL には TTL があります。保存せず、必要なときに署名し直してください |
補足
- コミュニティは API キーから導かれます。パスにもボディにもサイト id を送らないでください。
- スコープはキーの作成時に固定されます。変更するには新しいキーを作り、古いものを失効させてください。
- API キーはサーバー側に置いてください。クライアントコードに置けば、読み取れる人は誰でもあなたのメンバーとして投稿できます。
- OpenAPI 契約はすべてのパスとスキーマを網羅します。1 点だけ注意: 生成元のツールはリクエストボディの required を出力しないため、必須フィールドは仕様ファイルではなくこのページを参照してください。
構築を始める
コミュニティ管理画面でスコープ付きのキーを作り、/capabilities/get で動作を確認したら、あとは書き始めるだけです。
14日間の無料トライアル · クレジットカード不要