GCM HTTP Connection Server

This document describes the Google Cloud Messaging (GCM) HTTP connection server. Connection servers are the Google-provided servers that take messages from the 3rd-party application server and sending them to the device.

Note: The content in this document applies to GCM with Chrome apps as well as Android.

See Implementing GCM Server for a list of all the message parameters and which connection server(s) supports them.

Authentication

To send a message, the application server issues a POST request to https://android.googleapis.com/gcm/send.

A message request is made of 2 parts: HTTP header and HTTP body.

The HTTP header must contain the following headers:

  • Authorization: key=YOUR_API_KEY
  • Content-Type: application/json for JSON; application/x-www-form-urlencoded;charset=UTF-8 for plain text.

For example:

Content-Type:application/json
Authorization:key=AIzaSyB-1uEai2WiUapxCs2Q0GZYzPu7Udno5aA

{
  "registration_ids" : ["APA91bHun4MxP5egoKMwt2KZFBaFUH-1RYqx..."],
  "data" : {
    ...
  },
}

Note: If Content-Type is omitted, the format is assumed to be plain text.

The HTTP body content depends on whether you're using JSON or plain text. See Implementing GCM Server for a list of all the parameters your JSON or plain text message can contain.

Request Format

Here is the smallest possible request (a message without any parameters and just one recipient) using JSON:

{ "registration_ids": [ "42" ] }

And here the same example using plain text:

registration_id=42

Here is a message with a payload and 6 recipients:

{ "data": {
    "score": "5x1",
    "time": "15:10"
  },
  "registration_ids": ["4", "8", "15", "16", "23", "42"]
}

Here is a message with all optional fields set and 6 recipients:

{ "collapse_key": "score_update",
  "time_to_live": 108,
  "delay_while_idle": true,
  "data": {
    "score": "4x8",
    "time": "15:16.2342"
  },
  "registration_ids":["4", "8", "15", "16", "23", "42"]
}

And here is the same message using plain-text format (but just 1 recipient):

collapse_key=score_update&time_to_live=108&delay_while_idle=1&data.score=4x8&data.time=15:16.2342&registration_id=42
  

Note: If your organization has a firewall that restricts the traffic to or from the Internet, you need to configure it to allow connectivity with GCM in order for your Android devices to receive messages. The ports to open are: 5228, 5229, and 5230. GCM typically only uses 5228, but it sometimes uses 5229 and 5230. GCM doesn't provide specific IPs, so you should allow your firewall to accept outgoing connections to all IP addresses contained in the IP blocks listed in Google's ASN of 15169.

Response format

There are two possible outcomes when trying to send a message:

  • The message is processed successfully.
  • The GCM server rejects the request.

When the message is processed successfully, the HTTP response has a 200 status and the body contains more information about the status of the message (including possible errors). When the request is rejected, the HTTP response contains a non-200 status code (such as 400, 401, or 503).

The following table summarizes the statuses that the HTTP response header might contain. Click the troubleshoot link for advice on how to deal with each type of error.

Response Description
200 Message was processed successfully. The response body will contain more details about the message status, but its format will depend whether the request was JSON or plain text. See Interpreting a success response for more details.
400 Only applies for JSON requests. Indicates that the request could not be parsed as JSON, or it contained invalid fields (for instance, passing a string where a number was expected). The exact failure reason is described in the response and the problem should be addressed before the request can be retried.
401 There was an error authenticating the sender account. Troubleshoot
5xx Errors in the 500-599 range (such as 500 or 503) indicate that there was an internal error in the GCM server while trying to process the request, or that the server is temporarily unavailable (for example, because of timeouts). Sender must retry later, honoring any Retry-After header included in the response. Application servers must implement exponential back-off. Troubleshoot

Interpreting a success response

When a JSON request is successful (HTTP status code 200), the response body contains a JSON object with the following fields:

Field Description
multicast_id Unique ID (number) identifying the multicast message.
success Number of messages that were processed without an error.
failure Number of messages that could not be processed.
canonical_ids Number of results that contain a canonical registration ID. See Advanced Topics for more discussion of this topic.
results Array of objects representing the status of the messages processed. The objects are listed in the same order as the request (i.e., for each registration ID in the request, its result is listed in the same index in the response) and they can have these fields:
  • message_id: String representing the message when it was successfully processed.
  • registration_id: If set, means that GCM processed the message but it has another canonical registration ID for that device, so sender should replace the IDs on future requests (otherwise they might be rejected). This field is never set if there is an error in the request.
  • error: String describing an error that occurred while processing the message for that recipient. The possible values are the same as documented in the above table, plus "Unavailable" (meaning GCM servers were busy and could not process the message for that particular recipient, so it could be retried).

