Skip to content

Google Business API

Schedule and automate Google Business Profile posts with Zernio API - Updates, CTAs, location management, reviews, and local SEO

Quick Reference

PropertyValue
Character limit1,500
Images per post1
Videos per postNot supported
Image formatsJPEG, PNG only (WebP auto-converted)
Image max size5 MB
Image min dimensions400 x 300 px
Post typesText, Text+Image, Text+CTA, Event, Offer
SchedulingYes
Inbox (Reviews)Yes
Inbox (DMs/Comments)No
AnalyticsLocation-level only (per-post deprecated by Google)

Before You Start

Google Business Profile is not social media -- it's local SEO. Posts appear on Google Search, Google Maps, and Google Knowledge Panel. They contribute to local search ranking. Posts are visible for about 7 days before being archived. Post weekly minimum.

  • Requires a verified Google Business Profile
  • Posts appear in Google Search + Maps (not a social feed)
  • Videos are not supported
  • No text-only posts via API (media or CTA recommended for visibility)

Quick Start

Create a Google Business Profile post with an image:

typescript
const { post } = await zernio.posts.createPost({
  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
});
console.log('Posted to Google Business!', post._id);
python
result = client.posts.create_post(
    content="We are open this holiday weekend! Stop by for our special seasonal menu.",
    media_items=[
        {"type": "image", "url": "https://example.com/holiday-special.jpg"}
    ],
    platforms=[
        {"platform": "googlebusiness", "accountId": "YOUR_ACCOUNT_ID"}
    ],
    publish_now=True
)
post = result.post
print(f"Posted to Google Business! {post['_id']}")
bash
curl -X POST https://zernio.com/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
  }'

Content Types

Text + Image Post

The most common and recommended post type. A single image with text. No contentType field is needed -- this is the default when media is included.

typescript
const { post } = await zernio.posts.createPost({
  content: 'Fresh seasonal menu available now! Visit us to try our new dishes.',
  mediaItems: [
    { type: 'image', url: 'https://example.com/seasonal-menu.jpg' }
  ],
  platforms: [
    { platform: 'googlebusiness', accountId: 'YOUR_ACCOUNT_ID' }
  ],
  publishNow: true
});
python
result = client.posts.create_post(
    content="Fresh seasonal menu available now! Visit us to try our new dishes.",
    media_items=[
        {"type": "image", "url": "https://example.com/seasonal-menu.jpg"}
    ],
    platforms=[
        {"platform": "googlebusiness", "accountId": "YOUR_ACCOUNT_ID"}
    ],
    publish_now=True
)
bash
curl -X POST https://zernio.com/api/v1/posts \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Fresh seasonal menu available now! Visit us to try our new dishes.",
    "mediaItems": [
      {"type": "image", "url": "https://example.com/seasonal-menu.jpg"}
    ],
    "platforms": [
      {"platform": "googlebusiness", "accountId": "YOUR_ACCOUNT_ID"}
    ],
    "publishNow": true
  }'

Text-Only Post

Text-only posts are supported but have lower visibility on Google Search and Maps. Adding an image or CTA is recommended.

typescript
const { post } = await zernio.posts.createPost({
  content: 'Happy Friday! We are offering 20% off all services this weekend. Mention this post when you visit!',
  platforms: [
    { platform: 'googlebusiness', accountId: 'YOUR_ACCOUNT_ID' }
  ],
  publishNow: true
});
python
result = client.posts.create_post(
    content="Happy Friday! We are offering 20% off all services this weekend. Mention this post when you visit!",
    platforms=[
        {"platform": "googlebusiness", "accountId": "YOUR_ACCOUNT_ID"}
    ],
    publish_now=True
)
bash
curl -X POST https://zernio.com/api/v1/posts \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Happy Friday! We are offering 20% off all services this weekend. Mention this post when you visit!",
    "platforms": [
      {"platform": "googlebusiness", "accountId": "YOUR_ACCOUNT_ID"}
    ],
    "publishNow": true
  }'

Post with CTA Button

Add a call-to-action button to drive traffic. The CTA appears as a prominent button below the post content.

typescript
const { post } = await zernio.posts.createPost({
  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
});
python
result = client.posts.create_post(
    content="Book your appointment today! Limited spots available this week.",
    media_items=[
        {"type": "image", "url": "https://example.com/booking.jpg"}
    ],
    platforms=[{
        "platform": "googlebusiness",
        "accountId": "YOUR_ACCOUNT_ID",
        "platformSpecificData": {
            "callToAction": {
                "type": "BOOK",
                "url": "https://mybusiness.com/book"
            }
        }
    }],
    publish_now=True
)
bash
curl -X POST https://zernio.com/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
  }'

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

