Content API for Shopping

Content API for Shopping Developer's Guide: Python

Google provides the Google Data APIs Python Client Library for connecting to the Content API for Shopping server and interpreting the results. This client library is by no means the only way of connecting to the Content API for Shopping server. This document demonstrates how to use the Python client library for accessing and editing your data.


  1. Introduction
  2. Prerequisites
  3. Setup
  4. Performing authentication
  5. Creating and Editing a Product Object
    1. Sample Product as XML
    2. Building a Product in Python
    3. Editing a Product in Python
  6. Managing items
    1. Retrieving all your items
    2. Retrieving a single item
    3. Inserting items
    4. Updating items
    5. Deleting items
  7. Batching
    1. Creating a list of entries to be modified
    2. Batch insert
    3. Batch update
    4. Batch delete
    5. Mixed Batch requests
  8. Error Handling
    1. Errors on Individual Items
    2. Batch Errors
    3. Inspecting Errors
  9. Warnings and Dry-run Mode
  10. Accessing Product Performance Data
  11. The generic projection


The Google Data APIs Python Client Library is a generic client library that can be used with many of Google's APIs. For several of these APIs, a custom client class has been defined within the library to represent the data relevant to the API.

As part of this documentation, we provide an example of how you can do this. This example is only meant for teaching how the client library is used. It may not have all the data fields you might want to access; you can find information about fields not used in this example in the library documentation.


This document assumes that you know how to program in Python, and that you are familiar with the basic concepts of the Google Content API for Shopping.


For help setting up the client library, see the Getting Started Guide. The Python client library is available for download from the project page or from the Mercurial repository. The Python client library has dependencies such as the ElementTree module that you may also need to download and install. Note that in order to use the Python client library, you must be running Python 2.2 or newer.

This documentation has been written for and tested with version 2.0.17 of the client library. The latest version is not guaranteed to be backward compatible with this example. You can include the client library in your project by following the setup instructions.

Once you have installed the client library, you'll need to use the following import statements for the examples below:

import atom.core
import atom.http_core
import gdata.client
import gdata.contentforshopping.client
import gdata.gauth

Performing authentication

To make requests to the Content API for Shopping server, you need to be authenticated, which you can do using a number of methods provided by Google. For more information, read the Google Account Authentication documentation. We strongly recommend starting your new applications using OAuth 2.0 as well as migrating existing ones to it. As was recently announced, all authentication protocols other than OAuth 2.0 are being phased out in the near future.

For information specific to the Google Data Protocol, read the Authentication and Authorization documentation. The Google Data APIs Python Client Library provides a GDClient class that can perform authentication and submit API requests. To use the Content API for Shopping, we provide a specialized subclass: ContentForShoppingClient. Since the account ID is needed to construct the URL of each request to the Content API for Shopping server, the constructor for ContentForShoppingClient also takes an optional account_id parameter:

import gdata.contentforshopping.client

ACCOUNT_ID = '1234567'

shopping_client = gdata.contentforshopping.client.ContentForShoppingClient( account_id=ACCOUNT_ID)

Authorizing requests with OAuth 2.0

The details of the authorization process, or "flow," for OAuth 2.0 vary somewhat depending on what kind of application you're writing. The following general process applies to all application types:

  1. When you create your application, you register it with Google. Google then provides information you'll need later, such as a client ID and a client secret.
  2. When your application needs access to user data, it asks Google for a particular scope of access.
  3. Google displays an OAuth dialog to the user, asking them to authorize your application to request some of their data.
  4. If the user approves, then Google gives your application a short-lived access token.
  5. Your application requests user data, attaching the access token to the request.
  6. If Google determines that your request and the token are valid, it returns the requested data.

For detailed information about flows for various types of applications, see Google's OAuth 2.0 documentation. To use OAuth 2.0 with the Python client library, you can use the gdata.gauth.OAuth2Token class. You can follow a Google Developers blog post for an in-depth description of the process.

Creating an Empty Token Object

To create an initial token, you need your client ID and secret from the APIs console, and the scope for the Content API for Shopping:

import gdata.gauth

