This section describes how you can send time-sensitive updates of your inventory entities to Google. The Incremental Update API lets you push updates and delete entities in your Sandbox or Production inventory 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 Update API should be a change that must go live in no more than an hour. If your change does not need to be reflected immediately, you can use the batch ingestion instead. Incremental updates are processed in no more than five minutes.
Prerequisites
The following items are required before you implement incremental updates:
- A service account is created with the editor role to your Actions project. For more details, see Create and set up a project.
- Production or sandbox data feeds are hosted and ingested. For more details, see Batch ingestion.
- (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.
Endpoints
In the below requests, replace the following:
- PROJECT_ID: Google Cloud project ID associated with the project you created in Create and set up a project.
- TYPE: The entity type (
@type
property) of the object in your data feed you want to update. - ENTITY_ID (delete endpoint only): ID of the entity to be deleted. 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
Update endpoint
To modify an entity, make an HTTP POST request to the following endpoint and include a payload of updates and additions. You can make updates up to 1,000 entities in a single API call.
https://actions.googleapis.com/v2/apps/PROJECT_ID/entities:batchPush
For example, if you want to update entities in a project with an ID "delivery-provider-id" the endpoint would be the following:
https://actions.googleapis.com/v2/apps/delivery-provider-id/entities:batchpush
Delete endpoint
To delete an entity in your inventory, make an HTTP DELETE request to the following endpoint.
https://actions.googleapis.com/v2/apps/PROJECT_ID/entities/TYPE/ENTITY_ID?entity.vertical=FOODORDERING&delete_time=DELETE_TIME
For example, to delete a "MenuSection" entity with ID "menuSection_122" from your "delivery-provider-id" project, you would make an HTTP DELETE API call to:
https://actions.googleapis.com/v2/apps/delivery-provider-id/entities/MenuSection/menuSection_122?entity.vertical=FOODORDERING
Sandbox environment
To use the Incremental Update API in your sandbox inventory, follow the guidance in the Endpoints above, but
make requests to /v2/sandbox/apps/
instead of to /v2/apps/
.
https://actions.googleapis.com/v2/sandbox/apps/PROJECT_ID/entities:batchPush
https://actions.googleapis.com/v2/sandbox/apps/PROJECT_ID/entities/TYPE/ENTITY_ID?entity.vertical=FOODORDERING&delete_time=DELETE_TIME
Updating entities
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.
Update payload
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:
{ "requests": [ { "entity": { "data":"ENTITY_DATA", "name": "apps/project_id>/entities/type/entity_id" }, "update_time":"UPDATE_TIMESTAMP" }, ], "vertical": "FOODORDERING" }
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.
Examples
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/sandbox/apps/provider-project/entities:batchPush Host: actions.googleapis.com Content-Type: application/ld+json { "requests": [ { "entity": { "name": "apps/provider-project/entities/restaurant/restaurant12345", "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 multiple restaurants
To update two restaurant entities in a single API call, the HTTP POST request would be as follows:
POST v2/sandbox/apps/provider-project/entities:batchPush Host: actions.googleapis.com Content-Type: application/ld+json { "requests": [ { "entity": { "name": "apps/provider-project/entities/restaurant/restaurant12345", "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 } } }, { "entity": { "name": "apps/provider-project/entities/restaurant/restaurant123", "data": { "@type": "Restaurant", "@id": "restaurant123", "name": "Some Other Restaurant", "url": "https://www.provider.com/somerestaurant", "telephone": "+16501231235", "streetAddress": "385 Spear St", "addressLocality": "San Mateo", "addressRegion": "CA", "postalCode": "94115", "addressCountry": "US" } } } ] "vertical": "FOODORDERING" }
Example 3: 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/sandbox/apps/provider-project/entities:batchPush Host: actions.googleapis.com Content-Type: application/ld+json { "requests": [ { "entity": { "name": "apps/provider-project/entities/menuitemoffer/menuitemoffer6680262", "data": { "@type": "MenuItemOffer", "@id": "menuitemoffer6680262", "sku": "offer-cola", "menuItemId": "menuitem896532", "price": 1.00, "priceCurrency": "USD" }, "vertical": "FOODORDERING" } } ] "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.
Deleting a top-level entity
Consider a situation where you want to delete a restaurant in a feed. You must also delete its services and menus.
A sample endpoint for a menu entity with ID "provider/restaurant/menu/nr":
DELETE v2/apps/delivery-provider-id/entities/menu/provider%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/restaurant/provider%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/service/provider%2Frestaurant%2Fservice%2Fnr?entity.vertical=FOODORDERING
Host: actions.googleapis.com
}
Removing sub-entities
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 reverseReference.
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 Update 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 = 'your/entity/id' const PROJECT_ID = 'type/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(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:batchPush`, body: { requests: [ { entity: { data: JSON.stringify(entity) name: `apps/${PROJECT_ID}/entities/${ENTITY_ID}` } } ], vertical: 'FOODORDERING' }, json: true }, (err, res, body) => { if (err) { return console.log(err); } console.log(`Response: ${JSON.stringify(res)}`) }) } updateEntity(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 = 'type/your/entity/id' ENDPOINT = 'https://actions.googleapis.com/v2/apps/%s/entities:batchPush' % ( PROJECT_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() entity = {} entity['data'] = data #entity JSON-LD serialized as string entity['name'] = 'apps/%s/entities/%s' % (PROJECT_ID, urllib.quote(ENTITY_ID, '') ) # Populating the request request = {} request['entity'] = entity requestArray = [request] # Populating the payload payload = {} payload['requests'] = requestArray payload['vertical'] = 'FOODORDERING' response = authed_session.post(ENDPOINT, json=payload) 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 = "type/your-entity-id"; /** * 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 data) { String authToken = getAuthToken(); String endpoint = String.format("https://actions.googleapis.com/v2/apps/%s/entities/:batchPush", PROJECT_ID); JSONObject entity = new JSONObject(); entity.put("data", data.toString()); entity.put("name", String.format("apps/%s/entities/%s", PROJECT_ID, URLEncoder.encode(ENTITY_ID, "UTF-8"))); JSONObject request = new JSONObject(); request.put("entity", entity); JSONArray requestArray = new JSONArray(); requestArray.put(request); JSONObject payload = new JSONObject(); payload.put("requests", requestArray); payload.put("vertical", FOODORDERING); // Execute POST request executePostRequest(endpoint, authToken, payload); }
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 | Entity to update | Description and effects |
---|---|---|
Disabling a service | Service |
You need to disable a service for an unforeseen reason. Incremental updates: Update the Full feeds: Make sure to update the entity from the full feeds
to have |
Specific item is out-of-stock | MenuItemOffer |
Incremental updates: Send the encapsulating MenuItemOffer
entity with inventoryLevel set to 0 for the given
MenuItem , and all other data unchanged. |
Menu item price change | MenuItemOffer |
Incremental updates: Send the encapsulating MenuItemOffer
entity with 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. Full feeds: Add the entity in your data feeds and wait for batch ingestion. |
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 |
ServiceArea |
Incremental feeds: Send the ServiceArea entity in question with all its
fields intact, like you normally would within the full feeds, with new delivery area
specified within polygon , geoRadius , or postalCode . |
Update delivery estimated time of arrival in Service |
ServiceHours |
Incremental feeds: Send the ServiceHours the same as in
the feeds, except its leadTimeMin is updated
accordingly. |
Update delivery prices in Service |
Fee |
Incremental feeds: Send full delivery Fee with
price updated. |
Update delivery or takeout hours in Service |
ServiceHours |
Incremental feeds: Send the ServiceHours the same as in
the feeds, except its opens and closes properties are updated
accordingly. |
Service (change min order amount) |
Fee |
Incremental feeds: Send full Fee with
minPrice
updated |
Delete a MenuItem permanently |
Menu |
Incremental feeds: Send the MenuItem the same as in the
feeds, but with parentMenuSectionId empty.
|
SLO on processing time for batch jobs and incremental updates
An entity updated or deleted through a batch will be processed within 2 hours in best effort mode 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.