Send messages

Before you begin

To use the Admin FCM API, you must first follow the steps in Add the Firebase Admin SDK to your Server. You are also required to initialize the Admin SDK with your Firebase project ID. This can be achieved in one of three ways:

  • Explicitly specify the projectId Firebase app option.
  • Initialize the SDK with a service account credential.
  • Set the GCLOUD_PROJECT environment variable.

    export GCLOUD_PROJECT='my-project-id'
    

This guide provides code samples for all languages supported by the Admim FCM API: Node, Java, Python and Go. For details on each API, see the corresponding reference:

Send to individual devices

The Admin FCM API allows you to send messages to individual devices by specifying a registration token for the target device. Registration tokens are strings generated by the client FCM SDKs for each end-user client app instance.

Each of the Firebase client SDKs are able to generate these registration tokens: iOS, Android, Web, C++, and Unity.

You can include a registration token in a message payload and pass it to the send() method of the Admin SDK:

Node.js

// This registration token comes from the client FCM SDKs.
var registrationToken = 'YOUR_REGISTRATION_TOKEN';

// See documentation on defining a message payload.
var message = {
  data: {
    score: '850',
    time: '2:45'
  },
  token: registrationToken
};

// Send a message to the device corresponding to the provided
// registration token.
admin.messaging().send(message)
  .then((response) => {
    // Response is a message ID string.
    console.log('Successfully sent message:', response);
  })
  .catch((error) => {
    console.log('Error sending message:', error);
  });

Java

// This registration token comes from the client FCM SDKs.
String registrationToken = "YOUR_REGISTRATION_TOKEN";

// See documentation on defining a message payload.
Message message = Message.builder()
    .putData("score", "850")
    .putData("time", "2:45")
    .setToken(registrationToken)
    .build();

// Send a message to the device corresponding to the provided
// registration token.
String response = FirebaseMessaging.getInstance().send(message);
// Response is a message ID string.
System.out.println("Successfully sent message: " + response);

Python

# This registration token comes from the client FCM SDKs.
registration_token = 'YOUR_REGISTRATION_TOKEN'

# See documentation on defining a message payload.
message = messaging.Message(
    data={
        'score': '850',
        'time': '2:45',
    },
    token=registration_token,
)

# Send a message to the device corresponding to the provided
# registration token.
response = messaging.send(message)
# Response is a message ID string.
print('Successfully sent message:', response)

Go

// Obtain a messaging.Client from the App.
ctx := context.Background()
client, err := app.Messaging(ctx)

// This registration token comes from the client FCM SDKs.
registrationToken := "YOUR_REGISTRATION_TOKEN"

// See documentation on defining a message payload.
message := &messaging.Message{
	Data: map[string]string{
		"score": "850",
		"time":  "2:45",
	},
	Token: registrationToken,
}

// Send a message to the device corresponding to the provided
// registration token.
response, err := client.Send(ctx, message)
if err != nil {
	log.Fatalln(err)
}
// Response is a message ID string.
fmt.Println("Successfully sent message:", response)

On successful completion, the send() method returns a message ID string in the format of projects/{project_id}/messages/{message_id}. Otherwise it produces an error. For a full list of error codes, including descriptions and resolution steps, see Admin FCM API Errors.

Send to a topic

Based on the publish/subscribe model, FCM topic messaging allows you to send a message to multiple devices that have opted in to a particular topic. You compose topic messages as needed, and FCM handles routing and delivering the message reliably to the right devices.

For example, users of a local tide forecasting app could opt in to a "tidal currents alerts" topic and receive notifications of optimal saltwater fishing conditions in specified areas. Users of a sports app could subscribe to automatic updates in live game scores for their favorite teams.

Some things to keep in mind about topics:

  • Topic messaging is best suited for content such as weather, or other publicly available information.
  • Topic messages are optimized for throughput rather than latency. For fast, secure delivery to single devices or small groups of devices, target messages to registration tokens, not topics.
  • If you need to send messages to multiple devices per user, consider device group messaging for those use cases.
  • Topic messaging supports unlimited subscriptions for each topic. However, FCM enforces limits in these areas:
    • One app instance can be subscribed to no more than 2000 topics.
    • If you are using batch import to subscribe app instances, each request is limited to 1000 app instances.
    • The frequency of new subscriptions is rate-limited per project. If you send too many subscription requests in a short period of time, FCM servers will respond with a 429 RESOURCE_EXHAUSTED ("quota exceeded") response. Retry with exponential backoff.