CLIENT_ID = ''  # Provided in the APIs console
CLIENT_SECRET = 'SeCr3Tv4lu3'  # Provided in the APIs console
SCOPE = ''
USER_AGENT = 'content-api-example'

auth_token = gdata.gauth.OAuth2Token(
    client_id=CLIENT_ID, client_secret=CLIENT_SECRET,
    scope=SCOPE, user_agent=USER_AGENT)

Authorizing a Token Object

After you have created this token, you or the user need to authorize the token to make API requests by visiting the OAuth 2.0 authorize URL:

authorize_url = auth_token.generate_authorize_url(
print 'Please visit: %s' % authorize_url
Note: As mentioned in the OAuth 2.0 documentation, if your application loses the refresh token, then it needs to re-prompt the user for consent before obtaining another refresh token. If you need to re-prompt the user for consent, include the approval_prompt parameter in the authorization code request, and set the value to force:
authorize_url = auth_token.generate_authorize_url(

Exchanging an Authorization Code for an Access Token

After successfully authorizing, the user is redirected to a page that has a value in the query parameter code containing the needed authorization. You can retrieve the query parameters from the redirect URL and pass them to the OAuth2Token object to retrieve an access token:

import atom.http_core

redirect_url = ''
url = atom.http_core.ParseUri(redirect_url)

Using a Token Object

Finally, to use this token with a client object, you must authorize the client for use with the token:


Note: When an access token expires, the API requests signed with that token are rejected. After calling the token's authorize method with the client as the argument, the client can address such rejections. When the said requests fail, the client uses the auth_token to attempt obtaining a new access token (using the refresh token stored on the token object). If it successfully obtains a new access token, the client resends the original API request, signed with the new access token.

ClientLogin for installed/mobile applications

Detailed code samples that show how to use a GDClient instance to authenticate with ClientLogin are provided in the Authentication and Authorization documentation. In order to use the Content API for Shopping, you need to authenticate with the structuredcontent service.

EMAIL = ''
PASSWORD = 'pa$$word'
APPLICATION_NAME = 'content-api-example'
SERVICE = 'structuredcontent'

auth_token = shopping_client.ClientLogin(EMAIL, PASSWORD,

Once you have called ClientLogin, the auth_token attribute on the client object is set to the returned value. When making future calls to the API, an auth_token can either be explicitly passed with each request or the request method from the parent class falls back to the auth_token attribute on the client object.

Creating and Editing a Product Object

The API represents products as XML objects. Each data item starts with an <entry> tag, includes a set of attributes that describe the said item, and ends with the </entry> tag.

Using the Python client library, we will construct an example product.

Building a Product in Python

The Python client library provides a ProductEntry class whose attributes correspond to the XML product attributes. These can be set to instances of custom classes that encapsulate the value of the attributes that you set and the underlying XML namespace. These classes abstract from the creation and the structure of the XML object, allowing you to focus on your data.

The above example doesn't use every single attribute defined by the API; for details on attributes not listed here, check out the documentation for the custom data classes defined for the Content API for Shopping. In this example, each type of data class is used at least once to explain the mechanics corresponding to each type of XML attribute.

Before using of the classes provided by the Python client library, we need to import the module containing all the custom attribute classes:


We start with an empty <entry> element that is the "container" for all our data:

entry =

Elements from the Atom namespace

Since <title>, <content>, and <link> come from the xmlns="" namespace, which is shared across all GData APIs, we also need to import the module with Atom data classes:


To set the title and description, we can use custom Atom classes to directly set an attribute on the entry:

entry.title ='Camera')
entry.content =
    'A great compact body to make it easy to get a great shot everytime.')

To set the product link, we proceed a bit differently. The link attribute of each ProductEntry object is a list that starts out empty. To add the link, we append to the list of links:

link =
    rel='alternate', type='text/html',

Standard Elements

For the majority of the attributes in the sc and scp namespaces, adding the attribute to the ProductEntry object simply involves setting the attribute with an object from the custom class for that attribute:

entry.product_id ='123456')
entry.image_link =
entry.target_country ='US')
entry.content_language ='en')
entry.brand ='Acme')
entry.condition ='new')
entry.gtin ='00013803105384')
entry.color ='black')
entry.availability ='in stock')