Event Post

Promote an event with a title, date range, and optional CTA. Events appear prominently on your Google listing with the event title and schedule.

typescript
const { post } = await zernio.posts.createPost({
  content: 'Join us for our Grand Opening Weekend! Free samples and live music.',
  mediaItems: [
    { type: 'image', url: 'https://example.com/grand-opening.jpg' }
  ],
  platforms: [{
    platform: 'googlebusiness',
    accountId: 'YOUR_ACCOUNT_ID',
    platformSpecificData: {
      topicType: 'EVENT',
      event: {
        title: 'Grand Opening Weekend',
        schedule: {
          startDate: { year: 2026, month: 5, day: 15 },
          startTime: { hours: 9, minutes: 0 },
          endDate: { year: 2026, month: 5, day: 16 },
          endTime: { hours: 17, minutes: 0 }
        }
      },
      callToAction: {
        type: 'LEARN_MORE',
        url: 'https://mybusiness.com/grand-opening'
      }
    }
  }],
  publishNow: true
});
python
result = client.posts.create_post(
    content="Join us for our Grand Opening Weekend! Free samples and live music.",
    media_items=[
        {"type": "image", "url": "https://example.com/grand-opening.jpg"}
    ],
    platforms=[{
        "platform": "googlebusiness",
        "accountId": "YOUR_ACCOUNT_ID",
        "platformSpecificData": {
            "topicType": "EVENT",
            "event": {
                "title": "Grand Opening Weekend",
                "schedule": {
                    "startDate": {"year": 2026, "month": 5, "day": 15},
                    "startTime": {"hours": 9, "minutes": 0},
                    "endDate": {"year": 2026, "month": 5, "day": 16},
                    "endTime": {"hours": 17, "minutes": 0}
                }
            },
            "callToAction": {
                "type": "LEARN_MORE",
                "url": "https://mybusiness.com/grand-opening"
            }
        }
    }],
    publish_now=True
)
bash
curl -X POST https://zernio.com/api/v1/posts \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Join us for our Grand Opening Weekend! Free samples and live music.",
    "mediaItems": [
      {"type": "image", "url": "https://example.com/grand-opening.jpg"}
    ],
    "platforms": [{
      "platform": "googlebusiness",
      "accountId": "YOUR_ACCOUNT_ID",
      "platformSpecificData": {
        "topicType": "EVENT",
        "event": {
          "title": "Grand Opening Weekend",
          "schedule": {
            "startDate": {"year": 2026, "month": 5, "day": 15},
            "startTime": {"hours": 9, "minutes": 0},
            "endDate": {"year": 2026, "month": 5, "day": 16},
            "endTime": {"hours": 17, "minutes": 0}
          }
        },
        "callToAction": {
          "type": "LEARN_MORE",
          "url": "https://mybusiness.com/grand-opening"
        }
      }
    }],
    "publishNow": true
  }'

Schedule dates also accept ISO 8601 strings, which are converted automatically:

json
{
  "topicType": "EVENT",
  "event": {
    "title": "Grand Opening Weekend",
    "schedule": {
      "startDate": "2026-05-15T00:00:00Z",
      "startTime": "2026-05-15T09:00:00Z",
      "endDate": "2026-05-16T00:00:00Z",
      "endTime": "2026-05-16T17:00:00Z"
    }
  }
}

Offer Post

Promote a deal with coupon codes, redemption URLs, and terms. Offer posts include an event object that sets the offer period and title displayed on Google.

