LateLate API
Platforms

Google Business API


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

PropertyRequirement
Max Images1 per post
FormatsJPEG, PNG
Max File Size5 MB
Min Dimensions250 × 250 px
Recommended1200 × 900 px (4:3)

Aspect Ratios

RatioDimensionsNotes
4:31200 × 900 pxRecommended
1:11080 × 1080 pxSquare, good for profile
16:91200 × 675 pxLandscape

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

TypeDescriptionBest For
LEARN_MORELink to more informationArticles, about pages
BOOKBooking/reservation linkServices, appointments
ORDEROnline ordering linkRestaurants, food
SHOPE-commerce linkRetail, products
SIGN_UPRegistration linkEvents, newsletters
CALLPhone call actionContact, 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:

RequirementDetails
Public URLMust be publicly accessible
HTTPSSecure URLs only
No redirectsDirect link to image
No auth requiredCan'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 locationId format is locations/ 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

PropertyLimit
Post text1500 characters
CTA URLStandard 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

FeatureSupported
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.