業務システム

LINE Messaging APIのプッシュメッセージ送信:push・multicastの実装とリトライキー

LINE Messaging APIのプッシュメッセージ送信:push・multicastの実装とリトライキー

LINE Messaging APIのプッシュメッセージは、ユーザーの発言を待たずに、サーバー側の都合で任意のタイミングにLINE公式アカウントからメッセージを送る仕組みです。この記事では、チャネルアクセストークンと宛先ユーザーIDの準備から、curlとPythonで /v2/bot/message/push を呼ぶコード、500人ずつに分けるmulticast、受付後に進行状況を追うnarrowcastまでを順に実装します。後半で扱うのは、X-Line-Retry-Keyを使った二重送信の防止、409・429・500の判定、2026年10月改定後の単価での通数コストの見積もりです。Messaging APIそのものの概要や料金プランの選び方は、LINE Messaging APIのできることと料金を整理した記事にまとめています。

まとめ:宛先の数で送信APIを決め、全リクエストにリトライキーを付ける

1人や1グループへの通知はpush、友だちのうち特定の数百〜数万人へ同じ内容を送るならmulticastを500人ずつ、属性やオーディエンスで絞るならnarrowcast、友だち全員ならbroadcastを使います。どれも1リクエストで送れるメッセージは5件までで、通数は送信対象の人数で数えます。

本番の送信処理で外せないのは、送信ジョブの作成時にUUIDを1つ発行してX-Line-Retry-Keyに入れ、500とタイムアウトのときだけ同じキーで再送することです。409は「受理済み」なので成功として扱い、429は月の通数上限かレート制限かを切り分けてから再実行します。レスポンスのx-line-request-idは毎回ログに残してください。

予約や注文など業務システムのイベントを起点に1人ずつ通知するなら自前実装が向きます。セグメント配信と開封の分析が主目的なら、pushを自作せずLINE Official Account Managerや配信ツールで足ります。

プッシュメッセージ送信前に用意するアクセストークンと宛先ユーザーID

長期・v2.1・ステートレスの3種類から送信処理に合うトークンを選ぶ

プッシュメッセージのリクエストには、Authorizationヘッダに Bearer {channel access token} の形でチャネルアクセストークンを入れます。LINE Developersのチャネルアクセストークンの説明によると、種類ごとに有効期間と発行できる数が違います。

種類 有効期間 発行可能数
長期 無期限 1
任意の有効期間(v2.1) 最大30日 30
短期 30日 30
ステートレス 15分 無制限

検証段階なら、コンソールの[Messaging API設定]タブで発行する長期トークンで動かせます。本番では、JWTで発行するv2.1を期限前に自動で再発行する構成が無難です。ステートレストークンは発行後に取り消せないため、漏えい時の止め方を先に決めておきます。Bearerヘッダの書き方そのものはAuthorization: Bearerの書き方とJWTとの違いを解説した記事で確認できます。

Webhookのsource.userIdを保存して送信先リストを作る手順

pushの宛先 to に入れるのは、LINEアプリに表示されるLINE IDではなく、Webhookイベントで届く userId・groupId・roomId です。ユーザーIDの取得方法は4通りありますが、一般アカウントで使えるのは実質Webhookからの取得だけです。友だち全員のID一覧を返すAPIは、認証済アカウントかプレミアムアカウントに限られます。

実装では、友だち追加(follow)イベントを受けた時点で source.userId を会員テーブルに保存し、ブロック(unfollow)イベントで送信対象から外します。同じ人でもプロバイダーが違えばユーザーIDは別の値になるので、チャネルを別プロバイダーへ作り直すと保存済みIDは使えません。受信側で署名を検証し、同じイベントを二重に処理しない作りはWebhookの署名検証と冪等性をNode.js・Pythonで実装した記事の方法がそのまま当てはまります。

curlとPythonでpushエンドポイントから1人宛てに送る手順

curlでpushをPOSTしsentMessagesで送信結果を確認

まずはcurlで1通送り、トークンと宛先が正しいかを確かめます。リファレンスのプッシュメッセージの項どおり、ボディは to と messages の2つが必須です。

