Publicações

Endpoints de publicações com cURL, Python e Node: anúncios, eventos, ofertas, agendamento, campanhas por local e em massa, consulta de status e exclusão.

Uma publicação é uma atualização curta publicada no Google Business Profile ou na página do Facebook de um local: um anúncio, um evento com data definida ou uma oferta com cupom. Uma campanha pode abranger muitos locais e ambos os sites ao mesmo tempo. A publicação é assíncrona, então cada chamada de criação retorna imediatamente e você consulta periodicamente o resultado. Os três endpoints GET funcionam com uma chave Read (leitura); criar e excluir exige uma chave Write (escrita).

Visão geral dos endpoints

MétodoCaminhoO que fazReferência
POST/postsPublica um anúncio, uma mensagem simples com botão e imagem opcionais.Criar uma publicação de anúncio
POST/postsPublica um evento com título e um período com início e fim.Criar uma publicação de evento
POST/postsPublica uma oferta com código de cupom, desconto e condições.Criar uma publicação de oferta
GET/posts/{postId}Uma campanha com status de publicação e contagens de engajamento por site.Obter uma publicação
GET/locations/{locationId}/postsUma página de campanhas que abrangem um local.Buscar publicações de um local
DELETE/posts/{postId}Exclui permanentemente uma campanha e remove sua publicação de todos os lugares.Excluir uma publicação
POST/bulk-postsUma campanha distribuída por muitos locais.Criar uma publicação em massa
GET/bulk-posts/{postId}Uma campanha em massa com detalhamento de métricas por local.Buscar uma publicação em massa por ID
GET/locations/{locationId}/bulk-postsCampanhas em massa que incluem um local, com filtros e ordenação.Buscar publicações em massa de um local

Valores que você usará em todos os endpoints

Os quatro enums abaixo são compartilhados por todos os endpoints de criação.

CampoValores aceitos
postTypeANNOUNCEMENT, EVENT, OFFER, COVID19, PRODUCT
postSites e cada siteGOOGLE, FACEBOOK
postCta[].typeBOOK, ORDER, SHOP, LEARN_MORE, SIGN_UP, GET_OFFER
postMediaUrl[].typeIMAGE, VIDEO

Publicar um anúncio

POST /posts

Um anúncio recebe uma mensagem e, opcionalmente, um botão e uma imagem por site. Forneça uma entrada de postMessage para cada site listado em postSites. A resposta retorna a nova campanha com status definido como INPROGRESS, porque a publicação é executada em segundo plano.

Parâmetros

NomeEmObrigatórioObservações
input.postNamebodySimNome interno da campanha, nunca exibido aos clientes.
input.locationIdsbodySimArray de ids de locais em base64.
input.postTypebodySimANNOUNCEMENT aqui.
input.postSitesbodySimArray de GOOGLE e FACEBOOK.
input.postMessagebodyNãoArray de objetos com site e message.
input.postCtabodyNãoArray de objetos com site, type, url.
input.postMediaUrlbodyNãoArray de objetos com site, url, type.
input.clientMutationIdbodyNãoRetornado sem alterações para correlação.
Announcement
curl -X POST https://listingsapi.com/api/v4/posts \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "postName": "Grand Opening Announcement",    "locationIds": ["TG9jYXRpb246MTgwMDI4OQ=="],    "postType": "ANNOUNCEMENT",    "postSites": ["GOOGLE"],    "postMessage": [      { "site": "GOOGLE", "message": "We are now open! Visit us this week for our grand opening." }    ],    "postCta": [      { "site": "GOOGLE", "type": "LEARN_MORE", "url": "https://jennyhome.example/opening" }    ],    "postMediaUrl": [      { "site": "GOOGLE", "url": "https://cdn.example.com/opening.jpg", "type": "IMAGE" }    ]  }}'
JSON
{  "data": {    "createSocialPost": {      "success": true,      "errors": [],      "socialPost": {        "id": "U29jaWFsUG9zdDo0NDEyMg==",        "name": "Grand Opening Announcement",        "status": "INPROGRESS",        "type": "ANNOUNCEMENT",        "sites": ["GOOGLE"],        "publishDetails": [          { "site": "GOOGLE", "status": "PUBLISHING", "createdDate": "2026-06-28", "submissionError": null }        ],        "postLinks": [],        "locationIds": [1800289]      }    }  }}