If the value of failure and canonical_ids is 0, it's not necessary to parse the remainder of the response. Otherwise, we recommend that you iterate through the results field and do the following for each object in that list:

  • If message_id is set, check for registration_id:
    • If registration_id is set, replace the original ID with the new value (canonical ID) in your server database. Note that the original ID is not part of the result, so you need to obtain it from the list of code>registration_ids passed in the request (using the same index).
  • Otherwise, get the value of error:
    • If it is Unavailable, you could retry to send it in another request.
    • If it is NotRegistered, you should remove the registration ID from your server database because the application was uninstalled from the device or it does not have a broadcast receiver configured to receive com.google.android.c2dm.intent.RECEIVE intents.
    • Otherwise, there is something wrong in the registration ID passed in the request; it is probably a non-recoverable error that will also require removing the registration from the server database. See Interpreting an error response for all possible error values.

When a plain-text request is successful (HTTP status code 200), the response body contains 1 or 2 lines in the form of key/value pairs. The first line is always available and its content is either id=ID of sent message or Error=GCM error code. The second line, if available, has the format of registration_id=canonical ID. The second line is optional, and it can only be sent if the first line is not an error. We recommend handling the plain-text response in a similar way as handling the JSON response:

  • If first line starts with id, check second line:
    • If second line starts with registration_id, gets its value and replace the registration IDs in your server database.
  • Otherwise, get the value of Error:
    • If it is NotRegistered, remove the registration ID from your server database.
    • Otherwise, there is probably a non-recoverable error (Note: Plain-text requests will never return Unavailable as the error code, they would have returned a 500 HTTP status instead).

Interpreting an error response

Here are the recommendations for handling the different types of error that might occur when trying to send a message to a device:

Missing Registration ID
Check that the request contains a registration ID (either in the registration_id parameter in a plain text message, or in the registration_ids field in JSON).
Happens when error code is MissingRegistration.
Invalid Registration ID
Check the formatting of the registration ID that you pass to the server. Make sure it matches the registration ID the phone receives in the com.google.android.c2dm.intent.REGISTRATION intent and that you're not truncating it or adding additional characters.
Happens when error code is InvalidRegistration.
Mismatched Sender
A registration ID is tied to a certain group of senders. When an application registers for GCM usage, it must specify which senders are allowed to send messages. Make sure you're using one of those when trying to send messages to the device. If you switch to a different sender, the existing registration IDs won't work. Happens when error code is MismatchSenderId.
Unregistered Device
An existing registration ID may cease to be valid in a number of scenarios, including:
  • If the application manually unregisters by issuing a com.google.android.c2dm.intent.UNREGISTER intent.
  • If the application is automatically unregistered, which can happen (but is not guaranteed) if the user uninstalls the application.
  • If the registration ID expires. Google might decide to refresh registration IDs.
  • If the application is updated but the new version does not have a broadcast receiver configured to receive com.google.android.c2dm.intent.RECEIVE intents.
For all these cases, you should remove this registration ID from the 3rd-party server and stop using it to send messages.
Happens when error code is NotRegistered.
Message Too Big
The total size of the payload data that is included in a message can't exceed 4096 bytes. Note that this includes both the size of the keys as well as the values.
Happens when error code is MessageTooBig.
Invalid Data Key
The payload data contains a key (such as from or any value prefixed by google.) that is used internally by GCM in the com.google.android.c2dm.intent.RECEIVE Intent and cannot be used. Note that some words (such as collapse_key) are also used by GCM but are allowed in the payload, in which case the payload value will be overridden by the GCM value.
Happens when the error code is InvalidDataKey.
Invalid Time To Live
The value for the Time to Live field must be an integer representing a duration in seconds between 0 and 2,419,200 (4 weeks). Happens when error code is InvalidTtl.
Authentication Error
The sender account that you're trying to use to send a message couldn't be authenticated. Possible causes are:
  • Authorization header missing or with invalid syntax.
  • Invalid project number sent as key.
  • Key valid but with GCM service disabled.
  • Request originated from a server not whitelisted in the Server Key IPs.