For the <scp:product_type> element and other attributes that use taxonomy values, the ampersand (&), greater than sign (>), less than sign (<), and some other characters must be escaped as proper XML. Conveniently, this is done automatically by the Python client library:

product_type = 'Cameras & Optics > Cameras > Digital Cameras'
entry.product_type =

Elements with Parameters

Many of these classes take a single string in their constructor (the __init__ method in the Python world), but XML elements that also use parameters must be constructed using additional keyword arguments:

entry.price ='25.00', unit='usd')
entry.shipping_weight =
    '0.1', unit='lb')

Nested Elements

The <scp:shipping> and <scp:tax> elements are represented as list attributes on the ProductEntry object, just like the link attribute. Since they are also nested elements, the corresponding classes for shipping and tax also have their own attributes that can be set:

shipping =
shipping.shipping_country ='US')
shipping.shipping_region ='MA')
shipping.shipping_service =
    'Speedy Shipping - Ground')
shipping.shipping_price =
    '5.95', unit='usd')

tax =
tax.tax_country ='US')
tax.tax_region ='CA')
tax.tax_rate ='8.25')
tax.tax_ship ='true')

Note: The second shipping value and the second tax value can be added in the same fashion.

Finally, the only remaining attribute from the example XML is the required destination. Like tax and shipping, this is contained in a nested element, so setting the attribute is similar. Since it is possible to set more than one required destination, required_destination is a list attribute.

product_control =
required_destination =
entry.control = product_control
Note: Though we are using the full path to the module, you can shorten the module name by aliasing the import:
import as cfs_data
or you can explicitly import all classes that you will use:
from import ProductEntry
from import ProductId
from import ImageLink
from import TargetCountry
or you can simply import all names from the data module:
from import *
The last method is discouraged, since it makes debugging more difficult.

Editing a Product in Python

When making requests, you may also be working with ProductEntry objects that you did not construct yourself, but that you retrieved from your online inventory via a request to the Content API. To edit these objects, you can set the corresponding attribute as you did when first building the object. For example, if the price has lowered from $25 USD to $22.50 USD:

entry.price ='22.50', unit='usd')

If you have enabled warnings, then you may want to inspect the warnings list attribute on the product object:

warnings_list = entry.control.warnings.warnings
for warning_elt in warnings_list:
    print 'Code:', warning_elt.code.text
    print 'Domain:', warning_elt.domain.text
    print 'Location:', warning_elt.location.text
    print 'Message:', warning_elt.message.text

For the product defined above, you may get this output:

Code: validation/missing_recommended
Domain: ProductSearch
Location: google_product_category
Message: We recommend including this attribute.

You can address this error by updating the ProductEntry:

gpc = 'Cameras & Optics > Cameras > Digital Cameras'
entry.google_product_category = \

Managing items

All actions on items—getting, inserting, updating, and deleting—can be performed with a ContentForShoppingClient object. For each action performed with the client object, an account ID and an authentication token must also be provided. The account ID is needed to construct the request URL (read more in the reference), and the authentication token is needed to sign each request.

The following code snippets assume that the client object has been constructed with an account ID and has called some authentication method, which has set the auth_token attribute. When the account ID and token are set, each API request made by the client uses them as fallback values. You may also include them explicitly by using the account_id and auth_token keyword parameters for each function that makes calls to the Content API for Shopping.

Retrieving all your items

To retrieve the list of data items belonging to your inventory, you make an HTTP GET request to the items feed:

You can use a client object to create and send this request to the Content API for Shopping:

items_feed = shopping_client.GetProducts()

The response items_feed contains a ProductFeed object. This object represents the <feed> element that contains a list of <entry> elements, one for each item in the feed. The entry attribute on the ProductFeed object contains the list of ProductEntry objects returned in the feed:

for entry in items_feed.entry:
    print entry.title.text

Paging through results

For paging through your items, use the max-results and start-token query parameters (see the reference for more details).

To specify the maximum number of data items to be returned, use the max_results keyword argument. If you don't specify a value, the Python client library will not append any value to the API request and default value of 25 will be used.

max_results = 10
items_feed = shopping_client.GetProducts(max_results=max_results)