Guarde socialPost.id. Ele é o id da publicação em base64 exigido por todos os outros endpoints desta página. As funções auxiliares dos dois SDKs recebem uma única string message e a expandem em uma entrada por site, então você só precisa montar o array postMessage manualmente em cURL.

Publicar um evento

POST /posts

Um evento é exibido como um cartão com data definida. O período do próprio evento está em postContextInfo. O objeto separado postScheduledDates controla quando a publicação em si aparece e expira, o que geralmente corresponde ao período que antecede o evento. O Google rejeita um evento sem título, e ambos os SDKs geram um erro de validação antes do envio se title estiver ausente.

Parâmetros

NomeEmObrigatórioObservações
input.postTypebodySimEVENT aqui.
input.postContextInfo.titlebodySim, na práticaTítulo do evento. O Google rejeita eventos sem título.
input.postContextInfo.startDay, endDaybodyNãoDatas, YYYY-MM-DD.
input.postContextInfo.startTime, endTimebodyNãoStrings de exibição, como 10:00am.
input.postScheduledDatesbodyNãoObjeto com startDatetime, endDatetime, removalSites.

Tudo o que está na tabela de anúncios também se aplica.

Event
curl -X POST https://listingsapi.com/api/v4/posts \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "postName": "Summer Sale Kickoff Event",    "locationIds": ["TG9jYXRpb246MTgwMDI4OQ=="],    "postType": "EVENT",    "postSites": ["GOOGLE"],    "postContextInfo": {      "title": "Summer Sale Kickoff",      "startDay": "2026-07-15",      "startTime": "10:00am",      "endDay": "2026-07-15",      "endTime": "06:00pm"    },    "postMessage": [      { "site": "GOOGLE", "message": "Join us for the Summer Sale kickoff, refreshments and up to 40 percent off in store." }    ]  }}'
JSON
{  "data": {    "createSocialPost": {      "success": true,      "socialPost": {        "id": "U29jaWFsUG9zdDo0NDEyMw==",        "status": "INPROGRESS",        "type": "EVENT",        "scheduledStartDate": "2026-07-15",        "scheduledEndDate": "2026-07-15",        "contextInfo": {          "title": "Summer Sale Kickoff",          "startDay": "2026-07-15",          "startTime": "10:00am",          "endDay": "2026-07-15",          "endTime": "06:00pm"        }      }    }  }}

O contextInfo retornado é o período do evento. scheduledStartDate e scheduledEndDate descrevem o período da própria publicação, que aqui coincide com as datas do evento porque nenhum postScheduledDates foi enviado.

Publique uma oferta

POST /posts

Uma oferta é um cartão promocional que contém um código de cupom, um desconto, um link de resgate e termos. Tudo isso fica em postContextInfo, e startDay junto com endDay delimitam a validade da oferta, e não a da publicação.

Parâmetros

NomeEmObrigatórioObservações
input.postTypebodySimOFFER aqui.
input.postContextInfo.titlebodySim, na práticaDá um nome à oferta. Ambos os SDKs o exigem.
input.postContextInfo.couponCodebodyNãoCódigo que o cliente usa para resgatar a oferta.
input.postContextInfo.discountbodyNãoTexto livre, como 25%.
input.postContextInfo.redeemUrlbodyNãoOnde a oferta é resgatada.
input.postContextInfo.termsConditionsbodyNãoTermos e condições.
input.postContextInfo.startDay, endDaybodyNãoPeríodo de validade, YYYY-MM-DD.
Offer
curl -X POST https://listingsapi.com/api/v4/posts \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "postName": "Festive 25 Percent Off",    "locationIds": ["TG9jYXRpb246MTgwMDI4OQ=="],    "postType": "OFFER",    "postSites": ["GOOGLE"],    "postContextInfo": {      "title": "25% off storewide",      "couponCode": "FESTIVE25",      "discount": "25%",      "redeemUrl": "https://jennyhome.example/festive",      "termsConditions": "Valid in-store only through July 6.",      "startDay": "2026-06-22",      "endDay": "2026-07-06"    },    "postMessage": [      { "site": "GOOGLE", "message": "Celebrate with us, 25 percent off everything in store this week." }    ],    "postCta": [      { "site": "GOOGLE", "type": "GET_OFFER", "url": "https://jennyhome.example/festive" }    ]  }}'
JSON
{  "data": {    "createSocialPost": {      "success": true,      "socialPost": {        "id": "U29jaWFsUG9zdDo0NDEyNA==",        "status": "INPROGRESS",        "type": "OFFER",        "contextInfo": {          "title": "25% off storewide",          "couponCode": "FESTIVE25",          "discount": "25%",          "startDay": "2026-06-22",          "endDay": "2026-07-06"        }      }    }  }}

