Understanding message delivery Stay organized with collections Save and categorize content based on your preferences.
For troubleshooting ongoing message delivery failures, use theFCM troubleshooter and see thisblog postto understand the different reasons why you may not see your message. You canalso visit theFCM status dashboardto identify if there are any ongoing service disruptions affecting FCM.
FCM also provides three sets of tools to help you get insight into broadevaluation of messaging success and strategy:
- Firebase console message delivery reports
- Aggregated Android SDK delivery metrics from theFirebase Cloud Messaging Data API
- Comprehensive data export to Google BigQuery
BigQuery data export and theReports tab in theFirebase console requireGoogle Analytics in order to function. IfGoogle Analytics is notenabled for your project, you can set it up in theintegrationstab of your Firebase project settings.Aggregated Delivery Data does not requireGoogle Analytics to function.
Keep in mind that the reporting of many of the statistics on this page,are subject to delays up to 24 hours due to batching of analytics data.
Message delivery reports
In theReportstab in theFirebase console, you can view thefollowing data for messages sent to Android or Apple platform FCM SDKs,including those sent using the Notifications composer and the FCM APIs:
- Sends — The data message or notification message has been enqueued fordelivery or has been successfully passed to a third-party service like APNsfor delivery. Note that Sends statistics might lag for a couple of hours.Seelifetime of a messagefor more information.
- Received (available only on Android devices) — The data message ornotification message has been received by the app. This data is availablewhen the receiving Android device hasFCM SDK 18.0.1 orhigher installed.
- Impressions (available only for notification messages on Android devices) —The display notification has been displayed on the device while the app isin the background.
- Opens — The user opened the notification message. Reported only fornotifications received when the app is in the background.
This data is available for all messages with a notification payloadand alllabeleddata messages.To learn more about labels, seeAdding analytics labels to messages.
When viewing message reports, you can set a date range for the data displayed,with the option to export to CSV. You can also filter by these criteria:
- Platform (iOS or Android)
- App
- Custom analytics labels
Adding analytics labels to messages
Labeling messages is very useful for custom analysis, allowing you tofilter delivery statistics by labels or sets of labels. You can add alabel to any message sent using the HTTP v1 API by settingthefcmOptions.analyticsLabel field in themessage object, or in theplatform-specificAndroidFcmOptions orApnsFcmOptions fields.
Analytics labels are text strings in the format^[a-zA-Z0-9-_.~%]{1,50}$.Labels can include lower and upper case letters,numbers, and the following symbols:
-~%
Max length is 50 characters. You can specify up to 100 unique labels per day;messages with labels added beyond that limit are not reported.
In theFirebase console messagingReports tab, you can search alist of all existing labels and apply them singly or in combination to filterthe statistics displayed.
Aggregated delivery data using theFCM Data API
The Firebase Cloud Messaging Data API lets you retrieve information that canhelp you understand the outcomes of message requests targeted to Androidapplications. The API provides aggregated data across all datacollection-enabled Android devices in a project. This includes details onthe percentage of messages deliveredwithout delay as well as how many messages were delayed or dropped within theAndroid Transport Layer.Evaluating this data can reveal broad trends in message delivery and help youfind effective ways to improve the performance of your send requests. SeeAggregate data timelines for information on date range availability in the reports.
The API provides all data available for a given application. See theAPI reference documentation.
How is the data broken down?
Delivery data is broken down by application, date, andanalytics label.A call to the API will returndata for every combination of date, application, and analytics label. Forexample, a singleandroidDeliveryData JSON object would look like this:
{ "appId": "1:23456789:android:a93a5mb1234efe56", "date": { "year": 2021, "month": 1, "day": 1 }, "analyticsLabel": "foo", "data": { "countMessagesAccepted": "314159", "messageOutcomePercents": { "delivered": 71, "pending": 15 }, "deliveryPerformancePercents": { "deliveredNoDelay": 45, "delayedDeviceOffline": 11 } }How to Interpret the Metrics
Delivery data outlines the percentage of messages that fit each of the followingmetrics. It is possible that a single message fits multiple metrics.Due to limitations in how we collect the data andthe level of granularity at which we aggregated the metrics,some message outcomes are not represented in the metrics at all,so the percentages below will not sum to 100%.
Count Messages Accepted
The only count included in the dataset is the count of messages that wereaccepted by FCM for delivery to Android devices. All percentages use this valueas the denominator. Keep in mind that this count won't include messagestargeted to users who havedisabled the collection of usage and diagnostic information ontheir devices.
Message Outcome Percentages
The fields included in theMessageOutcomePercentsobject provide information on theoutcomes of message requests. The categories are all mutually exclusive. It cananswer questions such as "Are my messages being delivered?" and "What is causingmessages to be dropped?"
For example, a high value for thedroppedTooManyPendingMessages field couldsignal that app instances are receiving volumes ofnon-collapsible messagesexceedingFCM's limit of 100 pending messages.To mitigate this, make sure your app handles calls toonDeletedMessages,and consider sending collapsible messages. Similarly, high percentages fordroppedDeviceInactive could be a signal to update registration tokens on yourserver, removing stale tokens and unsubscribing them from topics. SeeManageFCM registration tokensfor best practices in this area.
Delivery Performance Percentages
The fields in theDeliveryPerformancePercentsobject provide information about messages that were successfully delivered. Itcan answer questions such as "Were my messages delayed?" and"Why are messages delayed?" For example, a high value fordelayedMessageThrottled would clearly indicate that you are exceedingper-device maximum limits,and should adjust the rate at which you are sending messages.
deliveredNoDelay/messageOutcomePercents.delivered. You can use thiscalculation for other fields withinDeliveryPerformancePercents as well.Dividing a given metric by thedelivered percentage isolates the outcomerelative only to messages that were successfully delivered.Message Insight Percentages
This object provides additional information about all message sends. ThepriorityLowered field expresses the percentage of accepted messages thathad priority lowered fromHIGH toNORMAL. If this value is high, try sending fewer high priority messages or ensure thatyou always display a notification when a high priority message is sent. Seeour documentation on message priority for more info
How does this data differ from data exported to BigQuery?
The BigQuery export provides individual message logs about message acceptance bythe FCM backend and message delivery in the SDK on the device (Steps 2 and 4 oftheFCM Architecture). This data is useful for ensuring individual messages wereaccepted and delivered. Read more aboutBigQuery data export in the next section.
By contrast, the Firebase Cloud Messaging Data API provides aggregated detailsabout what happens specifically in the Android Transport Layer (or Step 3 oftheFCM Architecture).This data specifically provides insight into the delivery ofmessages from FCM backends to the Android SDK. It’s particularly useful forshowing trends as to why messages were delayed or dropped during this transport.
In some cases, it is possible that the two data sets might not match preciselydue to the following:
- The aggregated metrics only sample a portion of all messages
- The aggregated metrics are rounded
- We don’t present metrics below a privacy threshold
- A portion of message outcomes are missing due to optimizations in how wemanage the large volume of traffic.
Limitations of the API
Aggregate Data Timelines
The API will return 7 days of historical data; however, data returned by this API will be delayed by up to 5 days. For example, onJanuary 20th, the data for January 9th - January 15th would be available, but not for January16th or later. Additionally, the data is provided at best effort. In the event ofa data outage, FCM will work to fix forward and won't backfill the data afterthe issue is fixed. In larger outages, the data could be unavailable for a weekor more.
Data Coverage
The metrics provided by the Firebase Cloud Messaging Data API are meant toprovide insight into broad trends of message delivery. However, they don'tprovide 100% coverage of all message scenarios. The following scenarios areknown outcomes not reflected in the metrics.
Expired messages
If theTime To Live(TTL)expires after the end of the given log date, the message won't be counted asdroppedTtlExpired on this date.
Messages to inactive devices
Messages sent to inactive devices may or may not show up in the datasetdepending on which data path they take. This can lead to some miscounting in thedroppedDeviceInactive andpending fields.
Messages to devices with certain user preferences
Users who have disabled the collection of usage and diagnostic information ontheir devices won't have their messages included in our counting, in keepingwith their preferences.
Rounding and Minimums
FCM deliberately rounds and excludes counts where the volumes are not largeenough.
BigQuery data export
You can export your message data intoBigQuery for further analysis. BigQuerylets you analyze the data using BigQuery SQL, export it to another cloudprovider, or use the data for your custom ML models. An export to BigQueryincludes all available data for messages, regardless ofmessage type or whether the message is sent using the API orthe Notifications composer.
For messages sent to devices with the followingFCM SDK minimumversions, you have the additional option to enable the export of messagedelivery data for your app:
- Android 20.1.0 or higher.
- iOS 8.6.0 or higher
- Firebase Web SDK 9.0.0 or higher
To get started, link your project to BigQuery:
Choose one of the following options:
Openthe Notifications composer, then clickAccess BigQuery at the bottom of the page.
From theIntegrations page in theFirebase console, clickLink in theBigQuery card.
This page displaysFCM export options for allFCM-enabled apps in the project.
Follow the on-screen instructions to enable BigQuery.
Refer toLink Firebase to BigQueryfor more information.
When you enableBigQuery export forCloud Messaging:
Firebaseexports your data toBigQuery. Notethat the initial propagation of data for export may take up to 48 hours tocomplete.
- You canmanually schedule data backfills for up to the past 30 days.
After the dataset is created, the locationcan't be changed, but you can copy the dataset to a different locationor manually move (recreate) the dataset in a different location. To learnmore, seeChange dataset location.
Firebase sets up regular syncs of your data from your Firebase project toBigQuery. These daily export operations begin at 4:00 AM Pacific Timeand usually finish in 24 hours.
By default, all apps in your project are linked toBigQuery and anyapps that you later add to the project are automatically linked toBigQuery. You canmanage which apps send data.
To deactivateBigQuery export,unlink your project in theFirebase console.
Note: There is no charge for exporting data fromFCM, andBigQuery provides generous no-cost usage limits. Refer toBigQuery pricing or theBigQuery sandbox.Enable message delivery data export
iOS+
iOS devices with theFCM SDK 8.6.0 or higher can enable their app'smessage delivery data export.FCMsupports data export for both alert and background notifications. Data export isdisabled by default at theapp level. Programmatically enabling it at theappinstance level lets you ask end users for permission to analyze theirmessage delivery data (recommended). When both are set, the app instance levelvalue overrides the app level value.
Before enabling these options, you must first create theFCM-BiqQuery link for your project as described inBigQuery data export.
Enable delivery data export for alert notifications
Because only alert notifications can trigger notification service appextensions, you must add a notification service extension to your app and callthis API inside a service extension to enable display message tracking. SeeApple's documentation onModifying Content in Newly Delivered Notifications
The following call must be made for every notification received:Swift
//Foralertnotifications,calltheAPIinsidetheserviceextension:classNotificationService:UNNotificationServiceExtension{overridefuncdidReceive(_request:UNNotificationRequest,withContentHandlercontentHandler:@escaping(UNNotificationContent)->Void){Messaging.extensionHelper().exportDeliveryMetricsToBigQuery(withMessageInfo:request.content.userInfo)}}
Objective-C
// For alert notifications, call the API inside the service extension:@implementationNotificationService-(void)didReceiveNotificationRequest:(UNNotificationRequest*)requestwithContentHandler:(void(^)(UNNotificationContent*_Nonnull))contentHandler{[[FIRMessagingextensionHelper]exportDeliveryMetricsToBigQueryWithMessageInfo:request.content.userInfo];}@end
If you are building send requests using the HTTP v1 API, make sure tospecify `mutable-content = 1` in thepayload object.
Enable delivery data export for background notifications
For background messages received when the app is in the foreground orbackground, you can call the data export API inside the main app's data messagehandler. This call must be made for every notification received:
Swift
// For background notifications, call the API inside the// UIApplicationDelegate or NSApplicationDelegate method:funcapplication(_application:UIApplication,didReceiveRemoteNotificationuserInfo:[AnyHashable:Any]){Messaging.extensionHelper().exportDeliveryMetricsToBigQuery(withMessageInfo:userInfo)}
Objective-C
// For background notifications, call the API inside the// UIApplicationDelegate or NSApplicationDelegate method:@implementationAppDelegate-(void)application:(UIApplication*)applicationdidReceiveRemoteNotification:(NSDictionary*)userInfofetchCompletionHandler:(void(^)(UIBackgroundFetchResult))completionHandler{[[FIRMessagingextensionHelper]exportDeliveryMetricsToBigQueryWithMessageInfo:userInfo];}@end
Android
Android devices with theFCM SDK 20.1.0 or higher can enable theirapp's message delivery data export. Data export is disabled by default at theapp level. Programmatically enabling it at theapp instance level letsyou ask end users for permission to analyze their message delivery data(recommended). When both are set, the app instance level value overrides theapp level value.
Before enabling these options, you must first create theFCM-BiqQuery link for your project as described inBigQuery data export.
Enable delivery data export for app instances
For most cases, we recommend that you enable message delivery data exportonly at the app instance level and leave it disabled at the app level.
FirebaseMessaging.getInstance().setDeliveryMetricsExportToBigQuery(true);
Enable delivery data export for an app
If you prefer to enable export at the app level, make sure tonot call thesetDeliveryMetricsExportToBigQuery method, and add the following property tothe application object in your app manifest:
<application><meta-dataandroid:name="delivery_metrics_exported_to_big_query_enabled"android:value="true"/></application>
Web
TheFCM SDK for Web 9.0.0 provides an Alpha release of delivery dataexport. Data export is disabled by default at theapp level. Programmaticallyenabling it at theapp instance level lets you ask end users for permissionto analyze their message delivery data (recommended). When both are set, theapp instance level value overrides the app level value. When an end user givesconsent or rejects data collection, the app should set the experimentalenable or disable flag for each app instance as shown:
//userConsentholdsthedecisionoftheusertogivebigqueryexportconsent.constuserConsent=...;constmessaging=getMessagingInSw(app);experimentalSetDeliveryMetricsExportedToBigQuery(messaging,userConsent);
What data is exported to BigQuery?
Note that targeting stale tokens or inactive registrations may inflate some ofthese statistics.
The schema of the exported table is:
| _PARTITIONTIME | TIMESTAMP | This pseudo column contains a timestamp for the start of the day (in UTC) in which the data was loaded. For the YYYYMMDD partition, this pseudo column contains the value TIMESTAMP('YYYY-MM-DD'). |
| event_timestamp | TIMESTAMP | Event timestamp as recorded by the server |
| project_number | INTEGER | The project number identifies the project that sent the message |
| message_id | STRING | The message ID identifies a message. Generated from the App ID and timestamp, the message ID might, in some cases, not be globally unique. |
| instance_id | STRING | The unique id of the app the message is sent to (when available). It can be an instance ID or anFirebase installation ID. |
| message_type | STRING | The type of the message. Can be Notification message or Data message. Topic is used to identify the original message for a topic or campaign send; the subsequent messages is either a notification or data message. |
| sdk_platform | STRING | The platform of the recipient app |
| app_name | STRING | The package name for Android apps or the bundle id for iOS apps |
| collapse_key | STRING | The collapse key identifies a group of messages that can be collapsed.When a device is not connected, only the last message with a given collapsekey is queued for eventual delivery |
| priority | INTEGER | The priority of the message. 5 is "normal" priority and 10 is "high"priority. |
| ttl | INTEGER | This parameter specifies how long (in seconds) the message should be keptin FCM storage if the device is offline |
| topic | STRING | The name of the topic to which a message was sent (when applicable) |
| bulk_id | INTEGER | The bulk ID identifies a group of related messages, such as a particularsend to a topic |
| event | STRING | The type of the event.Possible values are:
|
| analytics_label | STRING | With theHTTP v1 API,the analytics label can be set when sending the message, in order to markthe message for analytics purposes |
What can you do with the exported data?
The following sections offer examples of queries that you can run in BigQueryagainst your exportedFCM data.
Count sent messages by app
SELECT app_name, COUNT(1)FROM`project ID.firebase_messaging.data`WHERE _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD') AND event = 'MESSAGE_ACCEPTED' AND message_id != ''GROUP BY 1;Count unique app instances targeted by messages
SELECT COUNT(DISTINCT instance_id)FROM`project ID.firebase_messaging.data`WHERE _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD') AND event = 'MESSAGE_ACCEPTED';Count notification messages sent
SELECT COUNT(1)FROM`project ID.firebase_messaging.data`WHERE _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD') AND event = 'MESSAGE_ACCEPTED' AND message_type = 'DISPLAY_NOTIFICATION';Count data messages sent
SELECT COUNT(1)FROM`project ID.firebase_messaging.data`WHERE _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD') AND event = 'MESSAGE_ACCEPTED' AND message_type = 'DATA_MESSAGE';Count messages sent to a topic or campaign
SELECT COUNT(1)FROM`project ID.firebase_messaging.data`WHERE _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD') AND event = 'MESSAGE_ACCEPTED' AND bulk_id =your bulk id AND message_id != '';To track events for a message sent to particular topic, modify this query toreplaceAND message_id != '' withAND message_id = <your message id>;.
Compute fanout duration for a given topic or campaign
The fanout start time is when the original request is received, and the endtime is the time the last individual message targeting a single instanceis created.
SELECTTIMESTAMP_DIFF(end_timestamp,start_timestamp,MILLISECOND)ASfanout_duration_ms,end_timestamp,start_timestampFROM(SELECTMAX(event_timestamp)ASend_timestampFROM`projectID.firebase_messaging.data`WHERE_PARTITIONTIME=TIMESTAMP('dateasYYYY-MM-DD')ANDevent='MESSAGE_ACCEPTED'ANDbulk_id=yourbulkid)sentCROSSJOIN(SELECTevent_timestampASstart_timestampFROM`projectID.firebase_messaging.data`WHERE_PARTITIONTIME=TIMESTAMP('dateasYYYY-MM-DD')ANDevent='MESSAGE_ACCEPTED'ANDbulk_id=yourbulkidANDmessage_type='TOPIC')initial_message;
Count percentage of delivered messages
SELECTmessages_sent,messages_delivered,messages_delivered/messages_sent*100ASpercent_deliveredFROM(SELECTCOUNT(DISTINCTCONCAT(message_id,instance_id))ASmessages_sentFROM`project ID.firebase_messaging.data`WHERE_PARTITIONTIME=TIMESTAMP('date as YYYY-MM-DD')ANDevent='MESSAGE_ACCEPTED')sentCROSSJOIN(SELECTCOUNT(DISTINCTCONCAT(message_id,instance_id))ASmessages_deliveredFROM`project ID.firebase_messaging.data`WHERE_PARTITIONTIME=TIMESTAMP('date as YYYY-MM-DD')AND(event='MESSAGE_DELIVERED'ANDmessage_idIN(SELECTmessage_idFROM`project ID.firebase_messaging.data`WHERE_PARTITIONTIME=TIMESTAMP('date as YYYY-MM-DD')ANDevent='MESSAGE_ACCEPTED'GROUPBY1))delivered;
Track all events for a given message id and instance id
SELECT *FROM`project ID.firebase_messaging.data`WHERE _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD') AND message_id = 'your message id' AND instance_id = 'your instance id'ORDER BY event_timestamp;Compute latency for a given message id and instance id
SELECTTIMESTAMP_DIFF(MAX(delivered_time),MIN(accepted_time),MILLISECOND)ASlatency_msFROM(SELECTevent_timestampASaccepted_timeFROM`project ID.firebase_messaging.data`WHERE_PARTITIONTIME=TIMESTAMP('date as YYYY-MM-DD')ANDmessage_id='your message id'ANDinstance_id='your instance id'ANDevent='MESSAGE_ACCEPTED')sentCROSSJOIN(SELECTevent_timestampASdelivered_timeFROM`project ID.firebase_messaging.data`WHERE_PARTITIONTIME=TIMESTAMP('date as YYYY-MM-DD')ANDmessage_id='your message id'ANDinstance_id='your instance id'AND(event='MESSAGE_DELIVERED')delivered;
Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2026-02-18 UTC.