This section describes how you can send time-sensitive updates of your feeds to Google. The Incremental Updates API lets you update and delete entities in your feeds in nearly real time.
This functionality is primarily intended for updates that you can't foresee, such as emergency closures. As a rule, any change submitted through the Incremental Updates API should be a change that must go live in no more than one week. If your change does not need to be reflected immediately, you can use a batch update instead. Incremental updates are processed in no more than five minutes.
Setup
To implement incremental updates, do the following:
- Follow the steps outlined in Create and set up a project to create a project.
- Follow the steps outlined in Set up a service account to create a service account. Note that you need to be an "Owner" of the project to add an "Editor" role for the service account
- (Optional, but recommended) Install the Google Client library in the language of your choice to facilitate using OAuth 2.0 when calling the API. The code samples included below use these libraries. Otherwise, you'll need to handle token exchanges manually as described in Using OAuth 2.0 to Access Google APIs.
Endpoint
To notify Google of an update, make an HTTP POST request to the Incremental Updates API and include a payload of updates and additions. The inventory schema you use determines which endpoint to make your request to:
v2 inventory
https://actions.googleapis.com/v2/apps/PROJECT_ID/entities/TYPE/ENTITY_ID:push
v1 inventory
https://actions.googleapis.com/v2/apps/PROJECT_ID/entities/ENTITY_ID:push
To remove an entity, make an HTTP DELETE request to the following endpoint that corresponds to which inventory schema you use:
v2 inventory
https://actions.googleapis.com/v2/apps/PROJECT_ID/entities/TYPE/ENTITY_ID?entity.vertical=FOODORDERING&delete_time=DELETE_TIME
v1 inventory
https://actions.googleapis.com/v2/apps/PROJECT_ID/entities/ENTITY_ID?entity.vertical=FOODORDERING&delete_time=DELETE_TIME
In the above requests, replace the following:
- PROJECT_ID: Google Cloud project ID associated with the project you created in Create and set up a project.
- TYPE (v2 inventory schema only): The entity type (
@type
property) of the object in your data feed you want to update. - ENTITY_ID: ID of the entity included in the payload. Make sure to URL encode your entity ID.
- DELETE_TIME (delete endpoint only): Optional field to denote the
time the entity was deleted on your systems (default is when the request is
received). Time value must not be in the future. When sending an entity
through an incremental call, entity versioning
also uses the
delete_time
field in the case of a delete call. Format this value asyyyy-mm-ddTHH:mm:ssZ
For example, you have a project with an ID of "delivery-provider-id" that uses the v2 inventory schema. You want to make changes to the restaurant with a restaurant entity type of "MenuSection" and an entity ID of "menuSection_122". The endpoint for updates to your data would be the following:
https://actions.googleapis.com/v2/apps/delivery-provider-id/entities/MenuSection/menuSection_122:push
To remove this same entity, you would make this HTTP DELETE API call:
https://actions.googleapis.com/v2/apps/delivery-provider-id/entities/MenuSection/menuSection_122?entity.vertical=FOODORDERING
Sandbox requests
For sandbox requests, follow the guidance in Endpoint above, but
make requests to /v2/sandbox/apps/
instead of to /v2/apps/
. For instance, a
sandbox delete request for v2 inventory schema is structured as follows:
https://actions.googleapis.com/v2/sandbox/apps/PROJECT_ID/entities/TYPE/ENTITY_ID?entity.vertical=FOODORDERING&delete_time=DELETE_TIME
Updates and additions
Your daily batch feeds should also contain any changes submitted through this API. Otherwise, your batch updates will overwrite your incremental changes.
Payload
Each POST request must include the request parameters along with the JSON payload containing the structured data of any entity type listed in the inventory schema.
The JSON should appear the same as it would in the batch feed, with the following differences:
- The payload body should not exceed 5 MB in size. Similarly to batch feeds, we suggest you strip whitespaces in the interest of fitting more data.
- The envelope is as follows:
{ "entity": { "data":"ENTITY_DATA", "vertical":"FOODORDERING" }, "update_time":"UPDATE_TIMESTAMP" }
In the above payload, replace the following:
- ENTITY_DATA: Entity in JSON format serialized as a string. The
JSON-LD entity must be passed as a string in the
data
field. - UPDATE_TIMESTAMP (optional): Timestamp when entity was updated in
your systems. Time value must not be in the future. Default timestamp is when
Google receives the request. When sending an entity through an incremental
request, the entity versioning also uses the
update_time
field in the case of an add/update request.
Updating an entity
Example 1: Updating a restaurant
Suppose you urgently need to update the phone number of a restaurant. Your update contains the JSON for the entire restaurant.
Consider a batch feed that looks like the following:
{ "@type": "Restaurant", "@id": "restaurant12345", "name": "Some Restaurant", "url": "https://www.provider.com/somerestaurant", "telephone": "+16501234567", "streetAddress": "345 Spear St", "addressLocality": "San Francisco", "addressRegion": "CA", "postalCode": "94105", "addressCountry": "US", "latitude": 37.472842, "longitude": -122.217144 }
Then your incremental update by HTTP POST would be as follows:
POST v2/apps/provider-project/entities/Restaurant/restaurant12345:push Host: actions.googleapis.com Content-Type: application/ld+json { "entity": { "data": { "@type": "Restaurant", "@id": "restaurant12345", "name": "Some Restaurant", "url": "https://www.provider.com/somerestaurant", "telephone": "+16501235555", "streetAddress": "345 Spear St", "addressLocality": "San Francisco", "addressRegion": "CA", "postalCode": "94105", "addressCountry": "US", "latitude": 37.472842, "longitude": -122.217144 }, "vertical": "FOODORDERING" } }
Example 2: Updating a menu item price
Suppose you need to change the price of a menu item. As in Example 1, your update must contain the JSON for the entire top-level entity (the menu), and the feed uses the v1 inventory schema.
Consider a batch feed that looks like the following:
{ "@type": "MenuItemOffer", "@id": "menuitemoffer6680262", "sku": "offer-cola", "menuItemId": "menuitem896532", "price": 3.00, "priceCurrency": "USD" }
Then your incremental update via POST would be as follows:
POST v2/apps/provider-project/entities/MenuItemOffer/menuitemoffer6680262:push Host: actions.googleapis.com Content-Type: application/ld+json { "entity": { "data": { "@type": "MenuItemOffer", "@id": "menuitemoffer6680262", "sku": "offer-cola", "menuItemId": "menuitem896532", "price": 1.00, "priceCurrency": "USD" }, "vertical": "FOODORDERING" } }
Adding an entity
To add entities, avoid using inventory updates. Instead, use the batch feeds process as described for the v2 inventory schema.
Removing an entity
To remove top-level entities, you use a slightly modified endpoint, and use HTTP DELETE instead of HTTP POST in the request.
Don't use HTTP DELETE to remove a sub-entity within a top-level entity, such as a menu item within a menu. Instead, treat the removal of sub-entities as an update to a top-level entity in which the sub-entity is removed from the relevant list or parameter.
Example 1: Deleting a top-level entity
Consider a situation where you want to delete a restaurant in a feed that uses the v1 inventory schema. You must also delete its services and menus.
A sample endpoint for a menu entity with ID "https://www.provider.com/restaurant/menu/nr":
DELETE v2/apps/delivery-provider-id/entities/https%3A%2F%2Fwww.provider.com%2Frestaurant%2Fmenu%2Fnr?entity.vertical=FOODORDERING
Host: actions.googleapis.com
A sample endpoint for a restaurant entity with ID "https://www.provider.com/restaurant/nr":
DELETE v2/apps/delivery-provider-id/entities/https%3A%2F%2Fwww.provider.com%2Frestaurant%2Fnr?entity.vertical=FOODORDERING
Host: actions.googleapis.com
A sample endpoint for a service entity with ID "https://www.provider.com/restaurant/service/nr":
DELETE v2/apps/delivery-provider-id/entities/https%3A%2F%2Fwww.provider.com%2Frestaurant%2Fservice%2Fnr?entity.vertical=FOODORDERING
Host: actions.googleapis.com
}
Example 2: Removing sub-entities
To remove a sub-entity from within a top-level entity, you send the top-level entity with the sub-entity removed from the corresponding field. The following example assumes the feed uses the v1 inventory schema.
For example, to remove a service area, update the service with the service area
removed from the areaServed
list.
POST v2/apps/delivery-provider-id/entities/https%3A%2F%2Fwww.provider.com%2Frestaurant%2Fservice%2Fnr:push
Host: actions.googleapis.com
Content-Type: application/ld+json
{
"entity": {
// Note: "data" is not serialized as a string in our example for readability.
"data": {
"@type": "Service",
"provider": {
"@type": "Restaurant",
"@id": "https://www.provider.com/restaurant/nr"
},
"areaServed": [
{
"@type": "GeoCircle",
"geoMidpoint": {
"@type": "GeoCoordinates",
"latitude": "42.362757",
"longitude": "-71.087109"
},
"geoRadius": "10000"
}
// area2 is removed.
]
...
},
"vertical": "FOODORDERING"
}
}
API response codes
A successful call does not mean that the feed is valid or correct, only that the API call was made. Successful calls receive an HTTP response code 200, along with an empty response body:
{}
For failures, the HTTP response code will not be 200, and the response body indicates what went wrong.
For example, if the user has set the "vertical" value in the envelope to
FAKE_VERTICAL
, you would receive the message below:
{
"error": {
"code": 400,
"message": "Invalid value at 'entity.vertical' (TYPE_ENUM), \"FAKE_VERTICAL\"",
"status": "INVALID_ARGUMENT",
"details": [
{
"@type": "type.googleapis.com/google.rpc.BadRequest",
"fieldViolations": [
{
"field": "entity.vertical",
"description": "Invalid value at 'entity.vertical' (TYPE_ENUM), \"FAKE_VERTICAL\""
}
]
}
]
}
}
Code sample
Below are some samples of how to use the Incremental Updates API in various languages. These samples use the Google Auth Libraries, and assume a feed using the v1 inventory schema. For alternative solutions, refer to Using OAuth 2.0 for Server to Server Applications.
Updating entities
Node.js
This code uses the Google auth library for Node.js.
const {auth} = require('google-auth-library') const request = require('request'); // The service account client secret file downloaded from the Google Cloud Console const serviceAccountJson = require('./service-account.json') // entity.json is a file that contains the entity data in json format const entity = require('./entity.json') const ENTITY_ID = 'restaurant/http://www.provider.com/somerestaurant' const PROJECT_ID = 'your-project-id' /** * Get the authorization token using a service account. */ async function getAuthToken() { let client = auth.fromJSON(serviceAccountJson) client.scopes = ['https://www.googleapis.com/auth/assistant'] const tokens = await client.authorize() return tokens.access_token; } /** * Send an incremental update to update or add an entity */ async function updateEntity(entityId, entity) { const token = await getAuthToken() request.post({ headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, url: `https://actions.googleapis.com/v2/apps/${PROJECT_ID}/entities/${encodeURIComponent(entityId)}:push`, body: { entity: { data: JSON.stringify(entity), vertical: 'FOODORDERING', } }, json: true }, (err, res, body) => { if (err) { return console.log(err); } console.log(`Response: ${JSON.stringify(res)}`) }) } updateEntity(ENTITY_ID, entity)
Python
This code uses the Google auth library for Python.
from google.oauth2 import service_account from google.auth.transport.requests import AuthorizedSession import json import urllib PROJECT_ID = 'your-project-id' ENTITY_ID = 'restaurant/http://www.provider.com/somerestaurant' ENDPOINT = 'https://actions.googleapis.com/v2/apps/%s/entities/%s:push' % ( PROJECT_ID, urllib.quote(ENTITY_ID, '')) # service-account.json is the service account client secret file downloaded from the # Google Cloud Console credentials = service_account.Credentials.from_service_account_file( 'service-account.json') scoped_credentials = credentials.with_scopes( ['https://www.googleapis.com/auth/assistant']) authed_session = AuthorizedSession(scoped_credentials) # Retrieving the entity update_file = open("entity.json") #JSON file containing entity data in json format. data = update_file.read() # Populating the entity with wrapper entity = {} entity['data'] = data #entity JSON-LD serialized as string entity['vertical'] = 'FOODORDERING' request = {} request['entity'] = entity response = authed_session.post(ENDPOINT, json=request) print(response.text) #if successful, will be '{}'
Java
This code uses the Google auth library for Java.
private static final String PROJECT_ID = "your-project-id"; private static final String ENTITY_ID = "http://www.provider.com/somerestaurant"; /** * Get the authorization token using a service account. */ private static String getAuthToken() { InputStream serviceAccountFile = Example.class.getClassLoader().getResourceAsStream("service-account.json"); ServiceAccountCredentials.Builder credentialsSimpleBuilder = ServiceAccountCredentials.fromStream(serviceAccountFile).toBuilder(); credentialsSimpleBuilder.setScopes(ImmutableList.of("https://www.googleapis.com/auth/assistant")); AccessToken accessToken = credentialsSimpleBuilder.build().refreshAccessToken(); return accessToken.getTokenValue(); } /** * Send an incremental update to update or add an entity. * @param entityId The id of the entity to update. * @param entity the json of the entity to be updated. */ public void updateEntity(String entityId, JSONObject entity) { String authToken = getAuthToken(); String endpoint = String.format( "https://actions.googleapis.com/v2/apps/%s/entities/%s:push", PROJECT_ID, URLEncoder.encode(entityId, "UTF-8")); JSONObject data = new JSONObject(); data.put("data", entity.toString()); data.put("vertical", "FOODORDERING"); JSONObject jsonBody = new JSONObject(); jsonBody.put("entity", data); // Execute POST request executePostRequest(endpoint, authToken, jsonBody); }
Removing entities
Node.js
This code uses the Google auth library for Node.js.
const {auth} = require('google-auth-library') const request = require('request'); // The service account client secret file downloaded from the Google Cloud Console const serviceAccountJson = require('./service-account.json') // entity.json is a file that contains the entity data in json format const entity = require('./entity.json') const ENTITY_ID = 'restaurant/http://www.provider.com/somerestaurant' const PROJECT_ID = 'your-project-id' /** * Get the authorization token using a service account. */ async function getAuthToken() { let client = auth.fromJSON(serviceAccountJson) client.scopes = ['https://www.googleapis.com/auth/assistant'] const tokens = await client.authorize() return tokens.access_token; } /** * Send an incremental update to delete an entity */ async function deleteEntity(entityId) { const token = await getAuthToken() request.delete({ headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, url: `https://actions.googleapis.com/v2/apps/${PROJECT_ID}/entities/${encodeURIComponent(entityId)}?entity.vertical=FOODORDERING`, body: {}, json: true }, (err, res, body) => { if (err) { return console.log(err); } console.log(`Response: ${JSON.stringify(res)}`) }) } deleteEntity(ENTITY_ID)
Python
This code uses the Google auth library for Python.
from google.oauth2 import service_account from google.auth.transport.requests import AuthorizedSession import json import urllib # Service config PROJECT_ID = 'your-project-id' ENTITY_ID = 'restaurant/http://www.provider.com/somerestaurant' DELETE_TIME = '2018-04-07T14:30:00-07:00' ENDPOINT = 'https://actions.googleapis.com/v2/apps/%s/entities/%s?entity.vertical=FOODORDERING&delete_time=%s' % ( PROJECT_ID, urllib.quote(ENTITY_ID, ''), urllib.quote(DELETE_TIME, '')) # service-account.json is the service account client secret file downloaded from the # Google Cloud Console credentials = service_account.Credentials.from_service_account_file( 'service-account.json') scoped_credentials = credentials.with_scopes( ['https://www.googleapis.com/auth/assistant']) authed_session = AuthorizedSession(scoped_credentials) response = authed_session.delete(ENDPOINT) print(response.text) #if successful, will be '{}'
Java
This code uses the Google auth library for Java.
private static final String PROJECT_ID = "your-project-id"; private static final String ENTITY_ID = "restaurant/http://www.provider.com/somerestaurant"; /** * Get the authorization token using a service account. */ private static String getAuthToken() { InputStream serviceAccountFile = Example.class.getClassLoader().getResourceAsStream("service-account.json"); ServiceAccountCredentials.Builder credentialsSimpleBuilder = ServiceAccountCredentials.fromStream(serviceAccountFile).toBuilder(); credentialsSimpleBuilder.setScopes(ImmutableList.of("https://www.googleapis.com/auth/assistant")); AccessToken accessToken = credentialsSimpleBuilder.build().refreshAccessToken(); return accessToken.getTokenValue(); } /** * Send an incremental update to delete an entity. * @param entityId The id of the entity to delete. */ public void deleteEntity(String entityId) { String authToken = getAuthToken(); String endpoint = String.format( "https://actions.googleapis.com/v2/apps/%s/entities/%s?entity.vertical=FOODORDERING", PROJECT_ID, URLEncoder.encode(entityId, "UTF-8")); // Execute DELETE request System.out.println(executeDeleteRequest(endpoint, authToken)); }
Use cases
The following use cases are examples of incremental updates, full feed updates, and the content at a high level in the API call:
Scenario | Top-level entity | Description and effects |
---|---|---|
Disabling a service | DisabledService |
You need to disable a service for an unforeseen reason. Incremental updates: Send the Full feeds: Make sure to update the entity from the full feeds
to have |
Specific item is out-of-stock | Menu |
Incremental updates: Send the encapsulating Menu
entity with offer.inventoryLevel set to 0 for the given
MenuItem , and all other data unchanged. |
Menu item price change | Menu |
Incremental updates: Send the encapsulating Menu
entity with offer.price set to the updated price for the given
MenuItem , and all other data unchanged. |
Add new top-level entity Only applicable for entity of types |
Menu , Restaurant , Service |
For instance, you need to add a new menu to a restaurant. Incremental updates: Send the new menu entity, along with the restaurant
entity with its field |
Delete top-level entity permanently Only applicable for entity of types |
Menu , Restaurant , Service |
Incremental updates: Send an explicit delete. Full feeds: Make sure to remove the entity from the full feeds prior to the next fetch by Google, otherwise the entity will get re-added. |
Add a new delivery area in a specific Service |
Service |
Incremental feeds: Send the Service entity in question with all its
fields intact, like you normally would within the full feeds, with new delivery area
specified within areaServed of the Service . |
Update delivery estimated time of arrival in Service |
Service |
Incremental feeds: Send the Service the same as in
the feeds, except its hoursAvailable.deliveryHours is updated
accordingly. |
Update delivery prices in Service |
Service |
Incremental feeds: Send full Service with
offers.priceSpecification.price updated. |
Update delivery or takeout hours in Service |
Service |
Incremental feeds: Send the Service the same as in
the feeds, except its hoursAvailable is updated
accordingly. |
Service (change min order amount) |
Service |
Incremental feeds: Send full Service with
Service.offers.priceSpecification.eligibleTransactionVolume
updated |
Delete MenuItem permanently |
Menu |
Incremental feeds: Send the Menu the same as in the
feeds, but with this MenuItem removed from the
hasMenuItems list. |
SLO on processing time for batch jobs and incremental updates
An entity added through a batch or incremental update will be processed in 1-2 days. An entity updated or deleted through a batch will be processed in 2 hours whereas an entity updated through an incremental update will be processed in 5 minutes. A stale entity is deleted in 7 days.
You can either send Google:
- Multiple batch jobs per day to keep your inventory up to date, OR
- One batch job per day and Incremental APIs to keep your inventory up to date.