curl -i -X POST https://api.line.me/v2/bot/message/push \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer ${LINE_CHANNEL_ACCESS_TOKEN}" \
  -H "X-Line-Retry-Key: $(uuidgen)" \
  -d '{
    "to": "U4af4980629...",
    "messages": [
      {"type": "text", "text": "ご予約は明日10:00です。変更はメニューから受け付けます。"}
    ],
    "notificationDisabled": false
  }'

成功すると200とともに {"sentMessages":[{"id":"...","quoteToken":"..."}]} が返ります。-i を付けているのは、レスポンスヘッダの x-line-request-id を目で確かめるためです。テキストは1件あたり5,000字まで、配列 messages は最大5件まで入れられます。リクエスト全体が2MBを超えると413で弾かれます。

Python標準ライブラリでリトライキー付きの送信関数を書く例

SDKを入れなくても、HTTPを1本投げるだけなので標準ライブラリで書けます。リトライキーは関数の中で作らず、呼び出し側から渡す形にしました。理由は次の章で説明します。

import json
import time
import urllib.error
import urllib.request

API = "https://api.line.me/v2/bot/message"
TOKEN = "..."  # 環境変数やシークレット管理から読み込む


def _post(path, body, retry_key):
    req = urllib.request.Request(
        f"{API}/{path}",
        data=json.dumps(body).encode("utf-8"),
        method="POST",
        headers={
            "Content-Type": "application/json",
            "Authorization": f"Bearer {TOKEN}",
            "X-Line-Retry-Key": retry_key,
        },
    )
    return urllib.request.urlopen(req, timeout=10)


def send(path, body, retry_key, max_attempts=5):
    """push・multicast・broadcast・narrowcast 共通の送信。500とタイムアウトだけ同じキーで再送する"""
    for attempt in range(max_attempts):
        try:
            with _post(path, body, retry_key) as res:
                return res.status, res.headers.get("x-line-request-id"), res.read()
        except urllib.error.HTTPError as e:
            if e.code == 409:  # 同じキーのリクエストが受理済み=送信は成功している
                return 409, e.headers.get("x-line-accepted-request-id"), e.read()
            if e.code != 500:
                raise  # 400・401・403・429 は同じ内容のまま再送しない
        except (urllib.error.URLError, TimeoutError):
            pass  # タイムアウト・接続断は再送の対象
        time.sleep(min(2 ** attempt, 30))  # 指数バックオフ
    raise RuntimeError("再送の上限に達した")

呼び出しは send("push", {"to": user_id, "messages": [...]}, job.retry_key) です。LINE公式のline-bot-sdk-python(PyPIで3.27.0・2026年10月27日公開、Python 3.10以上)を使う場合は、push_message_with_http_info のように _with_http_info が付く関数を呼ぶと、ステータスコードとヘッダを受け取れます。

200が返っても届かない3種類の宛先とnotificationDisabledの使い所

pushは、送信処理が受理されれば200を返します。ただしリファレンスには、200でも実際には届かない宛先が3つ挙がっています。LINEアカウントを削除した人、そのLINE公式アカウントをブロックしている人、友だち追加していない人です。

例外として、友だちでない人でも、1対1のトークで公式アカウントへメッセージを送ってから7日以内なら送信できます。「200なら届いた」とは判断できないため、到達率を見たいときは後述の統計APIを使います。notificationDisabled を true にすると通知音とバナーが出ません。夜間に流す処理完了の連絡など、急ぎでない通知に向いた設定です。

push・multicast・broadcast・narrowcastの宛先とレート制限

宛先の指定方法とレート制限で比べる4つの送信APIの仕様一覧

リファレンスのレート制限の表と各エンドポイントの説明から、送信系の4 APIを並べました。

API 宛先 レート制限 成功時
push 1ユーザー・1グループ 2,000リクエスト/秒 200と送信ID
multicast ユーザーID最大500件 200リクエスト/秒 200と空JSON
narrowcast 属性・オーディエンス 60リクエスト/時 202と空JSON
broadcast 友だち全員 60リクエスト/時 200と空JSON