The send() method allows you to send a message to a topic by specifying the topic name:

Node.js

// The topic name can be optionally prefixed with "/topics/".
var topic = 'highScores';

// See documentation on defining a message payload.
var message = {
  data: {
    score: '850',
    time: '2:45'
  },
  topic: topic
};

// Send a message to devices subscribed to the provided topic.
admin.messaging().send(message)
  .then((response) => {
    // Response is a message ID string.
    console.log('Successfully sent message:', response);
  })
  .catch((error) => {
    console.log('Error sending message:', error);
  });

Java

// The topic name can be optionally prefixed with "/topics/".
String topic = "highScores";

// See documentation on defining a message payload.
Message message = Message.builder()
    .putData("score", "850")
    .putData("time", "2:45")
    .setTopic(topic)
    .build();

// Send a message to the devices subscribed to the provided topic.
String response = FirebaseMessaging.getInstance().send(message);
// Response is a message ID string.
System.out.println("Successfully sent message: " + response);

Python

# The topic name can be optionally prefixed with "/topics/".
topic = 'highScores'

# See documentation on defining a message payload.
message = messaging.Message(
    data={
        'score': '850',
        'time': '2:45',
    },
    topic=topic,
)

# Send a message to the devices subscribed to the provided topic.
response = messaging.send(message)
# Response is a message ID string.
print('Successfully sent message:', response)

Go

// The topic name can be optionally prefixed with "/topics/".
topic := "highScores"

// See documentation on defining a message payload.
message := &messaging.Message{
	Data: map[string]string{
		"score": "850",
		"time":  "2:45",
	},
	Topic: topic,
}

// Send a message to the devices subscribed to the provided topic.
response, err := client.Send(ctx, message)
if err != nil {
	log.Fatalln(err)
}
// Response is a message ID string.
fmt.Println("Successfully sent message:", response)

Send to a condition

Sometimes you want to send a message to a combination of topics. This is done by specifying a condition, which is a boolean expression that specifies the target topics. For example, the following condition will send messages to devices that are subscribed to TopicA and either TopicB or TopicC:

"'TopicA' in topics && ('TopicB' in topics || 'TopicC' in topics)"

FCM first evaluates any conditions in parentheses, and then evaluates the expression from left to right. In the above expression, a user subscribed to any single topic does not receive the message. Likewise, a user who does not subscribe to TopicA does not receive the message. These combinations do receive it:

  • TopicA and TopicB
  • TopicA and TopicC

The Admin FCM API allows you to specify a condition when calling the send() method to target devices which are subscribed to a combination of topics:

Node.js

// Define a condition which will send to devices which are subscribed
// to either the Google stock or the tech industry topics.
var condition = "'stock-GOOG' in topics || 'industry-tech' in topics";

// See documentation on defining a message payload.
var message = {
  notification: {
    title: '$GOOG up 1.43% on the day',
    body: '$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.'
  },
  condition: condition
};

// Send a message to devices subscribed to the combination of topics
// specified by the provided condition.
admin.messaging().send(message)
  .then((response) => {
    // Response is a message ID string.
    console.log('Successfully sent message:', response);
  })
  .catch((error) => {
    console.log('Error sending message:', error);
  });

Java

// Define a condition which will send to devices which are subscribed
// to either the Google stock or the tech industry topics.
String condition = "'stock-GOOG' in topics || 'industry-tech' in topics";

// See documentation on defining a message payload.
Message message = Message.builder()
    .setNotification(new Notification(
        "$GOOG up 1.43% on the day",
        "$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day."))
    .setCondition(condition)
    .build();

// Send a message to devices subscribed to the combination of topics
// specified by the provided condition.
String response = FirebaseMessaging.getInstance().send(message);
// Response is a message ID string.
System.out.println("Successfully sent message: " + response);

Python

# Define a condition which will send to devices which are subscribed
# to either the Google stock or the tech industry topics.
condition = "'stock-GOOG' in topics || 'industry-tech' in topics"

# See documentation on defining a message payload.
message = messaging.Message(
    notification=messaging.Notification(
        title='$GOOG up 1.43% on the day',
        body='$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.',
    ),
    condition=condition,
)