If your product inventory exceeds the maximum number of results requested, you need to submit another request to the Content API for Shopping to get the next page of results. In this case, the returned feed contains a link element resembling:

<link rel="next" type="application/atom+xml"

This link contains a token pointing to the next page of results. You can retrieve it from the feed and specify the start_token keyword argument in the next request:

start_token = items_feed.GetStartToken()
next_items_feed = shopping_client.GetProducts(max_results=max_results,

Note: The parameter max-results cannot exceed 250.

Retrieving a single item

You can retrieve a specific item's data by executing an HTTP GET request to its unique URL. This is formed using the item's channel, language, target country, and ID you submitted with the item. To retrieve the item from the above example, the request URL is:

To retrieve this item using the Python client library:

product_id, country, language = '123456', 'US', 'en'
entry = shopping_client.GetProduct(product_id, country, language)

Inserting items

After creating the product item you want to insert, use a client object to send a request to the Content API for Shopping:

inserted_product = shopping_client.InsertProduct(entry)

If the insert succeeds, the variable inserted_product contains a ProductEntry object parsed from the response. The server returns the item you just inserted, together with some additional attributes for your entry, such as the Atom id element and the published date and time.

Updating items

Updating an item is very similar to inserting it. The only difference with inserting a product is that it first checks if the product is already present in your inventory. By sending an HTTP PUT request to the URL of the item, you will overwrite the old item. The request therefore needs to contain all of the attributes, even those you don't want to change.

To update an item, you can use a client object to send a request to the Content API for Shopping:

updated_product = shopping_client.UpdateProduct(entry)

How the value entry is constructed may vary. After inserting a product, you may want to edit the product based on warnings returned from the Content API for Shopping server. Alternatively, changes in your inventory may cause you to first retrieve an item so that you may update its contents.

As with inserting, if the update request succeeds, the variable updated_product contains a ProductEntry object parsed from the response.

Note: The Python client library constructs the URL for the PUT request by using the product_id, target_country, and content_language attributes of the entry.

Deleting items

For deleting an item, we simply send a delete request to its URL:

delete_response = shopping_client.DeleteProduct(entry)
print delete_response.status, delete_response.reason

Rather than a ProductEntry element, the returned value is an httplib.HTTPResponse object. For a successful delete, the response status and reason is 200 OK.

Note: The Python client library constructs the URL for the DELETE request by using the product_id, target_country, and content_language attributes of the entry.


When sending many requests, it is better to combine them into a single batch request instead of sending them individually. Batching reduces the time to process your requests, and additionally reduces the load on your servers. For a general introduction to batching, read the GData documentation.

To perform batching with the Content API for Shopping server, create a list of entries to be modified (inserted, updated, deleted) in one batch. Each entry of the list can be a normal ProductEntry object, except that additionally the batch_operation attribute must be set with the desired operation. This property is enabled because the ProductEntry class inherits from the class rather than directly from the class.

Creating a list of entries to be modified

To tell the Content API for Shopping server what to do with the products in the list that you send as a batch, that is, if it should insert, update, or delete them, you need to add this information to every single product by setting its batch:operation field. Additionally, for update and delete requests, you need to set the atom:id field to the edit link, so that the server can identify the product. For example:


# Retrieve a product to update
product_id, country, language = '123456', 'US', 'en'
entry = shopping_client.GetProduct(product_id, country, language)

# Add batch attributes to the product
entry.batch_operation ='update')
edit_link = entry.GetEditLink()  # rel="edit" link contains the product's URL =

You may set the batch:id field to any value, so that you can more easily identify the products in the server's response. In case of products, it usually makes sense to set the batch ID (batch:id) to the same value as the product ID (sc:id):

entry.batch_id =

Batch insert

To submit a batch of items, all of which need to be inserted, the client object has a special method that will handle the setting of the batch operation.

If you have created two items—entry1 and entry2—you can insert them as follows:

products = [entry1, entry2]
inserted_feed = shopping_client.InsertProducts(products)

