Submit competitive report (generate)
Overview
Queue an asynchronous ReviewData competitive report. It compares one store location with one or two competitors on a shared publisher (review site). The request consumes one unit from the store's competitive_analysis plan quota at submission. Hard-stop plans return 403 when exhausted, while soft-stop plans bill the call as overage. An identical non-failed report is reused unless you set force. The response returns immediately with status: queued and a task_id. The completed report arrives through the COMPETITIVE_REPORT_RESULT webhook.
Complete the workflow
- Submit this request and save
data.competitive_report_idfrom the202response. - Call Check status (poll until complete) with the same
store_idand the saved report ID. - Continue polling while
data.statusisqueued,pending, orprocessing. - Read
data.report_dataafter the report completes, or inspectdata.failure_reasonwhendata.statusisfailed.
Prerequisites
- A bearer token. Callable with Partner and Account tokens.
Base URL
| Environment | URL |
|---|---|
| Production | https://production-api.shoutaboutus.com |
| Development | https://development-api.shoutaboutus.com |
Endpoint
POST /api/v1/account/competitive-report
Authentication
-
Requires a bearer token in the
Authorization: Bearer <bearer-token>header. -
Who can call it: Partner and Account tokens.
-
The route lives directly under the authenticated group (not behind partner-only access), so any authenticated user (Partner or Account) may call it.
-
The target store must sit inside your own account tree, keyed on
store_id, which walks the auth user's company hierarchy. -
As a result, a Partner reaches any store in its tree, an Account only its own.
-
review_site_idmust additionally be linked to the store (checked during validation).
Rate limit
- No rate limit.
Request body
- Body:
| Field | Type | Required | Description |
|---|---|---|---|
store_id | integer | yes | Must be an existing location inside your own account tree (must be in caller's company hierarchy). |
review_site_id | integer | yes | Must be an existing review site and must also be linked to the store. |
year | integer | yes | between:2000,2100. |
month | integer | no | between:1,12; omit for a full-year report. |
theme | string | no | max:64. Optional — when omitted it is auto-resolved from the account's industry (system default as final fallback). A theme slug from List available themes (e.g. home-services); general is no longer accepted by the reporting engine. |
competitors | array | yes | 1-2 items. |
competitors.*.name | string | yes | max:255. |
competitors.*.profile_key | string (url) | yes | max:1024. Competitor's review-page URL on the publisher. |
competitors.*.id | string | no | max:255. |
competitors.*.phone | string | no | max:64. |
competitors.*.description | string | no | max:2048. |
competitors.*.tags | array | no | max:30, each tag string max:64. |
competitors.*.address | object | no | optional street/city/state/zip/country strings. |
force | boolean | no | Explicit recall — bypasses dedup and bills a fresh RD run. |
{
"store_id": 42,
"review_site_id": 7,
"year": 2026,
"month": 5,
"theme": "home-services",
"competitors": [
{
"name": "Pacific Breeze Heating & Air",
"profile_key": "https://www.google.com/maps/place/pacific-breeze",
"phone": "+15551234567",
"address": {
"street": "123 Main St",
"city": "San Diego",
"state": "CA",
"zip": "92101",
"country": "US"
}
}
],
"force": false
}Response
-
data.usage_log_id(integer) — usage record id on a fresh submit. -
data.deduped(boolean) —truewhen a 200 returns an existing (deduped) report instead of creating a new one. -
data.competitive_report_id(integer) — the report ID. Use it asreport_idwhen you call Check status (poll until complete). -
data.store_id(integer) -
data.review_site_id(integer|null) -
data.publisher(string) — resolved publisher slug. -
data.year(integer) -
data.month(integer|null) -
data.theme(string) -
data.status(string) — typicallyqueued(orpending/existing status on dedup). -
data.task_id(string|null) — RD task id. -
data.competitors(array) — normalised competitor blocks. -
data.competitor_count(integer) -
data.review_count(integer|null) -
data.report_data(object|null) — null until the webhook lands. -
data.failure_reason(string|null) -
data.failure_details(mixed|null) -
data.completed_at(string|null, ISO 8601) -
data.remaining(integer|null) — quota left after this submit (merged viaextra). -
data.overage(boolean) — whether this call was billed as overage.
202 Queued · 202
202{
"data": {
"competitive_report_id": 1001,
"store_id": 42,
"review_site_id": 7,
"publisher": "google",
"year": 2026,
"month": 5,
"theme": "home-services",
"status": "queued",
"task_id": "9a893013-9aa8-4b8f-b3ff-6fd72f00a6ce",
"competitor_count": 1,
"remaining": 4,
"overage": false
}
}403 Quota exceeded / feature disabled · 403
403{
"status": "error",
"message": "Competitive analysis quota exceeded for this store.",
"errors": []
}422 Validation failed · 422
422{
"message": "The given data was invalid.",
"errors": {
"competitors": [
"At least one competitor is required."
]
}
}Errors
| Status | Meaning |
|---|---|
401 | The bearer token is missing, expired or invalid |
403 | Quota exceeded / feature disabled |
422 | Validation failed |
500 | Unexpected server error |
Example request
curl --request POST \
--url "https://production-api.shoutaboutus.com/api/v1/account/competitive-report" \
--header 'Authorization: Bearer <bearer-token>' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '{"store_id": 42, "review_site_id": 7, "year": 2026, "month": 5, "theme": "home-services", "competitors": [{"name": "Pacific Breeze Heating & Air", "profile_key": "https://www.google.com/maps/place/pacific-breeze", "phone": "+15551234567", "address": {"street": "123 Main St", "city": "San Diego", "state": "CA", "zip": "92101", "country": "US"}}], "force": false}'Updated 11 days ago