# Send a message to devices subscribed to the combination of topics
# specified by the provided condition.
response = messaging.send(message)
# Response is a message ID string.
print('Successfully sent message:', response)

Go

// Define a condition which will send to devices which are subscribed
// to either the Google stock or the tech industry topics.
condition := "'stock-GOOG' in topics || 'industry-tech' in topics"

// See documentation on defining a message payload.
message := &messaging.Message{
	Data: map[string]string{
		"score": "850",
		"time":  "2:45",
	},
	Condition: condition,
}

// Send a message to devices subscribed to the combination of topics
// specified by the provided condition.
response, err := client.Send(ctx, message)
if err != nil {
	log.Fatalln(err)
}
// Response is a message ID string.
fmt.Println("Successfully sent message:", response)

Sending in the dry run mode

Firebase Admin SDK supports sending FCM messages in the dry run mode. The SDK and the FCM service perform all the usual validations on the messages sent in this mode, but they are not actually delivered to the target devices. Therefore this feature can be used to check if a certain message will be accepted by the SDK and the FCM service for sending.

Node.js

// Send a message in the dry run mode.
var dryRun = true;
admin.messaging().send(message, dryRun)
  .then((response) => {
    // Response is a message ID string.
    console.log('Dry run successful:', response);
  })
  .catch((error) => {
    console.log('Error during dry run:', error);
  });

Java

// Send a message in the dry run mode.
boolean dryRun = true;
String response = FirebaseMessaging.getInstance().send(message, dryRun);
// Response is a message ID string.
System.out.println("Dry run successful: " + response);

Python

# Send a message in the dry run mode.
response = messaging.send(message, dry_run=True)
# Response is a message ID string.
print('Dry run successful:', response)

Go

// Send a message in the dry run mode.
response, err := client.SendDryRun(ctx, message)
if err != nil {
	log.Fatalln(err)
}
// Response is a message ID string.
fmt.Println("Dry run successful:", response)

Defining the message

The send() method of the Admin SDK accepts a Message object. The following tables outline all the fields that may be included in a Message. All fields are optional unless explicitly stated.

Top-level message parameters

Parameter Description
data A map of key-value pairs where all keys and values are strings.
notification An object with title and body fields.
android An object comprised of fields specific to Android messages. See Android-specific fields for details.
apns An object comprised of fields specific to Apple Push Notification Service (APNS). See APNS-specific fields for details.
webpush An object comprised of fields specific to WebPush protocol. See WebPush-specific fields for details.
token A registration token that identifies the recipient device of the message.
topic A topic name to send the message to. The topic name must not include the /topics/ prefix.
condition Condition to send the message to, e.g. "foo" in topics && "bar" in topics

It is possible to set android, apns, webpush and notification fields on the same message. FCM service will take all specified parameters into account and customize the message for each platform. However, a message must contain exactly one of the token, topic or condition fields. It is an error to specify zero or multiple fields.

The Admin SDK performs a wide range of validations locally before handing the message off to FCM service. Any validation failures will result in immediate exceptions describing the exact nature of the problem.

Android-specific fields

Node.js

var message = {
  android: {
    ttl: 3600 * 1000, // 1 hour in milliseconds
    priority: 'normal',
    notification: {
      title: '$GOOG up 1.43% on the day',
      body: '$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.',
      icon: 'stock_ticker_update',
      color: '#f45342'
    }
  },
  topic: 'industry-tech'
};

Java

Message message = Message.builder()
    .setAndroidConfig(AndroidConfig.builder()
        .setTtl(3600 * 1000) // 1 hour in milliseconds
        .setPriority(AndroidConfig.Priority.NORMAL)
        .setNotification(AndroidNotification.builder()
            .setTitle("$GOOG up 1.43% on the day")
            .setBody("$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.")
            .setIcon("stock_ticker_update")
            .setColor("#f45342")
            .build())
        .build())
    .setTopic("industry-tech")
    .build();

Python

message = messaging.Message(
    android=messaging.AndroidConfig(
        ttl=datetime.timedelta(seconds=3600),
        priority='normal',
        notification=messaging.AndroidNotification(
            title='$GOOG up 1.43% on the day',
            body='$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.',
            icon='stock_ticker_update',
            color='#f45342'
        ),
    ),
    topic='industry-tech',
)

Go

