Locais
Endpoints de locais com cURL, Python e Node, em ordem de uso: listar, buscar, consultar por ID/código da loja, criar, atualizar, arquivar e fotos.
Listagens, avaliações, publicações e análises são vinculadas a um ID de local. O recurso Locations tem treze endpoints: quatro de leitura para encontrar locais, quatro de escrita para criá-los, alterá-los e arquivá-los, e cinco para gerenciar fotos. Uma chave Read (leitura) permite as operações de leitura. Cada POST exige uma chave Write (escrita); ao usar uma chave Read em uma operação de escrita, o endpoint retorna HTTP 400 com SY90016.
Cada id neste guia é o id Relay em base64, por exemplo, TG9jYXRpb246MTgwMDI4OQ== (que, decodificado, resulta em Location:1800289). Ambos os SDKs também aceitam o databaseId numérico e o codificam para você.
Visão geral dos endpoints
Listar todos os locais
GET /locations percorre todos os locais da conta com paginação, dos mais recentes para os mais antigos. É uma conexão baseada em cursor: edges[].node contém cada registro, e pageInfo.endCursor fornece o valor do parâmetro after para a próxima página. Use esse endpoint para sincronizar toda a conta com o seu próprio sistema.
Parâmetros
curl "https://listingsapi.com/api/v4/locations?first=50" \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "allLocations": { "edges": [ { "cursor": "TG9jYXRpb246MTgwMDI4OQ==", "node": { "id": "TG9jYXRpb246MTgwMDI4OQ==", "databaseId": 1800289, "name": "Jenny Home", "storeId": null, "approved": "APPROVED", "archived": false, "archivalScheduledAt": null, "city": "Austin", "stateIso": "TX", "countryIso": "US", "subCategoryId": 1432, "planName": "Launch", "placeActionLinks": [], "discoveredPlaceActionLinks": [] } } ], "pageInfo": { "hasNextPage": false, "hasPreviousPage": false, "endCursor": "TG9jYXRpb246MTgwMDI4OQ==", "total": 1 } } }}Leia pageInfo.hasNextPage e pare quando o valor for false. A contagem no nível da página é pageInfo.total. O id do nó é o identificador esperado por todos os outros endpoints.
Pesquisar locais
GET /locations/search busca correspondências de uma palavra-chave no nome do local, endereço, cidade e id da loja. É uma conexão baseada em cursor com a mesma estrutura de edges[].node e pageInfo.endCursor usada na listagem. Use esse endpoint em um seletor com sugestões durante a digitação ou para confirmar que uma loja existe antes de criá-la.
Parâmetros
Ambos os SDKs também aceitam uma lista fields para restringir a busca por correspondências a campos específicos, por exemplo, apenas name.
curl "https://listingsapi.com/api/v4/locations/search?query=Jenny&first=10" \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "searchLocations": { "edges": [ { "cursor": "TG9jYXRpb246MTgwMDI4OQ==", "node": { "id": "TG9jYXRpb246MTgwMDI4OQ==", "databaseId": 1800289, "name": "Jenny Home", "storeId": null, "street": "600 Congress Ave, Suite 200", "city": "Austin", "approved": "APPROVED", "archived": false } } ], "pageInfo": { "endCursor": "TG9jYXRpb246MTgwMDI4OQ==", "hasNextPage": false, "hasPreviousPage": false, "total": 1 } } }}Um array edges vazio significa que nenhuma correspondência foi encontrada. O nó contém o endereço completo, a categoria e o status approved, portanto uma linha de resultado não precisa de uma segunda consulta.
Obter locais por IDs
GET /locations-by-ids retorna o registro completo de cada local de um lote de IDs em base64 em uma única chamada, usando menos requisições do que uma chamada por local. Tanto locais ativos quanto arquivados são encontrados. Retorna um array simples, não uma conexão. Use para obter os registros completos dos IDs de um payload de webhook ou do seu próprio armazenamento.
Parâmetros
curl -G https://listingsapi.com/api/v4/locations-by-ids \--data-urlencode 'ids=["TG9jYXRpb246MTgwMDI4OQ==","TG9jYXRpb246MTgwMDI5MA=="]' \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "getLocationsByIds": [ { "id": "TG9jYXRpb246MTgwMDI4OQ==", "databaseId": 1800289, "storeId": null, "name": "Jenny Home", "archived": false, "archivedAt": null, "city": "Austin", "stateIso": "TX", "countryIso": "US", "subCategoryId": 1432, "businessHours": [], "locationPhotos": [], "placeActionLinks": [], "discoveredPlaceActionLinks": [] } ] }}IDs que não existem na sua conta são omitidos silenciosamente, sem gerar um erro. Compare a quantidade retornada com a quantidade enviada para detectar IDs desatualizados.
Obter locais por códigos de loja
GET /locations-by-store-codes faz a busca inversa: encontra os registros completos correspondentes aos seus próprios códigos de loja. Retorna um array simples. Use quando seu sistema de ponto de venda ou cadastro de franquias identifica os locais pelo código da loja e você nunca armazenou nossos IDs.
Parâmetros
curl -G https://listingsapi.com/api/v4/locations-by-store-codes \--data-urlencode 'storeCodes=["ACME01","ACME02"]' \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "getLocationsByStoreCodes": [ { "id": "TG9jYXRpb246MTgwMDI4OQ==", "databaseId": 1800289, "storeId": "ACME01", "name": "Jenny Home", "archived": false, "city": "Austin", "countryIso": "US", "planName": "Launch" } ] }}Associe o storeId de cada objeto à entrada correspondente no seu cadastro. Códigos sem correspondência não são incluídos. Um local só tem um código de loja se storeId tiver sido definido na criação ou atualização.
Criar um local
POST /locations cria um local e o coloca na fila para envio a todas as plataformas de publicação do seu plano, ou apenas a enabledSiteIds quando você fornece esse campo. Todos os campos ficam dentro de input. 10 campos são obrigatórios; os demais são opcionais, e as plataformas de publicação exibem em branco os que você omitir.
Parâmetros
Além dos campos opcionais listados na página de referência: categorias GCID do Google, URLs de redes sociais, coordenadas, contato do proprietário, formas de pagamento, área de atendimento e outros.
curl -X POST https://listingsapi.com/api/v4/locations \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "name": "Acme Kitchen", "description": "Acme Kitchen is a family-owned neighborhood restaurant in downtown New York serving wood-fired pizza, hand-rolled pasta, and a short seasonal menu built around produce from nearby growers. Our dining room seats forty, takeout and delivery run until close, and the bar pours regional wine and beer on tap.", "storeId": "ACME01", "street": "123 Jump Street", "city": "New York", "stateIso": "NY", "postalCode": "10013", "countryIso": "US", "phone": "6443859313", "bizUrl": "https://acmekitchen.example.com", "subCategoryId": 1432, "businessHours": [ { "day": "MONDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "TUESDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "WEDNESDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "THURSDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "FRIDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "SATURDAY", "type": "OPEN", "slots": [{ "start": "10:00am", "end": "02:00pm" }] }, { "day": "SUNDAY", "type": "CLOSED", "slots": [] } ], "placeActionLinks": [ { "placeActionType": "FOOD_ORDERING", "uri": "https://order.acmekitchen.example.com", "isPreferred": true } ] }}'{ "data": { "createLocation": { "clientMutationId": null, "success": true, "errors": null, "location": { "id": "TG9jYXRpb246MTgwMDI5MA==", "databaseId": 1800290, "storeId": "ACME01", "name": "Acme Kitchen", "city": "New York", "countryIso": "US", "status": "PENDING", "placeActionLinks": [ { "placeActionType": "FOOD_ORDERING", "uri": "https://order.acmekitchen.example.com", "isPreferred": true, "name": null, "submissionStatus": null } ], "discoveredPlaceActionLinks": [] } } }}Guarde ambos os IDs: o id em base64 é necessário em todas as gravações posteriores, e databaseId é o número que você vê nos painéis e webhooks. status começa como PENDING e avança sem uma nova chamada à API. O name e o submissionStatus de um link de ação do local permanecem como null até que ele seja sincronizado com o Google. O locations.add() do Python valida os campos obrigatórios localmente e lança ValidationError antes de qualquer chamada de rede; locations.create(input) recebe um dict camelCase bruto em vez disso.
Atualizar um local
POST /locations/update é uma atualização parcial: input.id é o único campo obrigatório e apenas os campos que você envia são alterados. A mesma chamada corrige um número de telefone ou reescreve a programação semanal. Tanto locations.update() do Python quanto updateLocation() do Node recebem um objeto camelCase.
Parâmetros
Além dos campos opcionais listados na página de referência.
curl -X POST https://listingsapi.com/api/v4/locations/update \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "id": "TG9jYXRpb246MTgwMDI4OQ==", "phone": "5125550199", "bizUrl": "https://acmekitchen.example.com", "primaryGbpSiteCategoryId": "gcid:restaurant", "businessHours": [ { "day": "MONDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "TUESDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "WEDNESDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "THURSDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "FRIDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] }, { "day": "SATURDAY", "type": "OPEN", "slots": [{ "start": "10:00am", "end": "02:00pm" }] }, { "day": "SUNDAY", "type": "CLOSED", "slots": [] } ] }}'{ "data": { "updateLocation": { "clientMutationId": null, "success": true, "errors": null, "location": { "id": "TG9jYXRpb246MTgwMDI4OQ==", "databaseId": 1800289, "name": "Jenny Home", "phone": "5125550199", "status": "APPROVED", "placeActionLinks": [ { "placeActionType": "FOOD_ORDERING", "uri": "https://order.example.com", "isPreferred": true, "name": null, "submissionStatus": null } ], "discoveredPlaceActionLinks": [] } } }}A resposta retorna o local atualizado para que você possa confirmar que os novos valores foram aplicados. Toda atualização retorna o conjunto placeActionLinks atual completo do local. Para alterar a URL de um link, envie um delete para o link antigo e um add para o novo na mesma solicitação.
Arquivar locais
POST /locations/archive agenda o arquivamento de um ou mais locais. O arquivamento não é imediato: cada local entra em ARCHIVE_SCHEDULED, permanece ativo e sujeito a cobrança até o fim do ciclo de cobrança atual e é arquivado quando a próxima fatura é gerada. Use essa chamada para desativar filiais fechadas em lote.
Parâmetros
curl -X POST https://listingsapi.com/api/v4/locations/archive \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "locationIds": ["TG9jYXRpb246MTgwMDI4OQ==", "TG9jYXRpb246MTgwMDI5MA=="] }}'{ "data": { "archiveLocations": { "clientMutationId": null, "bulkEditTaskId": null, "errors": null, "success": null, "result": [ { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "status": "ARCHIVE_SCHEDULED", "success": true, "errors": null }, { "locationId": "TG9jYXRpb246MTgwMDI5MA==", "status": "ARCHIVE_SCHEDULED", "success": true, "errors": null } ] } }}O success de nível superior é null; leia o próprio success de cada entrada de result[], para que um id inválido não bloqueie o restante do lote.
Enquanto o arquivamento estiver agendado, o archivalScheduledAt do local fica definido nos resultados de listagem e pesquisa. Um local arquivado não pode ser atualizado até ser reativado.
Cancelar o arquivamento agendado
POST /locations/cancel_archive retorna os locais em ARCHIVE_SCHEDULED para ACTIVE, e a cobrança continua. Isso só funciona enquanto o ciclo de cobrança ainda está aberto; depois que um local é totalmente arquivado, essa chamada retorna 404 para ele.
Parâmetros
curl -X POST https://listingsapi.com/api/v4/locations/cancel_archive \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "locationIds": ["TG9jYXRpb246MTgwMDI4OQ=="] } }'{ "data": { "cancelLocationsArchive": { "clientMutationId": null, "bulkEditTaskId": null, "errors": null, "success": null, "result": [ { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "status": "ACTIVE", "success": true, "errors": null } ] } }}Cada entrada de result[] confirma que o local voltou para ACTIVE. Ambos os SDKs exigem dois argumentos posicionais extras, um tipo de seleção e um identificador changedBy, que enviam junto com locationIds; o corpo REST precisa apenas dos ids.
Enviar fotos para um local
POST /locations/photos anexa imagens por URL pública: você hospeda a imagem, e a API a busca e armazena. Um local mantém um LOGO, um COVER e qualquer quantidade de fotos ADDITIONAL. Enviar novamente um logotipo ou uma capa substitui o existente.
Parâmetros
curl -X POST https://listingsapi.com/api/v4/locations/photos \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "photos": [ { "photo": "https://cdn.example.com/jenny-home/logo.png", "type": "LOGO" }, { "photo": "https://cdn.example.com/jenny-home/storefront.jpg", "type": "ADDITIONAL" } ] }}'{ "data": { "addLocationPhotos": { "clientMutationId": null, "success": true, "request": {}, "photos": [ { "id": "TWVkaWFGaWxlOjU4Nzg5MzE=", "databaseId": 5878931, "type": "LOGO", "starred": false, "url": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/f47d62a6.png" }, { "id": "TWVkaWFGaWxlOjU4Nzg5MzI=", "databaseId": 5878932, "type": "ADDITIONAL", "starred": false, "url": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/ef4ffb12.jpg" } ], "errors": null } }}Guarde o id de cada foto; os endpoints de marcação com estrela e remoção o recebem. Esse é o formato da resposta direta para um lote pequeno. Um lote grande é colocado na fila: photos retorna vazio e request.requestId é definido, e você consulta periodicamente o próximo endpoint com esse id. O limite é uma configuração do servidor, portanto, escolha o fluxo conforme a presença de request.requestId.
Verificar o status do upload de fotos em lote
GET /locations/photos/requests/{requestId} consulta periodicamente um upload na fila até que ele seja concluído e, então, informa o resultado de cada imagem. Use-o para atualizar uma barra de progresso e mostrar exatamente quais arquivos foram rejeitados e por quê.
Parâmetros
curl https://listingsapi.com/api/v4/locations/photos/requests/e6c2d9d9-9a18-4015-88cf-9a4e19a6f49a \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "getLocationPhotosUploadStatus": { "requestId": "e6c2d9d9-9a18-4015-88cf-9a4e19a6f49a", "status": "SUCCESS", "photos": [ { "mediaId": "TWVkaWFGaWxlOjkxODI3", "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "status": "SUCCESS", "errorMessage": null }, { "mediaId": null, "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "status": "ERROR", "errorMessage": "Image dimensions below the minimum 250x250." } ] } }}O status da tarefa indica apenas que o processamento terminou, não que todas as imagens foram adicionadas. Aguarde alguns segundos entre as consultas; um loop sem intervalo aqui pode fazer você atingir 429 rapidamente.
Obter fotos de um local
GET /locations/{locationId}/photos retorna todos os arquivos de mídia de um local como um array simples: logotipo, capa e imagens adicionais, cada um com seu url público, thumbnailUrl, categoria, flag starred e viewsCount de todo o período. Faça essa chamada antes de um upload para ver qual espaço está vazio e coletar ids para as chamadas de marcação com estrela e remoção.
Parâmetros
curl https://listingsapi.com/api/v4/locations/TG9jYXRpb246MTgwMDI4OQ==/photos \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "mediaFilesOfLocation": [ { "id": "TWVkaWFGaWxlOjU4Nzg5MzE=", "categoryName": "Logo", "categoryId": "4", "fileType": "image", "starred": false, "viewsCount": 0, "url": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/f47d62a6.jpg", "thumbnailUrl": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/thumbnail_f47d62a6.jpg" }, { "id": "TWVkaWFGaWxlOjU4Nzg5MzI=", "categoryName": "Additional", "categoryId": "12", "fileType": "image", "starred": true, "viewsCount": 143, "url": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/ef4ffb12.jpg", "thumbnailUrl": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/thumbnail_ef4ffb12.jpg" } ] }}categoryName é Logo, Cover ou Additional. Use thumbnailUrl para uma grade e url para a visualização em tamanho original. Um local sem mídia retorna um array vazio, não um erro.
Marcar fotos do local com estrela
POST /locations/photos/star marca fotos adicionais para posicionamento prioritário em plataformas de publicação que oferecem suporte a imagens em destaque. Apenas fotos ADDITIONAL podem ser marcadas com estrela, no máximo 4 por local. A remoção da estrela funciona da mesma forma: a flag starred no corpo determina o que acontece.
Parâmetros
curl -X POST https://listingsapi.com/api/v4/locations/photos/star \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "photoIds": ["TWVkaWFGaWxlOjU4Nzg5MzI="], "starred": true }}'{ "data": { "starUnstarLocationPhotos": { "success": true, "photos": [ { "id": "TWVkaWFGaWxlOjU4Nzg5MzI=", "databaseId": 5878932, "type": "ADDITIONAL", "starred": true } ], "error": null } }}A chave da resposta é starUnstarLocationPhotos em ambos os caminhos e contém um único error, não um array errors. Uma quinta foto marcada com estrela faz a chamada inteira falhar com SY15012; a verificação conta as fotos já marcadas com estrela mais as desta requisição.
Remover fotos de um local
POST /locations/photos/remove desvincula fotos adicionais pelo id de mídia. Use-o para retirar imagens desatualizadas da fachada ou desfazer uma importação em lote incorreta. Apenas fotos ADDITIONAL podem ser removidas; substitua um logotipo ou uma capa fazendo o upload de uma nova imagem com esse type.
Parâmetros
curl -X POST https://listingsapi.com/api/v4/locations/photos/remove \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "photoIds": ["TWVkaWFGaWxlOjU4Nzg5MzI="] }}'{ "data": { "removeLocationPhotos": { "clientMutationId": null, "removedCount": 1, "errors": null } }}removedCount informa quantos arquivos foram efetivamente desvinculados, então 0 com errors: null significa que nenhum desses ids estava vinculado a este local. A remoção desvincula o arquivo; ela não necessariamente o exclui da sua biblioteca de mídia.
Erros que você encontrará
Operações de escrita também podem retornar HTTP 200 com success: false e os detalhes em data.<operation>.errors. A lista completa está em códigos de erro.
Próximos passos
- Adicionar um local, Atualizar um local, Enviar fotos de um local e Encontrar e arquivar locais: os guias de tarefas baseados nestes endpoints.
- Consultar países e categorias: os valores de
countryIso,stateIsoesubCategoryIdnecessários para uma operação de criação. - Links de ação do local: o modelo completo dos botões de ação do Google nas operações de criação e atualização.
- Locais: a página inicial do recurso e as referências dos SDKs para Python e Node.
- Os métodos de fotos têm suas próprias páginas nos SDKs: Fotos em Python e Fotos em Node.