レート制限はチャネルごと・エンドポイントごとに、トークンバケット方式でかかります。決まった時刻に一斉リセットされるのではなく、消費したぶんが時間とともに少しずつ戻る方式です。同じ公式アカウントでも、チャネルが違えば別々に数えます。multicastとbroadcastはグループトークへ送れないため、グループ宛てはpushを1件ずつ送ります。

multicastを500人ずつ分割し、分割ごとに別のリトライキーを振る

3,000人へ同じ案内を送るなら、multicastを6回呼びます。ここで落とし穴になるのが、リトライキーの発行場所です。送信関数の中で毎回UUIDを作ると、プロセスが落ちて再起動したときに新しいキーで送り直し、受理済みの分割まで二重に届きます。分割を作った時点でキーを決め、ジョブと一緒に保存しておけば、何度再実行しても409で止まります。

import uuid

def enqueue_multicast(user_ids, messages, db):
    for i in range(0, len(user_ids), 500):  # multicast の to は最大500件
        db.insert("send_jobs", {
            "path": "multicast",
            "body": {"to": user_ids[i:i + 500], "messages": messages},
            "retry_key": str(uuid.uuid4()),  # 分割ごとに1つ。再実行でも同じ値を使う
            "status": "queued",
        })

キーの有効期間は最初のリクエストから24時間です。それより後に同じキーで送ると、二重送信は防げません。翌日に持ち越したジョブは、送信済みかどうかを自前の記録で確かめてから扱います。再試行のガイドにあるとおり、キーは重複を防ぐためのもので、ユーザーへの配信までは保証しません。

narrowcastは202で受付、進行状況APIのphaseで完了を確かめる

narrowcastは非同期で処理され、受け付けた時点では202と空のJSONしか返りません。結果を知るには、レスポンスヘッダの x-line-request-id を控えておき、進行状況を取得するAPIへ requestId として渡します。

curl -s "https://api.line.me/v2/bot/message/progress/narrowcast?requestId=${REQUEST_ID}" \
  -H "Authorization: Bearer ${LINE_CHANNEL_ACCESS_TOKEN}"

phase は waiting、sending、succeeded、failed の4値です。succeededでも failureCount が0でなければ、配信中にブロックした人など受け取れなかった人がいます。failedのときは errorCode を見ます。2は送信対象が少なすぎる、4は50人未満のオーディエンスを含む、という意味です。属性やオーディエンスで絞る場合、最終的な送信対象は50人以上が必要で、下回っても受付時は202が返り、送信開始時に失敗します。進行状況を取れるのは受付から14日までです。

409・429・500を区別して再送する処理とログに残すリクエストID

同じキーで再送する500・タイムアウトと、再送しない409・400系

再試行のガイドが再送の対象にしているのは、500とタイムアウトの2つだけです。200番台、409、それ以外の400番台は再送しません。

  1. 500またはタイムアウト:同じリトライキー・同じボディで、指数バックオフをかけて再送する
  2. 409:同じキーのリクエストが受理済み。成功として記録し、x-line-accepted-request-id を保存する
  3. 400:宛先IDやメッセージオブジェクトの誤り。直すまで送らない
  4. 401:トークンの期限切れや取り消し。トークンを再発行してから新しいジョブとして扱う

pushで409が返った場合、ボディには受理時と同じ sentMessages のIDも入ります。引用トークンをあとで使う処理でも、409を失敗扱いにしないことが肝心です。再送もリクエスト1回として数えるので、間隔を空けずに繰り返すとレート制限に引っかかります。

429は月の通数上限とレート制限の2系統、quota APIで事前に切り分け

429の原因は、ステータスコードの説明によるとレート制限の超過、同時処理数の超過、無料メッセージ通数の超過、追加メッセージ上限目安の超過の4つです。レート制限なら数秒〜数分待てば通りますが、通数の上限なら月が替わるか上限を引き上げるまで通りません。同じ待ち方をすると、通数切れのときにキューが詰まり続けます。

大量送信の前には、当月の上限目安と利用数を取って比べておきます。

curl -s https://api.line.me/v2/bot/message/quota \
  -H "Authorization: Bearer ${LINE_CHANNEL_ACCESS_TOKEN}"
# {"type":"limited","value":1000}