Check that the token you're sending inside the Authorization header is the correct API key associated with your project. You can check the validity of your API key by running the following command:
# api_key=YOUR_API_KEY

# curl --header "Authorization: key=$api_key" --header Content-Type:"application/json" https://android.googleapis.com/gcm/send  -d "{\"registration_ids\":[\"ABC\"]}"
If you receive a 401 HTTP status code, your API key is not valid. Otherwise you should see something like this:
{"multicast_id":6782339717028231855,"success":0,"failure":1,"canonical_ids":0,"results":[{"error":"InvalidRegistration"}]}
If you want to confirm the validity of a registration ID, you can do so by replacing "ABC" with the registration ID.
Happens when the HTTP status code is 401.
Timeout
The server couldn't process the request in time. You should retry the same request, but you MUST obey the following requirements:
  • Honor the Retry-After header if it's included in the response from the GCM server.
  • Implement exponential back-off in your retry mechanism. This means an exponentially increasing delay after each failed retry (e.g. if you waited one second before the first retry, wait at least two second before the next one, then 4 seconds and so on). If you're sending multiple messages, delay each one independently by an additional random amount to avoid issuing a new request for all messages at the same time.
  • Senders that cause problems risk being blacklisted.
    Happens when the HTTP status code is between 501 and 599, or when the error field of a JSON object in the results array is Unavailable.
Internal Server Error
The server encountered an error while trying to process the request. You could retry the same request (obeying the requirements listed in the Timeout section), but if the error persists, please report the problem in the android-gcm group.
Happens when the HTTP status code is 500, or when the error field of a JSON object in the results array is InternalServerError.
Invalid Package Name
A message was addressed to a registration ID whose package name did not match the value passed in the request. Happens when error code is InvalidPackageName.
Device Message Rate Exceeded
The rate of messages to a particular device is too high. You should reduce the number of messages sent to this device and should not retry sending to this device immediately.
Happens when error code is DeviceMessageRateExceeded.

Example responses

This section shows a few examples of responses indicating messages that were processed successfully. See Request Format for the requests these responses are based on.

Here is a simple case of a JSON message successfully sent to one recipient without canonical IDs in the response:

{ "multicast_id": 108,
  "success": 1,
  "failure": 0,
  "canonical_ids": 0,
  "results": [
    { "message_id": "1:08" }
  ]
}

Or if the request was in plain-text format:

id=1:08

Here are JSON results for 6 recipients (IDs 4, 8, 15, 16, 23, and 42 respectively) with 3 messages successfully processed, 1 canonical registration ID returned, and 3 errors:

{ "multicast_id": 216,
  "success": 3,
  "failure": 3,
  "canonical_ids": 1,
  "results": [
    { "message_id": "1:0408" },
    { "error": "Unavailable" },
    { "error": "InvalidRegistration" },
    { "message_id": "1:1516" },
    { "message_id": "1:2342", "registration_id": "32" },
    { "error": "NotRegistered"}
  ]
}

In this example:

  • First message: success, not required.
  • Second message: should be resent (to registration ID 8).
  • Third message: had an unrecoverable error (maybe the value got corrupted in the database).
  • Fourth message: success, nothing required.
  • Fifth message: success, but the registration ID should be updated in the server database (from 23 to 32).
  • Sixth message: registration ID (42) should be removed from the server database because the application was uninstalled from the device.

Or if just the 4th message above was sent using plain-text format:

Error=InvalidRegistration

If the 5th message above was also sent using plain-text format:

id=1:2342
registration_id=32

Implementing an HTTP-Based App Server

This section gives examples of implementing an app server that works with the GCM HTTP connection server. Note that a full GCM implementation requires a client-side implementation, in addition to the server.

Requirements