typescript
const { post } = await zernio.posts.createPost({
  content: 'Holiday sale! 20% off everything through the end of December.',
  mediaItems: [
    { type: 'image', url: 'https://example.com/holiday-sale.jpg' }
  ],
  platforms: [{
    platform: 'googlebusiness',
    accountId: 'YOUR_ACCOUNT_ID',
    platformSpecificData: {
      topicType: 'OFFER',
      event: {
        title: 'Holiday Sale - 20% Off',
        schedule: {
          startDate: { year: 2026, month: 12, day: 1 },
          endDate: { year: 2026, month: 12, day: 31 }
        }
      },
      offer: {
        couponCode: 'HOLIDAY20',
        redeemOnlineUrl: 'https://mybusiness.com/shop',
        termsConditions: 'Valid in-store and online. Cannot be combined with other offers.'
      }
    }
  }],
  publishNow: true
});
python
result = client.posts.create_post(
    content="Holiday sale! 20% off everything through the end of December.",
    media_items=[
        {"type": "image", "url": "https://example.com/holiday-sale.jpg"}
    ],
    platforms=[{
        "platform": "googlebusiness",
        "accountId": "YOUR_ACCOUNT_ID",
        "platformSpecificData": {
            "topicType": "OFFER",
            "event": {
                "title": "Holiday Sale - 20% Off",
                "schedule": {
                    "startDate": {"year": 2026, "month": 12, "day": 1},
                    "endDate": {"year": 2026, "month": 12, "day": 31}
                }
            },
            "offer": {
                "couponCode": "HOLIDAY20",
                "redeemOnlineUrl": "https://mybusiness.com/shop",
                "termsConditions": "Valid in-store and online. Cannot be combined with other offers."
            }
        }
    }],
    publish_now=True
)
bash
curl -X POST https://zernio.com/api/v1/posts \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Holiday sale! 20% off everything through the end of December.",
    "mediaItems": [
      {"type": "image", "url": "https://example.com/holiday-sale.jpg"}
    ],
    "platforms": [{
      "platform": "googlebusiness",
      "accountId": "YOUR_ACCOUNT_ID",
      "platformSpecificData": {
        "topicType": "OFFER",
        "event": {
          "title": "Holiday Sale - 20% Off",
          "schedule": {
            "startDate": {"year": 2026, "month": 12, "day": 1},
            "endDate": {"year": 2026, "month": 12, "day": 31}
          }
        },
        "offer": {
          "couponCode": "HOLIDAY20",
          "redeemOnlineUrl": "https://mybusiness.com/shop",
          "termsConditions": "Valid in-store and online. Cannot be combined with other offers."
        }
      }
    }],
    "publishNow": true
  }'

Offer fields (all optional, but at least one recommended):

FieldTypeDescription
couponCodestringPromo code customers can use
redeemOnlineUrlstring (URI)URL where the offer can be redeemed
termsConditionsstringTerms and conditions text

Media Requirements

PropertyRequirement
Max images1 per post
FormatsJPEG, PNG (WebP auto-converted)
Max file size5 MB
Min dimensions400 x 300 px
Recommended1200 x 900 px (4:3)

Google may crop images. Use 4:3 aspect ratio for best results.

Platform-Specific Fields

All fields are set inside platformSpecificData on the platform entry.

FieldTypeDescription
topicType"STANDARD" | "EVENT" | "OFFER"Post type. Defaults to STANDARD. EVENT requires the event object. OFFER requires offer and optionally event for the offer period.
eventobjectEvent details. Required for EVENT, optional for OFFER (sets offer period). Contains title (string) and schedule with startDate, startTime, endDate, endTime. Dates accept { year, month, day } objects or ISO 8601 strings.
offerobjectOffer details for OFFER posts. Fields: couponCode, redeemOnlineUrl, termsConditions (all optional).
locationIdstringFor multi-location businesses. Format: locations/111111111. Get locations via GET /v1/accounts/{accountId}/gmb-locations. If omitted, posts to default location.
languageCodestringBCP 47 language code (e.g., en, de). Sets metadata only -- does not translate content.
topicTypeSTANDARD | EVENT | OFFER | ALERTPost type. STANDARD is a regular update. EVENT requires event. OFFER requires offer. Defaults to STANDARD if omitted.
callToAction{ type, url }CTA button. type: LEARN_MORE, BOOK, ORDER, SHOP, SIGN_UP, CALL. url: valid HTTPS URL.
eventobjectEvent details. Required when topicType is EVENT. Event schedule accepts both ISO 8601 strings (e.g. 2026-04-15T09:00:00Z) and Google's native { year, month, day } objects.
offerobjectOffer details. Required when topicType is OFFER.

Event and Offer Posts (topicType)

Google Business Profile supports three post types via platformSpecificData.topicType:

topicTypeWhen to useRequired fields
STANDARDRegular updatesNone
EVENTAnnouncements with a date range (grand openings, live music, workshops)event
OFFERPromotions and discountsoffer

> Note: For EVENT posts, Google returns a 400 error if event is omitted.

Event Post Example

