このページの内容
連絡先の作成が201を返しても、結果が正しいとは限りません。ソースIDが失われたり、別のリストを選んだり、以前の配信停止を見落としたりするためです。これはHTTPの細部ではなく、データの扱い方の問題です。
最初に動作する書き込みを示し、次に小規模で繰り返し実行できる同期の確認事項を説明します。
Brevo API がカバーする範囲
すべてが単一のホストと単一のバージョンパスの下にあります。開発者向けドキュメントは、この API を 4 つのプロダクト領域に分類しています。
- メッセージング: トランザクションメール、SMS、WhatsApp。バッチ送信、スケジュール送信、メッセージのアクティビティを含みます。
- マーケティングプラットフォーム: コンタクト、リスト、セグメント、メールキャンペーン。
- eコマース: 商品、注文、顧客イベントのトラッキング。
- カンバセーション: チャットウィジェットと、プログラムからの会話管理。
これらの領域は 1 つのアカウント、1 つのコンタクトデータベース、1 つの API キーを共有します。便利である一方で、ときに危険でもあります。検証環境のつもりで書いたスクリプトが、キャンペーンの送信先と同じコンタクトを相手にしているからです。
トランザクションとマーケティングの違い
この 2 つの系統は挙動が大きく異なり、混同することが最も多い設計ミスにつながります。
| トランザクション | マーケティング | |
|---|---|---|
| 主なエンドポイント | POST /v3/smtp/email | POST /v3/emailCampaigns |
| 宛先の指定 | リクエスト内で受信者を明示 | listIds または segmentIds |
| きっかけ | アプリケーションからリアルタイムで | スケジュール、または任意のタイミングで送信 |
| 典型的な流量の形 | 連続的、1 通ずつ | 突発的、1 回の大量送信 |
| レート制限の余裕 | 非常に大きい。general 階層で毎秒 1,000 リクエスト | 小さい。キャンペーン系は一般上限の対象 |
そもそも Brevo が適したプラットフォームかどうかを検討中であれば、プラットフォームの概要がその範囲を扱っています。
認証とキー管理
Brevo はカスタムヘッダーに素の API キーを入れる方式です。ヘッダー名は Authorization ではなく api-key で、Bearer の接頭辞もありません。他のメッセージング API を先に触った人はほぼ全員がここでつまずきます。
対応する操作ではOAuthのBearerトークンも使えます。通常のAPIキーは引き続きapi-keyヘッダーに入れます。
curl https://api.brevo.com/v3/account \ -H "api-key: $BREVO_API_KEY"キーは Brevo アプリのアカウント設定内、SMTP and API セクションの API keys タブで生成します。キーごとに、それを使うシステムがわかる名前を付けてください。キーの値は生成時に一度だけ表示されるため、紛失した場合は復旧ではなく新規発行になります。
実務上のルールをいくつか挙げます。
- デプロイ先ごと、サービスごとに別のキーを発行しましょう。漏えいしたキーの失効が、無関係な 3 つのシステムを止めることがあってはいけません。
- 標準の API キーはアカウント全体に及びます。どのキーも、コンタクト、送信、CRM データへのフルアクセスだと考えてください。
- Brevo は、他の Brevo アカウントを代理して動作するアプリケーション向けに OAuth 2.0 にも対応しています。詳細はキーのフローと並べて認証方式に記載されています。
- AI アシスタントが使う MCP サーバーは別のトークンを使い、こちらは bearer ヘッダーを利用します。そのトークンは同じ API keys 画面で生成しますが、REST 用のキーとは互換性がありません。
ベース URL、バージョン、最初の書き込み
ベース URL は https://api.brevo.com/v3/ です。バージョンはヘッダーではなくパスに含まれ、現行世代は v3 です。このガイドのパスはすべてこのベースからの相対です。
最初の書き込みには、自分で管理するテスト用メールアドレス、有効なリストID、安定したソースIDを使用します。Brevoで先にFNAME属性を作成してください。実際のキーを使うと、この例は直接書き込みます。
curl -X POST https://api.brevo.com/v3/contacts \ -H "api-key: $BREVO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "email": "[email protected]", "ext_id": "test-source-contact-1", "attributes": { "FNAME": "Dev" }, "listIds": [42], "updateEnabled": true, "getId": true }'42をテストリストのIDに置き換えてください。連絡先を読み直し、ID、名前、リスト、ブロック状態を確認します。2xxだけでは期待した結果を証明できません。
実際に使うことになるエンドポイント
コンタクト
POST /v3/contactsで連絡先を作成します。本文にはemail、定義済みのattributes、listIds、安定したソースキーのext_idを指定します。updateEnabled: trueは既存の連絡先を更新し、getId: trueは最終IDを要求します。Brevoがこれらのフィールドを説明しています。実行ごとに同じext_idを使い、ソースIDとBrevo IDの対応を保存してください。
書き込む前に、各属性を管理するシステムと、同意した連絡先を入れるリストを決めます。listIdsを明示し、通常のupsertでemailBlacklisted: falseを送らないでください。Brevoは、ブロックされた連絡先のメールアドレスを変更するとブロックが解除される可能性を示しています。アドレス変更は別に扱います。リストへの追加前にブロックと配信停止を確認してください。読み取りと書き込みの間に状態が変わる可能性があり、厳密な保護には送信直前の再確認が必要です。
読み取りは GET /v3/contacts を通します。limit(既定 50、最大 1000)と offset でページングし、UTC の modifiedSince と createdSince に対応しています。差分同期では全件を歩くのではなく modifiedSince に寄せましょう。なお filter パラメーターは等価演算子しかサポートしないため、それ以上の表現力が必要ならセグメント側で扱います。
一括投入には POST /v3/contacts/import を使います。fileUrl、fileBody、jsonBody を受け取り、listIds を対象にして非同期で動作し、processId を返します。Brevo はボディの上限を 10 MB と定めており、解析時にペイロードが膨らむため 8 MB 前後に抑えることを推奨しています。ポーリングせずに結果を受け取れるよう notifyUrl を指定してください。
トランザクションメール
POST /v3/smtp/email が主力です。sender、to、subject、htmlContent に加えて、知っておく価値のあるフィールドは次のとおりです。
templateIdとparams。インラインのコンテンツを Brevo のテンプレートと変数置換に置き換えます。バージョンごとの params は 100 KB、累計では 1000 KB が上限です。messageVersions。1 回の呼び出しでパーソナライズした複数のバリエーションを送れます。1 バージョンあたり最大 99 名です。tagsは、配信イベントを発生元のアプリケーション処理と結び付ける助けになります。scheduledAtとbatchId。グループ単位でキャンセルする可能性がある将来の送信に使います。headers。カスタム SMTP ヘッダーを Title-Case で指定します。
1 リクエストあたりの受信者は最大 2,000 名です。このエンドポイントとキャンペーン送信の違いについては、トランザクションメールのガイドがメッセージング戦略の観点から解説しています。
メールキャンペーン
POST /v3/emailCampaigns には name と sender、そしてコンテンツの供給元をちょうど 1 つ指定します。htmlContent(最小 10 文字、1 MB 未満)、htmlUrl、templateId のいずれかです。配信対象は recipients に listIds または segmentIds として渡し、scheduledAt は YYYY-MM-DDTHH:mm:ss.SSSZ の UTC 形式を使います。付随するルートとして、即時送信、テスト送信、ステータス更新、キャンペーンレポートの取得があります。
会社、取引、オブジェクト
Brevo の CRM には書き込み経路が 2 つあり、部分的に重なっています。どちらを選ぶかが重要です。
CRM のルートは POST /v3/companies、PATCH /v3/companies/{id}、DELETE /v3/companies/{id} と、取引向けの同等の一式です。これらは同期的です。PATCH は変更が適用された時点で 204 を返します。
オブジェクト API は一括処理の経路です。POST /v3/objects/{object_type}/batch/upsert は 1 リクエストあたり最大 1000 レコード、1 MB、1 レコードあたり最大 500 属性、オブジェクト型ごとに最大 10 件の関連レコードを受け取ります。返るのは processId 付きの 202 で、適用済みではなく受理済みという意味です。
curl -X POST https://api.brevo.com/v3/objects/company/batch/upsert \ -H "api-key: $BREVO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "records": [ { "identifiers": { "id": 12345 }, "attributes": { "domain": "acme.example", "industry": "retail" } } ] }'Brevo CRM ガイドは、このオブジェクトモデルを運用者の視点から扱っています。
公式 SDK
Brevo は getbrevo という GitHub 組織でクライアントを保守しています。
| 言語 | リポジトリ |
|---|---|
| Node.js | github.com/getbrevo/brevo-node |
| Python | github.com/getbrevo/brevo-python |
| PHP | github.com/getbrevo/brevo-php |
| Java | github.com/getbrevo/brevo-java |
| C# | github.com/getbrevo/brevo-csharp |
| Go | github.com/getbrevo/brevo-go |
| Ruby | github.com/getbrevo/brevo-ruby |
Node のクライアントは @getbrevo/brevo としてインストールします。
npm install @getbrevo/brevoimport { BrevoClient } from "@getbrevo/brevo";
// Reconcile uncertain send outcomes before retrying.const brevo = new BrevoClient({ apiKey: process.env.BREVO_API_KEY, maxRetries: 0 });
const result = await brevo.transactionalEmails.sendTransacEmail({ subject: "Order confirmed", htmlContent: "<html><body><p>Thanks for your order.</p></body></html>", tags: ["order-confirmation"],});
console.log("Message ID:", result.messageId);Python のクライアントは pip install brevo-python で導入します。エンドポイント 2 つのために SDK の依存を抱えたくない場合は、生の HTTP を直接叩いても十分小さな面積です。SDK のバージョン変動から切り離せるという利点もあります。
Pythonクライアントはpip install brevo-pythonで導入できます。少数のエンドポイントならHTTPを直接呼び出せます。
import osimport requests
BASE = "https://api.brevo.com/v3"HEADERS = {"api-key": os.environ["BREVO_API_KEY"]}
def upsert_consented_contact(source_id, email, first_name, list_id, consent): if not consent: raise ValueError("No marketing consent in the source") if not source_id or not isinstance(list_id, int): raise ValueError("Stable source ID and numeric list ID required") # Configure FNAME in Brevo before using this explicit field mapping. response = requests.post( f"{BASE}/contacts", headers=HEADERS, json={ "email": email, "ext_id": source_id, "attributes": {"FNAME": first_name}, "listIds": [list_id], "updateEnabled": True, "getId": True, }, timeout=30, ) response.raise_for_status() return response.json().get("id") if response.content else None成功した更新が本文のない204を返す場合があります。この場合、NoneはIDを得るために連絡先を読み直す必要があることを意味します。
この例は、呼び出し側が同意とBrevoのブロック状態を確認済みと想定します。ブロック解除はしません。実キーではアカウントに書き込むため、まずモック応答で試してください。ID競合はさまざまな4xxになる可能性があります。常に409と決めつけず、応答を読んでIDを照合します。
Webhook
Webhook は、送信後に何が起きたかを知るための仕組みです。POST /v3/webhooks で作成し、url、events、type と、任意で channel(email または sms)、batched、カスタムの headers、auth オブジェクトを指定します。
Webhook には 3 つの種類があり、それぞれイベントの語彙が異なります。
- トランザクション:
sent、request、delivered、hardBounce、softBounce、blocked、spam、invalid、deferred、click、opened、uniqueOpened、unsubscribed。 - マーケティング:
spam、opened、click、hardBounce、softBounce、unsubscribed、listAddition、delivered、contactUpdated、contactDeleted。 - 受信:
inboundEmailProcessedとreply。これらは追加でdomainが必要です。
curl -X POST https://api.brevo.com/v3/webhooks \ -H "api-key: $BREVO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://yourapp.example/hooks/brevo", "type": "transactional", "events": ["delivered", "hardBounce", "spam", "unsubscribed"], "description": "Deliverability signals" }'連絡先の変更にはcontactUpdatedを含むマーケティングWebhookを登録します。Brevoのペイロードではイベント名がcontact_updatedです。セキュリティガイドに従い、authのBearerトークンまたは独自ヘッダーで受信リクエストを検証します。処理前に秘密情報を検証し、ログに残さないでください。
イベント種別、連絡先ID、時刻から重複排除キーを作り、再配信期間中は保存します。時刻だけでは一意になりません。イベントは重複したり順序が入れ替わったりします。現在の連絡先を読み直し、信頼できるソースと照合してください。再生期間を限定し、復旧後は古いイベントを拒否し、定期的な増分確認で欠落を補います。Webhookだけでは双方向同期になりません。コネクターガイドも参照してください。
レート制限とエラー処理
Brevo のレート制限はエンドポイントごと、プラン階層ごとに定められており、エンドポイント間の開きは非常に大きくなっています。
| エンドポイント | General(すべてのプラン) | Advanced(Professional と Enterprise) |
|---|---|---|
POST /v3/smtp/email | 1,000 RPS | 2,000 RPS |
POST /v3/transactionalSMS/send | 150 RPS | 200 RPS |
/v3/contacts/... | 10 RPS、36,000 RPH | 20 RPS、72,000 RPH |
POST /v3/events | 10 RPS、36,000 RPH | 20 RPS、72,000 RPH |
GET /v3/smtp/emails | 2 RPS、7,200 RPH | 3 RPS、10,800 RPH |
| 独自の上限がないエンドポイント | 100 RPH | 200 RPH |
Brevoは制限対象のエンドポイントにx-sib-ratelimit-limit、x-sib-ratelimit-remaining、x-sib-ratelimit-resetを記載しています。成功時にも確認します。429の後はリセットを待ち、固定した回数と時間の範囲でのみ再試行します。resetは制限ウィンドウの単位による時間の長さで、通常は秒です。Unix時刻ではありません。
async function callBrevo(path, init) { for (let attempt = 0; attempt < 3; attempt++) { const response = await fetch(`https://api.brevo.com/v3${path}`, { ...init, headers: { "api-key": process.env.BREVO_API_KEY, ...init.headers }, }); if (response.status !== 429 || attempt === 2) return response; const raw = Number(response.headers.get("x-sib-ratelimit-reset")); const seconds = Number.isFinite(raw) && raw > 0 ? raw : 2 ** attempt; const delayMs = (seconds + 2 ** attempt) * 1000; if (delayMs > 60_000) return response; await new Promise(resolve => setTimeout(resolve, delayMs)); }}必要な待機が60秒を超えると、関数は429を返します。次の試行は後で実行するよう予定してください。
ヘルパーは、安全に再実行できると確認したリクエストだけに使ってください。送信結果が不明な場合、再送前に自分のメッセージIDで照合します。状態と制限ヘッダーを記録し、APIキーや連絡先情報を記録しないでください。
4xxを無条件で再試行しません。連絡先作成のリファレンスは400と425を挙げており、競合が必ず409になるわけではありません。書き込み後の5xxでは、変更が適用済みか先に確認します。
返されたステータスは?
-
429
予算内で待つ
リセット時間を守り、少ない試行回数で止めます。
-
書き込み後の5xx
結果を確認
エラー前に書き込まれた可能性があります。
-
400
リクエストを修正
入力が誤っていることが多いです。
-
IDの競合
IDを照合
決まった競合コードは保証されません。
メールを送らずにテストする
トランザクション送信に X-Sib-Sandbox ヘッダーを値 drop で追加します。Brevo はリクエストを検証して messageId 付きの 201 を返しますが、何も配信せず、メールログにも記録しません。
curl -X POST https://api.brevo.com/v3/smtp/email \ -H "api-key: $BREVO_API_KEY" \ -H "X-Sib-Sandbox: drop" \ -H "Content-Type: application/json" \ -d '{ "sender": { "email": "[email protected]" }, "to": [{ "email": "[email protected]" }], "subject": "Sandbox", "htmlContent": "<p>hi</p>" }'これが何を証明し、何を証明しないかを理解しておきましょう。サンドボックスモードが検証するのはリクエストの形式だけです。送信者認証、テンプレートのレンダリング、到達性については何も語りません。コンタクトや CRM データに触れるものの結合テストには、別の Brevo アカウントを用意してください。サンドボックスモードがカバーするのは送信であって、API の残りではありません。
連絡先同期前の確認
ext_idに安定したソースIDを指定し、Brevo IDを保存します。- 属性を先に定義し、テスト連絡先を書き込んだ後に読み直します。
- 明示したリストと信頼できる同意記録を使い、ブロックや配信停止を除きます。
- 受付、適用、スキップ、失敗を別々に数えます。202はまず受付のみを示します。
- マーケティングWebhookを認証・重複排除してから状態を照合します。
- 制限ヘッダーを監視し、上限で再試行を止めます。
よくある質問
Brevo API のベース URL は何ですか。
Brevo の REST 呼び出しはすべて https://api.brevo.com/v3/ に送ります。バージョンはパスの一部で、現行世代は v3 です。したがってトランザクション送信のようなエンドポイントは、フルパスで https://api.brevo.com/v3/smtp/email になります。
Brevo API ではどのように認証しますか。
api-key という名前の HTTP ヘッダーにキーを入れて送ります。標準の API キーでは Authorization ヘッダーや Bearer ヘッダーは使いません。キーはアカウント設定の SMTP and API にある API keys で生成し、値は一度しか表示されません。
Brevo API のレート制限はどうなっていますか。
制限はエンドポイントごと、レート制限の階層ごとに設定されています。すべてのプランに適用される general 階層では、トランザクション送信のエンドポイントが毎秒 1,000 リクエスト、コンタクト系のエンドポイントが毎秒 10 リクエスト、独自の上限がないエンドポイントは 1 時間あたり 100 リクエストが上限です。Professional と Enterprise プランでは advanced 階層を、Enterprise では extended 階層を利用できます。上限を超えると 429 が返ります。
Brevo に公式 SDK はありますか。
あります。Brevo は getbrevo という GitHub 組織で Node.js、Python、PHP、Java、C#、Go、Ruby のクライアントを公開しています。Node のパッケージは npm の @getbrevo/brevo、Python のパッケージは PyPI の brevo-python です。
実際のメールを送らずに Brevo API をテストするにはどうすればよいですか。
トランザクション送信に X-Sib-Sandbox ヘッダーを値 drop で追加します。Brevo はリクエストを検証して messageId 付きの 201 を返しますが、何も送信せず、メールログにも記録しません。検証されるのはリクエストの形式だけで、到達性は対象外です。
Brevo はどんな Webhook イベントに対応していますか。
トランザクションWebhookは配信、バウンス、配信停止を扱います。マーケティングWebhookにはcontactUpdatedとcontactDeletedがあります。受信イベントにはinboundEmailProcessedが記載されています。
連絡先の更新で以前の配信停止を守るには?
安定したソースIDと定義済み属性を使います。通常の更新でemailBlacklisted: falseを送らないでください。マーケティングリストに加える前に、連絡先の状態と同意の記録を確認します。
Webhookだけで双方向同期できますか?
できません。受信側を認証し、重複配信を除き、欠落や順序のずれたイベントを信頼できるソースと照合します。Webhookは変更の合図であり、両システムの状態一致を証明しません。