Export performance report to PDF
Overview
Get the data behind the account's performance report, ready to lay out as a PDF.
It returns JSON, not a file: the header block, KPIs, sentiment split, per-site and per-location breakdowns, and the review-response feed. With is_pdf=1 the feed is limited to 50 rows.
The KPIs include the previous period (total_reviews_previous, avg_rating_previous, response_previous): the window of the same length immediately before range_start.
You'll need a token and a date range (range_start, range_end, both Y-m-d H:i:s).
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/pdf/performance-report
Authentication
Partner or Account token
store_idmust be inside your own account tree; otherwise422.- Without
store_id, the report covers every location the token can see: 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. - The header block depends on the caller: an Account token gets its own name and an empty
store_name; any other token gets its parent company's name and its first location's name. - The logo is the partner's white-label logo.
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 | One location to report on. Must exist and be inside your account tree. Leave it out to cover every location your token can see. |
is_pdf | integer | No | 1 limits response_feed to 50 rows and uses site logos served by the API. 0 or left out returns the whole feed. |
Request payload
{
"range_start": "2026-09-28 00:00:00",
"range_end": "2026-09-28 23:59:59",
"store_id": 3007,
"is_pdf": 1
}Response body
The payload is inside data.
| Field | Type | Description |
|---|---|---|
start | string | range_start, as sent. |
end | string | range_end, as sent. |
company | object | The report header. |
company.account_name | string or null | For an Account token, the account's name; for any other token, the name of the caller's parent company (for a location user, its account). |
company.store_name | string or null | Empty for an Account token; otherwise the first location of the caller's company, as name - store code. |
company.logo | string | URL of the partner's white-label logo; empty when there's none. |
kpis_data | object | Totals for the period, and for the previous period (the window of the same length immediately before range_start). |
kpis_data.total_reviews | integer | Reviews in the period. |
kpis_data.avg_rating | number | Average star rating of the period's reviews. 0 when there are none. |
kpis_data.total_responded | integer | Responses of any kind to the period's reviews. |
kpis_data.response_rate_pct | number | Percentage of the period's reviews with a response of any kind. |
kpis_data.awaiting_response | integer | Reviews in the period with no response yet that the location may respond to. |
kpis_data.total_response_rate | number | Percentage of the period's reviews with a response, not counting failed ones (no login saved, login error, not responding, business already responded). |
kpis_data.avg_response_time | number or string | Average time to respond, in days. The string "0" when there are no responses. |
kpis_data.noLoginNoResponse | integer | Reviews whose response is waiting because no login is saved for the site. |
kpis_data.noLoginNoRespons | integer | Misspelt alias of noLoginNoResponse, kept for existing clients. Use noLoginNoResponse. |
kpis_data.total_reviews_previous | integer | Reviews in the previous period. Left out when range_end isn't after range_start. |
kpis_data.avg_rating_previous | number | Average star rating in the previous period. 0 when there are none. Left out when range_end isn't after range_start. |
kpis_data.response_previous | integer | Responses to the previous period's reviews. Left out when range_end isn't after range_start. |
reviewsSentiments | object | The period's reviews split into negative, positive and neutral. |
reviewsSentiments.negativeReviews | integer | Reviews rated below 3 stars. |
reviewsSentiments.negativeReviews_percent | number | Negative reviews as a percentage of the period's reviews. |
reviewsSentiments.positiveReviews | integer | Reviews rated above 3 stars. |
reviewsSentiments.positiveReviews_percent | number | Positive reviews as a percentage of the period's reviews. |
reviewsSentiments.neutralReviews | integer | The rest: 3-star reviews, and Facebook reviews with no star rating. |
reviewsSentiments.neutralReviews_percent | number | Neutral reviews as a percentage of the period's reviews. |
review_response_by_site[] | array of objects | One row per review site with reviews in the period, most reviews first. |
review_response_by_site[].id | integer | The review site's id. |
review_response_by_site[].review_site | string | The review site's name in lower case, e.g. google. |
review_response_by_site[].review_site_logo | string or null | Always null here. |
review_response_by_site[].total_reviews | integer | Reviews from this site in the period. |
review_response_by_site[].avg_rating | number | Average star rating of this site's reviews in the period. |
review_response_by_site[].per_of_total | number | This site's share of the period's reviews, in percent. |
review_response_by_site[].total_response | integer | Responses to this site's reviews, not counting failed ones. |
review_response_by_site[].per_total_response | number | total_response as a percentage of total_reviews. |
review_response_by_location[] | array of objects | One row per location in scope. |
review_response_by_location[].id | integer | The location's id (store_id). |
review_response_by_location[].name | string | The location's name, then - and its store code. |
review_response_by_location[].total_reviews | integer | The location's reviews in the period. |
review_response_by_location[].avg_rating | number | Average star rating of the location's reviews in the period. |
review_response_by_location[].per_of_total | number | The location's share of the period's reviews, in percent. |
review_response_by_location[].total_response | integer | Responses submitted for the location's reviews. |
review_response_by_location[].per_total_response | number | Percentage of the location's reviews with a response, not counting failed ones. |
review_response_by_location[].avg_response_time | number or string | Average time to respond, in days. The string "0" when there are no responses. |
response_feed[] | array of objects | The period's reviews with their responses. Limited to 50 when is_pdf is 1. |
response_feed[].id | integer | The review's id. |
response_feed[].source_platform | string | The platform the review came from, e.g. google. |
response_feed[].review | object | The review. |
response_feed[].review.id | integer | The review's id. |
response_feed[].review.reviewer | string | The reviewer's name. |
response_feed[].review.rating | integer | The star rating. |
response_feed[].review.message | string | The review text; empty when there's none. |
response_feed[].review.review_date | string or null | When the review was posted (ISO 8601). |
response_feed[].reviewsite | object | The review site. |
response_feed[].reviewsite.id | integer | The review site's id. |
response_feed[].reviewsite.name | string | The review site's name, e.g. Google. |
response_feed[].reviewsite.link | string | The review site's home page. |
response_feed[].reviewsite.review_site_logo | string | URL of the site's logo. |
response_feed[].reviewsite.review_site_color | string or null | The site's brand colour as a hex code. |
response_feed[].reviewsite.auth_type | string or null | How the site connects (see List supported review sites). |
response_feed[].reviewsite.scraping_enabled | boolean | Whether reviews are collected from this site. |
response_feed[].reviewsite.posting_enabled | boolean | Whether responses can be posted to this site. |
response_feed[].store | object | The location. |
response_feed[].store.id | integer | The location's id. |
response_feed[].store.name | string | The location's name, then - and its client_location_id (or store code). |
response_feed[].store.slug | string | The location's slug. |
response_feed[].store.address | string | The location's address; empty when not set. |
response_feed[].store_reviewsite | object | The location's listing on this review site. |
response_feed[].store_reviewsite.id | integer or null | The listing's id (store_review_site_id). |
response_feed[].store_reviewsite.url | string or null | URL of the listing. |
response_feed[].store_reviewsite.landing_page_url | string or null | The URL customers are sent to when asked for a review. |
response_feed[].store_reviewsite.connection_status | string or null | The listing's connection status, e.g. Connected or Disconnected. |
response_feed[].store_reviewsite.is_connected | boolean | Whether the listing is connected, so responses can be posted. |
response_feed[].flag[] | array of objects | Status badges for the response and any removal flag. [] when there are none. |
response_feed[].flag[].flag | string | Badge text, e.g. complete or Flagged — Pending Review. |
response_feed[].flag[].title | string | What the badge means. |
response_feed[].flag[].statusColor | string | success, warning or error. |
response_feed[].flag[].icon | string | Icon name for the badge. |
response_feed[].flag[].links[] | array of objects | Links to fix the problem, on some error badges. |
response_feed[].flag[].links[].name | string | Link text, e.g. Edit Review Site. |
response_feed[].flag[].links[].url | string | Link URL in the Shout About Us app. |
response_feed[].plan | string | Name of the plan behind the latest response; empty when there's none. |
response_feed[].review_flag | object or null | The removal flag on the review; null when it isn't flagged. |
response_feed[].review_flag.review_id | integer | The review's id. |
response_feed[].review_flag.reason | string | Why the review was flagged. |
response_feed[].review_flag.flag_status | string | The flag's status, e.g. pending. |
response_feed[].review_flag.flag_date | string | When it was flagged. |
response_feed[].review_flag.flag_reason_detail | string or null | Extra detail given with the flag. |
response_feed[].review_flag.reason_identifier | string | Short name of the reason. |
response_feed[].is_flaggable | boolean | Whether the review can be flagged for removal. |
response_feed[].flag_status | string | The flag's status, or none when the review isn't flagged. |
response_feed[].flag_details | object or null | The flag's details; null when the review isn't flagged. |
response_feed[].flag_details.reason | string | Why the review was flagged. |
response_feed[].flag_details.reason_identifier | string | Short name of the reason. |
response_feed[].flag_details.flag_reason_detail | string or null | Extra detail given with the flag. |
response_feed[].flag_details.flag_date | string or null | When it was flagged (ISO 8601). |
response_feed[].response_capabilities | object | Whether a response can be written for this review. |
response_feed[].response_capabilities.manual | boolean | A response can be written by hand. |
response_feed[].response_capabilities.ai | boolean | An AI response can be generated. |
response_feed[].response_capabilities.reason | string or null | Why responding is blocked, e.g. past_response_unavailable, past_response_quota_reached or past_response_window_expired; null when it isn't. |
response_feed[].response | object or null | The response; null when there's none. |
response_feed[].response.id | integer | The response's id. |
response_feed[].response.status | string | The response's status, e.g. complete or pending. |
response_feed[].response.public_contents | string or null | The public response text. |
response_feed[].response.private_contents | string or null | The private message, where the site supports one. |
response_feed[].response.response_date | string or null | When the response was posted. |
response_feed[].response.deleted | object or null | The deletion request for a response marked not responding (delete_on, status); otherwise null. |
Response payload
{
"data": {
"start": "2026-09-28 00:00:00",
"end": "2026-09-28 23:59:59",
"company": {
"account_name": "Acme Coffee",
"store_name": "",
"logo": ""
},
"kpis_data": {
"total_reviews": 1,
"avg_rating": 3,
"total_responded": 0,
"response_rate_pct": 0,
"awaiting_response": 1,
"total_response_rate": 0,
"avg_response_time": "0",
"noLoginNoResponse": 0,
"noLoginNoRespons": 0,
"total_reviews_previous": 1,
"avg_rating_previous": 4,
"response_previous": 0
},
"reviewsSentiments": {
"negativeReviews": 0,
"negativeReviews_percent": 0,
"positiveReviews": 0,
"positiveReviews_percent": 0,
"neutralReviews": 1,
"neutralReviews_percent": 100
},
"review_response_by_site": [
{
"id": 44,
"review_site": "google",
"review_site_logo": null,
"total_reviews": 1,
"avg_rating": 3,
"per_of_total": 100,
"total_response": 0,
"per_total_response": 0
}
],
"review_response_by_location": [
{
"id": 3007,
"name": "Acme Coffee - Sydney",
"total_reviews": 1,
"avg_rating": 3,
"per_of_total": 100,
"total_response": 0,
"per_total_response": 0,
"avg_response_time": "0"
}
],
"response_feed": [
{
"id": 287111,
"source_platform": "google",
"review": {
"id": 287111,
"reviewer": "Jane Smith",
"rating": 3,
"message": "",
"review_date": "2026-09-28T12:26:43.000000Z"
},
"reviewsite": {
"id": 44,
"name": "Google",
"link": "http://maps.google.com",
"review_site_logo": "https://production-api.shoutaboutus.com/static/images/img-google.png",
"review_site_color": "#4285F4",
"auth_type": null,
"scraping_enabled": false,
"posting_enabled": false
},
"store": {
"id": 3007,
"name": "Acme Coffee - docs-test-loc-001",
"slug": "sau-docs-test",
"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
}
]
}
}Examples
Success payload
See Response payload.
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."
]
}
}422 is_pdf not 0 or 1
{
"message": "The selected is pdf is invalid.",
"errors": {
"is_pdf": [
"The selected is pdf is invalid."
]
}
}| Status | Meaning |
|---|---|
200 | The report data. |
401 | The token is missing, expired or invalid. |
422 | A date is missing or not Y-m-d H:i:s, store_id doesn't exist or is outside your tree, or is_pdf isn't 0 or 1. |
500 | Unexpected server error. |
Example request
curl --request POST \
--url "https://production-api.shoutaboutus.com/api/v1/reports/pdf/performance-report" \
--header 'Authorization: Bearer <bearer-token>' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '{"range_start": "2026-09-28 00:00:00", "range_end": "2026-09-28 23:59:59", "store_id": 3007, "is_pdf": 1}'Updated 17 days ago
Did this page help you?