typescript
const { post } = await zernio.posts.createPost({
  content: 'Join us for our Grand Opening Weekend! Food, music, and giveaways.',
  mediaItems: [
    { type: 'image', url: 'https://example.com/grand-opening.jpg' }
  ],
  platforms: [{
    platform: 'googlebusiness',
    accountId: 'YOUR_ACCOUNT_ID',
    platformSpecificData: {
      topicType: 'EVENT',
      event: {
        title: 'Grand Opening Weekend',
        schedule: {
          startDate: { year: 2026, month: 5, day: 15 },
          startTime: { hours: 9, minutes: 0 },
          endDate: { year: 2026, month: 5, day: 16 },
          endTime: { hours: 17, minutes: 0 }
        }
      },
      callToAction: {
        type: 'LEARN_MORE',
        url: 'https://mybusiness.com/grand-opening'
      }
    }
  }],
  publishNow: true
});
python
result = client.posts.create_post(
    content="Join us for our Grand Opening Weekend! Food, music, and giveaways.",
    media_items=[
        {"type": "image", "url": "https://example.com/grand-opening.jpg"}
    ],
    platforms=[{
        "platform": "googlebusiness",
        "accountId": "YOUR_ACCOUNT_ID",
        "platformSpecificData": {
            "topicType": "EVENT",
            "event": {
                "title": "Grand Opening Weekend",
                "schedule": {
                    "startDate": {"year": 2026, "month": 5, "day": 15},
                    "startTime": {"hours": 9, "minutes": 0},
                    "endDate": {"year": 2026, "month": 5, "day": 16},
                    "endTime": {"hours": 17, "minutes": 0}
                }
            },
            "callToAction": {
                "type": "LEARN_MORE",
                "url": "https://mybusiness.com/grand-opening"
            }
        }
    }],
    publish_now=True
)
bash
curl -X POST https://zernio.com/api/v1/posts \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Join us for our Grand Opening Weekend! Food, music, and giveaways.",
    "mediaItems": [
      {"type": "image", "url": "https://example.com/grand-opening.jpg"}
    ],
    "platforms": [{
      "platform": "googlebusiness",
      "accountId": "YOUR_ACCOUNT_ID",
      "platformSpecificData": {
        "topicType": "EVENT",
        "event": {
          "title": "Grand Opening Weekend",
          "schedule": {
            "startDate": {"year": 2026, "month": 5, "day": 15},
            "startTime": {"hours": 9, "minutes": 0},
            "endDate": {"year": 2026, "month": 5, "day": 16},
            "endTime": {"hours": 17, "minutes": 0}
          }
        },
        "callToAction": {
          "type": "LEARN_MORE",
          "url": "https://mybusiness.com/grand-opening"
        }
      }
    }],
    "publishNow": true
  }'

Offer Post Example

typescript
const { post } = await zernio.posts.createPost({
  content: 'This weekend only: Save 20% on all services. Use code SAVE20.',
  mediaItems: [
    { type: 'image', url: 'https://example.com/save20.jpg' }
  ],
  platforms: [{
    platform: 'googlebusiness',
    accountId: 'YOUR_ACCOUNT_ID',
    platformSpecificData: {
      topicType: 'OFFER',
      offer: {
        offerType: 'OFFER',
        redeemOnlineUrl: 'https://mybusiness.com/redeem',
        couponCode: 'SAVE20',
        termsConditions: 'Valid Fri–Sun only. One per customer.'
      },
      callToAction: {
        type: 'SHOP',
        url: 'https://mybusiness.com/services'
      }
    }
  }],
  publishNow: true
});
python
result = client.posts.create_post(
    content="This weekend only: Save 20% on all services. Use code SAVE20.",
    media_items=[
        {"type": "image", "url": "https://example.com/save20.jpg"}
    ],
    platforms=[{
        "platform": "googlebusiness",
        "accountId": "YOUR_ACCOUNT_ID",
        "platformSpecificData": {
            "topicType": "OFFER",
            "offer": {
                "offerType": "OFFER",
                "redeemOnlineUrl": "https://mybusiness.com/redeem",
                "couponCode": "SAVE20",
                "termsConditions": "Valid Fri–Sun only. One per customer."
            },
            "callToAction": {
                "type": "SHOP",
                "url": "https://mybusiness.com/services"
            }
        }
    }],
    publish_now=True
)
bash
curl -X POST https://zernio.com/api/v1/posts \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "This weekend only: Save 20% on all services. Use code SAVE20.",
    "mediaItems": [
      {"type": "image", "url": "https://example.com/save20.jpg"}
    ],
    "platforms": [{
      "platform": "googlebusiness",
      "accountId": "YOUR_ACCOUNT_ID",
      "platformSpecificData": {
        "topicType": "OFFER",
        "offer": {
          "offerType": "OFFER",
          "redeemOnlineUrl": "https://mybusiness.com/redeem",
          "couponCode": "SAVE20",
          "termsConditions": "Valid Fri–Sun only. One per customer."
        },
        "callToAction": {
          "type": "SHOP",
          "url": "https://mybusiness.com/services"
        }
      }
    }],
    "publishNow": true
  }'

Language Code Example

By default, post language is auto-detected from text. If auto-detection may be inaccurate (very short posts, mixed-language content, transliterated text), set languageCode explicitly.

