Analytics

This document describes how to get messaging, conversation, and template analytics, such as the number of messages sent from a business phone number, the number of conversations and their costs for a WhatsApp Business Account (WABA), or the number of times a given template has been read.

Only metrics for business phone numbers and templates associated with your WABA at the time of the request will be included in responses.

Getting The Data

Use the WhatsApp Business Account endpoint to get analytics.

Query Syntax

GET /<WHATSAPP_BUSINESS_ACCOUNT_ID>
  ?fields=<FIELDS>.<FILTERING_PARAMETER>

Query String Parameters

PlaceholderDescriptionExample Value

<FIELDS>

Required.


Metric. Value can be one of:


analytics

<FILTERING_PARAMETERS>

Required.


Metric filtering parameter. Append additional filtering parameters using dots.


For possible values, see:


.start(1543543200).end(1544148000).granularity(DAY)

Messaging Analytics

The analytics field provides the number and type of messages sent and delivered by the phone numbers associated with a specific WABA — for conversation metrics, see Conversation Analytics. When calling /{whatsapp-business-account-ID}?fields=analytics.{filtering-parameters}, you can attach the following parameters.

Analytics Parameters

NameDescription (Click the arrow in the left column for supported options.)

start

type: UNIX Timestamp

Required.

The start date for the date range you are retrieving analytics for.

end

type: UNIX Timestamp

Required.

The end date for the date range you are retrieving analytics for.

granularity

type: String

Required.

The granularity by which you would like to retrieve the analytics.

Supported Options

  • HALF_HOUR
  • DAY
  • MONTH

phone_numbers

type: Array

Optional.

An array of phone numbers for which you would like to retrieve analytics. If not provided, all phone numbers added to your WABA are included.

product_types

type: Array

Optional.

The types of messages (notification messages and/or customer support messages) for which you want to retrieve notifications. Provide an array and include 0 for notification messages, and 2 for customer support messages. If not provided, analytics will be returned for all messages together.

country_codes

type: Array

Optional.

The countries for which you would like to retrieve analytics. Provide an array with 2 letter country codes for the countries you would like to include. If not provided, analytics will be returned for all countries you have communicated with.

Example

Scenario: You need to get the number of messages sent and delivered by all phone numbers associated with your WABA.

Suggested Solution: Assemble the URL you want to call and include the following filtering parameters: start, end, granularity. Then, make a GET request to that URL:

curl -i -X GET \ 
"https://graph.facebook.com/v21.0/{whatsapp-business-account-ID}
      ?fields=analytics
      .start(1543543200)
      .end(1544148000)
      .granularity(DAY)
      &access_token={access-token}"

A successful response returns an analytics object with the data you have requested:

{
  "analytics": {
    "phone_numbers": [
      "16505550111",
      "16505550112",
      "16505550113"
    ],
    "country_codes": [
      "US",
      "BR"
    ],
    "granularity": "DAY",
    "data_points": [
      {
        "start": 1543543200,
        "end": 1543629600,
        "sent": 196093,
        "delivered": 179715
      },
      {
        "start": 1543629600,
        "end": 1543716000,
        "sent": 147649,
        "delivered": 139032
      },
      {
        "start": 1543716000,
        "end": 1543802400,
        "sent": 61988,
        "delivered": 58830
      },
      {
        "start": 1543802400,
        "end": 1543888800,
        "sent": 132465,
        "delivered": 124392
      }
      # more data points
    ]
  },
  "id": "102290129340398"
}

Conversation Analytics

The conversation_analytics field provides cost and conversation information for a specific WABA. When calling /{whatsapp-business-account-ID}?fields=conversation_analytics.{filtering-parameters}, you can attach the following parameters.

Conversation Analytics Parameters

NameDescription (Click the arrow in the left column for supported options.)

start

type: UNIX Timestamp

Required.

The start date for the date range you are retrieving analytics for.

end

