GTM(Googleタグマネージャー)で設置する — イベントIDによる重複排除
広告タグ(ピクセル)とコンバージョンAPI(CAPI)で同じCVを送ると、そのままでは二重計上されます。Meta・TikTok・Yahoo!ディスプレイ/LINE は、両方に同じイベントIDを渡せば媒体側で1件に統合されます(重複排除の詳細)。
GTM を使うと、これを確実にできます。1つのタグの中でイベントIDを1回だけ発行し、その同じIDを広告タグとアプリの cvTrack() の両方に渡すという形です。IDの受け渡しをタグ間で手作業で合わせる必要がなくなります。
ヒント
考え方はシンプルです。「IDを作る」→「そのIDでピクセルを撃つ」→「同じIDでCAPIを撃つ」を1か所にまとめる。これだけで重複排除が成立します。
前提
- アプリ側で受信キーの発行とCVマッピングの登録が済んでいること。
- 各媒体の広告タグ(Metaピクセル、TikTokピクセル等)がすでに GTM で動いていること。
手順1【GTM】配布タグを全ページで読み込む
プロジェクト概要の「コンバージョンAPI(CAPI)」→「タグをコピー」で取得した配布タグを、GTM の カスタムHTMLタグとして登録します。
- トリガー: Initialization - All Pages(または All Pages)
- これで全ページに
cvTrack()/cvLineLink()が読み込まれ、クリックID(gclid など)もクッキーに保存されます。
注意
CVタグより先に読み込まれている必要があります。CVタグ側の「詳細設定 →タグの順序付け」で、「cvTrack が呼び出される前に配布タグを配信」に設定しておくと確実です。
手順2【GTM】イベントIDを発行する変数を作る(最重要)
イベントIDは、同じCVなら何度呼ばれても同じ値でなければいけません。ページをリロードするたびに違うIDになると、媒体側では別のCVとして数えられてしまいます。
そこで、注文番号などの業務IDから決定的に作るのが基本です。GTM の カスタムJavaScript変数(例: CV Event ID)として次を登録します。
function () {
var eventName = {{DLV - cv_event_name}} || 'Purchase';
var orderId = {{DLV - transaction_id}}; // dataLayer に積んだ注文番号
// 業務ID(注文番号)があれば、それを使って決定的に作る(リロードしても同じ値)
if (orderId) return eventName + '-' + orderId;
// 業務IDが無いCV(問い合わせ・資料請求など): セッション内で同じ値を使い回す
var key = '_cv_eid_' + eventName;
try {
var cached = sessionStorage.getItem(key);
if (cached) return cached;
var id = eventName + '-' + (
(window.crypto && crypto.randomUUID)
? crypto.randomUUID()
: Date.now() + '-' + Math.random().toString(36).slice(2)
);
sessionStorage.setItem(key, id);
return id;
} catch (e) {
return eventName + '-' + Date.now();
}
}
重要
Date.now() や乱数をそのままイベントIDにしてはいけません。完了ページをリロードするだけで別のCVとして計上されます。上のコードは、注文番号がある場合はそれを使い、無い場合でも sessionStorage にキャッシュして同じ値を返すようにしています。
CV名を接頭辞に付けているのは、同じ注文に対して種類の違うCV(InitiateCheckout と Purchase など)を送るときにIDが衝突しないようにするためです。
手順3【GTM】1つのタグでピクセルとCAPIを同じIDで発火する
カスタムHTMLタグを作り、手順2の変数を使って、広告タグとアプリの cvTrack() を同じイベントIDで呼びます。
<script>
(function () {
var eventId = {{CV Event ID}}; // 手順2の変数(この1回だけ評価する)
var eventName = 'Purchase'; // アプリ内CV名(CVマッピングと完全一致させる)
var value = {{DLV - value}}; // 金額(数値)
var currency = 'JPY';
// ① Meta ピクセル(第4引数の eventID が重複排除キー)
if (window.fbq) {
fbq('track', eventName, { value: value, currency: currency }, { eventID: eventId });
}
// ② TikTok ピクセル(第3引数の event_id が重複排除キー)
if (window.ttq) {
ttq.track(eventName, { value: value, currency: currency }, { event_id: eventId });
}
// ③ アプリのコンバージョンAPI(①②と“同じ” eventId を渡すのが要点)
if (window.cvTrack) {
cvTrack(eventName, { value: value, currency: currency, eventId: eventId });
}
})();
</script>
- トリガー: 購入完了ページの表示(または dataLayer の
purchaseイベント)など、CVが確定した瞬間に1回だけ発火するもの。 eventNameは、アプリのCVマッピングに登録したアプリ内CV名と大文字小文字まで完全一致させてください。- 媒体側のピクセルをGTMの公式テンプレート(Metaの「Facebookピクセル」テンプレート等)で運用している場合は、テンプレートの Event ID(イベントID)欄に
{{CV Event ID}}を指定し、CAPI用のカスタムHTMLタグではcvTrack()だけを呼ぶ形でも構いません。同じ変数を参照していれば同じ値になります。
媒体ごとの注意
| 媒体 | この方法で重複排除できるか |
|---|---|
| Meta | できる。ピクセルの eventID と cvTrack の eventId が一致すれば統合される |
| TikTok | できる。ピクセルの event_id と一致させる |
| Yahoo!ディスプレイ/LINE | できる。CAPIは eventId を transaction_id として送るため、サイトタグ側で渡している注文IDと同じ値にする |
| Google Ads | 不要。CAPIは「インポート(クリック)」型の別のコンバージョンアクションに入るため、サイトタグのCVとは統合されず別カウントになる。入札最適化に使うのはどちらか一方にする |
| SmartNews | できない。APIにイベントID相当のフィールドが無い。ピクセルかCAPIのどちらか一方だけを発火させる(このタグから cvTrack は呼ぶがピクセルは呼ばない、など) |
注意
SmartNews だけは、同じCVでピクセルとCAPIの両方を発火させると必ず二重計上されます。GTMのタグ内で、SmartNews のピクセル発火とCAPI送信のどちらを使うかを決めてください。
確認のしかた
- GTMのプレビュー(Tag Assistant) で、CVタグが1回だけ発火し、
{{CV Event ID}}が期待どおりの値(例:Purchase-20260713-0001)になっていることを確認します。 - 完了ページをリロードして、イベントIDが変わらないことを確認します(変わる場合は手順2の設定を見直してください)。
- Meta イベントマネージャの「イベントの重複排除」で、ブラウザとサーバーのイベントが統合されている(重複警告が出ない)ことを確認します。
- アプリの「コンバージョン設定」→ 転送結果ログで、送信が成功していることを確認します。同じIDで再送された場合の
DUPLICATE_ORDER_ID/CLICK_CONVERSION_ALREADY_EXISTS(Google)は「計上済み」を意味する正常な応答です。