typescript
const { post } = await zernio.posts.createPost({
  content: 'Diese Woche: 20% Rabatt auf alle Services.',
  mediaItems: [
    { type: 'image', url: 'https://example.com/promo.jpg' }
  ],
  platforms: [{
    platform: 'googlebusiness',
    accountId: 'YOUR_ACCOUNT_ID',
    platformSpecificData: {
      languageCode: 'de'
    }
  }],
  publishNow: true
});
python
result = client.posts.create_post(
    content="Diese Woche: 20% Rabatt auf alle Services.",
    media_items=[
        {"type": "image", "url": "https://example.com/promo.jpg"}
    ],
    platforms=[{
        "platform": "googlebusiness",
        "accountId": "YOUR_ACCOUNT_ID",
        "platformSpecificData": {
            "languageCode": "de"
        }
    }],
    publish_now=True
)
bash
curl -X POST https://zernio.com/api/v1/posts \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Diese Woche: 20% Rabatt auf alle Services.",
    "mediaItems": [
      {"type": "image", "url": "https://example.com/promo.jpg"}
    ],
    "platforms": [{
      "platform": "googlebusiness",
      "accountId": "YOUR_ACCOUNT_ID",
      "platformSpecificData": {
        "languageCode": "de"
      }
    }],
    "publishNow": true
  }'

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:

typescript
const locations = await zernio.connect.getGmbLocations('YOUR_ACCOUNT_ID');
console.log('Available locations:', locations);
python
locations = client.connect.get_gmb_locations("YOUR_ACCOUNT_ID")
print("Available locations:", locations)
bash
curl -X GET https://zernio.com/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-locations \
  -H "Authorization: Bearer YOUR_API_KEY"

Post to Multiple Locations

Use the same accountId multiple times with different locationId values:

typescript
const { post } = await zernio.posts.createPost({
  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
});
python
result = client.posts.create_post(
    content="Now open at all locations! Visit us today.",
    media_items=[
        {"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"}
        }
    ],
    publish_now=True
)
bash
curl -X POST https://zernio.com/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
  }'

The locationId format is locations/ followed by the location ID number.

Media URL Requirements

RequirementDetails
Public URLMust be publicly accessible
HTTPSSecure URLs only
No redirectsDirect link to image
No auth requiredCannot require login
https://mybucket.s3.amazonaws.com/image.jpg       (valid)
https://example.com/images/post.png                (valid)
https://example.com/image?token=abc                (invalid - auth required)
http://example.com/image.jpg                       (invalid - not HTTPS)

Business Profile Management

Beyond posting, you can manage your Google Business Profile listing directly through the API. Each subsection below covers a specific management feature.

Food Menus

Manage food menus for locations that support them (restaurants, cafes, etc.). Menu items support price (with currency code), dietaryRestriction (VEGETARIAN, VEGAN, GLUTEN_FREE), allergen (DAIRY, GLUTEN, SHELLFISH), spiciness, servesNumPeople, and preparationMethods.

typescript
// Get menus
const menus = await zernio.gmbfoodmenus.getGoogleBusinessFoodMenus('YOUR_ACCOUNT_ID');
console.log('Food menus:', menus);

// Update menus
await zernio.gmbfoodmenus.updateGoogleBusinessFoodMenus('YOUR_ACCOUNT_ID', {
  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'
});
python
# Get menus
menus = client.accounts.get_google_business_food_menus("YOUR_ACCOUNT_ID")
print("Food menus:", menus)

# Update menus
client.accounts.update_google_business_food_menus("YOUR_ACCOUNT_ID",
    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"]
                }
            }]
        }]
    }],
    update_mask="menus"
)
bash
# Get menus
curl -X GET https://zernio.com/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-food-menus \
  -H "Authorization: Bearer YOUR_API_KEY"

# Update menus
curl -X PUT https://zernio.com/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"
  }'

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. Use readMask to request specific fields and updateMask to update them. Available fields include regularHours, specialHours, profile.description, websiteUri, and phoneNumbers.

typescript
// Get location details
const details = await zernio.gmblocationdetails.getGoogleBusinessLocationDetails('YOUR_ACCOUNT_ID', {
  readMask: 'regularHours,specialHours,profile,websiteUri'
});

// Update business hours
await zernio.gmblocationdetails.updateGoogleBusinessLocationDetails('YOUR_ACCOUNT_ID', {
  updateMask: 'regularHours',
  regularHours: {
    periods: [
      { openDay: 'MONDAY', openTime: '09:00', closeDay: 'MONDAY', closeTime: '17:00' },
      { openDay: 'TUESDAY', openTime: '09:00', closeDay: 'TUESDAY', closeTime: '17:00' }
    ]
  }
});
python
# Get location details
details = client.accounts.get_google_business_location_details("YOUR_ACCOUNT_ID",
    read_mask="regularHours,specialHours,profile,websiteUri"
)

