Google Business API
Post to Google Business Profile with Late API - Updates, CTAs, and location posts
Quick Start
Create a Google Business Profile post:
curl -X POST https://getlate.dev/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "🎉 We are open this holiday weekend! Stop by for our special seasonal menu.",
"mediaItems": [
{"type": "image", "url": "https://example.com/holiday-special.jpg"}
],
"platforms": [
{"platform": "googlebusiness", "accountId": "YOUR_ACCOUNT_ID"}
],
"publishNow": true
}'const response = await fetch('https://getlate.dev/api/v1/posts', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
content: '🎉 We are open this holiday weekend! Stop by for our special seasonal menu.',
mediaItems: [
{ type: 'image', url: 'https://example.com/holiday-special.jpg' }
],
platforms: [
{ platform: 'googlebusiness', accountId: 'YOUR_ACCOUNT_ID' }
],
publishNow: true
})
});
const { post } = await response.json();
console.log('Posted to Google Business!', post._id);import requests
response = requests.post(
'https://getlate.dev/api/v1/posts',
headers={'Authorization': 'Bearer YOUR_API_KEY'},
json={
'content': '🎉 We are open this holiday weekend! Stop by for our special seasonal menu.',
'mediaItems': [
{'type': 'image', 'url': 'https://example.com/holiday-special.jpg'}
],
'platforms': [
{'platform': 'googlebusiness', 'accountId': 'YOUR_ACCOUNT_ID'}
],
'publishNow': True
}
)
post = response.json()['post']
print(f"Posted to Google Business! {post['_id']}")Overview
Google Business Profile posts support text and a single image. Videos are not supported.
Image Requirements
| Property | Requirement |
|---|---|
| Max Images | 1 per post |
| Formats | JPEG, PNG |
| Max File Size | 5 MB |
| Min Dimensions | 250 × 250 px |
| Recommended | 1200 × 900 px (4:3) |
Aspect Ratios
| Ratio | Dimensions | Notes |
|---|---|---|
| 4:3 | 1200 × 900 px | Recommended |
| 1:1 | 1080 × 1080 px | Square, good for profile |
| 16:9 | 1200 × 675 px | Landscape |
Note: Google may crop images. Use 4:3 for best results.
Call-to-Action Buttons
Google Business posts can include CTA buttons:
curl -X POST https://getlate.dev/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Book your appointment today! Limited spots available this week.",
"mediaItems": [
{"type": "image", "url": "https://example.com/booking.jpg"}
],
"platforms": [{
"platform": "googlebusiness",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {
"callToAction": {
"type": "BOOK",
"url": "https://mybusiness.com/book"
}
}
}],
"publishNow": true
}'const response = await fetch('https://getlate.dev/api/v1/posts', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
content: 'Book your appointment today! Limited spots available this week.',
mediaItems: [
{ type: 'image', url: 'https://example.com/booking.jpg' }
],
platforms: [{
platform: 'googlebusiness',
accountId: 'YOUR_ACCOUNT_ID',
platformSpecificData: {
callToAction: {
type: 'BOOK',
url: 'https://mybusiness.com/book'
}
}
}],
publishNow: true
})
});response = requests.post(
'https://getlate.dev/api/v1/posts',
headers={'Authorization': 'Bearer YOUR_API_KEY'},
json={
'content': 'Book your appointment today! Limited spots available this week.',
'mediaItems': [
{'type': 'image', 'url': 'https://example.com/booking.jpg'}
],
'platforms': [{
'platform': 'googlebusiness',
'accountId': 'YOUR_ACCOUNT_ID',
'platformSpecificData': {
'callToAction': {
'type': 'BOOK',
'url': 'https://mybusiness.com/book'
}
}
}],
'publishNow': True
}
)Available CTA Types
| Type | Description | Best For |
|---|---|---|
LEARN_MORE | Link to more information | Articles, about pages |
BOOK | Booking/reservation link | Services, appointments |
ORDER | Online ordering link | Restaurants, food |
SHOP | E-commerce link | Retail, products |
SIGN_UP | Registration link | Events, newsletters |
CALL | Phone call action | Contact, inquiries |
Post Without Image
Text-only posts are supported:
{
"content": "Happy Friday! 🎉 We're offering 20% off all services this weekend. Mention this post when you visit!",
"platforms": [
{ "platform": "googlebusiness", "accountId": "acc_123" }
]
}Image URL Requirements
Google Business has strict image requirements:
| Requirement | Details |
|---|---|
| Public URL | Must be publicly accessible |
| HTTPS | Secure URLs only |
| No redirects | Direct link to image |
| No auth required | Can't require login |
✅ https://mybucket.s3.amazonaws.com/image.jpg
✅ https://example.com/images/post.png
❌ https://example.com/image?token=abc (auth required)
❌ http://example.com/image.jpg (not HTTPS)Video Limitations
Google Business Profile does not support video posts. This is a platform limitation.
For video content:
- Post to other platforms (YouTube, Instagram)
- Include a link to the video in your text
- Use a video thumbnail as your image with a "Watch" CTA
Location Selection
If you have multiple locations, you must have a connected account for each location. The account ID determines which location receives the post.
Multi-Location Posting
If your connected Google Business account manages multiple locations, you can post to different locations from the same account connection.
List Available Locations
First, retrieve the list of locations you can post to:
curl -X GET https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-locations \
-H "Authorization: Bearer YOUR_API_KEY"const response = await fetch(
'https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-locations',
{
headers: { 'Authorization': 'Bearer YOUR_API_KEY' }
}
);
const locations = await response.json();
console.log('Available locations:', locations);response = requests.get(
'https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-locations',
headers={'Authorization': 'Bearer YOUR_API_KEY'}
)
locations = response.json()
print('Available locations:', locations)Post to Multiple Locations
Use the same accountId multiple times with different locationId values:
curl -X POST https://getlate.dev/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Now open at all locations! Visit us today 🎉",
"mediaItems": [
{"type": "image", "url": "https://example.com/store.jpg"}
],
"platforms": [
{
"platform": "googlebusiness",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {
"locationId": "locations/111111111"
}
},
{
"platform": "googlebusiness",
"accountId": "YOUR_ACCOUNT_ID",
"platformSpecificData": {
"locationId": "locations/222222222"
}
}
],
"publishNow": true
}'const response = await fetch('https://getlate.dev/api/v1/posts', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
content: 'Now open at all locations! Visit us today 🎉',
mediaItems: [
{ type: 'image', url: 'https://example.com/store.jpg' }
],
platforms: [
{
platform: 'googlebusiness',
accountId: 'YOUR_ACCOUNT_ID',
platformSpecificData: { locationId: 'locations/111111111' }
},
{
platform: 'googlebusiness',
accountId: 'YOUR_ACCOUNT_ID',
platformSpecificData: { locationId: 'locations/222222222' }
}
],
publishNow: true
})
});response = requests.post(
'https://getlate.dev/api/v1/posts',
headers={'Authorization': 'Bearer YOUR_API_KEY'},
json={
'content': 'Now open at all locations! Visit us today 🎉',
'mediaItems': [
{'type': 'image', 'url': 'https://example.com/store.jpg'}
],
'platforms': [
{
'platform': 'googlebusiness',
'accountId': 'YOUR_ACCOUNT_ID',
'platformSpecificData': {'locationId': 'locations/111111111'}
},
{
'platform': 'googlebusiness',
'accountId': 'YOUR_ACCOUNT_ID',
'platformSpecificData': {'locationId': 'locations/222222222'}
}
],
'publishNow': True
}
)Note: The
locationIdformat islocations/followed by the location ID number.
Post Visibility
Posts appear on:
- Your Google Business Profile
- Google Search (when searching your business)
- Google Maps
- Google Knowledge Panel
Character Limits
| Property | Limit |
|---|---|
| Post text | 1500 characters |
| CTA URL | Standard URL length |
Best Practices
Image Tips
- Use high-quality, relevant images
- Show your products/services
- Include your branding subtly
- Avoid excessive text overlay
- Keep important content in center (cropping)
Content Tips
- Include a clear call-to-action
- Mention offers or specials
- Keep it relevant and timely
- Update regularly (weekly posts)
Common Issues
"Image not found"
- Verify URL is publicly accessible
- Check for authentication requirements
- Ensure HTTPS
- Test URL in incognito browser
"Invalid image format"
- Use JPEG or PNG only
- WebP and GIF not supported
- Check file isn't corrupted
"Image too small"
Minimum 250 × 250 px. Recommended: 1200 × 900 px.
Post not appearing
- Posts may take 24-48 hours to appear
- Check Google Business Console for approval status
- Ensure account is verified
CTA not working
- Verify URL is valid and accessible
- Use HTTPS
- Avoid shortened URLs
Inbox
Requires Inbox add-on — $1/social set/month
Google Business supports reviews management only.
Reviews
| Feature | Supported |
|---|---|
| List reviews | ✅ |
| Reply to reviews | ✅ |
| Delete reply | ✅ |
Limitations
- No DMs — Google Business does not have a messaging system accessible via API
- No comments — Posts on Google Business do not support comments
See Reviews API Reference for endpoint details.
Food Menus
Manage food menus for Google Business Profile locations that support them (restaurants, cafes, etc.).
Get Menus
curl -X GET https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-food-menus \
-H "Authorization: Bearer YOUR_API_KEY"const response = await fetch(
'https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-food-menus',
{
headers: { 'Authorization': 'Bearer YOUR_API_KEY' }
}
);
const { menus } = await response.json();
console.log('Food menus:', menus);response = requests.get(
'https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-food-menus',
headers={'Authorization': 'Bearer YOUR_API_KEY'}
)
menus = response.json()
print('Food menus:', menus)Update Menus
curl -X PUT https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-food-menus \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"menus": [{
"labels": [{"displayName": "Lunch Menu", "languageCode": "en"}],
"sections": [{
"labels": [{"displayName": "Appetizers"}],
"items": [{
"labels": [{"displayName": "Caesar Salad", "description": "Romaine, parmesan, croutons"}],
"attributes": {
"price": {"currencyCode": "USD", "units": "12"},
"dietaryRestriction": ["VEGETARIAN"]
}
}]
}]
}],
"updateMask": "menus"
}'const response = await fetch(
'https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-food-menus',
{
method: 'PUT',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
menus: [{
labels: [{ displayName: 'Lunch Menu', languageCode: 'en' }],
sections: [{
labels: [{ displayName: 'Appetizers' }],
items: [{
labels: [{ displayName: 'Caesar Salad', description: 'Romaine, parmesan, croutons' }],
attributes: {
price: { currencyCode: 'USD', units: '12' },
dietaryRestriction: ['VEGETARIAN']
}
}]
}]
}],
updateMask: 'menus'
})
}
);response = requests.put(
'https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-food-menus',
headers={'Authorization': 'Bearer YOUR_API_KEY'},
json={
'menus': [{
'labels': [{'displayName': 'Lunch Menu', 'languageCode': 'en'}],
'sections': [{
'labels': [{'displayName': 'Appetizers'}],
'items': [{
'labels': [{'displayName': 'Caesar Salad', 'description': 'Romaine, parmesan, croutons'}],
'attributes': {
'price': {'currencyCode': 'USD', 'units': '12'},
'dietaryRestriction': ['VEGETARIAN']
}
}]
}]
}],
'updateMask': 'menus'
}
)Menu items support price (with currency code), dietaryRestriction (VEGETARIAN, VEGAN, GLUTEN_FREE), allergen (DAIRY, GLUTEN, SHELLFISH), spiciness, servesNumPeople, and preparationMethods.
See the GMB Food Menus API Reference for full schema details.
Location Details
Read and update your business information including hours, special hours, description, phone numbers, and website.
# Get location details
curl -X GET "https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-location-details?readMask=regularHours,specialHours,profile,websiteUri" \
-H "Authorization: Bearer YOUR_API_KEY"
# Update business hours
curl -X PUT https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-location-details \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"updateMask": "regularHours",
"regularHours": {
"periods": [
{"openDay": "MONDAY", "openTime": "09:00", "closeDay": "MONDAY", "closeTime": "17:00"},
{"openDay": "TUESDAY", "openTime": "09:00", "closeDay": "TUESDAY", "closeTime": "17:00"}
]
}
}'// Get location details
const details = await fetch(
'https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-location-details?readMask=regularHours,specialHours,profile,websiteUri',
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
).then(r => r.json());
// Update business hours
await fetch('https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-location-details', {
method: 'PUT',
headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' },
body: JSON.stringify({
updateMask: 'regularHours',
regularHours: {
periods: [
{ openDay: 'MONDAY', openTime: '09:00', closeDay: 'MONDAY', closeTime: '17:00' },
{ openDay: 'TUESDAY', openTime: '09:00', closeDay: 'TUESDAY', closeTime: '17:00' }
]
}
})
});# Get location details
details = requests.get(
'https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-location-details',
headers={'Authorization': 'Bearer YOUR_API_KEY'},
params={'readMask': 'regularHours,specialHours,profile,websiteUri'}
).json()
# Update business hours
requests.put(
'https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-location-details',
headers={'Authorization': 'Bearer YOUR_API_KEY'},
json={
'updateMask': 'regularHours',
'regularHours': {
'periods': [
{'openDay': 'MONDAY', 'openTime': '09:00', 'closeDay': 'MONDAY', 'closeTime': '17:00'},
{'openDay': 'TUESDAY', 'openTime': '09:00', 'closeDay': 'TUESDAY', 'closeTime': '17:00'}
]
}
}
)Use readMask to request specific fields and updateMask to update them. Available fields include regularHours, specialHours, profile.description, websiteUri, and phoneNumbers.
See the GMB Location Details API Reference for the full schema.
Media (Photos)
Upload, list, and delete photos for your Google Business Profile listing.
# List photos
curl -X GET https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-media \
-H "Authorization: Bearer YOUR_API_KEY"
# Upload a photo
curl -X POST https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-media \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sourceUrl": "https://example.com/photos/interior.jpg",
"description": "Dining area with outdoor seating",
"category": "INTERIOR"
}'// List photos
const media = await fetch(
'https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-media',
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
).then(r => r.json());
// Upload a photo
await fetch('https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-media', {
method: 'POST',
headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' },
body: JSON.stringify({
sourceUrl: 'https://example.com/photos/interior.jpg',
description: 'Dining area with outdoor seating',
category: 'INTERIOR'
})
});# List photos
media = requests.get(
'https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-media',
headers={'Authorization': 'Bearer YOUR_API_KEY'}
).json()
# Upload a photo
requests.post(
'https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-media',
headers={'Authorization': 'Bearer YOUR_API_KEY'},
json={
'sourceUrl': 'https://example.com/photos/interior.jpg',
'description': 'Dining area with outdoor seating',
'category': 'INTERIOR'
}
)Photo categories: COVER, PROFILE, LOGO, EXTERIOR, INTERIOR, FOOD_AND_DRINK, MENU, PRODUCT, TEAMS, ADDITIONAL.
See the GMB Media API Reference for full details.
Attributes
Manage amenities and services like delivery, Wi-Fi, outdoor seating, and payment types.
# Get attributes
curl -X GET https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-attributes \
-H "Authorization: Bearer YOUR_API_KEY"
# Update attributes
curl -X PUT https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-attributes \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"attributes": [
{"name": "has_delivery", "values": [true]},
{"name": "has_outdoor_seating", "values": [true]}
],
"attributeMask": "has_delivery,has_outdoor_seating"
}'// Get attributes
const attrs = await fetch(
'https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-attributes',
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
).then(r => r.json());
// Update attributes
await fetch('https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-attributes', {
method: 'PUT',
headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' },
body: JSON.stringify({
attributes: [
{ name: 'has_delivery', values: [true] },
{ name: 'has_outdoor_seating', values: [true] }
],
attributeMask: 'has_delivery,has_outdoor_seating'
})
});# Get attributes
attrs = requests.get(
'https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-attributes',
headers={'Authorization': 'Bearer YOUR_API_KEY'}
).json()
# Update attributes
requests.put(
'https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-attributes',
headers={'Authorization': 'Bearer YOUR_API_KEY'},
json={
'attributes': [
{'name': 'has_delivery', 'values': [True]},
{'name': 'has_outdoor_seating', 'values': [True]}
],
'attributeMask': 'has_delivery,has_outdoor_seating'
}
)Available attributes vary by business category. Common ones include has_dine_in, has_takeout, has_delivery, has_wifi, has_outdoor_seating, and pay_credit_card_types_accepted.
See the GMB Attributes API Reference for full details.
Place Actions
Manage booking, ordering, and reservation buttons that appear on your listing.
# List place actions
curl -X GET https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-place-actions \
-H "Authorization: Bearer YOUR_API_KEY"
# Create a place action
curl -X POST https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-place-actions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"uri": "https://order.ubereats.com/mybusiness",
"placeActionType": "FOOD_ORDERING"
}'// List place actions
const actions = await fetch(
'https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-place-actions',
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
).then(r => r.json());
// Create a place action
await fetch('https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-place-actions', {
method: 'POST',
headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' },
body: JSON.stringify({
uri: 'https://order.ubereats.com/mybusiness',
placeActionType: 'FOOD_ORDERING'
})
});# List place actions
actions = requests.get(
'https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-place-actions',
headers={'Authorization': 'Bearer YOUR_API_KEY'}
).json()
# Create a place action
requests.post(
'https://getlate.dev/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-place-actions',
headers={'Authorization': 'Bearer YOUR_API_KEY'},
json={
'uri': 'https://order.ubereats.com/mybusiness',
'placeActionType': 'FOOD_ORDERING'
}
)Action types: APPOINTMENT, ONLINE_APPOINTMENT, DINING_RESERVATION, FOOD_ORDERING, FOOD_DELIVERY, FOOD_TAKEOUT, SHOP_ONLINE.
See the GMB Place Actions API Reference for full details.
Related API Endpoints
- Connect Google Business Account — OAuth flow
- Create Post — Post creation and scheduling
- Upload Media — Image uploads
- GMB Reviews — Manage reviews
- GMB Food Menus — Manage food menus
- GMB Location Details — Hours, description, contact info
- GMB Media — Photos management
- GMB Attributes — Amenities and services
- GMB Place Actions — Booking and ordering links
- Reviews