Performance by location

Overview

Get a row per location with its reviews, responses, rating, and response rate for the period.

Use this for a leaderboard or to find locations that need attention.

You'll need a partner token and a date range; sortable.

Returns one row per location, a page at a time.

Prerequisites

  • A Partner bearer token. See Authentication.
  • A date range: range_start and range_end, both Y-m-d H:i:s.
  • Optional: a store_id or account_id (or your own client_location_id / client_account_id) to narrow the report. Leave them out to cover every location under your partner.

Base URL

EnvironmentURL
Productionhttps://production-api.shoutaboutus.com
Developmenthttps://development-api.shoutaboutus.com

Endpoint

POST /api/v1/partner/report/review-response/by-location

Authentication

Partner token

  • Only Partner tokens can call it.
  • Which locations are covered: store_id (one location) if sent, otherwise account_id (that account's locations), otherwise every location under your partner. client_location_id and client_account_id are used when store_id and account_id aren't sent.
  • When the user behind the token is limited to some locations, only those are covered.
  • Every id you send must be inside your own tree: a location or account under your partner. Anything else is rejected with 422.

Rate limit

120 requests per minute, counted per user (the token’s user). See Rate limits.

Request parameters

None.

Request body

FieldTypeRequiredDescription
range_startstringYesStart of the date range, as Y-m-d H:i:s.
range_endstringYesEnd of the date range, as Y-m-d H:i:s.
store_idintegerNoOne location to report on. Must exist and be inside your tree.
account_idintegerNoOne account under your partner (an account id, not a location). Must exist and be inside your tree.
client_location_idstringNoYour own id for a location. Used in place of store_id when store_id isn't sent. Max 255 characters; must be inside your tree.
client_account_idstringNoYour own id for an account. Used in place of account_id when account_id isn't sent. Max 255 characters; must be inside your tree.
sort_fieldstringNoColumn to sort by. Default location_name.
sort_bystringNoSort direction. Default ASC.
search_fieldstringNoField to search. Only location_name.
search_valuestringNoText to look for in the location's name, storeid or client_location_id. Used only with search_field.
limitintegerNoRows per page, 10 to 100. Values below 10 are raised to 10; above 100 returns 422. Default 10.
pageintegerNoPage number. Default 1.

Request payload

{
  "range_start": "2026-01-01 00:00:00",
  "range_end": "2026-09-30 23:59:59",
  "account_id": 5994,
  "sort_field": "total_reviews",
  "sort_by": "desc",
  "limit": 10,
  "page": 1
}

Response body

data is an array with one row per location that has reviews in the range. links and meta carry the pagination.

FieldTypeDescription
dataarray of objectsOne row per location that has reviews in the range.
data[].idintegerThe location's id (store_id).
data[].location_namestringThe location's name, followed by - and its client_location_id (or its storeid when there's no client_location_id).
data[].total_reviewsintegerReviews dated within the range.
data[].total_responsesintegerResponses submitted to the location's reviews in the range.
data[].avg_resp_timenumberAverage time to respond, in days, to 2 decimals. 0 when there are no responses.
data[].avg_ratingnumberAverage star rating of the reviews in the range, to 2 decimals.
data[].response_ratenumberPercentage of the location's reviews in the range that have a response (failed responses aren't counted), to 2 decimals.
linksobjectLinks to the other pages.
links.firststringURL of the first page.
links.laststringURL of the last page.
links.prevstring or nullURL of the previous page, or null on the first page.
links.nextstring or nullURL of the next page, or null on the last page.
metaobjectPagination details.
meta.current_pageintegerThe current page.
meta.frominteger or nullPosition of the first row on this page, or null when the page is empty.
meta.last_pageintegerThe last page number.
meta.linksarray of objectsPage links, for building a pager.
meta.links[].urlstring or nullURL of that page, or null.
meta.links[].labelstringLabel for the link: a page number, « Previous or Next ».
meta.links[].pageinteger or nullThe page number, or null for a previous/next link that has no page.
meta.links[].activebooleanWhether this is the current page.
meta.pathstringThe endpoint URL, without the page query.
meta.per_pageintegerRows per page.
meta.tointeger or nullPosition of the last row on this page, or null when the page is empty.
meta.totalintegerTotal rows across all pages.