# Update business hours
client.accounts.update_google_business_location_details("YOUR_ACCOUNT_ID",
    update_mask="regularHours",
    regular_hours={
        "periods": [
            {"openDay": "MONDAY", "openTime": "09:00", "closeDay": "MONDAY", "closeTime": "17:00"},
            {"openDay": "TUESDAY", "openTime": "09:00", "closeDay": "TUESDAY", "closeTime": "17:00"}
        ]
    }
)
bash
# Get location details
curl -X GET "https://zernio.com/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://zernio.com/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"}
      ]
    }
  }'

See the GMB Location Details API Reference for the full schema.

Media (Photos)

Upload, list, and delete photos for your Google Business Profile listing. Photo categories: COVER, PROFILE, LOGO, EXTERIOR, INTERIOR, FOOD_AND_DRINK, MENU, PRODUCT, TEAMS, ADDITIONAL.

typescript
// List photos
const media = await zernio.gmbmedia.listGoogleBusinessMedia('YOUR_ACCOUNT_ID');

// Upload a photo
await zernio.gmbmedia.createGoogleBusinessMedia('YOUR_ACCOUNT_ID', {
  sourceUrl: 'https://example.com/photos/interior.jpg',
  description: 'Dining area with outdoor seating',
  category: 'INTERIOR'
});
python
# List photos
media = client.accounts.list_google_business_media("YOUR_ACCOUNT_ID")

# Upload a photo
client.accounts.create_google_business_media("YOUR_ACCOUNT_ID",
    source_url="https://example.com/photos/interior.jpg",
    description="Dining area with outdoor seating",
    category="INTERIOR"
)
bash
# List photos
curl -X GET https://zernio.com/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-media \
  -H "Authorization: Bearer YOUR_API_KEY"

# Upload a photo
curl -X POST https://zernio.com/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"
  }'

See the GMB Media API Reference for full details.

Attributes

Manage amenities and services like delivery, Wi-Fi, outdoor seating, and payment types. 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.

typescript
// Get attributes
const attrs = await zernio.gmbattributes.getGoogleBusinessAttributes('YOUR_ACCOUNT_ID');

// Update attributes
await zernio.gmbattributes.updateGoogleBusinessAttributes('YOUR_ACCOUNT_ID', {
  attributes: [
    { name: 'has_delivery', values: [true] },
    { name: 'has_outdoor_seating', values: [true] }
  ],
  attributeMask: 'has_delivery,has_outdoor_seating'
});
python
# Get attributes
attrs = client.accounts.get_google_business_attributes("YOUR_ACCOUNT_ID")

# Update attributes
client.accounts.update_google_business_attributes("YOUR_ACCOUNT_ID",
    attributes=[
        {"name": "has_delivery", "values": [True]},
        {"name": "has_outdoor_seating", "values": [True]}
    ],
    attribute_mask="has_delivery,has_outdoor_seating"
)
bash
# Get attributes
curl -X GET https://zernio.com/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-attributes \
  -H "Authorization: Bearer YOUR_API_KEY"

# Update attributes
curl -X PUT https://zernio.com/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"
  }'

See the GMB Attributes API Reference for full details.

Place Actions

Manage booking, ordering, and reservation buttons that appear on your listing. Action types: APPOINTMENT, ONLINE_APPOINTMENT, DINING_RESERVATION, FOOD_ORDERING, FOOD_DELIVERY, FOOD_TAKEOUT, SHOP_ONLINE.

typescript
// List place actions
const actions = await zernio.gmbplaceactions.listGoogleBusinessPlaceActions('YOUR_ACCOUNT_ID');

// Create a place action
await zernio.gmbplaceactions.createGoogleBusinessPlaceAction('YOUR_ACCOUNT_ID', {
  uri: 'https://order.ubereats.com/mybusiness',
  placeActionType: 'FOOD_ORDERING'
});
python
# List place actions
actions = client.accounts.list_google_business_place_actions("YOUR_ACCOUNT_ID")

# Create a place action
client.accounts.create_google_business_place_action("YOUR_ACCOUNT_ID",
    uri="https://order.ubereats.com/mybusiness",
    place_action_type="FOOD_ORDERING"
)
bash
# List place actions
curl -X GET https://zernio.com/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-place-actions \
  -H "Authorization: Bearer YOUR_API_KEY"

# Create a place action
curl -X POST https://zernio.com/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"
  }'

See the GMB Place Actions API Reference for full details.

Services

Get and manage the services offered by a Google Business Profile location. Services can be structured (using a predefined serviceTypeId) or free-form (custom label), with an optional price.

> Note: Google's API requires full replacement of the service list. Use PUT /v1/accounts/{accountId}/gmb-services to replace the entire list.