for entry in inserted_feed.entry:
    print '%s inserted with code %s' % (entry.title.text,

To set the batch ID (batch:id) to the product ID (sc:id) or some other value, you'll need to do so on each ProductEntry before calling InsertProducts.

Batch update

As described above, a batch update can be accomplished by appending the batch operation and the product ID to the existing product data (with changes/updates included). As with inserting, to submit a batch of items, all of which need to be updated, the client object has a special method that will handle the setting of the batch operation.

For a ProductEntry object returned from the API, the "edit" link will contain the unique product URL; proceeding as above:

edit_link = entry.GetEditLink() =

If the edit link is not set, you need to construct the unique product URL by hand; this can be done using your account ID, along with the product_id, target_country, and content_language attributes of the entry.

After setting the atom:id field, you can update the items as follows:

products = [entry1, entry2]
updated_feed = shopping_client.UpdateProducts(products)

for entry in updated_feed.entry:
    print '%s updated with code %s' % (entry.title.text,

Batch delete

As described in the GData documentation, a batch delete can be accomplished by including only the batch operation and the product ID. The product URL in the atom:id element should be set to the value of the rel="edit" link of the product as in the updating section.

To submit a batch of items, all of which need to be deleted, the client object has a special method that will handle the setting of the batch operation:

products = [entry1, entry2]
deleted_feed = shopping_client.DeleteProducts(products)

for entry in deleted_feed.entry:
    print '%s deleted with code %s' % (entry.title.text,

Mixed Batch requests

A batch request can contain any combination of insert, update, and delete entries. You can use one of the client methods defined above (API_VERBProducts), or manually set batch_operation on each ProductEntry object, followed by adding every ProductEntry to an empty ProductFeed object.

For example, to mix different operations with three entries (insert_entry, update_entry, and delete_entry):

feed =

insert_entry.batch_operation ='insert')

update_entry.batch_operation ='update')
update_edit_link = update_entry.GetEditLink() =

delete_entry.batch_operation ='delete')
delete_edit_link = delete_entry.GetEditLink() =

mixed_feed = shopping_client.Batch(feed)

A request may not contain two or more actions that affect the same product. If it does, there is not necessarily an error message, but the outcome is undefined.

If you are inserting a new product that has the same sc:id as another one of your products that is already stored on the server, then the new product overwrites the previous one. This is another way of updating products.

Error Handling

The examples above do not contain any error-handling code to keep things simple. For more details on the errors that the Content API for Shopping server may return, see the error code list.

Errors on Individual Items

If a non-batch request is rejected by the Content API for Shopping server, the client method throws either an instance of gdata.client.RequestError, an instance of some subclass of RequestError, or an instance of gdata.client.Unauthorized. If you encounter a different error, it is likely caused by an issue other than the Python client library.

You can catch a gdata.client.RequestError and recover the body of the rejected request in the body attribute:

import gdata.client

    inserted_product = shopping_client.InsertProduct(entry)
except gdata.client.RequestError, exc:
    error_response = exc.body

You can parse the errors returned by the Content API for Shopping server into an instance of the ContentForShoppingErrors class:

import atom.core

