Get reviews for a location
Overview
Get reviews as a paginated list, newest first, each with its response (if any), the location and site it came from, its flag status, and whether you can respond to it. Also returns the average rating and a per-site summary for the reviews in scope.
This is the first call most integrations make. Use it to build a review feed, sync reviews into your own system, or find reviews that still need a response.
Optional filters: store_id or client_location_id for one location; account_id or client_account_id for one account; leave both out for everything under your partner. Also review_site_id, rating (one or several), response status (complete, pending, flagged, and so on, or awaiting_response for reviews with no reply yet), a date range, and a keyword search over reviewer name, review text, and location name. Page size defaults to 10.
Partner tokens use POST /partner/report/review-response/feed and default to everything under the partner. Account tokens use POST /reports/response/feed and default to the account's locations. Same call, different default scope.
To keep your own copy in sync, send updated_since with the time of your last sync, and include_removed: true to also get the reviews removed since then. Each review carries external_review_id, created_at, updated_at and deleted_at. See Keeping your copy in sync.
Returns one entry per review with pagination. Each entry includes response_capabilities telling you whether a manual or AI response is possible right now and, if not, why.
Prerequisites
- A bearer token. See Authentication.
- Optional: a location (
store_idorclient_location_id) or an account (account_idorclient_account_id) to narrow the feed.
Base URL
| Environment | URL |
|---|---|
| Production | https://production-api.shoutaboutus.com |
| Development | https://development-api.shoutaboutus.com |
Endpoint
POST /api/v1/partner/report/review-response/feed
Authentication
Partner token
- Without a location or account, the feed covers every location under the partner.
store_id,client_location_id,account_idandclient_account_idmust be inside your own account tree; otherwise422.- Account tokens use
POST /api/v1/reports/response/feed, with the same request and response, scoped to the account's own locations.
Rate limit
300 requests per minute, counted per user (the token’s user). See Rate limits.
Request parameters
None.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
range_start | string | No | Start of the review-date window, Y-m-d H:i:s. Use with range_end. |
range_end | string | No | End of the window, Y-m-d H:i:s. When both dates are sent and limit is left out, every matching review is returned in one response, without paging. |
limit | integer | No | Page size, 10–100. Default 10. Values below 10 are raised to 10; above 100 returns 422. Eligible partners default to 25, and a larger value is lowered to 25 without an error. |
page | integer | No | The page to return, from 1. |
sort_field | string | No | Sort by review_date, rating, rate (same as rating), reviewer, created_at, updated_at, response_date or store_name. Applied only with sort_by. Default order: review_date, newest first. |
sort_by | string | No | asc or desc. |
type | string | No | report or download. |
store_id | integer | No | Only this location. Must exist and be inside your account tree. |
client_location_id | string | No | Your own id for the location, instead of store_id. Up to 255 characters. |
account_id | integer | No | Only this account's locations. Must be an active account inside your account tree. |
client_account_id | string | No | Your own id for the account, instead of account_id. Up to 255 characters. |
is_pdf | integer | No | 0 or 1. 1: logo URLs point at the API host instead of the CDN. |
review_site_id | integer | No | Only reviews from this review site. |
rating | array of integers | No | Only these star ratings, e.g. [4, 5]. A single number is accepted too. |
status | string | No | Only reviews whose response is complete, pending, error, flagged or removed; or awaiting_response: reviews with no response yet that the location may respond to under its plan (the same set as the dashboard's Awaiting Response count). |
search | string | No | Matches part of the reviewer or customer name, the review text or title, the job id, or the location name. Up to 255 characters. |
created_since | string | No | Only reviews we collected at or after this time, UTC, Y-m-d H:i:s. Compares against created_at. |
updated_since | string | No | Only reviews that changed at or after this time, UTC, Y-m-d H:i:s. Compares against updated_at; with include_removed, also matches reviews removed at or after it. Send the time of your last sync to get only what changed. A change is a new review, an edit to its rating, text, reviewer name or date, a moderation change (for example hidden or suppressed), a removal or a restore. A new or changed reply, or a change to a removal request (flag), doesn't count: those arrive as response_* and flag_* webhooks. |
include_removed | boolean | No | true to also return reviews removed on our side, with deleted_at set. Off by default. Not the same as status=removed, which filters on the review site reporting the review gone. |
Keeping your copy in sync. Send the time your last sync started as
updated_since, withinclude_removed: true, and sort byupdated_atoldest first so new changes can't shift the pages you haven't read yet:{ "store_id": 3007, "updated_since": "2026-10-08 09:55:00", "include_removed": true, "sort_field": "updated_at", "sort_by": "asc", "limit": 100, "page": 1 }Page through to
meta.last_page. Update each review by itsid, and delete the ones withdeleted_atset. Start each sync a few minutes before the last one started, so a difference between your clock and ours can't skip a change; reviews you already have just come back again. Don't sendrange_startorrange_endwith it: those filter on the review's own date, not on when it changed. Replies and removal-request (flag) changes don't changeupdated_at, so take those from webhooks.
Request payload
{
"range_start": "2026-01-01 00:00:00",
"range_end": "2026-12-31 23:59:59",
"account_id": 5994,
"review_site_id": 44,
"rating": [
4,
5
],
"status": "awaiting_response",
"search": "clean",
"limit": 10,
"page": 1,
"sort_field": "review_date",
"sort_by": "desc"
}Response body
The reviews are in a top-level data array, next to links, meta, avg_rating and kpi_by_publisher. links and meta are left out when the result isn't paged (both dates sent, no limit).
How ratings work.
ratingis always a whole number from 0 to 5.0means the review has no star rating.
- Sites that rate out of 5 come through as they are. A decimal rating, such as 4.5, is rounded to the nearest whole star.
- Sites that rate out of 10 (Booking.com, Agoda, Expedia, Hotels.com, Orbitz, Travelocity, Trip.com, HolidayCheck and HomeStars) are halved to a 0–5 score, then rounded to the nearest whole star. The lowest they come through as is 1. For example, 8.6/10 becomes 4.3, which comes through as
4.- Facebook recommendations without stars count as
5(recommends) or1(doesn't recommend).- The site's original score and scale aren't included.
On the site (out of 10) rating1 or 2 1 3 or 4 2 5 or 6 3 7 or 8 4 9 or 10 5 Because of rounding, nearby scores can share a rating: 8.0 and 8.6 both come through as
4.
| Field | Type | Description |
|---|---|---|
data | array of objects | One entry per review, newest first by default. |
data[].id | integer | The review's id (review_id). |
data[].external_review_id | string or null | The review site's own id for this review, as we collected it, or null. |
data[].review_url | string or null | A link to this review on the review site, or null. Given when the site provides a link to the individual review; for sites that don't (for example Facebook, or Google connected through Google sign-in) it's null. The location's page on the site is store_reviewsite.url. |
data[].created_at | string or null | When we collected the review (ISO 8601, UTC). |
data[].updated_at | string or null | When the review last changed on our side (ISO 8601, UTC): collected, edited, moderated, removed or restored. A new or changed reply, or a flag change, doesn't update it. |
data[].deleted_at | string or null | When the review was removed (ISO 8601, UTC). Always null unless include_removed is sent. |
data[].source_platform | string | The site the review came from, e.g. google. |
data[].review | object | The review. |
data[].review.id | integer | The review's id. |
data[].review.reviewer | string | The reviewer's name as shown on the site. |
data[].review.rating | number | The star rating, a whole number from 0 to 5. 0 when the review has no rating. Sites that rate out of 10 are converted; see How ratings work under Response body. |
data[].review.message | string | The review text. "" when the review is a rating only. |
data[].review.review_date | string or null | When the review was posted (ISO 8601), or null if unknown. |
data[].reviewsite | object | The review site. |
data[].reviewsite.id | integer | The review site's id (review_site_id). |
data[].reviewsite.name | string | The review site's name, e.g. Google. |
data[].reviewsite.link | string | The site's home page. |
data[].reviewsite.review_site_logo | string | URL of the site's logo. |
data[].reviewsite.review_site_color | string | The site's brand colour, e.g. #4285F4. |
data[].reviewsite.auth_type | string or null | How the site connects: oauth, cookie, credential, default, restapi, or null. See List supported review sites. |
data[].reviewsite.scraping_enabled | boolean | Whether reviews can be collected from this site. |
data[].reviewsite.posting_enabled | boolean | Whether responses can be posted to this site. |
data[].store | object | The location. |
data[].store.id | integer | The location's id (store_id). |
data[].store.name | string | The location's name, followed by - and your client_location_id (or its storeId label) when it has one. |
data[].store.slug | string | The location's URL slug. |
data[].store.address | string | The location's address: street, city and state. |
data[].store_reviewsite | object | The connection between the location and the site. |
data[].store_reviewsite.id | integer or null | The connection's id (store_review_site_id). |
data[].store_reviewsite.url | string or null | The site's review URL for the location. |
data[].store_reviewsite.landing_page_url | string or null | The landing page URL used in review requests. |
data[].store_reviewsite.connection_status | string or null | The stored connection state, e.g. connected or Disconnected, or null. |
data[].store_reviewsite.is_connected | boolean | Whether the site is connected for this location. |
data[].flag | array of objects | Status chips for the response and any removal flag, in display order. Empty when there is nothing to show. |
data[].flag[].flag | string | The chip label, e.g. complete or Flagged — Pending Review. |
data[].flag[].title | string | What the chip means, in plain language. |
data[].flag[].statusColor | string | success, warning or error. |
data[].flag[].icon | string | Icon name for the chip. |
data[].flag[].links | array of objects | Only on some chips: where to fix the problem. |
data[].flag[].links[].name | string | Link label, e.g. Edit Review Site. |
data[].flag[].links[].url | string | Link URL in the Shout About Us web app. |
data[].plan | string | The plan name recorded with the latest update to the response, or "". |
data[].review_flag | object or null | The removal flag on this review, or null. |
data[].review_flag.review_id | integer | The review's id. |
data[].review_flag.reason | string | Why the review was flagged. |
data[].review_flag.flag_status | string | pending, submitted_to_platform, approved_removed or denied. |
data[].review_flag.flag_date | string | When it was flagged (ISO 8601). |
data[].review_flag.flag_reason_detail | string or null | The extra detail given when flagging, or null. |
data[].review_flag.reason_identifier | string or null | The flag reason category, e.g. Low quality information. |
data[].is_flaggable | boolean | Whether you can flag this review for removal now. |
data[].flag_status | string | none when not flagged, otherwise pending, submitted_to_platform, approved_removed or denied. |
data[].flag_details | object or null | The flag, or null when not flagged. |
data[].flag_details.reason | string | Why the review was flagged. |
data[].flag_details.reason_identifier | string or null | The flag reason category. |
data[].flag_details.flag_reason_detail | string or null | The extra detail given when flagging. |
data[].flag_details.flag_date | string or null | When it was flagged (ISO 8601). |
data[].response_capabilities | object | Whether a response can be sent now. |
data[].response_capabilities.manual | boolean | Whether you can write and send a response. |
data[].response_capabilities.ai | boolean | Whether you can send an AI-drafted response. |
data[].response_capabilities.reason | string or null | Why not, when either is false, e.g. no_store_plan, dormant_tier, pre_response_cutoff, past_response_window_expired, past_response_quota_reached or past_response_unavailable. Otherwise null. |
data[].response | object or null | The response to the review, or null when there is none. |
data[].response.id | integer | The response's id. |
data[].response.status | string | Where the response is: e.g. complete (posted), pending (in the posting queue), or why it wasn't posted, e.g. login_error. The flag chip explains it. |
data[].response.public_contents | string or null | The response text posted on the site. |
data[].response.private_contents | string or null | The private part of the response, or null. |
data[].response.response_date | string or null | When the response was made, or null. |
data[].response.deleted | object or null | Set when the response was removed from the site, otherwise null. |
data[].response.deleted.delete_on | string | When it was removed. |
data[].response.deleted.status | string | The removal status. |
links | object | Paging links. |
links.first | string | URL of the first page. |
links.last | string | URL of the last page. |
links.prev | string or null | URL of the previous page, or null. |
links.next | string or null | URL of the next page, or null. |
meta | object | Paging details. |
meta.current_page | integer | This page. |
meta.from | integer or null | Position of the first review on this page. |
meta.last_page | integer | The last page. |
meta.links | array of objects | Page links for a pager. |
meta.links[].url | string or null | The page's URL. |
meta.links[].label | string | The link label. |
meta.links[].page | integer or null | The page number. |
meta.links[].active | boolean | true for this page. |
meta.path | string | The endpoint's URL. |
meta.per_page | integer | Reviews per page. |
meta.to | integer or null | Position of the last review on this page. |
meta.total | integer | Reviews across all pages. |
avg_rating | number | Average rating of all the reviews in scope, over all time (not narrowed by the date range or the other filters), to 2 decimal places. Refreshed every 10 minutes. |
kpi_by_publisher | array of objects | One entry per review site with reviews in scope, narrowed by the date range and rating. Empty when there are no locations in scope. |
kpi_by_publisher[].review_site_id | integer | The review site's id. |
kpi_by_publisher[].review_site_name | string | The review site's name. |
kpi_by_publisher[].review_site_color | string or null | The site's brand colour. |
kpi_by_publisher[].rating | number or null | Average rating on the site, to 1 decimal place, or null. |
kpi_by_publisher[].total_reviews | integer | Number of reviews on the site. |
kpi_by_publisher[].was_bridged | boolean | true when rating is a blended rating rather than a simple average of the reviews. |
Response payload
{
"data": [
{
"id": 265997,
"external_review_id": "ChdDSUhNMG9nS0VJQ0FnSUM0bnRlM21BRRAB",
"review_url": null,
"created_at": "2026-09-23T10:31:02+00:00",
"updated_at": "2026-09-23T10:31:02+00:00",
"deleted_at": null,
"source_platform": "google",
"review": {
"id": 265997,
"reviewer": "Jane Smith",
"rating": 4,
"message": "Busy, but clean.",
"review_date": "2026-09-23T10:09:39.000000Z"
},
"reviewsite": {
"id": 44,
"name": "Google",
"link": "http://maps.google.com",
"review_site_logo": "https://d2ny6zb7otrnhl.cloudfront.net/static/images/img-google.png",
"review_site_color": "#4285F4",
"auth_type": "oauth",
"scraping_enabled": true,
"posting_enabled": true
},
"store": {
"id": 3007,
"name": "Acme Coffee - docs-test-loc-001",
"slug": "acme-coffee",
"address": "1 George St, Sydney, NSW"
},
"store_reviewsite": {
"id": 8784,
"url": "https://www.google.com/maps/place/Wynyard/@-33.8648805,151.2065622,1523m/data=!3m1!1e3!4m6!3m5!1s0x6b12ae40e6fcf9eb:0x45845f7ff56e0d1!8m2!3d-33.86594!4d151.2061!16s%2Fg%2F1262gn7v0?entry=ttu&g_ep=EgoyMDI2MDkyMC4wIKXMDSoASAFQAw%3D%3D",
"landing_page_url": "https://www.google.com/maps/place/Wynyard/@-33.8648805,151.2065622,1523m/data=!3m1!1e3!4m6!3m5!1s0x6b12ae40e6fcf9eb:0x45845f7ff56e0d1!8m2!3d-33.86594!4d151.2061!16s%2Fg%2F1262gn7v0?entry=ttu&g_ep=EgoyMDI2MDkyMC4wIKXMDSoASAFQAw%3D%3D",
"connection_status": "Disconnected",
"is_connected": false
},
"flag": [],
"plan": "",
"review_flag": null,
"is_flaggable": true,
"flag_status": "none",
"flag_details": null,
"response_capabilities": {
"manual": true,
"ai": true,
"reason": null
},
"response": null
},
{
"id": 279309,
"external_review_id": "ChZDSUhNMG9nS0VJQ0FnSURyeWZqd0dREAE",
"review_url": null,
"created_at": "2026-09-27T01:02:44+00:00",
"updated_at": "2026-09-28T14:48:21+00:00",
"deleted_at": null,
"source_platform": "google",
"review": {
"id": 279309,
"reviewer": "John Smith",
"rating": 4,
"message": "",
"review_date": "2026-09-27T00:22:21.000000Z"
},
"reviewsite": {
"id": 44,
"name": "Google",
"link": "http://maps.google.com",
"review_site_logo": "https://d2ny6zb7otrnhl.cloudfront.net/static/images/img-google.png",
"review_site_color": "#4285F4",
"auth_type": "oauth",
"scraping_enabled": true,
"posting_enabled": true
},
"store": {
"id": 3007,
"name": "Acme Coffee - docs-test-loc-001",
"slug": "acme-coffee",
"address": "1 George St, Sydney, NSW"
},
"store_reviewsite": {
"id": 8784,
"url": "https://www.google.com/maps/place/Wynyard/@-33.8648805,151.2065622,1523m/data=!3m1!1e3!4m6!3m5!1s0x6b12ae40e6fcf9eb:0x45845f7ff56e0d1!8m2!3d-33.86594!4d151.2061!16s%2Fg%2F1262gn7v0?entry=ttu&g_ep=EgoyMDI2MDkyMC4wIKXMDSoASAFQAw%3D%3D",
"landing_page_url": "https://www.google.com/maps/place/Wynyard/@-33.8648805,151.2065622,1523m/data=!3m1!1e3!4m6!3m5!1s0x6b12ae40e6fcf9eb:0x45845f7ff56e0d1!8m2!3d-33.86594!4d151.2061!16s%2Fg%2F1262gn7v0?entry=ttu&g_ep=EgoyMDI2MDkyMC4wIKXMDSoASAFQAw%3D%3D",
"connection_status": "Disconnected",
"is_connected": false
},
"flag": [
{
"flag": "Flagged — Pending Review",
"title": "This review is in the queue to be flagged for removal from the review site. (Review is off-topic, contains ads, or is gibberish or repetitive)",
"statusColor": "warning",
"icon": "fa-regular:flag"
}
],
"plan": "",
"review_flag": {
"review_id": 279309,
"reason": "Review is off-topic, contains ads, or is gibberish or repetitive",
"flag_status": "pending",
"flag_date": "2026-09-28T14:48:21.000000Z",
"flag_reason_detail": "Duplicate of an earlier review.",
"reason_identifier": "Low quality information"
},
"is_flaggable": true,
"flag_status": "pending",
"flag_details": {
"reason": "Review is off-topic, contains ads, or is gibberish or repetitive",
"reason_identifier": "Low quality information",
"flag_reason_detail": "Duplicate of an earlier review.",
"flag_date": "2026-09-28T14:48:21+00:00"
},
"response_capabilities": {
"manual": true,
"ai": true,
"reason": null
},
"response": null
}
],
"links": {
"first": "https://production-api.shoutaboutus.com/api/v1/partner/report/review-response/feed?page=1",
"last": "https://production-api.shoutaboutus.com/api/v1/partner/report/review-response/feed?page=69",
"prev": null,
"next": "https://production-api.shoutaboutus.com/api/v1/partner/report/review-response/feed?page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 69,
"links": [
{
"url": null,
"label": "« Previous",
"page": null,
"active": false
},
{
"url": "https://production-api.shoutaboutus.com/api/v1/partner/report/review-response/feed?page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": "https://production-api.shoutaboutus.com/api/v1/partner/report/review-response/feed?page=2",
"label": "2",
"page": 2,
"active": false
},
{
"url": "https://production-api.shoutaboutus.com/api/v1/partner/report/review-response/feed?page=2",
"label": "Next »",
"page": 2,
"active": false
}
],
"path": "https://production-api.shoutaboutus.com/api/v1/partner/report/review-response/feed",
"per_page": 10,
"to": 10,
"total": 682
},
"avg_rating": 4.21,
"kpi_by_publisher": [
{
"review_site_id": 44,
"review_site_name": "Google",
"review_site_color": "#4285F4",
"rating": 4.2,
"total_reviews": 682,
"was_bridged": false
}
]
}Examples
Success payload
See Response payload.
Error payload
422 Invalid values
{
"message": "The range start field must match the format Y-m-d H:i:s. (and 3 more errors)",
"errors": {
"range_start": [
"The range start field must match the format Y-m-d H:i:s."
],
"sort_field": [
"The selected sort field is invalid."
],
"status": [
"The selected status is invalid."
],
"rating.0": [
"The rating.0 field must be an integer."
]
}
}422 Location outside your tree
{
"message": "The selected store id does not belong to your company hierarchy.",
"errors": {
"store_id": [
"The selected store id does not belong to your company hierarchy."
]
}
}422 Account doesn't exist or is outside your tree
{
"message": "The selected account id is invalid. (and 1 more error)",
"errors": {
"account_id": [
"The selected account id is invalid.",
"The selected account id does not belong to your company hierarchy."
]
}
}422 limit over 100
{
"message": "The limit field must not be greater than 100.",
"errors": {
"limit": [
"The limit field must not be greater than 100."
]
}
}429 Too many requests
{
"message": "Too Many Attempts."
}| Status | Meaning |
|---|---|
200 | A page of reviews, with the average rating and per-site summary. |
401 | The token is missing, expired or invalid. |
422 | A value is invalid, a location or account is outside your tree, or limit is over 100. |
429 | More than 300 requests in a minute from this user. |
500 | Unexpected server error. |
Example request
curl --request POST \
--url "https://production-api.shoutaboutus.com/api/v1/partner/report/review-response/feed" \
--header 'Authorization: Bearer <bearer-token>' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '{"range_start": "2026-01-01 00:00:00", "range_end": "2026-12-31 23:59:59", "account_id": 5994, "review_site_id": 44, "rating": [4, 5], "status": "awaiting_response", "search": "clean", "limit": 10, "page": 1, "sort_field": "review_date", "sort_by": "desc"}'Updated 11 days ago