curl -s https://api.line.me/v2/bot/message/quota/consumption \
  -H "Authorization: Bearer ${LINE_CHANNEL_ACCESS_TOKEN}"
# {"totalUsage":500}

type がnoneなら上限目安は未設定です。どちらの値にもLINE Official Account Managerから送った分が含まれ、利用数は概算とされています。残数があるのに「You have reached your monthly limit.」が返る場合は、narrowcastが送信前に通数の枠を予約している可能性があります。予約を避けたいなら、narrowcastの limit.upToRemainingQuota を true にして、送信数を残数の範囲に収める設定が有効です。

x-line-request-idと送信時刻を記録し、問い合わせに答えられる形に

「通知が来ていない」と問い合わせを受けたとき、LINE側へ調査を頼むにも手元の記録が要ります。Messaging API開発ガイドラインは、送信時のログとして次の5項目を挙げています。

  • レスポンスヘッダの x-line-request-id
  • APIリクエストを行った時刻
  • HTTPメソッド
  • 呼び出したエンドポイント
  • 返ってきたステータスコード

実務ではこれにリトライキーと送信ジョブのIDを足し、1つのジョブに何回のリクエストが紐づいたかを追えるようにします。同じキーで再送した場合でも、リクエストIDはリクエストごとに異なる別の値です。リクエストボディにはユーザーIDやメッセージ本文が入るため、保存するなら保持期間と閲覧権限を決めてからにします。

送信対象の人数で数える通数と2026年10月改定後のコスト試算

通数は宛先の人数で決まり、応答メッセージは通数に数えない仕組み

メッセージ送信の説明によると、通数は送信対象になった人数で数えます。1リクエストに入れたメッセージオブジェクトの件数は関係なく、4件のメッセージを5人へpushすれば5通です。ブロック中のユーザーや存在しないユーザーIDは数えません。

Messaging APIの料金のページでは、push・multicast・broadcast・narrowcastが通数に入り、応答メッセージ(reply)は入りません。ユーザーの発言を受けたWebhookの処理中に返すなら、pushではなくreplyTokenを使った応答で送れば通数を消費しません。予約日の前日リマインドのように、こちらから先に送る通知だけがpushの出番です。

月15万通を送るスタンダードプランで追加メッセージ料金を試算

LINE公式アカウントの料金プランは、税別でコミュニケーションプランが月0円・無料200通、ライトプランが月5,000円・5,000通、スタンダードプランが月15,000円・30,000通です。無料通数を超えて送れるのはスタンダードプランだけです。2026年10月1日の追加メッセージ料金改定で、日本の追加メッセージは20万通/月までが1通3円、それを超えた分が1通2.5円になりました(2026年11月時点の公表値)。

会員3万人に予約確認・前日リマインドなどを月5回pushする場合、月15万通です。無料の30,000通を引いた12万通が追加メッセージになり、12万通×3円で36万円、月額と合わせて税別37.5万円です。人数を絞れない一斉送信を減らし、同じ用件はreplyで返す設計にすると、そのまま通数が減ります。LINE Notifyからの置き換えで社内通知を送っている場合の試算は、LINE Notify終了後のMessaging APIへの移行と通知コストの記事で扱っています。

プッシュ送信を自前で実装する条件と、配信ツールに任せる場面の判断基準

業務システムのイベント起点で顧客へ個別通知するなら自前実装を選ぶ

予約の確定、発送完了、支払い期限の前日など、業務データの変化を起点に1人ずつ違う内容を送るなら、pushを自前で実装する価値があります。宛先・本文・送信時刻がすべて自社のデータベースで決まり、管理画面からの手作業では回らないからです。予約台帳とLIFFを組み合わせた構成はLINE予約システムをLIFFとMessaging APIで自作する記事が具体例になります。

このとき作り込むのは、送信APIの呼び出しよりも周辺です。送信ジョブのキュー、リトライキーの保存、409の成功扱い、429の切り分け、follow・unfollowイベントによる宛先の更新、トークンの自動再発行。この6つを揃えて初めて本番運用に耐えます。会員基盤や予約システムとLINEの連携をまとめて任せたい場合は、一創のLINEミニアプリ開発でMessaging APIの送信処理を含めて設計から相談できます。

