Ranking report
Overview
Get the account's locations ranked against each other on rating, volume, and response.
Use this for an internal leaderboard.
You'll need a token and a period.
Returns the ranked list: one ranking each by review count, rating and response rate, and an overall ranking with a grade.
Prerequisites
- A bearer token. See Authentication.
- A date range (
range_start,range_end), bothY-m-d H:i:s. - Optional: a location id (
store_id). Leave it out to report on every location your token can see.
Base URL
| Environment | URL |
|---|---|
| Production | https://production-api.shoutaboutus.com |
| Development | https://development-api.shoutaboutus.com |
Endpoint
POST /api/v1/reports/ranking
Authentication
Partner or Account token
- Partner, account and location users can call it; any other user gets
403. store_idmust be inside your own account tree; otherwise422.- Without
store_id, every location the token can see is ranked: every location under the partner for a Partner token, the account's locations for an Account token, or only the assigned locations for a user limited to some locations.
Rate limit
This endpoint has no rate limit.
Request parameters
None.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
range_start | string | Yes | Start of the period, Y-m-d H:i:s (e.g. 2026-01-01 00:00:00). A date without a time is rejected. |
range_end | string | Yes | End of the period, Y-m-d H:i:s (e.g. 2026-09-30 23:59:59). |
store_id | integer | No | Rank only this location. Must exist and be inside your account tree. Leave it out to rank every location your token can see. |
Request payload
{
"range_start": "2026-01-01 00:00:00",
"range_end": "2026-09-30 23:59:59"
}Response body
The payload is inside data.
| Field | Type | Description |
|---|---|---|
account_name | string or null | Name of the account: the account of store_id when sent, otherwise the caller's account. null when there's no account above (e.g. a Partner token without store_id). |
interval | object | The period, as sent. |
interval.start | string | range_start, as sent. |
interval.end | string | range_end, as sent. |
locationRank | object | Four rankings of the same locations. All four are [] when there are no locations. |
locationRank.byReview[] | array of objects | Locations ranked by review_count, best first. |
locationRank.byReview[].id | integer | The location's id (store_id). |
locationRank.byReview[].name | string | The location's name, then - and its store code. |
locationRank.byReview[].review_count | integer | Reviews in the period. |
locationRank.byReview[].response_count | integer | Responses to those reviews. |
locationRank.byReview[].response_percent | number | response_count as a percentage of review_count. |
locationRank.byReview[].avgRating | number | Average star rating of the period's reviews, not counting legacy and off-platform reviews. 0 when there are none. |
locationRank.byReview[].rank | integer | Rank by review_count, from 1. Locations with the same value share a rank. |
locationRank.byRate[] | array of objects | Locations ranked by avgRating, best first. |
locationRank.byRate[].id | integer | The location's id (store_id). |
locationRank.byRate[].name | string | The location's name, then - and its store code. |
locationRank.byRate[].review_count | integer | Reviews in the period. |
locationRank.byRate[].response_count | integer | Responses to those reviews. |
locationRank.byRate[].response_percent | number | response_count as a percentage of review_count. |
locationRank.byRate[].avgRating | number | Average star rating of the period's reviews, not counting legacy and off-platform reviews. 0 when there are none. |
locationRank.byRate[].rank | integer | Rank by avgRating, from 1. Locations with the same value share a rank. |
locationRank.byResponse[] | array of objects | Locations ranked by response_percent, best first. |
locationRank.byResponse[].id | integer | The location's id (store_id). |
locationRank.byResponse[].name | string | The location's name, then - and its store code. |
locationRank.byResponse[].review_count | integer | Reviews in the period. |
locationRank.byResponse[].response_count | integer | Responses to those reviews. |
locationRank.byResponse[].response_percent | number | response_count as a percentage of review_count. |
locationRank.byResponse[].avgRating | number | Average star rating of the period's reviews, not counting legacy and off-platform reviews. 0 when there are none. |
locationRank.byResponse[].rank | integer | Rank by response_percent, from 1. Locations with the same value share a rank. |
locationRank.overAll[] | array of objects | Locations ranked overall: by totalRank, lowest first. |
locationRank.overAll[].id | integer | The location's id (store_id). |
locationRank.overAll[].name | string | The location's name, then - and its store code. |
locationRank.overAll[].review_count | integer | Reviews in the period. |
locationRank.overAll[].response_count | integer | Responses to those reviews. |
locationRank.overAll[].response_percent | number | response_count as a percentage of review_count. |
locationRank.overAll[].avgRating | number | Average star rating of the period's reviews, not counting legacy and off-platform reviews. 0 when there are none. |
locationRank.overAll[].reviewRank | integer | The location's rank in byReview. |
locationRank.overAll[].rateRank | integer | The location's rank in byRate. |
locationRank.overAll[].responseRank | integer | The location's rank in byResponse. |
locationRank.overAll[].totalRank | integer | Sum of the three ranks. Lower is better. |
locationRank.overAll[].final_grade | string | Grade from totalRank: A+, A, B, C or D. Fewer grades are used when there are fewer than five distinct totalRank values. |
locationRank.overAll[].final_grade_point | integer | The grade as a number: A+ 5, A 4, B 3, C 2, D 1. |
locationRank.overAll[].percentage_rank | string | Band such as Top 10% or Bottom 50%. Only present when there are 10 or more distinct totalRank values. |
Response payload
{
"data": {
"account_name": "Acme Coffee",
"interval": {
"start": "2026-01-01 00:00:00",
"end": "2026-09-30 23:59:59"
},
"locationRank": {
"byReview": [
{
"id": 3007,
"name": "Acme Coffee - Sydney",
"review_count": 223,
"response_count": 0,
"response_percent": 0,
"avgRating": 0,
"rank": 1
},
{
"id": 3008,
"name": "Acme Coffee - Circular Quay - Sydney",
"review_count": 0,
"response_count": 0,
"response_percent": 0,
"avgRating": 0,
"rank": 2
}
],
"byRate": [
{
"id": 3007,
"name": "Acme Coffee - Sydney",
"review_count": 223,
"response_count": 0,
"response_percent": 0,
"avgRating": 0,
"rank": 1
},
{
"id": 3008,
"name": "Acme Coffee - Circular Quay - Sydney",
"review_count": 0,
"response_count": 0,
"response_percent": 0,
"avgRating": 0,
"rank": 1
}
],
"byResponse": [
{
"id": 3007,
"name": "Acme Coffee - Sydney",
"review_count": 223,
"response_count": 0,
"response_percent": 0,
"avgRating": 0,
"rank": 1
},
{
"id": 3008,
"name": "Acme Coffee - Circular Quay - Sydney",
"review_count": 0,
"response_count": 0,
"response_percent": 0,
"avgRating": 0,
"rank": 1
}
],
"overAll": [
{
"id": 3007,
"name": "Acme Coffee - Sydney",
"review_count": 223,
"response_count": 0,
"response_percent": 0,
"avgRating": 0,
"reviewRank": 1,
"rateRank": 1,
"responseRank": 1,
"totalRank": 3,
"final_grade": "A+",
"final_grade_point": 5
},
{
"id": 3008,
"name": "Acme Coffee - Circular Quay - Sydney",
"review_count": 0,
"response_count": 0,
"response_percent": 0,
"avgRating": 0,
"reviewRank": 2,
"rateRank": 1,
"responseRank": 1,
"totalRank": 4,
"final_grade": "A",
"final_grade_point": 4
}
]
}
}
}Examples
Success payload
Two locations: see Response payload. In this example the reviews are all off-platform, so avgRating is 0.
No locations to rank:
{
"data": {
"account_name": "Acme Coffee",
"interval": {
"start": "2026-01-01 00:00:00",
"end": "2026-09-30 23:59:59"
},
"locationRank": {
"byReview": [],
"byRate": [],
"byResponse": [],
"overAll": []
}
}
}Error payload
422 Dates 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 Date not in Y-m-d H:i:s
{
"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 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."
]
}
}403 Not a partner, account or location user
{
"message": "Forbidden",
"status": "error",
"errors": "Only partner, account or location users can access this ranking report."
}| Status | Meaning |
|---|---|
200 | The ranked locations, or four empty lists when there are none. |
401 | The token is missing, expired or invalid. |
403 | The token's user isn't a partner, account or location user. |
422 | A date is missing or not Y-m-d H:i:s, or store_id doesn't exist or is outside your tree. |
500 | Unexpected server error. |
Example request
curl --request POST \
--url "https://production-api.shoutaboutus.com/api/v1/reports/ranking" \
--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"}'Updated 17 days ago
Did this page help you?