O Google aceita ORDER e o Facebook aceita GET_OFFER para botões de oferta. Se uma promoção terminar antes do previsto, exclua a campanha em vez de editá-la.

Consulte uma publicação e seu status

GET /posts/{postId}

Retorna o resultado publicado de uma campanha aceita. A resposta fica em data.socialPostView e contém o conteúdo da campanha, uma entrada publishDetails por site, contadores de engajamento e os links ativos após a publicação ser concluída com sucesso.

Parâmetros

NomeEmObrigatórioObservações
postIdpathSimID da publicação em Base64, obtido da resposta de criação ou de uma lista.
Get a post
curl https://listingsapi.com/api/v4/posts/U29jaWFsUG9zdDo0NDEyMg== \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "socialPostView": {      "socialPostId": "U29jaWFsUG9zdDo0NDEyMg==",      "socialPostInfo": {        "status": "SUCCESS",        "type": "ANNOUNCEMENT",        "views": 128,        "clicks": 14,        "publishDetails": [          { "site": "GOOGLE", "status": "PUBLISHED", "publishedDate": "2026-06-28", "submissionError": null }        ],        "postLinks": [          { "site": "GOOGLE", "link": "https://www.google.com/maps/place/?post=44122" }        ]      },      "publishedLocationsCount": 1,      "socialPostAnalyticsBySite": [        { "site": "GOOGLE", "views": 128, "clicks": 14, "reactions": 3, "shares": 0, "comments": 1 }      ]    }  }}

Dois campos de status são relevantes, e eles respondem a perguntas diferentes.

CampoValorSignificado
socialPostInfo.statusINPROGRESSAceita, a publicação ainda está em andamento.
SUCCESSPublicação concluída. Consulte publishDetails para ver o resultado por site.
SCHEDULEDAguardando o início do período de postScheduledDates.
EXPIREDO período agendado terminou e a publicação não está mais no ar.
publishDetails[].statusPUBLISHINGEsse site ainda não respondeu.
PUBLISHEDNo ar nesse site. Consulte postLinks para obter a URL.
ERROREsse site a recusou. submissionError informa o motivo.

Uma campanha pode apresentar SUCCESS enquanto um dos sites nela apresenta ERROR, portanto, sempre verifique publishDetails em vez de apenas o status de nível superior.

Liste as publicações de um local

GET /locations/{locationId}/posts

Um histórico com paginação por deslocamento de todas as campanhas que incluem um determinado local, em data.postsByLocation. Use-o para auditar o que já está no ar antes de agendar algo novo.

Parâmetros

NomeEmObrigatórioObservações
locationIdpathSimID em Base64, enviado sem alterações.
pagequeryNãoNúmero da página, começando em 1, com valor padrão 1.
perPagequeryNãoO valor padrão é 10 e não deve exceder 50.
Posts on a location
curl "https://listingsapi.com/api/v4/locations/TG9jYXRpb246MTgwMDI4OQ==/posts?page=1&perPage=10" \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "postsByLocation": {      "records": [        {          "id": "U29jaWFsUG9zdDo0NDEyMg==",          "name": "Summer Hours Update",          "status": "SUCCESS",          "type": "ANNOUNCEMENT",          "createdAt": "2026-06-28",          "views": 128,          "clicks": 14,          "sites": ["GOOGLE"]        }      ],      "pageInfo": { "totalPages": 1, "hasNextPage": false, "totalRecords": 1 }    }  }}

