Error Responses

Standard error responses

If a Management API request is successful, the API returns a 200 status code. If an error occurs with a request, the API returns an HTTP status code, status, and reason in the response based on the type of error. Additionally, the body of the response contains a detailed description of what caused the error. Here's an example of an error response:

{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "invalidParameter",
    "message": "Invalid value '-1' for max-results. Value must be within the range: [1, 1000]",
    "locationType": "parameter",
    "location": "max-results"
   }
  ],
  "code": 400,
  "message": "Invalid value '-1' for max-results. Value must be within the range: [1, 1000]"
 }
}

Error table

Code Reason Description Recommended Action
400 invalidParameter Indicates that a request parameter has an invalid value. The locationType and location fields in the error response provide information as to which value was invalid. Do not retry without fixing the problem. You need to provide a valid value for the parameter specified in the error response.
400 badRequest Indicates that the query was invalid. E.g., parent ID was missing or the combination of dimensions or metrics requested was not valid. Do not retry without fixing the problem. You need to make changes to the API query in order for it to work.
401 invalidCredentials Indicates that the auth token is invalid or has expired. Do not retry without fixing the problem. You need to get a new auth token.
403 insufficientPermissions Indicates that the user does not have sufficient permissions for the entity specified in the query. Do not retry without fixing the problem. You need to get sufficient permissions to perform the operation on the specified entity.
403 dailyLimitExceeded Indicates that user has exceeded the daily quota (either per project or per view (profile)). Do not retry without fixing the problem. You have used up your daily quota. See API Limits and Quotas.
403 userRateLimitExceeded Indicates that the Queries per 100 seconds per user limit has been exceeded. The default value set in Google API Console is 100 queries per 100 seconds per user. You can increase this limit in the Google API Console to a maximum of 1,000. Retry using exponential back-off. You need to slow down the rate at which you are sending the requests.
403 rateLimitExceeded Indicates that the project queries per 100 seconds rate limits have been exceeded. Retry using exponential back-off. You need to slow down the rate at which you are sending the requests.
403 quotaExceeded Indicates that the 10 concurrent requests per view (profile) in the Core Reporting API has been reached. Retry using exponential back-off. You need to wait for at least one in-progress request for this view (profile) to complete.
500 internalServerError Unexpected internal server error occurred. Do not retry this query more than once.
503 backendError Server returned an error. Do not retry this query more than once.

Handling 500 or 503 responses

A 500 or 503 error might result during heavy load or for larger more complex requests. For larger requests consider requesting data for a shorter time period. Also consider implementing exponential backoff. The frequency of these errors can be dependent on the view (profile) and the amount of reporting data associated with that view; A query that causes a 500 or 503 error for one view (profile) will not necessarily cause an error for the same query with a different view (profile).

Implementing Exponential Backoff

Exponential backoff is the process of a client periodically retrying a failed request over an increasing amount of time. It is a standard error handling strategy for network applications. The Management API is designed with the expectation that clients which choose to retry failed requests do so using exponential backoff. Besides being "required", using exponential backoff increases the efficiency of bandwidth usage, reduces the number of requests required to get a successful response, and maximizes the throughput of requests in concurrent environments.

The flow for implementing simple exponential backoff is as follows.

  1. Make a request to the API
  2. Receive an error response that has a retry-able error code
  3. Wait 1s + random_number_milliseconds seconds
  4. Retry request
  5. Receive an error response that has a retry-able error code
  6. Wait 2s + random_number_milliseconds seconds
  7. Retry request
  8. Receive an error response that has a retry-able error code
  9. Wait 4s + random_number_milliseconds seconds
  10. Retry request
  11. Receive an error response that has a retry-able error code
  12. Wait 8s + random_number_milliseconds seconds
  13. Retry request
  14. Receive an error response that has a retry-able error code
  15. Wait 16s + random_number_milliseconds seconds
  16. Retry request
  17. If you still get an error, stop and log the error.

In the above flow, random_number_milliseconds is a random number of milliseconds less than or equal to 1000. This is necessary to avoid certain lock errors in some concurrent implementations. random_number_milliseconds must be redefined after each wait.

Note: the wait is always (2 ^ n) + random_number_milliseconds, where n is a monotonically increasing integer initially defined as 0. n is incremented by 1 for each iteration (each request).

The algorithm is set to terminate when n is 5. This ceiling is in place only to stop clients from retrying infinitely, and results in a total delay of around 32 seconds before a request is deemed "an unrecoverable error".

The following Python code is an implementation of the above flow for recovering from errors that occur in a method called makeRequest.

import random
import time
from apiclient.errors import HttpError

def makeRequestWithExponentialBackoff(analytics):
  """Wrapper to request Google Analytics data with exponential backoff.

  The makeRequest method accepts the analytics service object, makes API
  requests and returns the response. If any error occurs, the makeRequest
  method is retried using exponential backoff.

  Args:
    analytics: The analytics service object

  Returns:
    The API response from the makeRequest method.
  """
  for n in range(0, 5):
    try:
      return makeRequest(analytics)

    except HttpError, error:
      if error.resp.reason in ['userRateLimitExceeded', 'quotaExceeded',
                               'internalServerError', 'backendError']:
        time.sleep((2 ** n) + random.random())
      else:
        break

  print "There has been an error, the request never succeeded."