Find locations and archive the ones you no longer manage

Search by keyword, resolve store codes and ids in bulk, schedule closed branches for archival at the end of the billing cycle, and reverse a scheduled archive.

Three read endpoints find locations by different keys: a free-text search, a store-code lookup, and an id lookup. Archiving is a scheduled write: the location stays live until the end of the current billing cycle, then stops syncing and stops being billed. Until that moment you can cancel it.

1. Search by keyword

GET /locations/search?query= matches the term against name, street address, city, and store id, and returns a cursor connection: edges[].node with pageInfo.endCursor, paged with first and after. Omit query to page the whole account.

Search
curl "https://listingsapi.com/api/v4/locations/search?query=Acme&first=10" \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "searchLocations": {      "edges": [        {          "cursor": "TG9jYXRpb246MTgwMDI4OQ==",          "node": {            "id": "TG9jYXRpb246MTgwMDI4OQ==",            "databaseId": 1800289,            "name": "Acme Kitchen",            "storeId": "ACME-NYC-001",            "approved": "APPROVED",            "archived": false,            "archivalScheduledAt": null          }        }      ],      "pageInfo": { "endCursor": "TG9jYXRpb246MTgwMDI4OQ==", "hasNextPage": false, "total": 1 }    }  }}

An empty edges array means nothing matched, which is the check to run before a create when you are unsure whether a store already exists. Both SDKs accept fields to restrict the match to name or store_id only.

2. Look up by store code

If your systems key locations by your own store code, resolve them directly with GET /locations-by-store-codes. The storeCodes parameter is a JSON array, URL-encoded. Codes with no match are simply absent from the result.

By store code
curl -G https://listingsapi.com/api/v4/locations-by-store-codes \--data-urlencode 'storeCodes=["ACME-NYC-001","ACME-NYC-002"]' \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "getLocationsByStoreCodes": [      {        "id": "TG9jYXRpb246MTgwMDI4OQ==",        "databaseId": 1800289,        "storeId": "ACME-NYC-001",        "name": "Acme Kitchen",        "archived": false      }    ]  }}

3. Hydrate a batch of ids

When you already hold base64 ids, from your own store or a webhook payload, GET /locations-by-ids returns the full record for all of them in one call. Archived locations resolve too, so read archived and archivedAt to tell them apart. Unknown ids are dropped silently; compare counts.

By ids
curl -G https://listingsapi.com/api/v4/locations-by-ids \--data-urlencode 'ids=["TG9jYXRpb246MTgwMDI4OQ==","TG9jYXRpb246MTgwMDI5MA=="]' \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "getLocationsByIds": [      { "id": "TG9jYXRpb246MTgwMDI4OQ==", "databaseId": 1800289, "name": "Acme Kitchen", "archived": false, "archivedAt": null },      { "id": "TG9jYXRpb246MTgwMDI5MA==", "databaseId": 1800290, "name": "Acme Kitchen Brooklyn", "archived": false, "archivedAt": null }    ]  }}

For a server-side slice of the whole account, GET /locations also accepts a JSON-encoded filter parameter, for example by approval status. See List all locations.

4. Archive locations you no longer manage

POST /locations/archive takes input.locationIds and returns one result entry per location. Archiving is scheduled, not immediate: each location enters ARCHIVE_SCHEDULED, keeps syncing until the current billing cycle ends, and is archived when the next invoice is generated. From then on it stops syncing to publishers and stops being billed.

Archive
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=="]  }}'
JSON
{  "data": {    "archiveLocations": {      "errors": null,      "result": [        { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "status": "ARCHIVE_SCHEDULED", "success": true, "errors": null },        { "locationId": "TG9jYXRpb246MTgwMDI5MA==", "status": "ARCHIVE_SCHEDULED", "success": true, "errors": null }      ]    }  }}
result[].statusMeaning
ARCHIVE_SCHEDULEDQueued. The location stays live and billed until the billing cycle ends.
ARCHIVEDAlready archived by an earlier call or cycle.

Read each entry's success: one bad id does not block the rest of the batch. While scheduled, the location's archivalScheduledAt field is set on list and search results. An archived location cannot be updated until it is reactivated. If webhooks are configured, a profile.deleted event fires; it is a soft delete, and there is no hard delete.

5. Cancel a scheduled archival

Before the cycle closes, POST /locations/cancel_archive with the same ids pulls them back to ACTIVE and billing continues. Once the location is fully archived this returns 404 for it. Cancelling emits no webhook event.

Cancel archive
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=="] } }'
JSON
{  "data": {    "cancelLocationsArchive": {      "errors": null,      "result": [        { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "status": "ACTIVE", "success": true, "errors": null }      ]    }  }}

Both SDKs require two extra positional arguments, a selection type and a changedBy identifier, which they send alongside locationIds. The REST body needs only the ids.

Next steps