Esta rota não tem filtro no servidor. Ela aceita apenas page e perPage e ignora qualquer outro parâmetro, portanto, leia type e status em cada registro e filtre no seu próprio código. Para ordenar e filtrar no servidor, use o endpoint de listagem em massa abaixo.

Exclua uma publicação

DELETE /posts/{postId}

A exclusão é permanente e retira a publicação do ar em todos os sites aos quais ela chegou. Não é possível desfazer a exclusão e não há endpoint de edição, portanto, corrigir uma publicação que está no ar exige excluí-la e publicar outra em seu lugar.

Parâmetros

NomeEmObrigatórioObservações
postIdpathSimID da publicação em Base64.
clientMutationIdbodyNãoOpcional, retornado sem alterações para correlação.
Delete
curl -X DELETE https://listingsapi.com/api/v4/posts/U29jaWFsUG9zdDo0NDEyMg== \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "clientMutationId": "delete-44122-req-1" }'
JSON
{  "data": {    "deleteSocialPost": {      "success": true,      "socialPostId": "U29jaWFsUG9zdDo0NDEyMg=="    }  }}

Excluir uma publicação desconhecida ou já excluída retorna uma resposta de erro com data definido como null em vez de success: false.

Publique uma campanha em vários locais

POST /bulk-posts

O mesmo contrato de POST /posts, desenvolvido para conjuntos de locais: liste todos os locais em locationIds e uma única campanha abrangerá todos eles. A resposta fica sob a chave createSocialPost, assim como em POST /posts.

Parâmetros

NomeEmObrigatórioObservações
input.postNamebodySimNome interno da campanha.
input.locationIdsbodySimTodos os IDs em base64 dos locais nos quais publicar.
input.postTypebodySimEVENT, OFFER e PRODUCT também precisam de postContextInfo ou products.
input.postSitesbodySimGOOGLE, FACEBOOK ou ambos.
input.postScheduledDatesbodyNãostartDatetime e endDatetime no formato ISO 8601. Cria a publicação como SCHEDULED.
input.componentIdentifierbodyNãoSua própria referência, útil como filtro posteriormente.
Bulk publish
curl -X POST https://listingsapi.com/api/v4/bulk-posts \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "postName": "July Reopening Announcement",    "locationIds": ["TG9jYXRpb246MTgwMDI4OQ==", "TG9jYXRpb246MTgwMDI5MA=="],    "postType": "ANNOUNCEMENT",    "postSites": ["GOOGLE", "FACEBOOK"],    "postMessage": [      { "site": "GOOGLE", "message": "We are back. Visit us this week for our summer menu." },      { "site": "FACEBOOK", "message": "We are back. Swing by for the new summer menu." }    ],    "postScheduledDates": {      "startDatetime": "2026-07-10T09:00:00Z",      "endDatetime": "2026-07-24T23:59:00Z"    }  }}'
JSON
{  "data": {    "createSocialPost": {      "success": true,      "errors": null,      "socialPost": {        "id": "U29jaWFsUG9zdDo5OTIwMQ==",        "name": "July Reopening Announcement",        "status": "SCHEDULED",        "type": "ANNOUNCEMENT",        "scheduledStartDate": "2026-07-10",        "scheduledEndDate": "2026-07-24",        "sites": ["GOOGLE", "FACEBOOK"],        "locationIds": [1800289, 1800290]      }    }  }}

As funções auxiliares de ambos os SDKs definem Google e Facebook como valor padrão de sites, expandem um mapa de mensagens por site para você e codificam os IDs numéricos dos locais, portanto, uma publicação em massa costuma ser a chamada mais curta em qualquer um dos SDKs.

Consultar uma campanha em massa

GET /bulk-posts/{postId}

O equivalente em massa de GET /posts/{postId}. Retorna a mesma estrutura, com publishedLocationsCount acima de um e um detalhamento das análises por loja em socialPostAnalyticsByLocationId.