typescript
// Get services
const services = await zernio.gmbservices.getGoogleBusinessServices('YOUR_ACCOUNT_ID');
console.log('Services:', services);

// Replace services (full replacement)
await zernio.gmbservices.updateGoogleBusinessServices('YOUR_ACCOUNT_ID', {
  serviceItems: [
    {
      freeFormServiceItem: {
        category: 'categories/gcid:plumber',
        label: {
          displayName: 'Pipe Repair',
          description: 'Emergency and scheduled pipe repair'
        }
      },
      price: { currencyCode: 'USD', units: '150' }
    }
  ]
});
python
# Get services
services = client.gmb_services.get_google_business_services("YOUR_ACCOUNT_ID")
print("Services:", services)

# Replace services (full replacement)
client.gmb_services.update_google_business_services("YOUR_ACCOUNT_ID",
    service_items=[
        {
            "freeFormServiceItem": {
                "category": "categories/gcid:plumber",
                "label": {
                    "displayName": "Pipe Repair",
                    "description": "Emergency and scheduled pipe repair"
                }
            },
            "price": {"currencyCode": "USD", "units": "150"}
        }
    ]
)
bash
# Get services
curl -X GET https://zernio.com/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-services \
  -H "Authorization: Bearer YOUR_API_KEY"

# Replace services (full replacement)
curl -X PUT https://zernio.com/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-services \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "serviceItems": [
      {
        "freeFormServiceItem": {
          "category": "categories/gcid:plumber",
          "label": {
            "displayName": "Pipe Repair",
            "description": "Emergency and scheduled pipe repair"
          }
        },
        "price": {
          "currencyCode": "USD",
          "units": "150"
        }
      }
    ]
  }'
typescript
// List place actions
const actions = await zernio.gmbplaceactions.listGoogleBusinessPlaceActions('YOUR_ACCOUNT_ID');

// Create a place action
await zernio.gmbplaceactions.createGoogleBusinessPlaceAction('YOUR_ACCOUNT_ID', {
  uri: 'https://order.ubereats.com/mybusiness',
  placeActionType: 'FOOD_ORDERING'
});
python
# List place actions
actions = client.accounts.list_google_business_place_actions("YOUR_ACCOUNT_ID")

# Create a place action
client.accounts.create_google_business_place_action("YOUR_ACCOUNT_ID",
    uri="https://order.ubereats.com/mybusiness",
    place_action_type="FOOD_ORDERING"
)
bash
# List place actions
curl -X GET https://zernio.com/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-place-actions \
  -H "Authorization: Bearer YOUR_API_KEY"

# Create a place action
curl -X POST https://zernio.com/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"
  }'

See the GMB Place Actions API Reference for full details.

Update an existing place action

Use PATCH /v1/accounts/{accountId}/gmb-place-actions to update an existing action link (change uri and/or placeActionType). Only fields included in the request body are updated.

typescript
await zernio.gmbplaceactions.updateGoogleBusinessPlaceAction('YOUR_ACCOUNT_ID', {
  name: 'locations/123/placeActionLinks/456',
  uri: 'https://order.doordash.com/joespizza'
});
python
client.accounts.update_google_business_place_action("YOUR_ACCOUNT_ID",
    name="locations/123/placeActionLinks/456",
    uri="https://order.doordash.com/joespizza"
)
bash
curl -X PATCH "https://zernio.com/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-place-actions" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "locations/123/placeActionLinks/456",
    "uri": "https://order.doordash.com/joespizza"
  }'

Analytics

> Included — Analytics is bundled with every paid account on the Usage plan.

Per-post analytics are not available for Google Business Profile. Google deprecated the per-post insights endpoint and did not ship a replacement. Per-post views, clicks and likes for GBP posts no longer exist on Google's side, not on Zernio, not anywhere.

For GBP engagement data, use the location-level Performance API documented below.

Performance Metrics

The Performance API returns daily time-series data for your Google Business Profile location. Metrics include impressions (Maps/Search, desktop/mobile), website clicks, call clicks, direction requests, conversations, bookings, and food orders. Data may be delayed 2-3 days. Max 18 months of historical data.

typescript
const { data } = await zernio.analytics.getGoogleBusinessPerformance({
  query: {
    accountId: 'YOUR_ACCOUNT_ID',
    startDate: '2026-01-01',
    endDate: '2026-03-31'
  }
});
console.log(data.metrics);
// { WEBSITE_CLICKS: { total: 42, values: [...] }, CALL_CLICKS: { total: 7, values: [...] }, ... }
python
response = client.analytics.get_google_business_performance(
    account_id='YOUR_ACCOUNT_ID',
    start_date='2026-01-01',
    end_date='2026-03-31'
)
print(response.metrics)
bash
curl "https://zernio.com/api/v1/analytics/googlebusiness/performance?accountId=YOUR_ACCOUNT_ID&startDate=2026-01-01&endDate=2026-03-31" \
  -H "Authorization: Bearer YOUR_API_KEY"

