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), both Y-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

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

Endpoint

POST /api/v1/reports/pdf/performance-report

Authentication

Partner or Account token

  • store_id must be inside your own account tree; otherwise 422.
  • 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

FieldTypeRequiredDescription
range_startstringYesStart 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_endstringYesEnd of the period, Y-m-d H:i:s (e.g. 2026-09-30 23:59:59).
store_idintegerNoOne location to report on. Must exist and be inside your account tree. Leave it out to cover every location your token can see.
is_pdfintegerNo1 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.

FieldTypeDescription
startstringrange_start, as sent.
endstringrange_end, as sent.
companyobjectThe report header.
company.account_namestring or nullFor 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_namestring or nullEmpty for an Account token; otherwise the first location of the caller's company, as name - store code.
company.logostringURL of the partner's white-label logo; empty when there's none.
kpis_dataobjectTotals for the period, and for the previous period (the window of the same length immediately before range_start).
kpis_data.total_reviewsintegerReviews in the period.
kpis_data.avg_ratingnumberAverage star rating of the period's reviews. 0 when there are none.
kpis_data.total_respondedintegerResponses of any kind to the period's reviews.
kpis_data.response_rate_pctnumberPercentage of the period's reviews with a response of any kind.
kpis_data.awaiting_responseintegerReviews in the period with no response yet that the location may respond to.
kpis_data.total_response_ratenumberPercentage 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_timenumber or stringAverage time to respond, in days. The string "0" when there are no responses.
kpis_data.noLoginNoResponseintegerReviews whose response is waiting because no login is saved for the site.
kpis_data.noLoginNoResponsintegerMisspelt alias of noLoginNoResponse, kept for existing clients. Use noLoginNoResponse.
kpis_data.total_reviews_previousintegerReviews in the previous period. Left out when range_end isn't after range_start.
kpis_data.avg_rating_previousnumberAverage star rating in the previous period. 0 when there are none. Left out when range_end isn't after range_start.
kpis_data.response_previousintegerResponses to the previous period's reviews. Left out when range_end isn't after range_start.
reviewsSentimentsobjectThe period's reviews split into negative, positive and neutral.
reviewsSentiments.negativeReviewsintegerReviews rated below 3 stars.
reviewsSentiments.negativeReviews_percentnumberNegative reviews as a percentage of the period's reviews.
reviewsSentiments.positiveReviewsintegerReviews rated above 3 stars.
reviewsSentiments.positiveReviews_percentnumberPositive reviews as a percentage of the period's reviews.
reviewsSentiments.neutralReviewsintegerThe rest: 3-star reviews, and Facebook reviews with no star rating.
reviewsSentiments.neutralReviews_percentnumberNeutral reviews as a percentage of the period's reviews.
review_response_by_site[]array of objectsOne row per review site with reviews in the period, most reviews first.
review_response_by_site[].idintegerThe review site's id.
review_response_by_site[].review_sitestringThe review site's name in lower case, e.g. google.
review_response_by_site[].review_site_logostring or nullAlways null here.
review_response_by_site[].total_reviewsintegerReviews from this site in the period.
review_response_by_site[].avg_ratingnumberAverage star rating of this site's reviews in the period.
review_response_by_site[].per_of_totalnumberThis site's share of the period's reviews, in percent.
review_response_by_site[].total_responseintegerResponses to this site's reviews, not counting failed ones.
review_response_by_site[].per_total_responsenumbertotal_response as a percentage of total_reviews.
review_response_by_location[]array of objectsOne row per location in scope.
review_response_by_location[].idintegerThe location's id (store_id).
review_response_by_location[].namestringThe location's name, then - and its store code.
review_response_by_location[].total_reviewsintegerThe location's reviews in the period.
review_response_by_location[].avg_ratingnumberAverage star rating of the location's reviews in the period.
review_response_by_location[].per_of_totalnumberThe location's share of the period's reviews, in percent.
review_response_by_location[].total_responseintegerResponses submitted for the location's reviews.
review_response_by_location[].per_total_responsenumberPercentage of the location's reviews with a response, not counting failed ones.
review_response_by_location[].avg_response_timenumber or stringAverage time to respond, in days. The string "0" when there are no responses.
response_feed[]array of objectsThe period's reviews with their responses. Limited to 50 when is_pdf is 1.
response_feed[].idintegerThe review's id.
response_feed[].source_platformstringThe platform the review came from, e.g. google.
response_feed[].reviewobjectThe review.
response_feed[].review.idintegerThe review's id.
response_feed[].review.reviewerstringThe reviewer's name.
response_feed[].review.ratingintegerThe star rating.
response_feed[].review.messagestringThe review text; empty when there's none.
response_feed[].review.review_datestring or nullWhen the review was posted (ISO 8601).
response_feed[].reviewsiteobjectThe review site.
response_feed[].reviewsite.idintegerThe review site's id.
response_feed[].reviewsite.namestringThe review site's name, e.g. Google.
response_feed[].reviewsite.linkstringThe review site's home page.
response_feed[].reviewsite.review_site_logostringURL of the site's logo.
response_feed[].reviewsite.review_site_colorstring or nullThe site's brand colour as a hex code.
response_feed[].reviewsite.auth_typestring or nullHow the site connects (see List supported review sites).
response_feed[].reviewsite.scraping_enabledbooleanWhether reviews are collected from this site.
response_feed[].reviewsite.posting_enabledbooleanWhether responses can be posted to this site.
response_feed[].storeobjectThe location.
response_feed[].store.idintegerThe location's id.
response_feed[].store.namestringThe location's name, then - and its client_location_id (or store code).
response_feed[].store.slugstringThe location's slug.
response_feed[].store.addressstringThe location's address; empty when not set.
response_feed[].store_reviewsiteobjectThe location's listing on this review site.
response_feed[].store_reviewsite.idinteger or nullThe listing's id (store_review_site_id).
response_feed[].store_reviewsite.urlstring or nullURL of the listing.
response_feed[].store_reviewsite.landing_page_urlstring or nullThe URL customers are sent to when asked for a review.
response_feed[].store_reviewsite.connection_statusstring or nullThe listing's connection status, e.g. Connected or Disconnected.
response_feed[].store_reviewsite.is_connectedbooleanWhether the listing is connected, so responses can be posted.
response_feed[].flag[]array of objectsStatus badges for the response and any removal flag. [] when there are none.
response_feed[].flag[].flagstringBadge text, e.g. complete or Flagged — Pending Review.
response_feed[].flag[].titlestringWhat the badge means.
response_feed[].flag[].statusColorstringsuccess, warning or error.
response_feed[].flag[].iconstringIcon name for the badge.
response_feed[].flag[].links[]array of objectsLinks to fix the problem, on some error badges.
response_feed[].flag[].links[].namestringLink text, e.g. Edit Review Site.
response_feed[].flag[].links[].urlstringLink URL in the Shout About Us app.
response_feed[].planstringName of the plan behind the latest response; empty when there's none.
response_feed[].review_flagobject or nullThe removal flag on the review; null when it isn't flagged.
response_feed[].review_flag.review_idintegerThe review's id.
response_feed[].review_flag.reasonstringWhy the review was flagged.
response_feed[].review_flag.flag_statusstringThe flag's status, e.g. pending.
response_feed[].review_flag.flag_datestringWhen it was flagged.
response_feed[].review_flag.flag_reason_detailstring or nullExtra detail given with the flag.
response_feed[].review_flag.reason_identifierstringShort name of the reason.
response_feed[].is_flaggablebooleanWhether the review can be flagged for removal.
response_feed[].flag_statusstringThe flag's status, or none when the review isn't flagged.
response_feed[].flag_detailsobject or nullThe flag's details; null when the review isn't flagged.
response_feed[].flag_details.reasonstringWhy the review was flagged.
response_feed[].flag_details.reason_identifierstringShort name of the reason.
response_feed[].flag_details.flag_reason_detailstring or nullExtra detail given with the flag.
response_feed[].flag_details.flag_datestring or nullWhen it was flagged (ISO 8601).
response_feed[].response_capabilitiesobjectWhether a response can be written for this review.
response_feed[].response_capabilities.manualbooleanA response can be written by hand.
response_feed[].response_capabilities.aibooleanAn AI response can be generated.
response_feed[].response_capabilities.reasonstring or nullWhy responding is blocked, e.g. past_response_unavailable, past_response_quota_reached or past_response_window_expired; null when it isn't.
response_feed[].responseobject or nullThe response; null when there's none.
response_feed[].response.idintegerThe response's id.
response_feed[].response.statusstringThe response's status, e.g. complete or pending.
response_feed[].response.public_contentsstring or nullThe public response text.
response_feed[].response.private_contentsstring or nullThe private message, where the site supports one.
response_feed[].response.response_datestring or nullWhen the response was posted.
response_feed[].response.deletedobject or nullThe 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."
    ]
  }
}
StatusMeaning
200The report data.
401The token is missing, expired or invalid.
422A 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.
500Unexpected 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}'

Did this page help you?