Create and upload POI feeds
When creating and uploading POI feeds, follow these instructions:
- Follow the spec described in the POI feed
for POI data files. We recommend using unique POI data
file names for each upload. Include a timestamp in the filename, for
example,
POI_1633621547.json. - Upload feeds to the local POI SFTP server daily as full refreshes.
- You can find SFTP server details in the Configuration > Feeds section of the Partner Portal.
- View feed ingestion status in the Ingestion > History section of the Partner Portal.
Feed Specifications
Field Requirements
VssPoi
Represents a single Point of Interest (POI) entity e.g. a hotel or restaurant.
| Field Name | Type | Requirement | Description |
|---|---|---|---|
poi_id | string | Required | Required. A string generated by the partner that identifies a POI. |
name | object(Text) | Required | Required. The name of the POI. |
telephone | string | The contact telephone number of the POI including its country and area codes, e.g. +14567891234. | |
url | string | The url of the POI's public website. Note: This will be used just for matching purposes, not for display. | |
location | object(GeoCoordinates) | Required | Required. The location of the POI. |
display_address | object(Text) | The address displayed on the UI. | |
images | array of object(Image) | Images of the POI. Max number of images: 5. | |
rating | number | Average rating for the POI. | |
num_ratings | number | The number of contributing ratings for the rating field. | |
rating_scale | number | The rating scale used for the rating field. If max rating is 5, then
rating_scale is 5. | |
category | enum(Category) | Required | Required. Represents the category of the POI. |
description | object(Text) | A description of the POI. | |
| oneOf(additional_data) | Required | Only one of the fields in this oneOf can be set. |
Text
Represents a text with localizations.
| Field Name | Type | Requirement | Description |
|---|---|---|---|
localizations | array of object(LocalizedString) | The localized strings. | |
default_locale | string | The locale to use as the default language it must be present in the localizations. |
LocalizedString
Represents a localized string.
| Field Name | Type | Requirement | Description |
|---|---|---|---|
locale | string | The text's language tag, such as "en", "en-US" or "sr-Latn". | |
text | string | The text in the specified locale. |
GeoCoordinates
The Geo data of a location, including latitude, longitude, and address.
| Field Name | Type | Requirement | Description |
|---|---|---|---|
latitude | number | [-90, +90] degrees (inclusive). Required if longitude is set, otherwise nice to have. | |
longitude | number | [-180, +180] degrees (inclusive). Required if latitude is set, otherwise nice to have. | |
| oneOf(addresses) | Required | Only one of the fields in this oneOf can be set. |
PostalAddress
The postal address for the location.
| Field Name | Type | Requirement | Description |
|---|---|---|---|
country | string | Required | Required. The country, using ISO 3166-1 alpha-2 country code, e.g. "US". |
locality | string | Required | Required. The locality/city, e.g. "Mountain View". |
region | string | The region/state/province, e.g. "CA". This field is only required in countries where region is commonly a part of the address. (optional) | |
postal_code | string | Required | Required. The postal code, e.g. "94043". |
street_address | string | Required | Required. The street address, e.g. "1600 Amphitheatre Pkwy". |
Image
Represents an image of the Point of Interest (POI).
| Field Name | Type | Requirement | Description |
|---|---|---|---|
url | string | The url of the image. Google will crawl the media hosted at this URL. Max length: 2000. | |
alt_text | object(Text) | The alternative text to be used for accessibility. |
HotelData
Hotel specific feed data.
| Field Name | Type | Requirement | Description |
|---|---|---|---|
hotel_star_class | number | The official hotel class star value. Can be used in a label like "5-star hotel." This value is expected to be an integer between 1 and 5. | |
brand_ids | array of string | The brands that can display this hotel. If this field is empty, the hotel can be displayed under any of the brands associated with the feed. |
LocalData
Establishment specific feed data.
| Field Name | Type | Requirement | Description |
|---|---|---|---|
business_hours | object(BusinessHours) | The regular business hours of the establishment. | |
price_range | object(PriceRange) | Price range of the services offered by the establishment. | |
establishment_category | object(Text) | The type of establishment. | |
brand_landing_pages | array of object(BrandLandingPages) | The brand landing pages. |
BusinessHours
Represents the time periods that this location is open for business. Holds a collection of [TimeRange][madden.vss_poi_feed.TimeRange] instances. Example: Open on Saturdays from 09:00-12:00 and 13:00-17:00: time_ranges { open_day: SATURDAY open_time: { hours: 9, minutes: 0 } close_day: SATURDAY close_time: { hours: 12, minutes: 0 } } time_rages { open_day: SATURDAY open_time: { hours: 13, minutes: 0 } close_day: SATURDAY close_time: { hours: 17, minutes: 0 } } Example: Open on Saturdays from 21:00 to Sunday 02:00: time_ranges { open_day: SATURDAY open_time: { hours: 21, minutes: 0 } close_day: SUNDAY close_time: { hours: 2, minutes: 0 } }
| Field Name | Type | Requirement | Description |
|---|---|---|---|
time_ranges | array of object(TimeRange) | A collection of times that this POI is open for business. Each period represents a range of hours when the POI is open during the week. |
TimeRange
Represents a span of time that the POI is open, starting on the specified open day/time and closing on the specified close day/time. The closing time must occur after the opening time, for example later in the same day, or on a subsequent day.
| Field Name | Type | Requirement | Description |
|---|---|---|---|
open_day | enum(DayOfWeek) | The open day of the time range. | |
open_time | object(TimeOfDay) | The open time of the time range. | |
close_day | enum(DayOfWeek) | The close day of the time range. | |
close_time | object(TimeOfDay) | The close time of the time range. |
TimeOfDay
| Field Name | Type | Requirement | Description |
|---|---|---|---|
hours | number | Hours of a day in 24 hour format. Must be greater than or equal to 0 and typically must be less than or equal to 23. An API may choose to allow the value "24:00:00" for scenarios like business closing time. | |
minutes | number | Minutes of an hour. Must be greater than or equal to 0 and less than or equal to 59. | |
seconds | number | Seconds of a minute. Must be greater than or equal to 0 and typically must be less than or equal to 59. An API may allow the value 60 if it allows leap-seconds. | |
nanos | number | Fractions of seconds, in nanoseconds. Must be greater than or equal to 0 and less than or equal to 999,999,999. |
PriceRange
Price range of the services offered by the POI.
| Field Name | Type | Requirement | Description |
|---|---|---|---|
min_price | object(Money) | The minimum price of the services offered by the POI. | |
max_price | object(Money) | The maximum price of the services offered by the POI. |
Money
Represents an amount of money with its currency type.
| Field Name | Type | Requirement | Description |
|---|---|---|---|
currency_code | string | The three-letter currency code defined in ISO 4217. | |
units | number | The whole units of the amount.
For example if currencyCode is "USD", then 1 unit is one US dollar. | |
nanos | number | Number of nano (10^-9) units of the amount.
The value must be between -999,999,999 and +999,999,999 inclusive.
If units is positive, nanos must be positive or zero.
If units is zero, nanos can be positive, zero, or negative.
If units is negative, nanos must be negative or zero.
For example $-1.75 is represented as units=-1 and nanos=-750,000,000. |
BrandLandingPages
The Localized brand landing pages for a single Point of Interest (POI).
| Field Name | Type | Requirement | Description |
|---|---|---|---|
brand_id | string | The brand this configuration applies to. | |
localized_landing_pages | array of object(LocalizedLandingPage) | The localized landing pages. The url and locales fields values must be unique across all the localized landing pages. | |
default_url | string | The default landing page url to be used when none of the localized landing pages matches the user's language. |
LocalizedLandingPage
Localized landing page for the Point of Interest (POI).
| Field Name | Type | Requirement | Description |
|---|---|---|---|
url | string | The url of the landing page. Max length: 2000. | |
locales | array of string | Restricts this page to users with the specified language preference. Must contain language tags, such as "en", "en-US" or "sr-Latn". |
Category
Represents the category of the POI.
It should match the additional_data oneof field below.
| Name | Description |
|---|---|
UNKNOWN_CATEGORY | |
HOTEL | |
LOCAL |
DayOfWeek
Represents a day of the week.
| Name | Description |
|---|---|
DAY_OF_WEEK_UNSPECIFIED | The day of the week is unspecified. |
MONDAY | Monday |
TUESDAY | Tuesday |
WEDNESDAY | Wednesday |
THURSDAY | Thursday |
FRIDAY | Friday |
SATURDAY | Saturday |
SUNDAY | Sunday |
additional_data
Required. Category specific fields.
It should match the category field above.
| Field Name | Type | Requirement | Description |
|---|---|---|---|
hotel_data | object(HotelData) | Mutally exclusive with | Hotel specific fields. |
local_data | object(LocalData) | Mutally exclusive with | Local specific fields. |
addresses
Required. Address for a location.
| Field Name | Type | Requirement | Description |
|---|---|---|---|
address | object(PostalAddress) | Postal address of the location. |
- Format: Must be JPEG, PNG, or WebP.
- Max File Size: Under 30 MB per image.
- Max Dimensions: Less than 75 Megapixels total (width x height < 75,000,000).
- URL Type: Direct path to the image asset (e.g., ends in .jpg).
- Permissions: Ensure the hosting server allows access to Googlebot or Crawlers and has no robots.txt blocking the image directories.
- Safe Search: Images must comply with Google's safe search policies.
Definitions
VssPoiFeed Definition
// Represents a Point of Interest (POI) data feed provided by a partner. export message VssPoiFeed { // The POIs in the feed. repeated VssPoi data = 1; }
VssPoi Definition
// Represents a single Point of Interest (POI) entity e.g. a hotel or // restaurant. export message VssPoi { // Required. A string generated by the partner that identifies a POI. string poi_id = 1; // The entity name, telephone, url and location are used to support // matching partner inventory with entities already present on Google. // Required. The name of the POI. Text name = 2; // The contact telephone number of the POI including its country and // area codes, e.g. +14567891234. string telephone = 3 [(datapol.semantic_type) = ST_PHONE_NUMBER]; // The url of the POI's public website. // Note: This will be used just for matching purposes, not for display. string url = 4; // Required. The location of the POI. GeoCoordinates location = 5; // The address displayed on the UI. Text display_address = 17; // Images of the POI. // Max number of images: 5. repeated Image images = 6; // Average rating for the POI. float rating = 12; // The number of contributing ratings for the `rating` field. int64 num_ratings = 13; // The rating scale used for the `rating` field. If max rating is 5, then // rating_scale is 5. int32 rating_scale = 14; // Represents the category of the POI. // It should match the `additional_data` oneof field below. export enum Category { LOCAL = 4; } // Required. Represents the category of the POI. Category category = 9; // A description of the POI. Text description = 16; // Required. Category specific fields. // It should match the `category` field above. oneof additional_data { // Local specific fields. LocalData local_data = 15; } }
Text Definition
// Represents a text with localizations. message Text { // Represents a localized string. message LocalizedString { // The text's language tag, such as "en", "en-US" or "sr-Latn". string locale = 1; // The text in the specified locale. string text = 2; } // The localized strings. repeated LocalizedString localizations = 1; // The locale to use as the default language it must be present in the // localizations. string default_locale = 2; }
GeoCoordinates Definition
// The Geo data of a location, including latitude, longitude, and address. message GeoCoordinates { option (datapol.msg_semantic_type) = ST_LOCATION; // [-90, +90] degrees (inclusive). // Required if longitude is set, otherwise nice to have. double latitude = 1; // [-180, +180] degrees (inclusive). // Required if latitude is set, otherwise nice to have. double longitude = 2; // Required. Address for a location. oneof addresses { // Postal address of the location. PostalAddress address = 3; } }
PostalAddress Definition
// The postal address for the location. message PostalAddress { option (datapol.msg_semantic_type) = ST_LOCATION; // Required. The country, using ISO 3166-1 alpha-2 country code, e.g. "US". string country = 1; // Required. The locality/city, e.g. "Mountain View". string locality = 2; // The region/state/province, e.g. "CA". This field is only required in // countries where region is commonly a part of the address. (optional) string region = 3; // Required. The postal code, e.g. "94043". string postal_code = 4; // Required. The street address, e.g. "1600 Amphitheatre Pkwy". string street_address = 5; }
Image Definition
// Represents an image of the Point of Interest (POI). export message Image { // The url of the image. Google will crawl the media hosted at this URL. // Max length: 2000. string url = 1; // The alternative text to be used for accessibility. Text alt_text = 2; }
LocalData Definition
// Establishment specific feed data. message LocalData { // The regular business hours of the establishment. BusinessHours business_hours = 1; // Price range of the services offered by the establishment. PriceRange price_range = 2; // The type of establishment. Text establishment_category = 3; // The brand landing pages. repeated BrandLandingPages brand_landing_pages = 4; }
BrandLandingPages Definition
// The Localized brand landing pages for a single Point of Interest (POI). message BrandLandingPages { // The brand this configuration applies to. string brand_id = 1 [(datapol.semantic_type) = ST_PARTNER_ID]; // Localized landing page for the Point of Interest (POI). message LocalizedLandingPage { // The url of the landing page. // Max length: 2000. string url = 1; // Restricts this page to users with the specified language preference. // Must contain language tags, such as "en", "en-US" or "sr-Latn". repeated string locales = 3; } // The localized landing pages. // The url and locales fields values must be unique across all the localized // landing pages. repeated LocalizedLandingPage localized_landing_pages = 2; // The default landing page url to be used when none of the localized // landing pages matches the user's language. string default_url = 3; }
Samples
Point of Interest Feed
Filename: poi_1707240000.json
{ "data": [ { "poi_id": "test_restaurant_1", "name": { "localizations": [ { "locale": "en", "text": "Tasty Bites Test Restaurant" } ], "default_locale": "en" }, "telephone": "+1234567890", "url": "https://www.tastybitestest.com", "location": { "latitude": 37.422, "longitude": -122.084, "address": { "country": "US", "locality": "Mountain View", "postal_code": "94043", "street_address": "1600 Amphitheatre Pkwy" } }, "display_address": { "localizations": [ { "locale": "en", "text": "1600 Amphitheatre Pkwy, Mountain View, CA 94043" } ], "default_locale": "en" }, "images": [ { "url": "https://www.tastybitestest.com/images/interior.jpg", "alt_text": { "localizations": [ { "locale": "en", "text": "Cozy dining area of Tasty Bites" } ], "default_locale": "en" } } ], "rating": 4.7, "num_ratings": 150, "rating_scale": 5, "category": "LOCAL", "description": { "localizations": [ { "locale": "en", "text": "A mock restaurant for testing the POI feed ingestion." } ], "default_locale": "en" }, "local_data": { "establishment_category": { "localizations": [ { "locale": "en", "text": "Restaurant" } ], "default_locale": "en" }, "price_range": { "min_price": { "currency_code": "USD", "units": 15, "nanos": 0 }, "max_price": { "currency_code": "USD", "units": 45, "nanos": 0 } }, "business_hours": { "time_ranges": [ { "open_day": "MONDAY", "open_time": { "hours": 11, "minutes": 0 }, "close_day": "MONDAY", "close_time": { "hours": 21, "minutes": 0 } }, { "open_day": "TUESDAY", "open_time": { "hours": 11, "minutes": 0 }, "close_day": "TUESDAY", "close_time": { "hours": 21, "minutes": 0 } }, { "open_day": "WEDNESDAY", "open_time": { "hours": 11, "minutes": 0 }, "close_day": "WEDNESDAY", "close_time": { "hours": 21, "minutes": 0 } }, { "open_day": "THURSDAY", "open_time": { "hours": 11, "minutes": 0 }, "close_day": "THURSDAY", "close_time": { "hours": 22, "minutes": 0 } }, { "open_day": "FRIDAY", "open_time": { "hours": 11, "minutes": 0 }, "close_day": "FRIDAY", "close_time": { "hours": 23, "minutes": 0 } }, { "open_day": "SATURDAY", "open_time": { "hours": 10, "minutes": 0 }, "close_day": "SATURDAY", "close_time": { "hours": 23, "minutes": 0 } }, { "open_day": "SUNDAY", "open_time": { "hours": 10, "minutes": 0 }, "close_day": "SUNDAY", "close_time": { "hours": 20, "minutes": 0 } } ] }, "brand_landing_pages": [ { "brand_id": "tasty_bites_brand", "localized_landing_pages": [ { "url": "https://www.tastybitestest.com/en", "locales": ["en-US", "en-GB"] } ], "default_url": "https://www.tastybitestest.com" } ] } } ] }
Multi-lingual POI Feed
Filename: poi_multilingual.json
{ "data": [ { "poi_id": "test_restaurant_1", "name": { "localizations": [ { "locale": "en", "text": "Tasty Bites Test Restaurant" }, { "locale": "fr", "text": "Restaurant de test Tasty Bites" } ], "default_locale": "en" }, "telephone": "+1234567890", "url": "https://www.tastybitestest.com", "location": { "latitude": 37.422, "longitude": -122.084, "address": { "country": "US", "locality": "Mountain View", "postal_code": "94043", "street_address": "1600 Amphitheatre Pkwy" } }, "display_address": { "localizations": [ { "locale": "en", "text": "1600 Amphitheatre Pkwy, Mountain View, CA 94043" }, { "locale": "fr", "text": "1600 Amphitheatre Pkwy, Mountain View, CA 94043" } ], "default_locale": "en" }, "images": [ { "url": "https://www.tastybitestest.com/images/interior.jpg", "alt_text": { "localizations": [ { "locale": "en", "text": "Cozy dining area of Tasty Bites" }, { "locale": "fr", "text": "Espace salle à manger accueillant de Tasty Bites" } ], "default_locale": "en" } } ], "rating": 4.7, "num_ratings": 150, "rating_scale": 5, "category": "LOCAL", "description": { "localizations": [ { "locale": "en", "text": "A mock restaurant for testing the POI feed ingestion." }, { "locale": "fr", "text": "Un faux restaurant pour tester l'ingestion de flux de points d'intérêt." } ], "default_locale": "en" }, "local_data": { "establishment_category": { "localizations": [ { "locale": "en", "text": "Restaurant" }, { "locale": "fr", "text": "Restaurant" } ], "default_locale": "en" }, "price_range": { "min_price": { "currency_code": "USD", "units": 15, "nanos": 0 }, "max_price": { "currency_code": "USD", "units": 45, "nanos": 0 } }, "business_hours": { "time_ranges": [ { "open_day": "MONDAY", "open_time": { "hours": 11, "minutes": 0 }, "close_day": "MONDAY", "close_time": { "hours": 21, "minutes": 0 } }, { "open_day": "TUESDAY", "open_time": { "hours": 11, "minutes": 0 }, "close_day": "TUESDAY", "close_time": { "hours": 21, "minutes": 0 } }, { "open_day": "WEDNESDAY", "open_time": { "hours": 11, "minutes": 0 }, "close_day": "WEDNESDAY", "close_time": { "hours": 21, "minutes": 0 } }, { "open_day": "THURSDAY", "open_time": { "hours": 11, "minutes": 0 }, "close_day": "THURSDAY", "close_time": { "hours": 22, "minutes": 0 } }, { "open_day": "FRIDAY", "open_time": { "hours": 11, "minutes": 0 }, "close_day": "FRIDAY", "close_time": { "hours": 23, "minutes": 0 } }, { "open_day": "SATURDAY", "open_time": { "hours": 10, "minutes": 0 }, "close_day": "SATURDAY", "close_time": { "hours": 23, "minutes": 0 } }, { "open_day": "SUNDAY", "open_time": { "hours": 10, "minutes": 0 }, "close_day": "SUNDAY", "close_time": { "hours": 20, "minutes": 0 } } ] }, "brand_landing_pages": [ { "brand_id": "tasty_bites_brand", "localized_landing_pages": [ { "url": "https://www.tastybitestest.com/fr", "locales": ["fr-FR", "fr-CA"] }, { "url": "https://www.tastybitestest.com/en", "locales": ["en-US", "en-GB"] } ], "default_url": "https://www.tastybitestest.com" } ] } } ] }