errors = atom.core.parse(

Batch Errors

In a batch request, many operations are combined into one request. Some of these operations may succeed, while some may fail. When batching, the batch request should return an HTTP success code, even if there were errors for individual products. In the batch response, the result of each operation is represented as an <entry> element. For operations that fail, the errors are returned in the following element:

<content type='application/'>
For such errors, the reason field contains the error message you would have received, had you sent the product as an individual request.

To determine if an operation has failed or succeeded, you can check the response code from the <batch:status> element. If the code is something other than 200 or 201, there will be an errors block within the <content> element, which you can access with a custom method on the ProductEntry:

feed = shopping_client.InsertProducts(products)

for entry in feed.entry:
    if entry.batch_status.code != 200 and entry.batch_status.code != 201:
        errors = entry.GetBatchErrors()

If an errors element is not found, errors is None; if it is found, a ContentForShoppingErrors is returned.

In rare cases, a batch interruption can occur. In this case, you get an entry returned that doesn't contain any product or batch:id, but instead contains the field batch:interrupted. This means that all products that are not reported as processed, have been dropped.

Inspecting Errors

The errors returned by the Content API for Shopping server are in the gd:errors element with a list of gd:error elements corresponding to each error. These errors contain similar information as the warnings elements:

for error in errors.errors:
    print 'Code:', error.code.text
    print 'Domain:', error.domain.text
    print 'Location:', error.location.text
    print 'Internal Reason:', error.internal_reason.text

Warnings and Dry-run Mode

The Content API for Shopping allows you to run all the requests that modify persistent data (insert, update, delete) in dry-run mode. A dry-run request is similar to a normal request (authentication checks are performed as well as validity checks of the operation and the item), but the final step of persisting the changes is skipped. Dry-run mode is intended to check the validity of your requests. For update and delete operations, the validity of the request is determined independently of the item's existence.

You can set a single request or all operations in a batch to be executed in dry-run, by appending the dry-run query parameter at the end of the batch URL request. The Python client library does this for you by providing a dry_run keyword argument to all client methods. This argument defaults to False, but when set to True, the parameter is appended to the query. For example, to test if an insert of entry will succeed:

inserted_product = shopping_client.InsertProduct(entry, dry_run=True)

The Content API for Shopping can also return warnings, which allow some non-fatal product data issues to be detected.

As with the dry-run mode, warnings are opt-in, and should be enabled by adding the warnings parameter to the request URL. If this parameter is provided, additional warning elements are returned. As noted in the editing section, warnings can be used to make edits to improve your product data. As with dry-run mode, a warnings keyword parameter can be used:

inserted_product = shopping_client.InsertProduct(entry, warnings=True)

When used, the warnings appear in the app:control element. When parsed by the Python client library, we can access these warnings from the product control attribute:

warnings = inserted_product.control.warnings

Accessing Product Performance Data

The Content API for Shopping allows users to view data on how often products have been clicked on Google (for example, Google Shopping and Google Book Search).

To access this data, add the query parameters performance.start and performance.end to the GET request URI when retrieving a list of products. Each of these parameters should be a date, one for the start, and one for the end of the required period of performance data.

This is enabled in the GetProducts client method via the keyword parameters performance_start and performance_end. To specify these dates, you can use a string date represented as 'YYYY-MM-DD'. For example, to retrieve the product data for your first 5 products from January 1, 2012 to January 5, 2012:

items_feed = shopping_client.GetProducts(max_results=5,

By enabling this, we can access the performance data for each parsed ProductEntry in the result items_feed using the performance attribute:

for entry in items_feed.entry:
    print entry.title.text

    performance = entry.performance
    for datapoint in performance.datapoint:
        print '%s paid clicks on %s' % (

This attribute contains a Performance object that holds a list of Datapoint objects, each of which has clicks and date set.

The generic projection

The schema projection is used in all of the examples above, but the Python client library can also handle the generic projection. Product data returned from the generic projection does not have any scp elements; they are all replaced by generic attributes.

Instead of the scp attributes, we get a list of generic attributes (sc:attribute) and generic attribute groups (sc:group).

For example, rather than

<scp:availability>in stock</scp:availability>
you receive
<sc:attribute name="availability">in stock</sc:attribute>

For a group example, rather than

you receive
<sc:group name="tax">
  <sc:attribute name="country">US</sc:attribute>
  <sc:attribute name="region">CA</sc:attribute>
  <sc:attribute name="rate">8.25</sc:attribute>
  <sc:attribute name="tax ship">true</sc:attribute>

The Python client library represents them with the Attribute and Group classes. To construct the tax group above and add it to a product entry:

country ='US', name='country')
region ='CA', name='region')
rate ='8.25', name='rate')
tax_ship ='true', name='tax ship')

tax_group ='tax')
tax_group.attribute.extend([country, region, rate, tax_ship])

We only used the name attribute of the sc:attribrute XML elements, but it is also possible to use the XML attributes type and unit as keyword arguments in the Attribute constructor or as attributes on the object.

The advantage of this method is that it can be used for products as well as other item categories that might be available in the future. The downside is that locating and using returned attributes becomes more complicated, potentially slower, and error-prone, because of the unsafe conversion logic from standard attributes to generic attributes.

Authentication required

You need to be signed in with Google+ to do that.

Signing you in...

Google Developers needs your permission to do that.