Fetch reviews for one location or your whole account
List the latest reviews for a location, filter to unanswered low ratings, page with cursors, sweep the account, and get notified by webhook.
Reviews are called interactions in the API. Every review from Google, Facebook or Yelp lands in one list per location, and one rollup for the account, so a single integration covers every source you connect.
1. List the latest reviews for one location
GET /locations/{locationId}/reviews is a cursor connection at
data.interactions. Results come newest first, and first defaults to 20, so
one call is never the full history. The path accepts the base64 id or the
plain number.
curl "https://listingsapi.com/api/v4/locations/TG9jYXRpb246MTgwMDI4OQ==/reviews?first=10" \-H "Authorization: API $LISTINGSAPI_KEY"{ "data": { "interactions": { "pageInfo": { "hasNextPage": true, "endCursor": "SW50ZXJhY3Rpb246MV8xNTU0..." }, "edges": [ { "node": { "id": "9e26d2a8-a6ed-42ea-8454-47d1c92ea841", "source": "maps.google.com", "content": "nice place for family together", "authorName": "vaibhav nakate", "rating": 5, "date": "2019-04-02T22:11:13.036997+00:00", "responded": false, "responseCount": 0, "canRespond": true, "permalink": "https://maps.google.com/maps?cid=..." }, "cursor": "SW50ZXJhY3Rpb246MV8xNTU0..." } ], "totalCount": 425 } }}Read data.interactions.edges[].node. The id is a plain UUID, not a base64
Relay id, and it is what you pass when you reply. totalCount is the number
of matches across the whole result set, not this page. canRespond says
whether that source accepts owner replies from the API.
2. Filter to unanswered low ratings
Array filters are JSON strings on the query string: ratingFilters=[1,2]
keeps one and two star reviews, and responseStatus=["PENDING"] keeps reviews
without an owner reply. The brackets and quotes must be URL-encoded, so the
cURL sample uses -G with --data-urlencode, which encodes each value and
appends it to the URL for you. The equivalent literal query string is
ratingFilters=%5B1%2C2%5D&responseStatus=%5B%22PENDING%22%5D.
curl -G "https://listingsapi.com/api/v4/locations/TG9jYXRpb246MTgwMDI4OQ==/reviews" \-H "Authorization: API $LISTINGSAPI_KEY" \--data-urlencode 'ratingFilters=[1,2]' \--data-urlencode 'responseStatus=["PENDING"]' \--data-urlencode 'sortOrder=["NEWEST_FIRST"]' \--data-urlencode 'first=50'The filters you can combine:
3. Page through with cursors
Pass pageInfo.endCursor back as after. Stop when hasNextPage is false
or a page returns an empty edges array; a boundary that lands on the oldest
review is followed by one empty page. Both SDKs can walk the pages for you.
curl "https://listingsapi.com/api/v4/locations/TG9jYXRpb246MTgwMDI4OQ==/reviews?first=50&after=SW50ZXJhY3Rpb246MV8xNTU0..." \-H "Authorization: API $LISTINGSAPI_KEY"Each request counts against your rate limit, and a 425-review location at
first=50 is nine calls. On a 429, wait for Retry-After before the next
page. For a scheduled sync, bound the run with startDate and endDate
instead of re-reading history. See Rate limits.
4. Sweep the whole account with the rollup
GET /rollup_interactions returns the same nodes across every location the
key can see, at data.rollupInteractions, with a locationId on each node.
It takes the same filters plus tag, which defaults to all. There is no
location filter; group by locationId yourself. Neither SDK wraps this
endpoint yet, so the SDK samples call it with requests and fetch.
curl -G "https://listingsapi.com/api/v4/rollup_interactions" \-H "Authorization: API $LISTINGSAPI_KEY" \--data-urlencode 'responseStatus=["PENDING"]' \--data-urlencode 'sortOrder=["NEWEST_FIRST"]' \--data-urlencode 'first=50'Each node has the same shape as step 1 plus a numeric locationId, the
databaseId of the store. The rollup's pageInfo may omit endCursor, so
take the cursor of the last edge as after; the two values are equivalent.
5. Fetch full details by id
When you have stored ids, GET /reviewDetails returns fresh copies without
paging. interactionIds is a JSON array, URL-encoded like the other filters.
The response is a plain array at data.interactionDetails.interactions, and
each interaction includes its responses.
curl -G "https://listingsapi.com/api/v4/reviewDetails" \-H "Authorization: API $LISTINGSAPI_KEY" \--data-urlencode 'interactionIds=["9e26d2a8-a6ed-42ea-8454-47d1c92ea841"]'{ "data": { "interactionDetails": { "interactions": [ { "id": "9e26d2a8-a6ed-42ea-8454-47d1c92ea841", "responded": true, "responseCount": 1, "responses": [ { "id": "2090753a-ece6-4837-8336-8494ad308523", "content": "Thank you so much for the kind words!", "interactionStatus": "CONFIRMED" } ] } ] } }}An id from another account returns 200 with interactionDetails: null and
an SY90001 error, so check for null before indexing.
6. Get told instead of polling
With a webhooks URL configured, an
interaction.review event fires
when a review is ingested or changes. The review fields sit under
data.interaction, not directly under data, and data.interaction.id is
the same UUID the list returns.
Two cautions. location_id on this event is passed through unnormalized: it
can be a numeric string or null, so convert it yourself rather than trusting
the type. And the same id arrives again whenever the review changes, so
treat the event as a prompt to re-read the review, not as a one-time insert.
Delivery is a single attempt with no retry, so keep a periodic sweep from step
4 as the backstop. Setup is in
React to events with webhooks.
Next steps
- Respond to reviews: reply to the pending reviews you just found, then edit or archive the reply.
- Review analytics: rating, volume and response rate for the same location.
- Reference: List interactions, Get rollup interactions, Get interactions by IDs, Interaction attributes.
- SDKs: Python reviews, Node reviews.