Parâmetros

NomeEmObrigatórioObservações
postIdpathSimID da publicação em massa em Base64, não um número inteiro bruto.
Get a bulk post
curl https://listingsapi.com/api/v4/bulk-posts/U29jaWFsUG9zdDo1MTA4OA== \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "socialPostView": {      "socialPostId": "U29jaWFsUG9zdDo1MTA4OA==",      "socialPostInfo": {        "name": "Festive Offer - All Stores",        "status": "SUCCESS",        "type": "OFFER",        "views": 4210,        "clicks": 318,        "sites": ["GOOGLE", "FACEBOOK"]      },      "socialPostAnalyticsByLocationId": [        {          "locationId": 1800289,          "siteStats": [            { "site": "GOOGLE", "status": "PUBLISHED", "views": 1040, "clicks": 80 },            { "site": "FACEBOOK", "status": "PUBLISHED", "views": 360, "clicks": 26 }          ]        }      ]    }  }}

socialPostAnalyticsBySite totaliza os números da campanha em todas as lojas, enquanto socialPostAnalyticsByLocationId detalha os mesmos números por loja. Examine os valores de status por loja para encontrar qualquer local em que um site tenha apresentado falha.

Listar campanhas em massa em um local

GET /locations/{locationId}/bulk-posts

Com paginação por offset em data.rollupSocialPosts. Diferentemente da lista por local, esta rota aceita filtros e ordenação, o que a torna o endpoint adequado para um painel de campanhas.

Parâmetros

NomeEmObrigatórioObservações
locationIdpathSimID em Base64, passado sem alterações.
page, perPagequeryNãoPaginação por offset.
filtersqueryNãoCodificado em JSON, por type, status, creationDateRange ou siteUrls.
sortFieldsqueryNãoCodificado em JSON, como o campo created_at com a ordem Descending.
locationIdsqueryNãoArray codificado em JSON com IDs de locais adicionais a incluir.
componentIdentifierqueryNãoLimita os resultados às campanhas criadas pela sua integração.

Os campos que permitem ordenação são name, type, status, created_at, schedule_start_date, schedule_end_date, client_name, clicks, views e shares.

Bulk posts on a location
curl -G https://listingsapi.com/api/v4/locations/TG9jYXRpb246MTgwMDI4OQ==/bulk-posts \-H "Authorization: API $LISTINGSAPI_KEY" \--data-urlencode 'page=1' \--data-urlencode 'perPage=10' \--data-urlencode 'sortFields={"field":"created_at","order":"Descending"}'
JSON
{  "data": {    "rollupSocialPosts": {      "records": [        {          "id": "U29jaWFsUG9zdDo1MTA4OA==",          "name": "Festive Offer - All Stores",          "status": "SUCCESS",          "type": "OFFER",          "createdAt": "2026-06-20",          "views": 4210,          "clicks": 318        }      ],      "pageInfo": { "totalPages": 1, "hasNextPage": false, "totalRecords": 1 }    }  }}

Ambos os SDKs codificam os objetos filters e sortFields em JSON para você, então passe-os como dicionários ou objetos nativos.

Erros que você verá

Código ou statusSignificadoCorreção
400 com a variável $input ausenteO corpo não foi encapsulado em input.Aninhe todos os campos sob uma única chave input.
SY90016 como HTTP 400Uma chave Read foi usada em uma operação de criação ou exclusão.Use uma chave Write.
401Chave ausente ou inválida.Envie Authorization: API <YOUR_API_KEY>.
429 com Retry-AfterChamadas em excesso, frequentemente devido a consultas periódicas ao status de uma publicação.Aguarde o número de segundos informado. Consulte Limites de requisições.
200 com success: falseFalha na validação.Leia errors em data.createSocialPost.
per_page must not exceed 50perPage acima do limite na lista de locais.Solicite 50 ou menos.
tag is not a supported parameter for this querytag enviado à rota de publicações por local.Envie apenas page e perPage.

Lista completa em Códigos de erro.

Próximos passos