Find and fix listing errors
Filter a location's listings to the rows that need attention, read the publisher's reason, apply the matching fix, re-poll safely, and sweep the whole account.
Most listings sync without help. The ones that do not tell you why in
displayStatus and syncIssue, and nearly every cause maps to one of five
fixes. This guide walks from the raw listing rows to a per-account report of
what is blocked and what to do about it.
1. Pull the listings and keep the problem rows
Fetch the premium listings and drop everything healthy. A row needs attention
when actionRequired is true, or when syncStatus is FAILED,
CANNOT_SUBMIT, or REQUIRING_ACTION. IN_PROGRESS and PENDING_APPROVAL
are not errors; they are the publisher's queue.
curl -s https://listingsapi.com/api/v4/locations/TG9jYXRpb246MTgwMDI4OQ==/listings/premium \-H "Authorization: API $LISTINGSAPI_KEY" \| jq '[.data.listingsForLocation[] | select(.actionRequired or (.syncStatus | IN("FAILED", "CANNOT_SUBMIT", "REQUIRING_ACTION")))]'{ "data": { "listingsForLocation": [ { "site": { "name": "Google Maps", "url": "maps.google.com" }, "syncStatus": "REQUIRING_ACTION", "displayStatus": "Connect your account to sync the listing", "actionRequired": true, "syncIssue": null }, { "site": { "name": "Local Mint", "url": "localmint.com" }, "syncStatus": "FAILED", "displayStatus": "Partner experiencing issues; awaiting response", "actionRequired": false, "syncIssue": "Publisher requires website URL" } ] }}2. Read the reason
Two fields explain a problem row. displayStatus is the platform's summary
of where the row is. syncIssue is the publisher's own reason, when the
publisher gave one. Read both: in the sample above, Local Mint's
displayStatus says the partner is having issues, but syncIssue gives the
real cause, a missing website.
The location itself also carries a createErrors array on every read from
List all locations. It holds validation problems
found when the record was created and is a second place to look when a row
fails with no syncIssue.
3. Map the cause to the fix
The same handful of causes account for almost every blocked row.
To check the country and hidden-address rules, pull the plan sites once and cache them; the list is unpaginated and rarely changes.
curl -s https://listingsapi.com/api/v4/plan-sites \-H "Authorization: API $LISTINGSAPI_KEY" \| jq '.data.planSites[] | select(.name == "Yelp") | {name, supportedCountries, hideAddressSupportedCountries}'{ "name": "Yelp", "supportedCountries": ["US", "CA", "GB", "DE", "FR"], "hideAddressSupportedCountries": ["US", "CA", "AU", "GB"]}If the location's countryIso is not in supportedCountries, that
publisher's row will never leave CANNOT_SUBMIT. Exclude it from your
coverage figure rather than chasing it.
4. Re-poll after the fix
A location update triggers a fresh submission to the affected publishers. The
row moves from FAILED or REQUIRING_ACTION back to IN_PROGRESS, then to
SYNCED when the publisher accepts it. Publishers take minutes to weeks, so
poll on a schedule measured in hours, not seconds.
Every poll counts against the rate limit. On a 429, read Retry-After and
wait that many seconds; the SDKs raise RateLimitError with the same value.
# -D - prints response headers so you can see Retry-After on a 429curl -s -D - https://listingsapi.com/api/v4/locations/TG9jYXRpb246MTgwMDI4OQ==/listings/premium \-H "Authorization: API $LISTINGSAPI_KEY" \| grep -i -E "^HTTP|^Retry-After"lastUpdate moving forward is the sign the publisher has looked at the new
submission, even before syncStatus changes. If you have webhooks, the
listing.submission event replaces
this loop.
5. Run an account-wide sweep
To find every blocked row across the account, page through GET /locations
(a cursor connection: first, after, edges[].node) and call the premium
endpoint once per location. Collect the problem rows into one report keyed by
location. The Python SDK follows cursors with auto_paging_iter(); the Node
SDK does it with fetchAll: true. The samples reuse the backoff helper from
step 4.
# One page of locations, then one listings call per location.# Repeat with &after=<pageInfo.endCursor> while hasNextPage is true.curl -s "https://listingsapi.com/api/v4/locations?first=50" \-H "Authorization: API $LISTINGSAPI_KEY" \| jq -r '.data.allLocations.edges[].node.id' \| while read -r id; do curl -s "https://listingsapi.com/api/v4/locations/$id/listings/premium" \ -H "Authorization: API $LISTINGSAPI_KEY" \ | jq --arg id "$id" '[.data.listingsForLocation[] | select(.syncStatus != "SYNCED" and .syncStatus != "COMPLETED") | {location: $id, site: .site.name, status: .syncStatus, issue: .syncIssue}]' sleep 6 # stay under 10 requests a minute on Launch doneThe report groups rows that are still in flight with rows that are truly
blocked. Split it by the table in step 3 before handing it to anyone:
IN_PROGRESS and PENDING_APPROVAL need patience, REQUIRING_ACTION needs
a connection, and FAILED needs a field on the location.
Next steps
- Check listing status and live links: the healthy side of the same data.
- Update a location: set
bizUrl, description, and categories. - Connect Google and import locations
and Connect Facebook Pages: clear the
REQUIRING_ACTIONrows. - Reference: Get premium listings, List plan sites, List all locations, Error codes.