type: UNIX Timestamp

Required.

The end date for the date range you are retrieving analytics for.

granularity

type: String

Required.

The granularity by which you would like to retrieve the analytics.

Supported Options

  • HALF_HOUR
  • DAILY
  • MONTHLY

phone_numbers

type: Array

Optional.

An array of phone numbers for which you would like to retrieve analytics. If not provided, all phone numbers added to your WABA are included.

metric_types

Optional.

List of metrics you would like to receive. If you send an empty list, we return results for all metric types.

Supported Options {#supported}

  • COST: Includes approximate charges for that time range, in the WABA’s currency.
  • CONVERSATION: Includes the count of conversations for that time range.

As of July 1, 2023, COST is no longer shown for businesses who bill through a Solution Partner. To understand your charges, please reach out to your partner. If you bill through a partner, this is the behavior to expect:

  1. If no metric_types are specified in your request, only CONVERSATION is returned
  2. If only CONVERSATION is specified, only CONVERSATION is returned
  3. If only COST is specified, the following exception is returned:
    • Title: “Cost not available”
    • Message: “Cost is no longer shown for businesses who bill through a partner (i.e., BSP). To understand your charges, please reach out to your partner.”

If you query a time period that includes dates on or after July 1, 2023, (e.g., May 1, 2023 through August 1, 2023), the response will include the exception above.

There is no change for partners when querying the conversation_analytics endpoint.

conversation_categories

Optional.

List of conversation categories. If you send an empty list, we return results for all conversation categories.

Supported Options

  • AUTHENTICATION
  • MARKETING
  • SERVICE
  • UTILITY

conversation_types

Optional.

List of conversation types. If you send an empty list, we return results for all conversation types.

Supported Options

  • FREE_ENTRY: Conversations originating from a free entry point.
  • FREE_TIER: Conversations within the monthly free tier.
  • REGULAR: Any conversations that did not originate from a free entry point or are above the monthly free tier allotment.

conversation_directions

Optional.

List of conversation directions. If you send an empty list, we return results for all conversation directions.

Supported Options

  • BUSINESS_INITIATED: Conversations initiated by the business.
  • USER_INITIATED: Conversations initiated by an end user/customer.

dimensions

Optional.

List of breakdowns you would like to apply to your metrics. If you send an empty list, we return results without any breakdowns.

Supported Options

  • CONVERSATION_CATEGORY
  • CONVERSATION_DIRECTION
  • CONVERSATION_TYPE
  • COUNTRY
  • PHONE

Analytics data is approximate and may differ from what’s shown on invoices due to small variations in data processing.

Examples

Given a time range, you can get conversation and cost information associated with your WABA. If you want, you can filter and break down your results. See the code samples below for examples.

Getting Monthly Data, Using All Breakdowns

Scenario: Given a month, you want to retrieve all conversation and cost information for all phone numbers associated with a WABA.

Suggested Solution: Assemble the URL you want to call and include the following filtering parameters:

  • start: Start of your time range. In this case, the beginning of the month you want metrics for.
  • end: End of your time range. In this case, the end of the month you want metrics for.
  • granularity: How granular you want your data points to be. In the example below, we use MONTHLY, so each datapoint will represent a month’s worth of data.
  • phone_numbers: Send an empty array and we return information for all phone numbers associated with the WABA.
  • dimensions: Set it to all available breakdowns: "CONVERSATION_CATEGORY", "CONVERSATION_TYPE", "COUNTRY", and "PHONE".

In this case, you do not need to specify country_codes, metric_types, conversation_types and conversation_categories. If you don't send us anything for those fields, we return all available options. Once you set up the URL, make a GET request:

curl -i -X GET
"https://graph.facebook.com/v21.0/{whatsapp-business-account-id}
  ?fields=conversation_analytics
  .start(1685602800).end(1688194800)
  .granularity(MONTHLY)
  .phone_numbers([])
  .dimensions(["CONVERSATION_CATEGORY","CONVERSATION_TYPE","COUNTRY","PHONE"])
  &access_token={access-token}"

A successful response returns a conversation_analytics object with the data you have requested. In the following example, the WABA contains only one phone number.

{
  "conversation_analytics": {
    "data": [
      {
        "data_points": [
          {
            "start": 1685602800,
            "end": 1688194800,
            "conversation": 1558,
            "phone_number": "15550458206",
            "country": "US",
            "conversation_type": "REGULAR",
            "conversation_direction": "UNKNOWN",
            "conversation_category": "AUTHENTICATION",
            "cost": 15.58
          },
          {
            "start": 1685602800,
            "end": 1688194800,
            "conversation": 2636,
            "phone_number": "15550458206",
            "country": "US",
            "conversation_type": "REGULAR",
            "conversation_category": "MARKETING",
            "cost": 26.36
          },
          {
            "start": 1685602800,
            "end": 1688194800,
            "conversation": 2238,
            "phone_number": "15550458206",
            "country": "US",
            "conversation_type": "REGULAR",
            "conversation_category": "SERVICE",
            "cost": 22.38
          },
          {
            "start": 1685602800,
            "end": 1688194800,
            "conversation": 1782,
            "phone_number": "15550458206",
            "country": "US",
            "conversation_type": "REGULAR",
            "conversation_category": "UTILITY",
            "cost": 17.82
          },
          {
            "start": 1685602800,
            "end": 1688194800,
            "conversation": 1568,
            "phone_number": "15550458206",
            "country": "US",
            "conversation_type": "FREE_TIER",
            "conversation_category": "AUTHENTICATION",
            "cost": 15.68
          },
          {
            "start": 1685602800,
            "end": 1688194800,
            "conversation": 2716,
            "phone_number": "15550458206",
            "country": "US",
            "conversation_type": "FREE_TIER",
            "conversation_category": "MARKETING",
            "cost": 27.16
          },
          {
            "start": 1685602800,
            "end": 1688194800,
            "conversation": 2180,
            "phone_number": "15550458206",
            "country": "US",
            "conversation_type": "FREE_TIER",
            "conversation_category": "SERVICE",
            "cost": 21.8
          },
          {
            "start": 1685602800,
            "end": 1688194800,
            "conversation": 1465,
            "phone_number": "15550458206",
            "country": "US",
            "conversation_type": "FREE_TIER",
            "conversation_category": "UTILITY",
            "cost": 14.65
          },
          {
            "start": 1685602800,
            "end": 1688194800,
            "conversation": 1433,
            "phone_number": "15550458206",
            "country": "US",
            "conversation_type": "FREE_ENTRY_POINT",
            "conversation_category": "SERVICE",
            "cost": 14.33
          }
        ]
      }
    ]
  },
  "id": "102290129340398",
}

Getting Data for a Specific Phone Number, Using All Breakdowns and Half Hour Granularity

Scenario: Given a time range, you want to retrieve all conversation and cost information for a specific phone number associated with a WABA. In the results, you want to use all possible breakdowns. You need each data point to represent half an hour’s worth of data.

Suggested Solution: Assemble the URL you want to call and include the following filtering parameters:

  • start: Start of your time range.
  • end: End of your time range.
  • granularity: How granular you want your data points to be. In the example below, we use HALF_HOUR, so each datapoint represents half an hour’s worth of data.
  • phone_numbers: The phone number you need information for.
  • dimensions: Set it to all available breakdowns: CONVERSATION_CATEGORY, CONVERSATION_TYPE, COUNTRY, and PHONE.

In this case, you do not need to specify country_codes, metric_types, conversation_types, or conversation_categories. If you don’t send us anything for those fields, we return all available options. Once you set up the URL, make a GET request:

curl -i -X GET \
"https://graph.facebook.com/v21.0/{whatsapp-business-account-id}
  ?fields=conversation_analytics
  .start(1685602800)
  .end(1685689200)
  .granularity(HALF_HOUR)
  .phone_numbers(["19195552584"])
  .dimensions(["CONVERSATION_CATEGORY","CONVERSATION_TYPE","COUNTRY,PHONE"])
  &access_token=your-access-token"

A successful response returns a conversation_analytics object with the data you have requested:

{
  "conversation_analytics": {
    "data": [
      {
        "data_points": [
          {
            "start": 1685602800,
            "end": 1685604600,
            "conversation": 4,
            "phone_number": "19195552584",
            "country": "US",
            "conversation_type": "REGULAR",
            "conversation_direction": "UNKNOWN",
            "conversation_category": "SERVICE",
            "cost": 0.0232
          },
          {
            "start": 1685602800,
            "end": 1685604600,
            "conversation": 4,
            "phone_number": "19195552584",
            "country": "US",
            "conversation_type": "REGULAR",
            "conversation_direction": "UNKNOWN",
            "conversation_category": "MARKETING",
            "cost": 0.0232
          },
         # ... more data points
        ]
      }
    ]
  },
  "id": "102290129340398"
}

Getting Monthly Data, Using Conversation Type Breakdowns

Scenario: Given a time range, you want to retrieve all conversation and cost information for all phone numbers associated with a WABA. In the results, you want to break down by conversation type.

Suggested Solution: Assemble the URL you want to call and include the following filtering parameters:

  • start: Start of your time range.
  • end: End of your time range.
  • granularity: How granular you want your data points to be. In the example below, we use MONTHLY, so each datapoint represents half a month’s worth of data.
  • phone_numbers: Send an empty array and we’ll return information for all phone numbers associated with the WABA.
  • dimensions: Set it to CONVERSATION_TYPE.

In this case, you do not need to specify country_codes, metric_types, conversation_types, conversation_directions, or conversation_categories. If you don't send us anything for those fields, we return all available options. Once you set up the URL, make a GET request:

curl -i -X GET
"https://graph.facebook.com/v21.0/{whatsapp-buiness-account-id}
      ?fields=conversation_analytics
      .start(1643702400).end(1646121600)
      .granularity(MONTHLY)
      .phone_numbers([])
      .dimensions([CONVERSATION_TYPE])
      &access_token={access-token}"

A successful response returns a conversation_analytics object with the data you have requested:

{
  "data": [
    {
      "data_points": [
        {
          "start": 1643702400,
          "end": 1646121600,
          "conversation": 8500,
          "conversation_type": "REGULAR",
          "cost": 88.1010
        },
        {
          "start": 1643702400,
          "end": 1646121600,
          "conversation”: 1000,
          "conversation_type": "FREE_TIER",
          "cost": 0.0000
        }
        {
          "start": 1643702400,
          "end": 1646121600,
          "conversation”: 250,
          "conversation_type": "FREE_ENTRY_POINT",
          "cost": 0.0000
        }
      ]
    }
  ]
}

Getting Half-Hour Data Broken Down by Conversation Category


Request:

curl -i -X GET \
 "https://graph.facebook.com/v21.0/{whatsapp-buiness-account-id}
  ?fields=conversation_analytics
  .start(1685527200)
  .end(1685613600)
  .granularity(HALF_HOUR)
  .conversation_categories(["MARKETING","AUTHENTICATION"])
  .dimensions(["CONVERSATION_CATEGORY"])
  &access_token={access-token}"  

Response:

{
  "conversation_analytics": {
    "data": [
      {
        "data_points": [
          {
            "start": 1685529000,
            "end": 1685530800,
            "conversation": 2,
            "conversation_category": "AUTHENTICATION",
            "cost": 0.0128
          },
          {
            "start": 1685527200,
            "end": 1685529000,
            "conversation": 3,
            "conversation_category": "MARKETING",
            "cost": 0.0432
          }
        ]
      }
    ]
  },
  "id": "102290129340398"
}

Getting Half-Hour Data Broken Down by Conversation Type and Conversation Category


Request:

curl -i -X GET \
 "https://graph.facebook.com/v21.0/{whatsapp-buiness-account-id}
  ?fields=conversation_analytics
  .start(1685527200)
  .end(1685613600)
  .granularity(HALF_HOUR)
  .conversation_categories(["MARKETING","AUTHENTICATION"])
  .dimensions(["CONVERSATION_CATEGORY","CONVERSATION_TYPE"])
  &access_token={access-token}"  

Response:

{
  "conversation_analytics": {
    "data": [
      {
        "data_points": [
          {
            "start": 1685527200,
            "end": 1685529000,
            "conversation": 3,
            "conversation_type": "REGULAR",
            "conversation_category": "MARKETING",
            "cost": 0.0432
          },
          {
            "start": 1685529000,
            "end": 1685530800,
            "conversation": 2,
            "conversation_type": "REGULAR",
            "conversation_category": "AUTHENTICATION",
            "cost": 0.0128
          }
        ]
      }
    ]
  },
  "id": "102290129340398"
}

Template Analytics

Template analytics describe the number of times a template has been sent, delivered, and read, and the number of times URL buttons or Quick Reply buttons in the template have been clicked.

Data is returned with a daily granularity in the UTC timezone with a lookback of up to 90 days. Template analytics can also be found in the WhatsApp Manager > Message templates > Template details > Insights panel.

Limitations

  • Template Analytics are only available for On-Premises API if the account has not opted into Cloud API template analytics.
  • On-Premises API template analytics are subject to aggregation and anonymization guidelines, which require that there be a minimum of 1000 events before the count is shown to the user.
  • Button click analytics are only available for templates categorized as MARKETING or UTILITY.
  • WABAs owned by or shared with Meta Business Accounts in the European Union, United Kingdom, or Japan, or that have a business phone number with a country calling code from any of those countries or regions, are not supported.

Reporting Bugs

To report template analytics bugs, submit a Direct Support ticket with the following selections:

  • Question Topic: WABiz: Cloud API
  • Request Type: Bug or Implementation Issue

Confirming Template Analytics

You must confirm template analytics on your WhatsApp Business Account before you can get template analytics. You can confirm template analytics using the WhatsApp Manager or the API. To confirm via API, send the following request:

POST /<WHATSAPP_BUSINESS_ACCOUNT_ID>?is_enabled_for_insights=true

Once confirmed, we will begin capturing template analytics for the WhatsApp Business Account. Once confirmed, template analytics cannot be disabled.

Upon success, the API will respond with your WhatsApp Business Account ID. For example:

{                          
  "id": 102290129340398
}

Template Analytics Parameters

NameDescriptionExample Value

start

UNIX Timestamp

Required.

The start timestamp for the date range you are retrieving analytics for. As template analytics are being provided with a daily granularity in the UTC timezone, a start timestamp other than 0:00 UTC would be corrected to its prior 0:00 UTC.

1543536000

end

UNIX Timestamp

Required.


The end date for the date range you are retrieving analytics for. As template analytics are being provided with a daily granularity in the UTC timezone, an end timestamp other than 0:00 UTC would be corrected to its next 0:00 UTC.

1543708800

granularity

Enum

Required.

The granularity by which you would like to retrieve the analytics. Value must be DAILY.

DAILY

template_ids

Array of IDs

Required.

An array of template IDs for which you would like to retrieve analytics for.

Maximum 10.

[1924084211297547,954638012257287,969725530748535]

metric_types

Array of enums

Optional.

COST node is NOT accessible to businesses who bill through a Solution Partner. To understand your charges, please reach out to your partner.

The types of metrics which you want to retrieve. If omitted or an empty array, analytics for all metric types will be returned.

Possible values:

  • COST
  • CLICKED
  • DELIVERED
  • READ
  • SENT

Clicks are only returned for URL buttons and quick-reply buttons in templates categorized as MARKETING or UTILITY.

Cost metrics are returned as an array of cost objects, each with a type and value. Types can be:

  • amount_spent — Total amount spent on conversations opened within the start and end timeframe as a result of sending the template. See Opening Conversations.
  • cost_per_delivered — The amount_spent value divided by the number of times the template was delivered within the start and end timeframe.
  • cost_per_url_button_click — The amount_spent value divided by the number of times the template's URL button was clicked, within the start and end timeframe. Quick reply button clicks are not included. Object omitted if the template does not have a URL button.

[SENT,DELIVERED,READ]

Examples

Getting all template analytics

Scenario: Given a 1-day timeframe, get all template analytics metric types for an authentication template and a marketing template with a URL button.

Example Request:

curl -g 'https://graph.facebook.com/v21.0/109259195336416/template_analytics?start=1718064000&end=1718122745&granularity=daily&metric_types=cost%2Cclicked%2Cdelivered%2Cread%2Csent&template_ids=[1421988012088524%2C2632273056924580]' \
-H 'Authorization: Bearer EAAJB...'

Example Response:

{
  "data": [
    {
      "granularity": "DAILY",
      "product_type": "cloud_api", // Only available to businesses in Marketing Messages Lite API alpha
      "data_points": [
        {
          "template_id": "1421988012088524",
          "start": 1718064000,
          "end": 1718150400,
          "sent": 1,
          "delivered": 1,
          "read": 1,
          "cost": [
            {
              "type": "amount_spent",
              "value": 0.01
            },
            {
              "type": "cost_per_delivered",
              "value": 0.01
            }
          ]
        },
        {
          "template_id": "2632273056924580",
          "start": 1718064000,
          "end": 1718150400,
          "sent": 1,
          "delivered": 1,
          "read": 1,
          "clicked": [
            {
              "type": "url_button",
              "button_content": "Contact Support",
              "count": 1
            }
          ],
          "cost": [
            {
              "type": "amount_spent",
              "value": 0.03
            },
            {
              "type": "cost_per_delivered",
              "value": 0.03
            },
            {
              "type": "cost_per_url_button_click",
              "value": 0.03
            }
          ]
        }
      ]
    }
  ],
  "paging": {
    "cursors": {
      "before": "MAZDZD",
      "after": "MjQZD"
    }
  }
}

Disabling Button Click Analytics

You can disable button click tracking on an individual template by setting its cta_url_link_tracking_opted_out field to true. Once disabled, the API will no longer return the clicked property in template analytics or display button engagement/clicks in the WhatsApp Manager when viewing the template's insights.

Request Syntax

POST /<TEMPLATE_ID>
  ?cta_url_link_tracking_opted_out=<OPT_OUT>
  &category=<TEMPLATE_CATEGORY>

Request Parameters

PlaceholderDescriptionExample Value

<WHATSAPP_TEMPLATE_ID>

Template ID

Required.

Template ID.

245435364965041

<OPT_OUT>

Boolean

Required.

Indicates if template button click tracking is disabled. Set to true to disable button click tracking on the template, or false to enable.

This value is set to false upon template creation.

true

<TEMPLATE_CATEGORY>

String

Required.

Template's current category.

If you set the template category to a value other than its current category, the template status will be set to PENDING and the template must undergo template review to be approved.

marketing

Example Request

curl -X POST 'https://graph.facebook.com/v21.0/245435364965041?cta_url_link_tracking_opted_out=true&category=marketing' \
-H 'Authorization: Bearer EAAJB...'

Example Response

Upon success, the API will respond with:

{
    "success": true
}

Reference

For a list of all possible values for each field, refer to the Graph API reference of the WhatsApp Business Account Analytics field.