已连接账户
每个已连接账户端点均提供 cURL、Python 和 Node 示例:连接 Google 和 Facebook、列出账户、匹配并关联商家信息、文件夹、创建 GMB、断开连接。
已连接账户是 Google Business Profile 或 Facebook Pages 所有者授予的 OAuth 授权,允许 API 读取和管理该账户可访问的商家信息。此资源涵盖 3 项操作:创建 OAuth 链接、将账户的商家信息与您的地点进行匹配或关联,以及断开连接。用于列表和详情读取的 2 个 GET 操作可使用 Read(读取)密钥;所有 POST 操作(包括只读的商家信息获取操作)都需要 Write(写入)密钥,使用 Read 密钥执行写入操作会返回 HTTP 400,并附带 SY90016。
以下每个示例都假定已按第一个示例所示的方式创建了一个客户端,并从环境中读取 LISTINGSAPI_KEY。ID 分别为列表端点返回的 connectedAccountId UUID 和 base64 地点 ID(TG9jYXRpb246MTgwMDI4OQ==);切勿对其中任何一个进行 URL 编码。
端点概览
创建 Google 连接链接
POST /connected-accounts/connect-google
返回短期有效的 OAuth URL。将账户所有者重定向至该 URL;Google 会将其重定向回您的 successUrl 或 errorUrl。该链接的作用范围限定为拥有此 API 密钥的账户,因此请求体只包含您的 2 个重定向地址。为授权已过期的账户重新生成链接会刷新现有连接,而不会创建重复连接。
curl -X POST https://listingsapi.com/api/v4/connected-accounts/connect-google \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "successUrl": "https://app.example.com/connect/google/success", "errorUrl": "https://app.example.com/connect/google/error" }}'{ "data": { "bulkConnectLinkForGoogle": { "success": true, "url": "https://connect.example.com/oauth?token=8f3c1d92-...", "errors": null } }}上面的 url 是占位符。请引导所有者访问您实际收到的响应中返回的 url。
链接的有效期为 24 小时。仅在生成失败时才会填充 errors,因此请检查 url 是否为非 null 值。使用扁平结构或包含 accountId 的请求体会被拒绝,并返回 HTTP 400。
创建 Facebook 连接链接
POST /connected-accounts/connect-facebook
这是对应的 Facebook 操作,使用相同的请求体。在此流程中,所有者接受 Facebook 的条款,并授予 API 所需的页面管理权限;如果所有者跳过某项权限,连接将无法同步,因此需要重新生成链接并让其再次同意授权。
curl -X POST https://listingsapi.com/api/v4/connected-accounts/connect-facebook \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "successUrl": "https://app.example.com/connect/facebook/success", "errorUrl": "https://app.example.com/connect/facebook/error" }}'{ "data": { "bulkConnectLinkForFacebook": { "success": true, "url": "https://connect.example.com/oauth?token=8f3c1d92-...", "errors": null } }}上面的 url 是占位符。请引导所有者访问您实际收到的响应中返回的 url。
响应中的键为 bulkConnectLinkForFacebook;其余读取方式与 Google 链接完全相同。所有者完成流程后,该账户会以 FacebookAccount 的形式出现在列表端点的返回结果中。
为一个地点创建连接链接
POST /locations/oauth_connect_url
当您已知所有者要连接哪个地点时,使用此方式。该链接绑定到您指定的地点,因此后续无需执行匹配步骤:所有者同意授权后,凭据会直接关联到该地点。上面的两种批量链接连接的则是整个账号。
curl -X POST https://listingsapi.com/api/v4/locations/oauth_connect_url \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "site": "GOOGLE", "successUrl": "https://app.example.com/locations/1800289/connected", "errorUrl": "https://app.example.com/locations/1800289/connect-failed" }}'{ "data": { "createConnectUrl": { "success": true, "url": "https://connect.example.com/oauth?token=8f3c1d92-...", "errors": null } }}上面的 url 是占位符。请将所有者引导至您自己的响应中返回的 url。
该链接只能使用一次,因此请为每次尝试创建新链接,不要缓存。重定向 URL 为相对 URL 或无法解析时,会返回 success: true,并附带 null url,因此请检查 url,而不只是 success。使用无权访问此地点的密钥时,会返回 403,并附带 SY90003。SDK 会自动将 site 转为大写。
列出已连接的账号
GET /connected-accounts
返回已向您的 API 账号授权的所有账号,包括连接健康状况以及每个账号关联的地点数量。此接口采用偏移量分页:返回 records[] 和 pageInfo.totalRecords,通过平铺的 page 和 perPage 查询参数分页。排序固定为按账号电子邮件地址升序排列。
curl "https://listingsapi.com/api/v4/connected-accounts?publisher=GoogleAccount&status=Connected&page=1&perPage=20" \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "connectedAccountsInfo": { "pageInfo": { "hasNextPage": false, "hasPreviousPage": false, "totalPages": 1, "totalRecords": 1 }, "records": [ { "connectedAccountId": "4f712c17-4f95-42dd-90f4-97171a2e67b5", "connectedAccountType": "GoogleAccount", "email": "owner@brightsmile-dental.com", "status": "CONNECTED", "connectivityIssue": null, "connectedLocationsCount": 0, "requestMatchesStatus": "MATCH_COMPLETED", "lastFetchMatchesRequestedAt": "2026-07-06 21:43:07 UTC" } ] } }}选择 email 对应于同意授权的所有者的记录,并保存其 connectedAccountId;后续每次调用都需要使用它。除这 4 个查询参数外,其他输入都会被忽略,而不会被拒绝,因此传入 JSON pageInfo 时会直接返回第 1 页,且不会报错。
status 筛选参数与记录中的 status 字段使用的取值不同。将 CONNECTED 作为筛选值传回时,会因枚举值无效而返回 400。
获取一个账号的详细信息
GET /connected-accounts/{connectedAccountId}/details
返回与列表中单条记录相同的字段,范围限定为一个账号。您可以在触发匹配后使用此接口轮询 requestMatchesStatus,也可以在通过该账号推送更新前检查 status 和 connectivityIssue。
curl https://listingsapi.com/api/v4/connected-accounts/4f712c17-4f95-42dd-90f4-97171a2e67b5/details \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "connectedAccountDetails": { "details": { "connectedAccountId": "4f712c17-4f95-42dd-90f4-97171a2e67b5", "connectedAccountType": "GoogleAccount", "status": "CONNECTED", "connectivityIssue": null, "connectedLocationsCount": 0, "requestMatchesStatus": "MATCH_COMPLETED" } } }}记录位于下一层的 data.connectedAccountDetails.details 中。匹配建议就绪后,requestMatchesStatus 会从 MATCH_IN_PROGRESS 变为 MATCH_COMPLETED。Completed 表示匹配程序已完成,并不表示已建立任何关联:在您确认或连接商家信息之前,connectedLocationsCount 会一直保持为 0。
获取账号可访问的商家信息
POST /connected-accounts/connected-account-listings
列出该账号有权访问的所有 Google Business Profile 或 Facebook 商家信息,无论这些商家信息是否已关联到您的某个地点。每条记录的 id 就是 connect-listing 和 create-location-from-listing 接受的 connectedAccountListingId。这是一个仅执行读取操作的 POST,采用偏移量分页:返回 records[] 和 pageInfo,通过请求体中平铺的 page 和 perPage 键分页。
curl -X POST https://listingsapi.com/api/v4/connected-accounts/connected-account-listings \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "connectedAccountId": "4f712c17-4f95-42dd-90f4-97171a2e67b5", "perPage": 50, "locationInfo": "William St" }'{ "data": { "connectedAccountListings": { "pageInfo": { "hasNextPage": false, "totalPages": 1, "totalRecords": 60 }, "records": [ { "id": "R21iQnVsa0RhdGFMYWtlOjA2MDQ4NmExLTA0NzQtNDc4NS1iMjNhLTBkOGM4MzhmNGQ5OA==", "accountTypeName": "Google", "locationName": "Trial", "address": "123 William St, NY 10038, Manhattan, US", "phone": "(718) 667-7787", "liveLink": null } ] } }}使用 pageInfo.hasNextPage 和 pageInfo.totalPages 分页遍历结果集。SDK 会自动将 connectedAccountId 放入请求体,因此这只影响手动编写的 REST 调用。
触发匹配
POST /connected-accounts/trigger-matches
将任务加入队列,该任务会检索指定账户可访问的所有资料,并将每份资料与您的一个地点配对。匹配会考虑所有处于活跃状态、未归档且尚未连接的地点。此操作仅写入建议;在您确认之前,不会建立任何关联。
curl -X POST https://listingsapi.com/api/v4/connected-accounts/trigger-matches \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "connectedAccountIds": ["4f712c17-4f95-42dd-90f4-97171a2e67b5"] } }'{ "data": { "connectedAccountsTriggerMatches": { "success": true, "failedIds": null } }}success: true 表示任务已加入队列,并不表示匹配已完成。所有无法加入队列的账户都会列在 failedIds 中,且 success 为 false。请轮询详情端点,直到 requestMatchesStatus 为 MATCH_COMPLETED 后再读取建议;使用有次数上限的循环,每 15 秒轮询一次,可使 Launch 套餐的请求量保持在每分钟 10 次的限制内。
读取连接建议
GET /connected-accounts/{connectedAccountId}/connection-suggestions
返回匹配程序建议的配对:一侧是您的地点(locationInfo,其 databaseId 为 locationId),另一侧是发布平台上的商家信息(suggestedLocationInfo)。此端点采用基于偏移量的分页,通过非嵌套的查询参数 page 和 perPage 控制分页;排序固定为按地点信息升序排列。
curl "https://listingsapi.com/api/v4/connected-accounts/4f712c17-4f95-42dd-90f4-97171a2e67b5/connection-suggestions?page=1&perPage=20" \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "connectionSuggestionsForAccount": { "pageInfo": { "hasNextPage": false, "totalPages": 1, "totalRecords": 2 }, "records": [ { "accountType": "GMB", "matchedDataDatabaseId": "0d5e306f-0da0-46e3-929f-fd3c67ccd8cc", "connectedAccountId": "4f712c17-4f95-42dd-90f4-97171a2e67b5", "locationId": "73415", "locationInfo": { "name": "My Business", "city": "Lincoln", "stateIso": "NE" }, "suggestedLocationInfo": { "locationInfo": { "name": "My Business", "city": "Lincoln" } } } ] } }}建议中没有 id。请将 matchedDataDatabaseId 和 accountType 一起保存;confirm-matches 需要这两个值来构建其 id。如果在 MATCH_COMPLETED 之前过早读取,端点会返回 records: [],其中包含 totalRecords: 0,看起来与“没有匹配结果”完全相同。
确认匹配
POST /connected-accounts/confirm-matches
接受您批准的建议,并建立实际关联。每个 id 都是 <MatchRecordType>:<matchedDataDatabaseId> 的 base64 编码,其中的类型从上表中选择。账户根据记录推断,因此没有 connectedAccountId 字段;如果 input 中包含未知字段,请求会被拒绝并返回 400。
# base64 of GmbLocationMatchedData:0d5e306f-0da0-46e3-929f-fd3c67ccd8cccurl -X POST https://listingsapi.com/api/v4/connected-accounts/confirm-matches \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "matchRecordIds": ["R21iTG9jYXRpb25NYXRjaGVkRGF0YTowZDVlMzA2Zi0wZGEwLTQ2ZTMtOTI5Zi1mZDNjNjdjY2Q4Y2M="] } }'{ "data": { "confirmConnectMatches": { "success": true, "failedIds": null } }}failedIds 包含无法确认的已解码 matchedDataDatabaseId 值,例如记录已被使用,或地点在匹配后已归档,因此请将这些值与建议记录对应起来。未提交的建议保持不变。确认操作会将地点与商家信息关联;此操作本身不会向发布平台推送数据。
将一条商家信息连接到一个地点
POST /connected-accounts/connect-listing
将您已选定的一条商家信息关联到一个地点,无需建议或确认步骤。当匹配结果不明确时可使用此操作,例如附近有 2 家名称相似的门店,由操作人员从账户的商家信息中选择正确的一条。
curl -X POST https://listingsapi.com/api/v4/connected-accounts/connect-listing \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "connectedAccountId": "4f712c17-4f95-42dd-90f4-97171a2e67b5", "connectedAccountListingId": "R21iQnVsa0RhdGFMYWtlOjA2MDQ4NmExLTA0NzQtNDc4NS1iMjNhLTBkOGM4MzhmNGQ5OA==" }}'{ "data": { "connectListing": { "success": true, "message": null } }}关联失败时会返回 success: false,其中 message 不为 null,例如无法解析商家信息 id 时返回 Listing record not found,或返回说明商家信息已连接到另一个地点的提示。在这 2 个 SDK 中,此方法均位于 listings 资源上,而不在 connected accounts 资源上。
断开一条商家信息与一个地点的连接
POST /connected-accounts/disconnect-listing
解除一个地点与一个发布平台之间的关联。地点及其数据会保留;仅停止向该发布平台同步数据。此端点不支持批量操作,因此请循环处理各个地点。要将门店改为关联另一个 Google 账户,请先在此处断开连接,然后在新账户上进行匹配或连接。
curl -X POST https://listingsapi.com/api/v4/connected-accounts/disconnect-listing \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "site": "GOOGLE" } }'{ "data": { "disconnectConnectedAccountsLocations": { "clientMutationId": null, "success": true, "errors": null } }}如果既不提交 site,也不提交 providerId,则返回 SY10056。如果地点与该站点之间没有有效连接,则返回 SY10057,而不会直接返回成功,因此 success: true 确实表示已移除关联。SDK 仅接受 site,并将其转换为大写。
从商家信息创建地点
POST /connected-accounts/create-location-from-listing
匹配操作只会将商家信息与您已有的地点配对。对于尚未关联地点的商家信息,此操作会根据发布平台的数据创建地点,并立即将其与该商家信息关联。Google 会导入名称、地址、坐标、电话、网站、描述、常规营业时间和特殊营业时间、类别、开业日期、门店代码和时区,以及根据商家信息的 Google 属性提取的付款方式和社交链接。Facebook 会导入名称、地点、营业时间、类别、描述和联系信息。
从 Google 导入时,数据不会重新提交到 Google,因此其商家信息会保持 IN_PROGRESS 状态,直到您首次更新地点并将更新发布到该商家信息。其他发布平台均会正常收到提交,且此端点不接受站点范围参数,因此创建地点时会使用您账户的全部发布平台。创建后请立即使用 enabledSiteIds 缩小范围,并立即发送该更新:其他发布平台的订单项会在创建后不久生成,而此更新不会撤回已生成的订单项。
这 2 个 SDK 都尚未封装此端点,因此 Python 和 Node 示例使用相同的请求头调用 REST。
curl -X POST https://listingsapi.com/api/v4/connected-accounts/create-location-from-listing \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "connectedAccountId": "4f712c17-4f95-42dd-90f4-97171a2e67b5", "connectedAccountListingId": "R21iQnVsa0RhdGFMYWtlOjA2MDQ4NmExLTA0NzQtNDc4NS1iMjNhLTBkOGM4MzhmNGQ5OA==" }}'{ "data": { "createLocationFromConnectedAccountListing": { "success": true, "locationId": 88277, "errors": null } }}locationId 是新地点的原始 databaseId。失败时,success 为 false,locationId 为 null,errors 包含表示原因的代码,例如 SY81067(无法通过商家信息 id 找到对应记录);其他原因包括商家信息已关联到另一个地点,或账户的凭据已过期。创建后,您可以像管理其他地点一样管理该地点。
列出 Google 账户的文件夹
GET /connected-accounts/{connectedAccountId}/folders
返回已连接的 Google 账户可以访问的 Google Business Profile 账户组(文件夹)。在创建 GMB 商家信息之前使用此端点,以便将新的商家资料创建在所有者预期的组中。响应中的 getFoldersUnderGoogleAccount 下是一个普通数组,不是分页结果。此路径仅适用于 GoogleAccount 连接。
curl https://listingsapi.com/api/v4/connected-accounts/4f712c17-4f95-42dd-90f4-97171a2e67b5/folders \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "getFoldersUnderGoogleAccount": [ { "folderId": "accounts/104890024806530350198", "folderName": "Bright Smile Dental", "locationCount": null } ] }}请原样使用 folderId;它是 accounts/{id} 字符串,不是数字。如果 Google 未提供 locationCount,其值可能为 null,因此请将其视为可选字段。
为地点创建 Google Business Profile
POST /locations/create/gmb-listing
为尚无 Google 商家信息的地点在已连接的 Google 账户下创建全新的 Google 商家信息,也可以选择在某个文件夹中创建。与此不同,connect-listing 用于关联已有的商家信息。地点必须已包含名称、地址和电话,且每个地点只能有 1 条 Google 商家信息。
curl -X POST https://listingsapi.com/api/v4/locations/create/gmb-listing \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "connectedAccountId": "4f712c17-4f95-42dd-90f4-97171a2e67b5", "folderId": "accounts/104890024806530350198" }}'{ "data": { "createGmbListingForLocation": { "clientMutationId": null, "success": true, "errors": null } }}创建操作是异步的:success: true 表示请求已被接受,Google 会在新的商家资料发布前对其进行审核。当 success 为 false 时,errors 会说明原因:缺少必填字段、已有 Google 商家信息、地点已归档或账户未处于活跃状态。
断开整个 Google 账号的连接
POST /connected-accounts/disconnect-google
断开整个账号的连接:通过该账号关联的所有地点都会停止同步。没有 locationIds 字段;若要仅断开一个地点的连接并保留账号,请改用 disconnect-listing。当客户不再使用服务,或无法清除异常授权且您希望通过新的连接链接重新开始时,可使用此操作。
curl -X POST https://listingsapi.com/api/v4/connected-accounts/disconnect-google \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "connectedAccountId": "d2397854-52c2-41d7-95f1-b90cba16498d" } }'{ "data": { "gmbBulkDisconnect": { "success": true } }}即使 connectedAccountId 未知,也会返回 success: true,因此它表示“请求已处理”,而不是“已移除有效连接”。如果您需要确认,请重新列出已连接的账号。
断开整个 Facebook 账号的连接
POST /connected-accounts/disconnect-facebook
这是对应的 Facebook 操作,使用相同的请求体。通过该账号关联的主页会停止同步,Facebook 互动也会停止显示。
curl -X POST https://listingsapi.com/api/v4/connected-accounts/disconnect-facebook \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "connectedAccountId": "d2397854-52c2-41d7-95f1-b90cba16498d" } }'{ "data": { "fbBulkDisconnect": { "success": true } }}响应键为 fbBulkDisconnect。与 Google 一样,对于未知 id,success: true 表示已处理,而不是已移除。
断开单个地点与发布平台的连接
POST /locations/oauth-disconnect
这是按地点执行的 OAuth 流程中断开连接的部分。它通过另一个路径提供与 disconnect-listing 相同的操作:input 请求体完全相同,disconnectConnectedAccountsLocations 响应键也相同。请选择在您的集成中更易理解的路径。
curl -X POST https://listingsapi.com/api/v4/locations/oauth-disconnect \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "site": "GOOGLE" } }'{ "data": { "disconnectConnectedAccountsLocations": { "clientMutationId": null, "success": true, "errors": null } }}相同的 SY10056 和 SY10057 规则同样适用,使用无权操作此地点的密钥时,会收到包含 SY90003 的 403 响应。断开连接会完整保留地点及其数据;仅停止向该发布平台同步。之后,如需关联正确的商家信息,请让所有者重新访问连接 URL,或运行匹配操作和 connect-listing。
您可能遇到的错误
请求格式错误或密钥无效时,会返回 4xx 响应,其中包含顶层 errors[],其 message 以错误代码开头。写入操作也可能返回 200,并包含 success: false,原因则位于 data.<operation> 中,因此请检查每次写入的响应体。完整列表见错误代码。
后续步骤
- 连接 Google 并导入地点:完整的 Google 批量操作流程,包括轮询和 webhook 事件。
- 连接 Facebook 主页:Facebook 的相同流程,包括断开账号连接。
- 创建 Google Business Profile:为尚无 Google 商家信息的地点创建文件夹和 GMB。
- 检查商家信息状态和在线链接:观察关联完成后 Google 行的状态变为
SYNCED。 - 参考索引:已连接的账号;SDK 页面:Python 已连接的账号、Node 已连接的账号、Python 商家信息、Node 商家信息。