Custom Aggregation Unitsを使ってLINEメッセージングの統計情報を取得する

この記事は、[ミロゴス Advent Calendar 2022](https://qiita.com/advent-calendar/2022/milogos) 14日目の記事です。

# Custom Aggregation Units（CAU）とは

Custom Aggregation Units（以下、CAU）は、LINEのメッセージ送信時に指定するユニット名を使って、ユニット毎の統計情報を取得できる機能です。

この機能を使用するとユニット名を使って、指定した期間のメッセージ開封数、メッセージ中に含まれるURLのタップ数、吹き出し内の動作や音声が再生された回数などの情報をAPI経由で取得できます。
CAUを活用すると、メッセージ配信内容の仮説検証に活用したり、得られたデータを蓄積することで時系列の分析に活用したりすることが可能になります。

**※この機能は、2022年9月28日までは法人ユーザー向けのオプションとして提供されていたものです。現在はMessaging APIの標準機能となり、誰でも利用できます。**

LINE Developersのサイトを見れば概要説明からAPIリファレンスまで参照できますが、Qiita上での解説や利用例は見当たらなかったので、紹介も兼ねて記事にすることにしました。

# LINEのメッセージング種類と統計情報

LINEのメッセージングにはいくつか種類があります。

- プッシュメッセージ
    - 1対1のメッセージング。
    - ユーザー、グループ、トークルームを指定して送る。
    - 商品購入ユーザーに発送通知を送る場合などに使う。
- マルチキャストメッセージ
    - 多数のUIDを指定した1対多のメッセージング。
    - 複数のユーザーに対して同じメッセージを効率よく送信する。
    - ショッピングサイト利用ユーザーに新商品の案内を一斉に通知する場合などに使う。
- ブロードキャストメッセージ
    - LINE公式アカウントと友だちになっている全ユーザーに対するメッセージング。
- ナローキャストメッセージ
    - 特定の条件を指定した1対多のメッセージング。
    - 属性情報やリターゲティング（オーディエンス）を指定して送信する。


このうちブロードキャストメッセージとナローキャストメッセージでは、もともとリクエストID毎に統計情報を取得できました。

CAUはプッシュメッセージとマルチキャストメッセージでもユニット名を付与することで統計情報を取得できるようにするオプションです。

LINE Messaging APIを利用したメッセージングでは、本文をJSON形式で組み立てます。
組み立て方のイメージは、弊社メンバーが投稿している以下の記事を参照してください。

https://qiita.com/tokkun5552/items/c515f11b1d5496971459

https://qiita.com/koji_fumi/items/a02a912e5a9c8e21cd10

送信に必要なプロパティに加えて `customAggregationUnits` を加えることによって、後から統計情報を得られるようになります。

# CAUの指定方法

単純なテキストの吹き出しを持つメッセージのJSON例です。
`customAggregationUnits` の配列部分がCAUのユニット指定になります。

```json
{
    "to": "xxxxxxxxxxxxxxxx",
    "messages":[
        {
            "type": "text",
            "text": "ミロゴス株式会社がお届けします。"
        }
    ],
    "customAggregationUnits": [
        "promotion_milogos"
    ]
}
```

CAUのユニット名は、大文字と小文字は区別されます（`promotion_milogos` と `promotion_MILOGOS`は別ものとして扱われます）。

配列による指定となっていますが、2022/12/12時点では**最大ユニット数は1**となっており、複数のユニット名が指定できるわけではありません。
最大文字列長は30、使用可能文字種は半角英数とアンダースコアのみとなっています。

すでに送信したメッセージに対して後からCAUを設定したり、ユニット名を変更したりすることはできないことに注意が必要です。

# 統計情報の取得

CAUのユニット名を用いて、統計情報を得るにはLINE Messaging APIのaggregation APIを利用します。

エンドポイントは以下の通りです。リクエストする際は`Authorization`リクエストヘッダー（値：`Bearer {channel access token}`）が必要になります。
```
GET https://api.line.me/v2/bot/insight/message/event/aggregation?customAggregationUnit={customAggregationUnit}&from={from}&to={to}
```

- customAggregationUnit
    - メッセージ送信時に付与したユニット名を指定します。
- from
    - 集計対象期間の開始日を指定します。
    - フォーマットは `yyyyMMdd`です。
    - タイムゾーンはUTC+9となります。
- to
    - 集計対象期間の終了日を指定します。
    - フォーマットは `yyyyMMdd`です。
    - タイムゾーンはUTC+9となります。
    - 終了日は、開始日の30日後まで指定できます。
        - ただし、後述する更新期間の制限があることに注意が必要です。

`from`と `to` に同一日を指定した場合は、その日1日の総計を取得することができます。

送信直後からAPIを叩くことが可能ですが、叩いたタイミングまでに集計が完了している値が取得できます。

# 統計情報APIのレスポンス

aggregation APIの実行に成功すると、ステータスコード`200`と、統計情報がまとめられたJSONが得られます。

以下は、ある配信における日次集計のJSONです。

```json
{
   "overview":{
      "uniqueImpression":970309,
      "uniqueClick":9841,
      "uniqueMediaPlayed":null,
      "uniqueMediaPlayed100Percent":null
   },
   "messages":[
      {
         "seq":1,
         "impression":22278,
         "uniqueImpression":21654,
         "mediaPlayed":null,
         "mediaPlayed25Percent":null,
         "mediaPlayed50Percent":null,
         "mediaPlayed75Percent":null,
         "mediaPlayed100Percent":null,
         "uniqueMediaPlayed":null,
         "uniqueMediaPlayed25Percent":null,
         "uniqueMediaPlayed50Percent":null,
         "uniqueMediaPlayed75Percent":null,
         "uniqueMediaPlayed100Percent":null
      },
      {
         "seq":2,
         "impression":36551,
         "uniqueImpression":35326,
         "mediaPlayed":null,
         "mediaPlayed25Percent":null,
         "mediaPlayed50Percent":null,
         "mediaPlayed75Percent":null,
         "mediaPlayed100Percent":null,
         "uniqueMediaPlayed":null,
         "uniqueMediaPlayed25Percent":null,
         "uniqueMediaPlayed50Percent":null,
         "uniqueMediaPlayed75Percent":null,
         "uniqueMediaPlayed100Percent":null
      },
      {
         "seq":3,
         "impression":983529,
         "uniqueImpression":966276,
         "mediaPlayed":null,
         "mediaPlayed25Percent":null,
         "mediaPlayed50Percent":null,
         "mediaPlayed75Percent":null,
         "mediaPlayed100Percent":null,
         "uniqueMediaPlayed":null,
         "uniqueMediaPlayed25Percent":null,
         "uniqueMediaPlayed50Percent":null,
         "uniqueMediaPlayed75Percent":null,
         "uniqueMediaPlayed100Percent":null
      }
   ],
   "clicks":[
      {
         "seq":1,
         "url":"https://xxxxxx",
         "click":1319,
         "uniqueClick":1302,
         "uniqueClickOfRequest":1302
      },
      {
         "seq":2,
         "url":"https://xxxxxx",
         "click":145,
         "uniqueClick":143,
         "uniqueClickOfRequest":143
      },
      {
         "seq":2,
         "url":"https://xxxxxx",
         "click":699,
         "uniqueClick":683,
         "uniqueClickOfRequest":683
      },
      {
         "seq":2,
         "url":"https://xxxxxx",
         "click":267,
         "uniqueClick":266,
         "uniqueClickOfRequest":266
      },
      {
         "seq":3,
         "url":"https://xxxxxx",
         "click":7747,
         "uniqueClick":7690,
         "uniqueClickOfRequest":7690
      }
   ]
}
```

送信に用いたメッセージは以下のJSONです。
3つの吹き出し（bubble）を持ち、それぞれがイメージマップ、イメージカルーセル（横スライド可能なカルーセルで構成されるもの）、イメージマップとなっています。

```json
[
   {
      "type":"imagemap",
      "baseUrl":"https://xxxxxx",
      "altText":"【サンプル】ミロゴス株式会社のミッションは豊かさのおすそわけ",
      "baseSize":{
         "width":1040,
         "height":1040
      },
      "actions":[
         {
            "type":"uri",
            "area":{
               "x":0,
               "y":0,
               "width":1040,
               "height":1040
            },
            "linkUri":"https://xxxxxx"
         }
      ]
   },
   {
      "type":"template",
      "altText":"【サンプル】ミロゴス株式会社のミッションは豊かさのおすそわけ",
      "template":{
         "type":"image_carousel",
         "columns":[
            {
               "imageUrl":"https://xxxxxx.jpg",
               "action":{
                  "type":"uri",
                  "uri":"https://xxxxxx"
               }
            },
            {
               "imageUrl":"https://xxxxxx.jpg",
               "action":{
                  "type":"uri",
                  "uri":"https://xxxxxx"
               }
            },
            {
               "imageUrl":"https://xxxxxx.jpg",
               "action":{
                  "type":"uri",
                  "uri":"https://xxxxxx"
               }
            }
         ]
      }
   },
   {
      "type":"imagemap",
      "baseUrl":"https://xxxxxx",
      "altText":"【サンプル】ミロゴス株式会社のミッションは豊かさのおすそわけ",
      "baseSize":{
         "width":1040,
         "height":1040
      },
      "actions":[
         {
            "type":"uri",
            "area":{
               "x":0,
               "y":0,
               "width":1040,
               "height":1040
            },
            "linkUri":"https://xxxxxx"
         }
      ]
   }
]
```

## 解説：overview - メッセージ全体の統計

```json
{
   "overview":{
      "uniqueImpression":970309,
      "uniqueClick":9841,
      "uniqueMediaPlayed":null,
      "uniqueMediaPlayed100Percent":null
   },
   "messages":[
       ...
```

`overview`はメッセージ全体に関する統計情報を示します。

- uniqueImpression
    - メッセージを開封した人数です。
    - 少なくともひとつの吹き出しを表示した人数を表します。
- uniqueClick
    - メッセージ内のいずれかのURLをタップした人数です。
- uniqueMediaPlayed
    - メッセージ内に動画もしくは音声が含まれていた場合に、そのメディアの再生を開始した人数です。
- uniqueMediaPlayed100Percent
    - メディアを最後まで視聴した人数です。

有効な統計情報が含まれて項目については`null`が返ります。

今回の場合、イメージマップにはURLが設定されていてタップした人がいるため`uniqueClick`に値が入り、動画や音声はメッセージ内に含まれていないため`uniqueMediaPlayed`と`uniqueMediaPlayed100Percent`は`null`となります。

URLは設定されているがタップした人がいない場合は`uniqueClick`は`null`になるため、データの意味を正しく理解するためには送信したメッセージと対応づけて見る必要があります。

## 解説：messages - 吹き出し別の統計

```json
    "messages":[
      {
         "seq":1,
         "impression":22278,
         "uniqueImpression":21654,
         "mediaPlayed":null,
         "mediaPlayed25Percent":null,
         "mediaPlayed50Percent":null,
         "mediaPlayed75Percent":null,
         "mediaPlayed100Percent":null,
         "uniqueMediaPlayed":null,
         "uniqueMediaPlayed25Percent":null,
         "uniqueMediaPlayed50Percent":null,
         "uniqueMediaPlayed75Percent":null,
         "uniqueMediaPlayed100Percent":null
      },
      {
         "seq":2,
         ...
```

`messages`は吹き出し（バブル）に関する情報を表す配列です。
メッセージに含まれる吹き出しの数だけ要素が出現します。

- seq
    - 吹き出しの通し番号です。
    - 1から始まります。
- impression
    - 吹き出しが表示された**回数**です。
- mediaPlayed
    - 吹き出し内の動画もしくは音声が再生開始された回数です。
- mediaPlayed25Percent / mediaPlayed50Percent / mediaPlayed75Percent / mediaPlayed100Percent
    - メディアが再生開始され、それぞれ25%、50%、75%、100%再生された回数です。
- uniqueMediaPlayed
    - メディアを再生開始した**人数**です。
- uniqueMediaPlayed25Percent / uniqueMediaPlayed50Percent / uniqueMediaPlayed75Percent / uniqueMediaPlayed100Percent
    - メディアを再生開始し、それぞれ25%、50%、75%、100%再生した人数です。

LINE Messaging APIを使用する場合、1回のメッセージで最大5吹き出しまで送ることが可能（OAM上は3吹き出し）なため、`seq`の最大値は5になります。

## 解説：Clicks - URLタップの統計

```json
   "clicks":[
      {
         "seq":1,
         "url":"https://xxxxxx",
         "click":1319,
         "uniqueClick":1302,
         "uniqueClickOfRequest":1302
      },
      {
         "seq":2,
         "url":"https://xxxxxx",
         "click":145,
         "uniqueClick":143,
         "uniqueClickOfRequest":143
      },
      {
         "seq":2,
         "url":"https://xxxxxx",
         "click":699,
         "uniqueClick":683,
         "uniqueClickOfRequest":683
      },
      {
         "seq":2,
         "url":"https://xxxxxx",
         "click":267,
         "uniqueClick":266,
         "uniqueClickOfRequest":266
      },
      {
         "seq":3,
         "url":"https://xxxxxx",
         "click":7747,
         "uniqueClick":7690,
         "uniqueClickOfRequest":7690
      }
   ]
```

`clicks`はタップしたURLに関する情報を表す配列です。

- seq
    - URLが含まれている吹き出しの通し番号です。
    - `messages`の`seq`と対応づけられます。
- url
    - URLです。
- click
    - `url`が示す吹き出し内のURLをタップした回数です。
- uniqueClick
    - `url`が示す吹き出し内のURLをタップした人数です。
- uniqueClickOfRequest
    - メッセージ内のうち、`url`と同じURLをタップした人数です。
    - 他の吹き出しに`url`と同じURLが設定されている場合に、1ユーザーがそれぞれのURLをタップした場合でも1回だけカウントされます。

この例で注目すべきは`seq=2`の要素が複数出現していることです。

サンプルではURLをすべて`"https://xxxxxx"`でマスクしてしまっていますが、これらには異なるURLが設定されています。

今回の例で送信しているメッセージのJSONにて、2つ目にカルーセルを使っており、3つのイメージマップをスライド表示できるようにしています。そのイメージマップそれぞれにURLを設定しているため、`seq=2`の要素に含まれるURL分要素が出現しています。

# 統計情報APIの活用

CAUオプションを指定してAPI経由で得られる統計情報は、基本的にLINE OAMの画面上で見られる情報と同一です。
そちらを閲覧するだけで事足りる場合は、特に利用する必要はないでしょう。

このAPIが活きるのは配信データを蓄積して、時系列データとして自由に集計や分析に活用できる点にあります。
吹き出しやURL単位での細かい情報も得られるため、どのようなメッセージを組み立てればよりユーザーに見てもらえるのか仮説検証も行いやすくなります。

取得できる期間の制限、APIのリクエストレート制限、集計がどのような周期で行われているのか仕様が非開示といった制限があるものの、とても有用な情報となるのではないかと思います。

ミロゴスでは、GASを利用してGoogleスプレッドシートで簡易的な情報の蓄積・集計をしたり、DMPと連携させてデータを活用したりするような取り組みで本APIを活用しています。

![スクリーンショット 2022-12-13 2.13.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2977/72d3adf4-b2bf-4f23-682b-1c21063a31c7.png)

# 注意事項

## 統計情報の更新期間は14日間

統計情報はメッセージの送信時刻から14日間のみ更新され、それ以降は更新されません。

統計情報APIの終了日（to）は開始日の30日後まで指定できますが、１配信における最大の更新期間との関係性に注意をしないと値の解釈を取り違える可能性があるので注意が必要です。

## ユニット数の設定上限は月1,000件

ユニットには、1つのチャネルにつき最大で月に1,000種類のユニット名を利用できます。

ユニットの数は毎月1日にリセットされます。
当月中に1,000種類を超えるユニット名を付与した場合、メッセージの送信はできますが、当該ユニット名は付与されていないものとして扱われます。

当月に利用したユニット数は以下のAPIで確認できます。

```
GET https://api.line.me/v2/bot/message/aggregation/info
```

```json
{
  "numOfCustomAggregationUnits": 1
}
```

また、当月に利用したユニット名のリストは以下のAPIで確認できます。

```
GET https://api.line.me/v2/bot/message/aggregation/list
```

クエリーパラメーターとして、1回のリクエストで取得する最大数を指定する`limit`、最大数を指定した際に次の要素がある場合にレスポンスのJSONで返却される`next`を指定して続きを取得するための`start`が使えます。

```json
{
  "customAggregationUnits": [
    "promotion_milogos",
    "promotion_milogos2"
  ],
  "next": "jxEWCEEP..."
}
```

## 統計情報の誤差と人数の制限

統計情報の数値は、多少の誤差を含むことがあることが公式から明示されています。

また、プライバシーを保護するための制限として、以下の場合は個人の操作に関するプロパティの値は`null`になります。

- プロパティの値が20未満だった場合
- プロパティの値が20以上であっても、そのイベントを発生させた実人数が20人未満だった場合
    - messages[].mediaPlayedは30だが、messages[].uniqueMediaPlayedが15だった場合は、どちらの値もnullになる。

検証用のアカウントなどを使ってCAUの動作確認をする場合は、この制限がネックになる場合があるので、仕様として頭に入れておく必要があります。

## 複数の配信に同一のユニット名を付与した場合の挙動

CAUを指定して得られる統計情報はメッセージの構成やタイミングを区別しません。
メッセージの内容や吹き出し数、吹き出しの順番などが異なったメッセージに同一のユニット名を指定した場合でも、統計情報はまとめて集計されてしまいます。

また、情報の更新期間である14日を過ぎていない状態で、別タイミングで同一ユニット名を指定した配信を行なった場合、更新期間が被っている期間については両者の値が合計されて取得されます。
最初の配信の更新期間を過ぎると、以後は後から行なった配信分のみの結果となります。

# おわりに

CAUを用いたユニット単位の統計情報の取得についてご紹介しました。
LINE Messaging APIは、日々機能追加や法人オプションだったものの利用緩和などが行われています。
特に今回のような配信結果に関するデータを手軽に得られる手段が増えることは、その蓄積と活用に繋げられ可能性が広がります。
この記事がその助けになれば幸いです。