セグメント配信と効果測定が主目的ならpushを自作しない判断

反対に、キャンペーン告知を属性で絞って送り、開封やクリックを見たいだけなら、pushやnarrowcastを自作する必要はありません。LINE Official Account Managerの配信機能で絞り込みと予約配信ができ、統計も画面で見られます。自作すると、オーディエンス管理と50人未満の制限、60リクエスト/時のレート制限まで自分で扱うことになります。

判断の目安は、送信内容が1人ごとに変わるかどうかです。全員に同じ文面なら管理画面、1人ごとに予約日時や注文番号が入るならAPI。両方が必要な場合も、告知は管理画面に残し、個別通知だけをAPIへ切り出す分け方で十分です。送信ごとの到達数を集計したいなら、customAggregationUnits にユニット名(最大30字・1リクエストに1つ・月1,000種類まで)を付けて送ると、ユニット単位の統計を取得できます。

よくある質問

LINE Messaging APIでプッシュメッセージを送る実装について、つまずきやすい質問をまとめました。

Webhookを受けた処理の中で返信するなら、pushとreplyのどちらを使いますか?

replyを使います。Webhookイベントに含まれるreplyTokenで送る応答メッセージは通数に数えられないため、同じ内容をpushで送るより費用がかかりません。pushを使うのは、ユーザーの発言を待たずにこちらから送るときです。たとえば、注文を受けた直後の「受け付けました」はreplyで返し、発送完了の連絡は後日pushで送る、という分け方になります。

LINE IDや電話番号を宛先にしてプッシュメッセージを送れますか?

送れません。pushの to に入れられるのは、Webhookイベントで受け取る userId・groupId・roomId だけです。LINEアプリに表示されるLINE IDとは別物で、そのまま宛先には使えません。友だち追加のイベントを受けた時点でユーザーIDを保存し、自社の会員IDと紐づける仕組みを先に作ります。

友だち追加していない人にもプッシュメッセージは届きますか?

原則として届きません。送信処理をしても200は返りますが、実際には配信されない扱いです。例外は、1対1のトークでその人が公式アカウントへメッセージを送ってから7日以内の場合で、この期間だけは友だちでなくても送れます。multicastは友だちだけが対象なので、この例外は当てはまりません。

1回のリクエストで何件のメッセージを送れますか?

push・multicast・broadcast・narrowcastのいずれも、messages 配列に入れられるのは最大5件です。テキストメッセージは1件あたり5,000字までで、リクエスト全体のサイズは2MBを上限としています。5件まとめて送っても、通数は送信対象の人数で数えるので、1人に5件送れば1通です。6件以上送りたいときはリクエストを分けますが、その場合は2通分になります。

LINE WORKSのBotから送るメッセージとは何が違いますか?

宛先と製品が違います。Messaging APIはLINE公式アカウントから、一般のLINEユーザー(顧客)へ送るためのAPIです。LINE WORKSは社内向けのビジネスチャットで、Botが送る相手は自社のメンバーになり、認証方式もAPIの体系も別です。社内連絡の自動化ならLINE WORKS APIのJWT認証とBot送信の実装記事を参照してください。

関連記事

お気に入りに入れた記事の一覧

一創の公式 SNS

X では毎朝の新着記事と IT ニュースの速報を、YouTube・TikTok・Instagram・LinkedIn・Facebook では記事の解説動画を配信しています。

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.11.03 テックブログ クラブツーリズム不正アクセス:約432万件の対象確認と偽メールの見分け方・流出検知の設計
  2. 2026.11.04 テックブログ CVE-2026-119977:WordPress更新中のRCEと安全な更新手順
  3. 2026.11.03 テックブログ ノジマ不正アクセスで約700万件漏えい:対象確認・窓口と乗っ取り注文を止める実装
  4. 2026.11.02 テックブログ アララの不正アクセス:メール約7,700万件の持ち出しと契約企業が行うAPIキー再設定・代替送信・連名報告
  5. 2026.10.29 テックブログ ソニー生命の情報漏洩38.5万件|ログインできない理由、再開状況と窓口の電話番号

RELATED POSTS 関連記事

目次