Response payload

{
  "data": [
    {
      "id": 3007,
      "location_name": "Acme Coffee - docs-test-loc-001",
      "total_reviews": 120,
      "total_responses": 108,
      "avg_resp_time": 1.5,
      "avg_rating": 4.44,
      "response_rate": 90.0
    }
  ],
  "links": {
    "first": "https://production-api.shoutaboutus.com/api/v1/partner/report/review-response/by-location?page=1",
    "last": "https://production-api.shoutaboutus.com/api/v1/partner/report/review-response/by-location?page=1",
    "prev": null,
    "next": null
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 1,
    "links": [
      {
        "url": null,
        "label": "« Previous",
        "page": null,
        "active": false
      },
      {
        "url": "https://production-api.shoutaboutus.com/api/v1/partner/report/review-response/by-location?page=1",
        "label": "1",
        "page": 1,
        "active": true
      },
      {
        "url": null,
        "label": "Next »",
        "page": null,
        "active": false
      }
    ],
    "path": "https://production-api.shoutaboutus.com/api/v1/partner/report/review-response/by-location",
    "per_page": 10,
    "to": 1,
    "total": 1
  }
}

Examples

Success payload

See Response payload.

No location has reviews in the range:

{
  "data": [],
  "links": {
    "first": "https://production-api.shoutaboutus.com/api/v1/partner/report/review-response/by-location?page=1",
    "last": "https://production-api.shoutaboutus.com/api/v1/partner/report/review-response/by-location?page=1",
    "prev": null,
    "next": null
  },
  "meta": {
    "current_page": 1,
    "from": null,
    "last_page": 1,
    "links": [
      {
        "url": null,
        "label": "« Previous",
        "page": null,
        "active": false
      },
      {
        "url": "https://production-api.shoutaboutus.com/api/v1/partner/report/review-response/by-location?page=1",
        "label": "1",
        "page": 1,
        "active": true
      },
      {
        "url": null,
        "label": "Next »",
        "page": null,
        "active": false
      }
    ],
    "path": "https://production-api.shoutaboutus.com/api/v1/partner/report/review-response/by-location",
    "per_page": 10,
    "to": null,
    "total": 0
  }
}

Error payload

422 Date range missing

{
  "message": "The range start field is required. (and 1 more error)",
  "errors": {
    "range_start": [
      "The range start field is required."
    ],
    "range_end": [
      "The range end field is required."
    ]
  }
}

422 Wrong date format

{
  "message": "The range start field must match the format Y-m-d H:i:s.",
  "errors": {
    "range_start": [
      "The range start field must match the format Y-m-d H:i:s."
    ]
  }
}

422 Account outside your tree

{
  "message": "The selected account id does not belong to your company hierarchy.",
  "errors": {
    "account_id": [
      "The selected account id does not belong to your company hierarchy."
    ]
  }
}

422 Location doesn't exist

{
  "message": "The selected store id is invalid. (and 1 more error)",
  "errors": {
    "store_id": [
      "The selected store id is invalid.",
      "The selected store id does not belong to your company hierarchy."
    ]
  }
}

422 Unknown sort field or direction

{
  "message": "The selected sort field is invalid. (and 1 more error)",
  "errors": {
    "sort_field": [
      "The selected sort field is invalid."
    ],
    "sort_by": [
      "The selected sort by is invalid."
    ]
  }
}

422 limit above 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."
}
StatusMeaning
200One row per location, a page at a time.
401The token is missing, expired or invalid.
422A date is missing or not Y-m-d H:i:s, an id doesn't exist or is outside your tree, sort_field, sort_by or search_field isn't an allowed value, or limit is above 100.
429More than 120 requests in a minute from this user.
500Unexpected server error.

Example request

curl --request POST \
  --url "https://production-api.shoutaboutus.com/api/v1/partner/report/review-response/by-location" \
  --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-09-30 23:59:59", "account_id": 5994, "sort_field": "total_reviews", "sort_by": "desc", "limit": 10, "page": 1}'

Did this page help you?