oneHour := time.Duration(1) * time.Hour
message := &messaging.Message{
	Android: &messaging.AndroidConfig{
		TTL:      &oneHour,
		Priority: "normal",
		Notification: &messaging.AndroidNotification{
			Title: "$GOOG up 1.43% on the day",
			Body:  "$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.",
			Icon:  "stock_ticker_update",
			Color: "#f45342",
		},
	},
	Topic: "industry-tech",
}

The following fields are allowed in the android section of a message:

Parameter Description
collapseKey An identifier of a group of messages that can be collapsed, so that only the last message gets sent when delivery can be resumed. A maximum of 4 different collapse keys is allowed at any given time.
priority Message priority. Must be one of normal or high.
ttl The time-to-live duration of the message. This is how long the message will be kept in FCM storage if the target devices are offline. Maximum allowed is 4 weeks, which is also the default. Set to 0 to send the message immediately (fire and forget). In case of Node.js and Java this duration is set in milliseconds. In Python it can be specified as a `datimetime.timedelta` value. In Go it is specified as a `time.Duration`.
restrictedPackageName Package name of the application where the registration tokens must match in order to receive the message.
data A map of key-value pairs where each key and value are strings. If specified, overrides the data field set on the top-level message.
notification An object comprised of fields specific to Android notifications. See the following table for a list of supported fields.

The Android notification object may contain the following fields:

Parameter Description
title Title of the notification. If set, overrides the title field set on the top-level message notification.
body The body of the notification. If set, overrides the body field set on the top-level message notification.
icon The notification icon. If not specified, FCM displays the launcher icon specified in your app manifest.
color The notification's icon color, expressed in #rrggbb format.
sound The sound to play when the device receives the notification. Supports default or the filename of a sound resource bundled in the app. Sound files must reside in /res/raw/.
tag Identifier used to replace existing notifications in the notification drawer. If not specified, each request creates a new notification. If specified and a notification with the same tag is already being shown, the new notification replaces the existing one in the notification drawer.
clickAction The action associated with a user click on the notification. If specified, an activity with a matching intent filter is launched when a user clicks on the notification.
bodyLocKey The key to the body string in the app's string resources to use to localize the body text to the user's current localization.
bodyLocArgs A list of string values to be used in place of the format specifiers in bodyLocKey to use to localize the body text to the user's current localization.
titleLocKey The key to the title string in the app's string resources to use to localize the title text to the user's current localization.
titleLocArgs A list of string values to be used in place of the format specifiers in titleLocKey to use to localize the title text to the user's current localization.

APNS-specific fields

Node.js

var message = {
  apns: {
    headers: {
      'apns-priority': '10'
    },
    payload: {
      aps: {
        alert: {
          title: '$GOOG up 1.43% on the day',
          body: '$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.',
        },
        badge: 42,
      }
    }
  },
  topic: 'industry-tech'
};

Java

Message message = Message.builder()
    .setApnsConfig(ApnsConfig.builder()
        .putHeader("apns-priority", "10")
        .setAps(Aps.builder()
            .setAlert(ApsAlert.builder()
                .setTitle("$GOOG up 1.43% on the day")
                .setBody("$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.")
                .build())
            .setBadge(42)
            .build())
        .build())
    .setTopic("industry-tech")
    .build();

Python

message = messaging.Message(
    apns=messaging.APNSConfig(
        headers={'apns-priority': '10'},
        payload=messaging.APNSPayload(
            aps=messaging.Aps(
                alert=messaging.ApsAlert(
                    title='$GOOG up 1.43% on the day',
                    body='$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.',
                ),
                badge=42,
            ),
        ),
    ),
    topic='industry-tech',
)

Go

badge := 42
message := &messaging.Message{
	APNS: &messaging.APNSConfig{
		Headers: map[string]string{
			"apns-priority": "10",
		},
		Payload: &messaging.APNSPayload{
			Aps: &messaging.Aps{
				Alert: &messaging.ApsAlert{
					Title: "$GOOG up 1.43% on the day",
					Body:  "$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.",
				},
				Badge: &badge,
			},
		},
	},
	Topic: "industry-tech",
}

The following fields are allowed in the apns section of a message:

Parameter Description
headers HTTP request headers defined in Apple Push Notification Service. Refer to APNS request headers for supported headers.
payload APNS payload including the aps dictionary and other custom key-value pairs. Refer to APNS payload key reference for supported fields. Admin SDK provides typed setters for the common APNS payload fields.