Available metrics: BUSINESS_IMPRESSIONS_DESKTOP_MAPS, BUSINESS_IMPRESSIONS_DESKTOP_SEARCH, BUSINESS_IMPRESSIONS_MOBILE_MAPS, BUSINESS_IMPRESSIONS_MOBILE_SEARCH, BUSINESS_CONVERSATIONS, BUSINESS_DIRECTION_REQUESTS, CALL_CLICKS, WEBSITE_CLICKS, BUSINESS_BOOKINGS, BUSINESS_FOOD_ORDERS, BUSINESS_FOOD_MENU_CLICKS.

Search Keywords

The Search Keywords API returns keywords that triggered impressions for your location, aggregated monthly. Keywords below a minimum impression threshold set by Google are excluded. Max 18 months of historical data.

typescript
const { data } = await zernio.analytics.getGoogleBusinessSearchKeywords({
  query: {
    accountId: 'YOUR_ACCOUNT_ID',
    startMonth: '2026-01',
    endMonth: '2026-03'
  }
});
data.keywords.forEach(k => console.log(`${k.keyword}: ${k.impressions} impressions`));
python
response = client.analytics.get_google_business_search_keywords(
    account_id='YOUR_ACCOUNT_ID',
    start_month='2026-01',
    end_month='2026-03'
)
for k in response.keywords:
    print(f"{k.keyword}: {k.impressions} impressions")
bash
curl "https://zernio.com/api/v1/analytics/googlebusiness/search-keywords?accountId=YOUR_ACCOUNT_ID&startMonth=2026-01&endMonth=2026-03" \
  -H "Authorization: Bearer YOUR_API_KEY"

What You Can't Do

These features are not available through Google Business Profile's API:

  • Post videos
  • Respond to Q&A (deprecated by Google, replaced by AI-powered "Ask Maps")
  • Manage service areas
  • Manage business categories

Common Errors

Google Business has a 6.5% failure rate across Zernio's platform (557 failures out of 8,529 attempts). Here are the most frequent errors and how to fix them:

ErrorMeaningFix
"Image not found"Image URL is inaccessible or requires authenticationVerify URL is publicly accessible. Ensure HTTPS. Test URL in an incognito browser.
"Invalid image format"Unsupported file format or corrupted fileUse JPEG or PNG only. GIF is not supported. Re-export the image if corrupted.
"Image too small"Image dimensions below minimumUse at least 400 x 300 px. Recommended: 1200 x 900 px.
Post not appearingPost may be pending review or account not verifiedPosts may take 24-48 hours to appear. Check Google Business Console for approval status. Ensure account is verified.
CTA not workingInvalid or inaccessible URLVerify URL is valid and accessible. Use HTTPS. Avoid shortened URLs.

Inbox

> Included — Inbox (DMs, comments, reviews) is bundled with every paid account on the Usage plan.

Google Business supports reviews management with real-time notifications.

Reviews

FeatureSupported
List reviews
Reply to reviews
Delete reply
Real-time webhooks✅ (review.new, review.updated)

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.

Batch get reviews (multiple locations)

Use POST /v1/accounts/{accountId}/gmb-reviews/batch to fetch reviews across multiple locations in a single request. Reviews are grouped by location in the response.

bash
curl -X POST https://zernio.com/api/v1/accounts/YOUR_ACCOUNT_ID/gmb-reviews/batch \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "locationNames": [
      "accounts/123/locations/456",
      "accounts/123/locations/789"
    ],
    "pageSize": 50
  }'
javascript
const { data } = await zernio.accounts.batchGetGoogleBusinessReviews('YOUR_ACCOUNT_ID', {
  locationNames: [
    'accounts/123/locations/456',
    'accounts/123/locations/789'
  ],
  pageSize: 50
});

data.locationReviews.forEach(lr => {
  console.log(lr.locationName, lr.totalReviewCount, lr.averageRating);
});
python
data = client.accounts.batch_get_google_business_reviews("YOUR_ACCOUNT_ID",
    location_names=[
        "accounts/123/locations/456",
        "accounts/123/locations/789"
    ],
    page_size=50
)

for lr in data["locationReviews"]:
    print(lr["locationName"], lr.get("totalReviewCount"), lr.get("averageRating"))
  • 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 Services - List and replace services
  • GMB Place Actions - Booking and ordering links
  • Performance Metrics - Daily impressions, clicks, calls, directions, bookings
  • Search Keywords - Keywords that triggered impressions
  • Reviews