For the web server:

  • Ant 1.8 (it might work with earlier versions, but it's not guaranteed).
  • One of the following:
  • A Google account registered to use GCM.
  • The API key for that account.

For the Android application:

  • Emulator (or device) running Android 2.2 with Google APIs.
  • The Google API project number of the account registered to use GCM.

Setting Up GCM

Before proceeding with the server and client setup, it's necessary to register a Google account with the Google API Console, enable Google Cloud Messaging in GCM, and obtain an API key from the Google API Console.

For instructions on how to set up GCM, see Getting Started.

Setting Up an HTTP Server

This section describes the different options for setting up an HTTP server.

Using a standard web server

To set up the server using a standard, servlet-compliant web server:

  1. From the open source site, download the following directories: gcm-server, samples/gcm-demo-server, and samples/gcm-demo-appengine.

  2. In a text editor, edit the samples/gcm-demo-server/WebContent/WEB-INF/classes/api.key and replace the existing text with the API key obtained above.
  3. In a shell window, go to the samples/gcm-demo-server directory.
  4. Generate the server's WAR file by running ant war:
  5. $ ant war
    
    Buildfile:build.xml
    
    init:
       [mkdir] Created dir: build/classes
       [mkdir] Created dir: dist
    
    compile:
       [javac] Compiling 6 source files to build/classes
    
    war:
         [war] Building war: dist/gcm-demo.war
    
    BUILD SUCCESSFUL
    Total time: 0 seconds
    
  6. Deploy the dist/gcm-demo.war to your running server. For instance, if you're using Jetty, copy gcm-demo.war to the webapps directory of the Jetty installation.
  7. Open the server's main page in a browser. The URL depends on the server you're using and your machine's IP address, but it will be something like http://192.168.1.10:8080/gcm-demo/home, where gcm-demo is the application context and /home is the path of the main servlet.

Note: You can get the IP by running ifconfig on Linux or MacOS, or ipconfig on Windows.

You server is now ready.

Using App Engine for Java

To set up the server using a standard App Engine for Java:

  1. Get the files from the open source site, as described above.

  2. In a text editor, edit samples/gcm-demo-appengine/src/com/google/android/gcm/demo/server/ApiKeyInitializer.java and replace the existing text with the API key obtained above.

    Note: The API key value set in that class will be used just once to create a persistent entity on App Engine. If you deploy the application, you can use App Engine's Datastore Viewer to change it later.

  3. In a shell window, go to the samples/gcm-demo-appengine directory.
  4. Start the development App Engine server by ant runserver, using the -Dsdk.dir to indicate the location of the App Engine SDK and -Dserver.host to set your server's hostname or IP address:
  5. $ ant -Dsdk.dir=/opt/google/appengine-java-sdk runserver -Dserver.host=192.168.1.10
    Buildfile: gcm-demo-appengine/build.xml
    
    init:
        [mkdir] Created dir: gcm-demo-appengine/dist
    
    copyjars:
    
    compile:
    
    datanucleusenhance:
      [enhance] DataNucleus Enhancer (version 1.1.4) : Enhancement of classes
      [enhance] DataNucleus Enhancer completed with success for 0 classes. Timings : input=28 ms, enhance=0 ms, total=28 ms. Consult the log for full details
      [enhance] DataNucleus Enhancer completed and no classes were enhanced. Consult the log for full details
    
    runserver:
         [java] Jun 15, 2012 8:46:06 PM com.google.apphosting.utils.jetty.JettyLogger info
         [java] INFO: Logging to JettyLogger(null) via com.google.apphosting.utils.jetty.JettyLogger
         [java] Jun 15, 2012 8:46:06 PM com.google.apphosting.utils.config.AppEngineWebXmlReader readAppEngineWebXml
         [java] INFO: Successfully processed gcm-demo-appengine/WebContent/WEB-INF/appengine-web.xml
         [java] Jun 15, 2012 8:46:06 PM com.google.apphosting.utils.config.AbstractConfigXmlReader readConfigXml
         [java] INFO: Successfully processed gcm-demo-appengine/WebContent/WEB-INF/web.xml
         [java] Jun 15, 2012 8:46:09 PM com.google.android.gcm.demo.server.ApiKeyInitializer contextInitialized
         [java] SEVERE: Created fake key. Please go to App Engine admin console, change its value to your API Key (the entity type is 'Settings' and its field to be changed is 'ApiKey'), then restart the server!
         [java] Jun 15, 2012 8:46:09 PM com.google.appengine.tools.development.DevAppServerImpl start
         [java] INFO: The server is running at http://192.168.1.10:8080/
         [java] Jun 15, 2012 8:46:09 PM com.google.appengine.tools.development.DevAppServerImpl start
         [java] INFO: The admin console is running at http://192.168.1.10:8080/_ah/admin
    
  6. Open the server's main page in a browser. The URL depends on the server you're using and your machine's IP address, but it will be something like http://192.168.1.10:8080/home, where /home is the path of the main servlet.
  7. Note: You can get the IP by running ifconfig on Linux or MacOS, or ipconfig on Windows.

You server is now ready.