WebPush-specific fields

Node.js

var message = {
  webpush: {
    notification: {
      title: '$GOOG up 1.43% on the day',
      body: '$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.',
      icon: 'https://my-server/icon.png'
    }
  },
  topic: 'industry-tech'
};

Java

Message message = Message.builder()
    .setWebpushConfig(WebpushConfig.builder()
        .setNotification(new WebpushNotification(
            "$GOOG up 1.43% on the day",
            "$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.",
            "https://my-server/icon.png"))
        .build())
    .setTopic("industry-tech")
    .build();

Python

message = messaging.Message(
    webpush=messaging.WebpushConfig(
        notification=messaging.WebpushNotification(
            title='$GOOG up 1.43% on the day',
            body='$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.',
            icon='https://my-server/icon.png',
        ),
    ),
    topic='industry-tech',
)

Go

message := &messaging.Message{
	Webpush: &messaging.WebpushConfig{
		Notification: &messaging.WebpushNotification{
			Title: "$GOOG up 1.43% on the day",
			Body:  "$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.",
			Icon:  "https://my-server/icon.png",
		},
	},
	Topic: "industry-tech",
}

The following fields are allowed in the webpush section of a message:

Parameter Description
headers HTTP request headers defined in WebPush protocol Refer to WebPush specification for supported headers.
data A map of key-value pairs where each key and value are strings. If specified, overrides the data field set on the top-level message.
notification An object with title, body, and icon fields. Title and body overrides the corresponding fields on the top-level message notification.

Putting it all together

A message may contain configuration parameters for multiple device platforms. This means it is possible to include android, apns and webpush fields in the same message. The FCM service customizes the message for each target platform when delivering. The following example shows how a notification has been customized for Android and iOS platforms:

Node.js

var message = {
  notification: {
    title: '$GOOG up 1.43% on the day',
    body: '$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.',
  },
  android: {
    ttl: 3600 * 1000,
    notification: {
      icon: 'stock_ticker_update',
      color: '#f45342',
    },
  },
  apns: {
    payload: {
      aps: {
        badge: 42,
      },
    },
  },
  topic: 'industry-tech'
};

Java

Message message = Message.builder()
    .setNotification(new Notification(
        "$GOOG up 1.43% on the day",
        "$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day."))
    .setAndroidConfig(AndroidConfig.builder()
        .setTtl(3600 * 1000)
        .setNotification(AndroidNotification.builder()
            .setIcon("stock_ticker_update")
            .setColor("#f45342")
            .build())
        .build())
    .setApnsConfig(ApnsConfig.builder()
        .setAps(Aps.builder()
            .setBadge(42)
            .build())
        .build())
    .setTopic("industry-tech")
    .build();

Python

message = messaging.Message(
    notification=messaging.Notification(
        title='$GOOG up 1.43% on the day',
        body='$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.',
    ),
    android=messaging.AndroidConfig(
        ttl=datetime.timedelta(seconds=3600),
        priority='normal',
        notification=messaging.AndroidNotification(
            icon='stock_ticker_update',
            color='#f45342'
        ),
    ),
    apns=messaging.APNSConfig(
        payload=messaging.APNSPayload(
            aps=messaging.Aps(badge=42),
        ),
    ),
    topic='industry-tech',
)

Go

oneHour := time.Duration(1) * time.Hour
badge := 42
message := &messaging.Message{
	Notification: &messaging.Notification{
		Title: "$GOOG up 1.43% on the day",
		Body:  "$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.",
	},
	Android: &messaging.AndroidConfig{
		TTL: &oneHour,
		Notification: &messaging.AndroidNotification{
			Icon:  "stock_ticker_update",
			Color: "#f45342",
		},
	},
	APNS: &messaging.APNSConfig{
		Payload: &messaging.APNSPayload{
			Aps: &messaging.Aps{
				Badge: &badge,
			},
		},
	},
	Topic: "industry-tech",
}

In the same vein, it is possible include both data and notification fields in the same message.

Legacy send operations

In addition to the send() method discussed above, the Admin Node.js SDK supports four additional send operations:

  • sendToDevice()
  • sendToDeviceGroup()
  • sendToTopic()
  • sendToCondition()

Refer Legacy FCM API for details on how and when to use these APIs.

Send feedback about...

Need help? Visit our support page.