Download OpenAPI specification:
Local Dominator
Heatmap scan endpoints — create one-off grid scans, read their results, and analyze them.
grid_size is the number of pins across the diameter (circle) or side (square), while distance is the distance in meters between each pin.
For example, grid_size: 10 and distance: 100 gives a ~900m diameter.
resource_category and serp_device interact: googleMaps supports serp_device desktop, mobile, or both (one scan_uuid; both = desktop + mobile heatmaps per keyword). googleLocalFinder always runs on desktop — mobile and both do not add a mobile leg (one heatmap per keyword).
Lists completed one-time scans for the API key owner (team scope). Includes legacy Express heatmapRecords and Nest-backed one-time scans (same set the LD Grid Scans One Time tab merges). Nest rows use the Nest scan UUID as scan_uuid / heatmap_record_id.
| text | string Filter scans by text(name, keywords, labels) |
| per_page | integer Default: 10 Example: per_page=10 |
| page | integer Default: 0 Example: page=0 |
| start_date | string <date> Example: start_date=2024-01-01 Retrieve scans on or after this date (YYYY-MM-DD). When both start_date and end_date are omitted, the API defaults start_date to 90 days ago (UTC) so list/count can prune monthly heatmaprecords partitions. Pass an older start_date explicitly to include older history. |
| end_date | string <date> Example: end_date=2024-08-31 Retrieve scans that occurred on or before the specified end date (in YYYY-MM-DD format). |
| labels | Array of strings An array of labels associated with the scan |
{- "totalcount": 10,
- "scans": [
- {
- "keyword": "plumber hollywood fl",
- "name": "Ocean Plumbing and Air",
- "vicinity": "3220 Stirling Rd, Hollywood, FL 33021",
- "place_id": "ChIJ03jJ2Omr2YgRNkx36YLd3q4",
- "data_id": "0x88d9abe9d8c978d3:0xaededd82e9774c36",
- "grid_size": 6,
- "locations_distance": 1609.344,
- "shape": "circle",
- "creation_timestamp": "2026-04-10 14:08:50.736785+00",
- "center_lat": 26.0481,
- "center_lng": -80.1819,
- "average_rank": 5.5,
- "share_links": {
}, - "run_analysis_map": {
- "property1": {
- "status": "Completed",
}, - "property2": {
- "status": "Completed",
}
}, - "content": [
- [
- 5,
- 5,
- 5,
- 4,
- 4,
- 8,
- 5
], - [
- 5,
- 5,
- 5,
- 3,
- 1,
- 3,
- 2
], - [
- 5,
- 5,
- 2,
- 2,
- 2,
- 2,
- 2
], - [
- 4,
- 3,
- 2,
- 1,
- 1,
- 2,
- 4
], - [
- 3,
- 3,
- 2,
- 2,
- 2,
- 3,
- 2
], - [
- null,
- 3,
- 3,
- 2,
- 2,
- 6,
- 5
], - [
- null,
- null,
- null,
- null,
- 2,
- 2,
- 2
]
], - "labels": [
- "string"
], - "resource_category": "googleMaps",
- "scan_uuid": "xPH5x_SZcmcf8E7FROCNQ",
}
]
}Initiates a scan based on the specified parameters.
| latitude required | number <float> Latitude of the center point from where the scan will be performed. |
| longitude required | number <float> Longitude of the center point from where the scan will be performed. |
| shape required | string Enum: "circle" "square" Shape of the scan area, either circle or square. |
| distance required | integer <int32> Distance in meters between adjacent pins in the scan grid. This does not define the total diameter directly. |
| google_place_id required | string The PlaceID in Google. For example, |
| grid_size required | integer <int32> <= 21 Defines the number of points on each square side or diameter to perform the search from. Maximum value is 21. |
| search_terms required | Array of strings Array of strings representing the search terms to use during the scan. |
| webhook_url | string A url where we will POST the results back once the scan is completed. |
| preschedule_analysis | boolean Default: false When set to true, automatically schedules analysis when the scan is completed. |
| resource_category | string Default: "googleMaps" Enum: "googleMaps" "googleLocalFinder" Search surface for the scan. googleMaps — full Maps grid; supports all serp_device values ( |
| serp_device | string Default: "desktop" Enum: "desktop" "mobile" "both" SERP device for provider requests. On googleMaps: |
{- "latitude": 40.712776,
- "longitude": -74.005974,
- "shape": "circle",
- "distance": 1000,
- "google_place_id": "ChIJgUbEo8cfqokR5lP9_Wh_DaM",
- "grid_size": 13,
- "search_terms": [
- "coffee shop",
- "restaurant"
], - "preschedule_analysis": true,
- "resource_category": "googleLocalFinder",
- "serp_device": "desktop"
}{- "message": "Scan creation started.",
- "scan_uuid": "xPH5x_SZcmcf8E7FROCNQ"
}Returns the details of the scan specified by scan_id.
| scan_id required | string [ 21 .. 36 ] characters Scan id from POST /v1/scans (21-char nanoid) or Nest/website one-time scan UUID (36-char) as returned by GET /v1/scans. |
[- {
- "keyword": "plumber hollywood fl",
- "name": "Ocean Plumbing and Air",
- "vicinity": "3220 Stirling Rd, Hollywood, FL 33021",
- "place_id": "ChIJ03jJ2Omr2YgRNkx36YLd3q4",
- "data_id": "0x88d9abe9d8c978d3:0xaededd82e9774c36",
- "grid_size": 6,
- "locations_distance": 1609.344,
- "shape": "circle",
- "creation_timestamp": "2026-04-10 14:08:50.736785+00",
- "center_lat": 26.0481,
- "center_lng": -80.1819,
- "average_rank": 5.5,
- "share_links": {
}, - "run_analysis_map": {
- "property1": {
- "status": "Completed",
}, - "property2": {
- "status": "Completed",
}
}, - "content": [
- [
- 5,
- 5,
- 5,
- 4,
- 4,
- 8,
- 5
], - [
- 5,
- 5,
- 5,
- 3,
- 1,
- 3,
- 2
], - [
- 5,
- 5,
- 2,
- 2,
- 2,
- 2,
- 2
], - [
- 4,
- 3,
- 2,
- 1,
- 1,
- 2,
- 4
], - [
- 3,
- 3,
- 2,
- 2,
- 2,
- 3,
- 2
], - [
- null,
- 3,
- 3,
- 2,
- 2,
- 6,
- 5
], - [
- null,
- null,
- null,
- null,
- 2,
- 2,
- 2
]
], - "labels": [
- "string"
], - "resource_category": "googleMaps",
- "scan_uuid": "xPH5x_SZcmcf8E7FROCNQ",
}
]The details of each business in every point in the scan.
| scan_id required | string [ 21 .. 36 ] characters Scan id from POST /v1/scans (21-char nanoid) or Nest/website one-time scan UUID (36-char) as returned by GET /v1/scans. |
[- {
- "scanId": 1,
- "compressed_grid": [
- [
- [
- 0
]
]
], - "detailsArray": [
- {
- "placeId": "ChIJ03jJ2Omr2YgRNkx36YLd3q4",
- "dataId": "0x88d9abe9d8c978d3:0xaededd82e9774c36",
- "name": "Ocean Plumbing and Air",
- "vicinity": "3220 Stirling Rd, Hollywood, FL 33021",
- "location": {
- "latitude": 26.0481,
- "longitude": -80.1819
}, - "rating": 2.5,
- "ratingCount": 620,
- "primaryCategory": "Plumber",
- "secondaryCategories": [
- "string"
],
}
], - "ids": [
- [
- "string"
]
]
}
]Initiates or retrieves post-run insights for a scan: ranking context, competitors, and optimization-oriented notes derived from the scan grid.
| scan_id required | string Prefer |
{- "scan_uuid": "xPH5x_SZcmcf8E7FROCNQ",
- "scanId": "xPH5x_SZcmcf8E7FROCNQ",
- "message": "Group was successfully analyzed",
- "analysis": {
- "property1": {
- "analysis_content": {
- "summary": {
- "overall_status": "string",
- "top_competitors": [
- {
- "name": "string",
- "average_rank": 0,
- "reviews_count": 0,
- "average_rating": 0,
- "total_appearances": 0,
- "performance_summary": "string",
- "appearances_position_1": 0,
- "appearances_position_2": 0,
- "appearances_position_3": 0
}
], - "competitor_analysis": "string",
- "competition_overview": "string",
- "top_competitor_action_items": [
- "string"
]
}, - "basic_info": {
- "grid_size": 0,
- "scan_date": "string",
- "grid_shape": "string",
- "business_name": "string",
- "primary_keyword": "string",
- "locations_amount": 0,
- "average_position_rank": 0
}, - "action_plan": [
- {
- "actions": [
- "string"
], - "category": "string"
}
], - "seo_insights": {
- "keyword_uniformity": "string",
- "geographic_patterns": {
- "weak_zones": [
- {
- "details": "string",
- "direction": "string"
}
], - "strong_zones": [
- {
- "details": "string",
- "direction": "string"
}
]
}, - "directional_patterns": [
- {
- "direction": "string",
- "performance": "string"
}
], - "geographic_performance_summary": "string"
}, - "opportunity_statement": "string"
},
}, - "property2": {
- "analysis_content": {
- "summary": {
- "overall_status": "string",
- "top_competitors": [
- {
- "name": "string",
- "average_rank": 0,
- "reviews_count": 0,
- "average_rating": 0,
- "total_appearances": 0,
- "performance_summary": "string",
- "appearances_position_1": 0,
- "appearances_position_2": 0,
- "appearances_position_3": 0
}
], - "competitor_analysis": "string",
- "competition_overview": "string",
- "top_competitor_action_items": [
- "string"
]
}, - "basic_info": {
- "grid_size": 0,
- "scan_date": "string",
- "grid_shape": "string",
- "business_name": "string",
- "primary_keyword": "string",
- "locations_amount": 0,
- "average_position_rank": 0
}, - "action_plan": [
- {
- "actions": [
- "string"
], - "category": "string"
}
], - "seo_insights": {
- "keyword_uniformity": "string",
- "geographic_patterns": {
- "weak_zones": [
- {
- "details": "string",
- "direction": "string"
}
], - "strong_zones": [
- {
- "details": "string",
- "direction": "string"
}
]
}, - "directional_patterns": [
- {
- "direction": "string",
- "performance": "string"
}
], - "geographic_performance_summary": "string"
}, - "opportunity_statement": "string"
},
}
}
}Scheduled scans — recurring campaigns: list, create, update, or delete a campaign; trigger a run; or bulk-delete runs on a campaign by creation date.
A campaign holds cron scheduling, grid geometry, keywords, and notification settings. Each execution is a run (see Scheduled scan campaign runs).
Returns scheduled scans for the API key owner, with optional filters and pagination. Filter by schedule category, resource category, grid shape, campaign creation date range, labels, and free-text search. start_date / end_date are aliases for date_from / date_to (filter on the calendar date the campaign was created).
| text | string Filter by place name, vicinity, or search term (substring match). |
string or Array of strings Conjunction filter: the campaign must have every label. Send one comma-separated | |
| resource_category | string Enum: "googleMaps" "googleLocalFinder" Filter by Maps vs Local Finder (scheduled scan setting). |
| shape | string Enum: "circle" "square" Filter by grid shape. |
| schedule | string Enum: "daily" "weekly" "biweekly" "monthly" Filter by high-level schedule category (daily, weekly, biweekly, monthly). |
| date_from | string <date> Lower bound on campaign creation date (YYYY-MM-DD), inclusive. |
| date_to | string <date> Upper bound on campaign creation date (YYYY-MM-DD), inclusive. |
| start_date | string <date> Same as date_from. |
| end_date | string <date> Same as date_to. |
| order_property | string Enum: "date" "place_name" "placeName" "search_term" "searchTerm" "average_rank" "averageRank" "grid_size" "size" "last_run_date" "lastScan" "lastRunDate" "creation_date" "creationDate" "next_run" "nextRunTimestamp" Sortable field; maps to the same |
| ascending | string Enum: "true" "false" Sort direction; default false (descending). |
| per_page | string^[0-9]+$ Page size as digits only (default 50 if omitted; parsed as a positive integer). |
| page | string^[0-9]+$ Zero-based page index as digits only (default 0 if omitted; parsed as a non-negative integer). |
{- "scheduled_scans": [
- {
- "id": "550e8400-e29b-41d4-a716-446655440000",
- "search_term": "emergency plumber hollywood fl",
- "place_name": "Ocean Plumbing and Air",
- "resource_category": "googleMaps",
- "serp_device": "desktop",
- "shape": "circle",
- "grid_size": 0,
- "distance": 0,
- "vicinity": "string",
- "center_lat": 0,
- "center_lng": 0,
- "preschedule_analysis": true,
- "in_progress": true,
- "scheduling": "0 9 * * 1",
- "scheduling_type": "weekly",
- "last_run_date": "2026-04-10",
- "next_run_at": "2019-08-24T14:15:22Z",
- "labels": [
- "string"
], - "creation_date": "2019-08-24",
- "creation_time": "string",
- "share_links": {
}, - "last_run_analysis_map": {
- "property1": {
- "status": "Completed",
}, - "property2": {
- "status": "Completed",
}
}
}
], - "total": 12,
- "page": 0,
- "per_page": 50
}Creates a scheduled scan. google_place_id identifies the place. scheduling is a five-field cron (minute, hour, day of month, month, day of week); invalid values return 400. The response includes the new id (UUID). 201 means the resource is available immediately in GET /v1/scheduled-scans/{scheduled_scan_id}; 202 means the request was accepted and the same response body shape applies, but the campaign may not be readable yet. For 202, use either GET /v1/scheduled-scans/{scheduled_scan_id} checks with the returned id, or webhook_url delivery if configured. Each execution of the campaign is a scan run.
| google_place_id required | string Google Place ID for the business location. |
| latitude required | number Grid center latitude. |
| longitude required | number Grid center longitude. |
| shape required | string Enum: "circle" "square" |
| distance required | integer >= 1 Distance in meters between adjacent pins. |
| grid_size required | integer [ 1 .. 21 ] |
required | Array of strings or objects non-empty unique Keywords for this campaign. Most clients send a list of plain strings (each keyword is active). You may instead send objects with term and optional status (active / inactive); when status is omitted on an object, it defaults to active. Duplicate terms (case-insensitive) are not allowed. Strings and objects may be mixed in one array. |
| scheduling required | string Five-field cron expression (minute, hour, day of month, month, day of week) for recurring runs. Must be accepted by the API; invalid values return 400. |
| notify | boolean Email notifications when runs complete. |
| run_now | boolean If true, triggers an immediate manual run after creation. |
| labels | Array of strings |
| skippable | boolean Used for biweekly-style recurrence when combined with your scheduling expression. |
| preschedule_analysis | boolean When true, analysis is scheduled when runs complete. When omitted, the server uses the default for new tasks (typically true); send false to disable. |
| alias | string Optional display name for the scheduled scan. |
| resource_category | string Enum: "googleMaps" "googleLocalFinder" Optional. Which search surface recurring runs use. |
| webhook_url | string <uri> <= 2048 characters ^https?:// Optional. http or https absolute URL only (request validation: |
| serp_device | string Enum: "desktop" "mobile" SERP device for provider requests. Defaults to desktop when omitted. |
{- "google_place_id": "ChIJ03jJ2Omr2YgRNkx36YLd3q4",
- "latitude": 26.0481,
- "longitude": -80.1819,
- "shape": "circle",
- "distance": 1,
- "grid_size": 1,
- "search_terms": [
- "plumber hollywood fl",
- {
- "term": "emergency plumber hollywood fl",
- "status": "inactive"
}
], - "scheduling": "0 9 * * 1",
- "notify": true,
- "run_now": true,
- "labels": [
- "string"
], - "skippable": true,
- "preschedule_analysis": true,
- "alias": "string",
- "resource_category": "googleMaps",
- "serp_device": "desktop"
}{- "message": "Scheduled scan created successfully.",
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08"
}Starts a new scan run immediately (manual execution). Processing is asynchronous; 200 means the run was accepted.
| scheduled_scan_id required | string <uuid> Opaque scheduled scan identifier (same id as in list/detail responses). |
{- "message": "Run started."
}Returns scheduled scan metadata and the list of scan runs (run_uuid per run). Optional date_range filters runs (default 3M). Campaign share_links reflect the latest completed (notified) run in the filtered window that has heatmap rows with shareable static URLs; run-level share_links use the same per-run resolution (no campaign_link on run objects).
| scheduled_scan_id required | string <uuid> Opaque scheduled scan identifier (same id as in GET /v1/scheduled-scans list items). |
| date_range | string Default: "3M" Enum: "MAX" "1Y" "6M" "3M" "1M" Limits which scan runs appear in runs (by run creation time relative to the campaign’s latest run), using the same window tokens as the dashboard task-runs |
{- "id": "550e8400-e29b-41d4-a716-446655440000",
- "search_term": "emergency plumber hollywood fl",
- "place_name": "Ocean Plumbing and Air",
- "resource_category": "googleMaps",
- "serp_device": "desktop",
- "shape": "circle",
- "grid_size": 0,
- "distance": 0,
- "vicinity": "string",
- "center_lat": 0,
- "center_lng": 0,
- "preschedule_analysis": true,
- "in_progress": true,
- "scheduling": "0 9 * * 1",
- "scheduling_type": "weekly",
- "last_run_date": "2026-04-10",
- "next_run_at": "2019-08-24T14:15:22Z",
- "labels": [
- "string"
], - "creation_date": "2019-08-24",
- "creation_time": "string",
- "share_links": {
}, - "last_run_analysis_map": {
- "property1": {
- "status": "Completed",
}, - "property2": {
- "status": "Completed",
}
}, - "runs": [
- {
- "run_uuid": "550e8400-e29b-41d4-a716-446655440000",
- "created_at": "2026-04-10T08:00:00.000Z",
- "average_rank": 4.2,
- "record_count": 9,
- "manual": false,
- "share_links": {
}, - "run_analysis_map": {
- "property1": {
- "status": "Completed",
}, - "property2": {
- "status": "Completed",
}
}
}
]
}Updates this scheduled scan (recurrence, grid, keywords, notifications, analysis option). Omitted fields stay unchanged. Same validation rules as create where applicable. resource_category and is_placed_manually cannot be changed — including either field returns 400. Deleted scheduled scans are not found (404).
| scheduled_scan_id required | string <uuid> Opaque scheduled scan identifier (same id as in GET /v1/scheduled-scans list items). |
Array of strings or objects (ScheduledScanPatchSearchTerm) non-empty Same rules as search_terms on create: array of strings and/or | |
| scheduling | string Five-field cron expression; must be accepted by the API (400 if invalid). |
| skippable | boolean When |
| grid_size | integer [ 1 .. 21 ] |
| distance | integer >= 1 |
| shape | string Enum: "circle" "square" |
| latitude | number |
| longitude | number |
| notify | boolean |
| preschedule_analysis | boolean |
| webhook_url | string or null <uri> null or "" clears the webhook. A non-empty string must be http or https and max 2048 characters (same as |
| serp_device | string Enum: "desktop" "mobile" |
| selected_day_of_week | integer [ 0 .. 6 ] Day of week (0 = Sunday … 6 = Saturday) for biweekly next_run_at anchoring when skippable is |
| selected_hour | integer [ 0 .. 23 ] Local hour (scheduled scan timezone) for biweekly next_run_at when skippable is |
| selected_minute | integer [ 0 .. 59 ] Local minute for biweekly next_run_at when skippable is |
{- "search_terms": [
- "plumber hollywood fl",
- "emergency plumber hollywood fl"
], - "scheduling": "0 9 * * 1",
- "notify": true
}{- "message": "Scheduled scan updated successfully.",
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08"
}Deletes this scheduled scan and its associated data. It no longer appears in GET /v1/scheduled-scans or GET …/scheduled-scans/{scheduled_scan_id}. Repeating DELETE returns 404.
| scheduled_scan_id required | string <uuid> Opaque scheduled scan identifier (same id as in GET /v1/scheduled-scans list items). |
{- "message": "Scheduled scan deleted successfully."
}Deletes scan runs for this scheduled scan, selected by when each run started (UTC). creation_date is a filter: only runs whose created_at falls on that calendar day (UTC YYYY-MM-DD) are removed. The calendar day is not a resource—only runs are deleted.
| scheduled_scan_id required | string <uuid> Opaque scheduled scan identifier. |
| creation_date required | string <date> Filter: UTC calendar day (YYYY-MM-DD). Matches the date part of each scan run’s created_at. |
{- "message": "Scheduled scan runs deleted for the selected run-creation date.",
- "creation_date": "2025-04-14",
- "runs_deleted": 2
}Scheduled scans — runs: each run is one execution of a campaign. Fetch scan rows or compressed grid results for a run, or delete a single run.
Run identifiers (run_uuid) come from the campaign detail response and run listings.
Returns compressed grid results for this scan run (all keywords; camelCase keys and compressed_grid).
| run_id required | string <uuid> Scan run identifier ( |
[- {
- "scanId": 1,
- "compressed_grid": [
- [
- [
- 0
]
]
], - "detailsArray": [
- {
- "placeId": "ChIJ03jJ2Omr2YgRNkx36YLd3q4",
- "dataId": "0x88d9abe9d8c978d3:0xaededd82e9774c36",
- "name": "Ocean Plumbing and Air",
- "vicinity": "3220 Stirling Rd, Hollywood, FL 33021",
- "location": {
- "latitude": 26.0481,
- "longitude": -80.1819
}, - "rating": 2.5,
- "ratingCount": 620,
- "primaryCategory": "Plumber",
- "secondaryCategories": [
- "string"
],
}
], - "ids": [
- [
- "string"
]
]
}
]Returns one scan row per search term for this scan run (ranks, links, map fields). Each object is a single-keyword scan inside the run. Each row includes scheduled_scan_id, run_uuid, and run_analysis_map (per-keyword SEO report status, same key shape as run objects under GET /v1/scheduled-scans/{id}).
| run_id required | string <uuid> Scan run identifier ( |
[- {
- "keyword": "plumber hollywood fl",
- "name": "Ocean Plumbing and Air",
- "vicinity": "3220 Stirling Rd, Hollywood, FL 33021",
- "place_id": "ChIJ03jJ2Omr2YgRNkx36YLd3q4",
- "data_id": "0x88d9abe9d8c978d3:0xaededd82e9774c36",
- "grid_size": 6,
- "locations_distance": 1609.344,
- "shape": "circle",
- "creation_timestamp": "2026-04-10 14:08:50.736785+00",
- "center_lat": 26.0481,
- "center_lng": -80.1819,
- "average_rank": 5.5,
- "share_links": {
}, - "run_analysis_map": {
- "property1": {
- "status": "Completed",
}, - "property2": {
- "status": "Completed",
}
}, - "content": [
- [
- 5,
- 5,
- 5,
- 4,
- 4,
- 8,
- 5
], - [
- 5,
- 5,
- 5,
- 3,
- 1,
- 3,
- 2
], - [
- 5,
- 5,
- 2,
- 2,
- 2,
- 2,
- 2
], - [
- 4,
- 3,
- 2,
- 1,
- 1,
- 2,
- 4
], - [
- 3,
- 3,
- 2,
- 2,
- 2,
- 3,
- 2
], - [
- null,
- 3,
- 3,
- 2,
- 2,
- 6,
- 5
], - [
- null,
- null,
- null,
- null,
- 2,
- 2,
- 2
]
], - "labels": [
- "string"
], - "resource_category": "googleMaps",
- "scan_uuid": "xPH5x_SZcmcf8E7FROCNQ",
- "scheduled_scan_id": "550e8400-e29b-41d4-a716-446655440000",
- "run_uuid": "82e97fd8-71b6-4da3-b7dd-fa235ab9ec89"
}
]Deletes this scan run (soft-delete). Does not delete the scheduled scan.
| run_id required | string <uuid> Scan run identifier ( |
{- "message": "Scheduled scan run deleted successfully."
}Create a business-listings prospect scan across directories and, if the results warrant it, order a fix that resubmits correct data across 50 or 100 directories.
Creates a business record and kicks off an asynchronous listings scan across directories. Free — no credits are charged.
The scan typically takes 10–30 seconds. Poll
GET /v1/listings/prospect/{search_id} until status is
completed.
Rate-limited to 10 requests per hour per API key.
| business_name required | string |
| address required | string |
| city | string |
| state | string |
| zip | string |
| phone | string |
| website | string <uri> |
| country | string Default: "US" |
{- "business_name": "Acme Inc",
- "address": "123 Main St",
- "city": "New York",
- "state": "NY",
- "zip": "10001",
- "phone": "+1234567890",
- "country": "US"
}{- "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
- "order_number": "ABC123",
- "status": "pending"
}| page | integer Default: 1 |
| limit | integer <= 100 Default: 20 |
{- "data": [
- {
- "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
- "business_name": "string",
- "address": "string",
- "status": "pending",
- "total_listings": 42,
- "correct_listings": 12,
- "order_number": "ABC123",
- "created_at": "2019-08-24T14:15:22Z",
- "fix_requested": true,
- "fix_plan": "50",
- "payment_status": "unpaid",
- "campaign_progress": "ordered"
}
], - "page": 1,
- "limit": 20,
- "total": 47
}| search_id required | string Prospect id returned from |
{- "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
- "business_name": "string",
- "address": "string",
- "status": "pending",
- "total_listings": 42,
- "correct_listings": 12,
- "order_number": "ABC123",
- "created_at": "2019-08-24T14:15:22Z",
- "fix_requested": true,
- "fix_plan": "50",
- "payment_status": "unpaid",
- "campaign_progress": "ordered",
- "city": "string",
- "state": "string",
- "zip": "string",
- "phone": "string",
- "website": "string",
- "country": "string",
- "results": [
- {
- "directory_name": "GOOGLE",
- "listing_url": "string",
- "name_status": "correct",
- "address_status": "correct",
- "phone_status": "correct",
- "website_status": "correct",
- "sync_status": "found"
}
]
}Charges credits from the caller's wallet and submits a fix order for the given prospect.
"50" costs 55,650 credits and fixes up to 50 directories."100" costs 96,500 credits and fixes up to 100 directories.If the upstream submission fails, the charge is automatically
refunded via a compensating manualAdd transaction and the
upstream error is returned.
Idempotent: a second call for the same search_id returns
409 and does not charge.
| search_id required | string |
| plan required | string Enum: "50" "100" |
{- "search_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
- "plan": "50"
}{- "order_id": "string",
- "order_number": "string",
- "status": "ordered",
- "plan": "50",
- "credits_charged": 55650
}{- "search_id": "string",
- "order_number": "string",
- "campaign_progress": "ordered",
- "plan": "50",
- "progress": {
- "total": 0,
- "live": 0,
- "existing": 0,
- "pending": 0,
- "to_do": 0
}
}Partner-facing AI Tracker endpoints available through the Public API
gateway. Endpoints are authenticated by ld_... API keys and include
server-side credit charging where applicable.
{- "external_user_id": "string",
- "display_name": "string",
- "role": "string",
- "is_team": true,
- "created_at": "string",
- "total_campaigns": 0,
- "active_campaigns": 0,
- "total_scans": 0,
- "total_credits_consumed": 0
}Generates prompt previews for the given keywords. Does not create a campaign or charge credits. Same rate limits as campaign commit.
| name required | string |
| domain required | string |
| location | string |
| engines | Array of strings Items Enum: "chatgpt" "gemini" "google_ai_mode" "perplexity" "claude" "grok" |
| keywords required | Array of strings non-empty |
{- "name": "string",
- "domain": "string",
- "location": "string",
- "engines": [
- "chatgpt"
], - "keywords": [
- "string"
]
}{ }Two-step flow: commit after preview. On the Public API side, credits
are reserved up to the per-run maximum, then adjusted to match
actual prompts_count returned by the AI Tracker backend.
| name required | string |
| domain required | string |
| location | string |
| engines | Array of strings Items Enum: "chatgpt" "gemini" "google_ai_mode" "perplexity" "claude" "grok" |
required | Array of objects [ 1 .. 50 ] items |
{- "name": "string",
- "domain": "string",
- "location": "string",
- "engines": [
- "chatgpt"
], - "prompts": [
- {
- "keyword": "string",
- "prompt": "string"
}
]
}{- "id": "string",
- "name": "string",
- "domain": "string",
- "status": "string",
- "prompts_count": 0,
- "billable_prompts": 0,
- "credits_per_prompt": 100,
- "credits_charged": 0
}Paginated results for scans that produced an answer, plus jobs
for the current run (not paginated). A missing result is not a
missed citation when the matching job ended in failed or
cancelled: treat that as unknown.
Note the spelling mismatch: engine in results and jobs comes
back hyphenated (google-ai-mode), while the canonical write value
is google_ai_mode. Sending the hyphenated form back on write is
accepted and folded onto the canonical one.
| id required | string |
| limit | integer <= 500 |
| offset | integer |
{- "total": 0,
- "limit": 0,
- "offset": 0,
- "results": [
- { }
], - "jobs": [
- {
- "id": "string",
- "engine": "string",
- "prompt_id": "string",
- "keyword_id": "string",
- "status": "pending",
- "error_category": "configuration",
- "error_message": "Engine is not available for this scan",
- "retry_count": 0,
- "created_at": "string",
- "started_at": "string",
- "completed_at": "string"
}
]
}Returns the committed keyword and prompt text for an existing campaign.
Use with GET .../results to resolve prompt_id and keyword_id to strings
(fetch once and cache locally). Does not bill credits.
| id required | string |
{- "campaign_id": "string",
- "keywords": [
- {
- "id": "string",
- "keyword": "string",
- "intent": "string",
- "priority": true
}
], - "prompts": [
- {
- "id": "string",
- "prompt_text": "string",
- "intent": "string",
- "keyword_id": "string",
- "keyword": "string"
}
]
}| id required | string |
| keywords required | Array of strings non-empty |
{- "keywords": [
- "string"
]
}{ }| id required | string |
required | Array of objects [ 1 .. 50 ] items |
{- "prompts": [
- {
- "keyword": "string",
- "prompt": "string"
}
]
}{- "billable_prompts": 0,
- "credits_per_prompt": 0,
- "credits_charged": 0
}| id required | string |
{- "triggered": true,
- "campaign_id": "string",
- "scan_jobs_count": 0,
- "status": "string",
- "billable_prompts": 0,
- "credits_per_prompt": 100,
- "credits_charged": 0
}B2B gateway to Review Velocity (HMAC upstream). Authenticated with
ld_... API keys; external_user_id is the team owner id (prefixed
with qa- on QA). Proxies upstream /v1/* paths.
Billing (LD wallet): scrape (1 credit/review), analyze (100 credits/run),
reply (1 credit/reply). Billed endpoints return a partner-facing billing
object with hold → finalize lifecycle. Upstream billing blocks are stripped.
Rate limits (LD): write methods (POST, PATCH, DELETE) are limited
to 30/hour and 50/day per API key (in addition to upstream quotas).
Async analyze: POST …/velocity/analyze and POST …/reputation/analyze
return 202; poll the corresponding GET every ~5 seconds until 200.
If the web dyno restarts mid-job, poll recovery re-checks upstream after
poll_after_ms and may re-kick a stale job. billing is returned only
on the first GET 200 after analyze — persist it from that response.
Velocity ai_pending: first GET …/velocity may return 200 with
analysis.result.ai_pending: true (score ready, full AI narrative still
generating upstream). Poll the same GET every 30–60s until ai_pending
is false; no extra credits are charged.
| campaign_id required | string <uuid> Existing campaign UUID; anchor is its main business |
{- "campaign_id": "78614b6c-fe7c-41e2-8e25-c9b3a3c91904"
}Billed synchronously (1 credit). Returns billing.status=completed on success.
| id required | string |
| tone | string |
| language | string |
| max_chars | integer |
{- "tone": "string",
- "language": "string",
- "max_chars": 0
}{- "billing": {
- "operation": "scrape",
- "unit": "review",
- "operation_id": "cb4ede3c-a5d1-45e3-a9d2-fe83accbce52",
- "status": "held",
- "estimated_units": 0,
- "estimated_credits": 0,
- "held_credits": 0,
- "actual_units": 0,
- "charged_credits": 0,
- "released_credits": 0
}
}| id required | string |
| limit | string |
| offset | string |
{- "billing": {
- "operation": "scrape",
- "unit": "review",
- "operation_id": "cb4ede3c-a5d1-45e3-a9d2-fe83accbce52",
- "status": "held",
- "estimated_units": 0,
- "estimated_credits": 0,
- "held_credits": 0,
- "actual_units": 0,
- "charged_credits": 0,
- "released_credits": 0
}
}| id required | string |
{- "status": "pending",
- "analysis_type": "velocity",
- "campaign_id": "string",
- "started_at": "2019-08-24T14:15:22Z",
- "poll": "string",
- "poll_after_ms": 5000,
- "billing": {
- "operation": "scrape",
- "unit": "review",
- "operation_id": "cb4ede3c-a5d1-45e3-a9d2-fe83accbce52",
- "status": "held",
- "estimated_units": 0,
- "estimated_credits": 0,
- "held_credits": 0,
- "actual_units": 0,
- "charged_credits": 0,
- "released_credits": 0
}
}| id required | string |
{- "status": "pending",
- "analysis_type": "velocity",
- "campaign_id": "string",
- "started_at": "2019-08-24T14:15:22Z",
- "poll": "string",
- "poll_after_ms": 5000,
- "billing": {
- "operation": "scrape",
- "unit": "review",
- "operation_id": "cb4ede3c-a5d1-45e3-a9d2-fe83accbce52",
- "status": "held",
- "estimated_units": 0,
- "estimated_credits": 0,
- "held_credits": 0,
- "actual_units": 0,
- "charged_credits": 0,
- "released_credits": 0
}
}Credits are held in the LD wallet before the upstream scrape preview call.
Optional body field estimated_total_reviews improves the initial hold; the hold is
reconciled to the upstream estimate in the response.
| id required | string |
| estimated_total_reviews | integer Optional hint for the pre-upstream credit hold. |
{- "estimated_total_reviews": 0
}{- "preview_token": "string",
- "estimated_total_reviews": 0,
- "billing": {
- "operation": "scrape",
- "unit": "review",
- "operation_id": "cb4ede3c-a5d1-45e3-a9d2-fe83accbce52",
- "status": "held",
- "estimated_units": 0,
- "estimated_credits": 0,
- "held_credits": 0,
- "actual_units": 0,
- "charged_credits": 0,
- "released_credits": 0
}
}| id required | string |
| preview_token required | string |
{- "preview_token": "string"
}{- "billing": {
- "operation": "scrape",
- "unit": "review",
- "operation_id": "cb4ede3c-a5d1-45e3-a9d2-fe83accbce52",
- "status": "held",
- "estimated_units": 0,
- "estimated_credits": 0,
- "held_credits": 0,
- "actual_units": 0,
- "charged_credits": 0,
- "released_credits": 0
}
}Accepts immediately (202), runs LLM analysis in the background on the
web dyno, and holds 100 credits. Poll
GET /v1/review-velocity/campaigns/{id}/velocity every ~5 seconds until 200.
| id required | string |
{- "status": "pending",
- "analysis_type": "velocity",
- "campaign_id": "string",
- "started_at": "2019-08-24T14:15:22Z",
- "poll": "string",
- "poll_after_ms": 5000,
- "billing": {
- "operation": "scrape",
- "unit": "review",
- "operation_id": "cb4ede3c-a5d1-45e3-a9d2-fe83accbce52",
- "status": "held",
- "estimated_units": 0,
- "estimated_credits": 0,
- "held_credits": 0,
- "actual_units": 0,
- "charged_credits": 0,
- "released_credits": 0
}
}Accepts immediately (202), runs LLM analysis in the background on the
web dyno, and holds 100 credits. Poll
GET /v1/review-velocity/campaigns/{id}/reputation every ~5 seconds until 200.
| id required | string |
{- "status": "pending",
- "analysis_type": "velocity",
- "campaign_id": "string",
- "started_at": "2019-08-24T14:15:22Z",
- "poll": "string",
- "poll_after_ms": 5000,
- "billing": {
- "operation": "scrape",
- "unit": "review",
- "operation_id": "cb4ede3c-a5d1-45e3-a9d2-fe83accbce52",
- "status": "held",
- "estimated_units": 0,
- "estimated_credits": 0,
- "held_credits": 0,
- "actual_units": 0,
- "charged_credits": 0,
- "released_credits": 0
}
}B2B gateway to Site Audit backend-api (HMAC upstream). Authenticated with
ld_... API keys; external_user_id is the team owner id (prefixed with
qa- on QA). Proxies upstream REST paths (/account, /sites, /audits, …).
Billing: LD wallet hold on POST …/audits and POST …/audits/:id/rerun;
finalize on GET …/audits/:id (or child routes …/issues, …/pages, etc.)
when the audit reaches complete or failed. Site crawl: reserve
max_pages × credits_per_page (4 credits/page since 2026-03-10 UTC dual-rate
cutoff; 3/page before), charge page_count × rate at finalize. Landing page:
flat 100 credits. Failed audit with zero pages releases the full hold.
Returns partner-facing billing object; upstream billing is never exposed.
Async audits: POST …/audits returns 201 with status: queued. Poll
GET …/audits/:id until status is complete or failed (or read child
routes — billing finalizes there too when the audit is terminal).
Rate limits (LD): write methods (POST, PATCH, DELETE) are limited
to 30/hour and 50/day per API key. Upstream also enforces 50/day and 500/month
on POST /sites, POST /audits, and POST /audits/:id/rerun.
| site_id required | string |
| mode | string Enum: "site_audit" "landing_page" |
| max_pages | integer |
| max_depth | integer |
| exclude_noindex | boolean |
| start_url | string |
| target_keyword | string |
| competitor_urls | Array of strings |
{- "site_id": "string",
- "mode": "site_audit",
- "max_pages": 0,
- "max_depth": 0,
- "exclude_noindex": true,
- "start_url": "string",
- "target_keyword": "string",
- "competitor_urls": [
- "string"
]
}{- "billing": {
- "operation": "site_audit_crawl",
- "billing_kind": "per_page",
- "operation_id": "cb4ede3c-a5d1-45e3-a9d2-fe83accbce52",
- "audit_id": "string",
- "status": "held",
- "estimated_units": 0,
- "held_credits": 0,
- "credits_per_page": 0,
- "flat_credits": 0,
- "actual_units": 0,
- "charged_credits": 0,
- "released_credits": 0
}
}| id required | string |
{- "billing": {
- "operation": "site_audit_crawl",
- "billing_kind": "per_page",
- "operation_id": "cb4ede3c-a5d1-45e3-a9d2-fe83accbce52",
- "audit_id": "string",
- "status": "held",
- "estimated_units": 0,
- "held_credits": 0,
- "credits_per_page": 0,
- "flat_credits": 0,
- "actual_units": 0,
- "charged_credits": 0,
- "released_credits": 0
}
}{- "billing": {
- "operation": "site_audit_crawl",
- "billing_kind": "per_page",
- "operation_id": "cb4ede3c-a5d1-45e3-a9d2-fe83accbce52",
- "audit_id": "string",
- "status": "held",
- "estimated_units": 0,
- "held_credits": 0,
- "credits_per_page": 0,
- "flat_credits": 0,
- "actual_units": 0,
- "charged_credits": 0,
- "released_credits": 0
}
}SERP campaign tracking via the Public API gateway. Create campaigns, add keywords, trigger rescans, and read SERP result snapshots. Credits are deducted from the team wallet by the backend (4 credits per keyword on single-device campaigns, 8 on dual-device). Export jobs can be created and polled for status; file download is not exposed on the Public API (use the product UI).
HTTP conventions: single-resource updates use PATCH; single-resource
deletes use DELETE. Multi-resource or bulk operations use POST with a
JSON body (for example POST /v1/serp/campaigns/bulk-delete).
| page | string |
| limit | string |
| search | string |
| status | string Enum: "active" "paused" "completed" "failed" |
string or Array of strings Filter by label name(s); pass once or repeat the query param | |
| domain | string |
| sort_by | string Enum: "business_name" "domain" "created_at" "updated_at" |
| sort_order | string Enum: "ASC" "DESC" |
Creates a campaign and consumes one campaign slot when applicable.
Add keywords with POST /v1/serp/campaigns/{id}/keywords, then
trigger a scan with POST /v1/serp/campaigns/{id}/rescan.
| name required | string |
| domain required | string |
| location_code required | integer |
| location_name | string |
| country_code | string |
| country_location_code | integer |
| language_code | string |
| search_engine | string |
| device | string Enum: "desktop" "mobile" "tablet" |
| devices | Array of strings Items Enum: "desktop" "mobile" |
| business_name | string |
| metadata | object |
{- "name": "string",
- "domain": "string",
- "location_code": 0,
- "location_name": "string",
- "country_code": "string",
- "country_location_code": 0,
- "language_code": "string",
- "search_engine": "string",
- "device": "desktop",
- "devices": [
- "desktop"
], - "business_name": "string",
- "metadata": { }
}| id required | string <uuid> |
| name | string |
| domain | string |
| location_code | integer |
| location_name | string |
| country_code | string |
| country_location_code | integer |
| language_code | string |
| search_engine | string |
| device | string Enum: "desktop" "mobile" "tablet" |
| devices | Array of strings Items Enum: "desktop" "mobile" |
| business_name | string |
| metadata | object |
| status | string Enum: "active" "paused" "completed" "failed" |
{- "name": "string",
- "domain": "string",
- "location_code": 0,
- "location_name": "string",
- "country_code": "string",
- "country_location_code": 0,
- "language_code": "string",
- "search_engine": "string",
- "device": "desktop",
- "devices": [
- "desktop"
], - "business_name": "string",
- "metadata": { },
- "status": "active"
}| id required | string <uuid> |
| page | string |
| limit | string |
| search | string |
| device | string Enum: "desktop" "mobile" |
| min_position | string |
| max_position | string |
| sort_by | string Enum: "keyword" "position" "previous_position" "best_position" "search_volume" "difficulty" "cpc" "ai_detected" "created_at" |
| sort_order | string Enum: "ASC" "DESC" |
| id required | string <uuid> |
required | Array of objects [ 1 .. 5000 ] items |
| devices | Array of strings Items Enum: "desktop" "mobile" |
{- "keywords": [
- {
- "keyword": "string"
}
], - "devices": [
- "desktop"
]
}| id required | string <uuid> |
| keyword_id required | string <uuid> |
| keyword | string |
| position | integer |
| url | string |
| search_volume | integer |
| difficulty | integer |
{- "keyword": "string",
- "position": 0,
- "url": "string",
- "search_volume": 0,
- "difficulty": 0
}Bulk operations use POST with a JSON body. For deleting a single
campaign, prefer DELETE /v1/serp/campaigns/{id}.
| campaign_ids required | Array of strings <uuid> [ items <uuid > ] |
{- "campaign_ids": [
- "497f6eca-6276-4993-bfeb-53cbbbba6f08"
]
}| id required | string <uuid> |
| keyword_ids required | Array of strings <uuid> [ items <uuid > ] |
| device required | string Enum: "desktop" "mobile" |
{- "keyword_ids": [
- "497f6eca-6276-4993-bfeb-53cbbbba6f08"
], - "device": "desktop"
}| id required | string <uuid> |
| keyword_ids required | Array of strings <uuid> [ items <uuid > ] |
{- "keyword_ids": [
- "497f6eca-6276-4993-bfeb-53cbbbba6f08"
]
}| id required | string <uuid> |
| keyword_ids required | Array of strings <uuid> [ items <uuid > ] |
{- "keyword_ids": [
- "497f6eca-6276-4993-bfeb-53cbbbba6f08"
]
}| cid | string |
| place_id | string |
| location_name | string |
| business_name | string |
| category | string |
| url | string |
| address | string |
{- "cid": "string",
- "place_id": "string",
- "location_name": "string",
- "business_name": "string",
- "category": "string",
- "url": "string",
- "address": "string"
}| keyword required | string |
| location_code | integer |
| location_name | string |
| language_code | string |
| limit | integer [ 1 .. 100 ] |
| min_search_volume | integer |
| max_search_volume | integer |
{- "keyword": "string",
- "location_code": 0,
- "location_name": "string",
- "language_code": "string",
- "limit": 1,
- "min_search_volume": 0,
- "max_search_volume": 0
}| campaign_id required | string <uuid> |
| label_ids required | Array of strings <uuid> [ items <uuid > ] |
{- "label_ids": [
- "497f6eca-6276-4993-bfeb-53cbbbba6f08"
]
}| campaign_id required | string <uuid> |
| label_ids required | Array of strings <uuid> [ items <uuid > ] |
{- "label_ids": [
- "497f6eca-6276-4993-bfeb-53cbbbba6f08"
]
}| export_type required | string Enum: "csv" "pdf" |
| campaign_id | string <uuid> |
{- "export_type": "csv",
- "campaign_id": "78614b6c-fe7c-41e2-8e25-c9b3a3c91904"
}Paid Projects (Prospect / Track). Create multi-feature setups,
poll status via project detail or report snapshots, convert Prospect →
Track, and soft-delete. Authenticated with ld_... API keys; proxies
Nest /v1/projects/* with a team-owner JWT. Maps and SERP credits are
charged on Nest create (same wallet as the dashboard).
Feature keys accept snake_case (maps_grid, serp_tracker, site_audit,
review_velocity, listing_builder, ai_tracker) or Nest camelCase.
{- "projects": [
- {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "type": "prospect",
- "status": "string",
- "name": "string",
- "label": "string",
- "business_profile": { },
- "keyword": "string",
- "prompt": "string",
- "target_location": "string",
- "enabled_features": [
- "string"
], - "feature_configs": { },
- "feature_refs": { },
- "schedule": { },
- "credits_used_total": 0,
- "credits_used_last_run": 0,
- "health_score": 0,
- "health_score_partial": true,
- "last_run_at": "2019-08-24T14:15:22Z",
- "next_run_at": "2019-08-24T14:15:22Z",
- "converted_from_project_id": "9386c646-62cd-4751-bf95-9f351d1ac816",
- "source_trial_project_id": "584e67a9-08d0-421e-9e08-38af1ba24ad0",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
]
}Creates a Prospect or Track project and launches enabled features. Charges wallet credits for the features that bill up front, from the feature configs.
prospect runs each feature once. track creates recurring grid-scan
campaigns instead of a one-off scan — one per device and source, since a
campaign carries a single device and a single source — and their runs bill
themselves as they fire.
Limits, rejected with 400 before anything is charged: at most 50 AI
prompts, at most 10 AI topics, and at most 1000 pages per site-audit run.
Duplicate search_terms are billed and scanned once.
| type | string Default: "prospect" Enum: "prospect" "track" |
| name | string <= 256 characters |
| label | string <= 256 characters |
required | object GBP / place fields ( |
| keyword required | string <= 256 characters |
| prompt | string <= 5000 characters |
| target_location | string <= 256 characters |
| enabled_features | Array of strings (ProjectFeatureKey) Items Enum: "maps_grid" "mapsGrid" "one_time_scan" "oneTimeScan" "serp_tracker" "serpTracker" "site_audit" "siteAudit" "review_velocity" "reviewVelocity" "listing_builder" "listingBuilder" "ai_tracker" "aiTracker" |
object | |
object |
{- "type": "prospect",
- "name": "string",
- "label": "string",
- "business_profile": { },
- "keyword": "string",
- "prompt": "string",
- "target_location": "string",
- "enabled_features": [
- "maps_grid"
], - "feature_configs": { },
- "schedule": { }
}{- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "type": "prospect",
- "status": "string",
- "name": "string",
- "label": "string",
- "business_profile": { },
- "keyword": "string",
- "prompt": "string",
- "target_location": "string",
- "enabled_features": [
- "string"
], - "feature_configs": { },
- "feature_refs": { },
- "schedule": { },
- "credits_used_total": 0,
- "credits_used_last_run": 0,
- "health_score": 0,
- "health_score_partial": true,
- "last_run_at": "2019-08-24T14:15:22Z",
- "next_run_at": "2019-08-24T14:15:22Z",
- "converted_from_project_id": "9386c646-62cd-4751-bf95-9f351d1ac816",
- "source_trial_project_id": "584e67a9-08d0-421e-9e08-38af1ba24ad0",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}{- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "type": "prospect",
- "status": "string",
- "name": "string",
- "label": "string",
- "business_profile": { },
- "keyword": "string",
- "prompt": "string",
- "target_location": "string",
- "enabled_features": [
- "string"
], - "feature_configs": { },
- "feature_refs": { },
- "schedule": { },
- "credits_used_total": 0,
- "credits_used_last_run": 0,
- "health_score": 0,
- "health_score_partial": true,
- "last_run_at": "2019-08-24T14:15:22Z",
- "next_run_at": "2019-08-24T14:15:22Z",
- "converted_from_project_id": "9386c646-62cd-4751-bf95-9f351d1ac816",
- "source_trial_project_id": "584e67a9-08d0-421e-9e08-38af1ba24ad0",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}| project_id required | string <uuid> |
| name | string <= 256 characters |
| label | string or null <= 256 characters |
object | |
| keyword | string <= 256 characters |
| prompt | string or null <= 5000 characters |
| target_location | string <= 256 characters |
| enabled_features | Array of strings (ProjectFeatureKey) Items Enum: "maps_grid" "mapsGrid" "one_time_scan" "oneTimeScan" "serp_tracker" "serpTracker" "site_audit" "siteAudit" "review_velocity" "reviewVelocity" "listing_builder" "listingBuilder" "ai_tracker" "aiTracker" |
object | |
object or null |
{- "name": "string",
- "label": "string",
- "business_profile": { },
- "keyword": "string",
- "prompt": "string",
- "target_location": "string",
- "enabled_features": [
- "maps_grid"
], - "feature_configs": { },
- "schedule": { }
}{- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "type": "prospect",
- "status": "string",
- "name": "string",
- "label": "string",
- "business_profile": { },
- "keyword": "string",
- "prompt": "string",
- "target_location": "string",
- "enabled_features": [
- "string"
], - "feature_configs": { },
- "feature_refs": { },
- "schedule": { },
- "credits_used_total": 0,
- "credits_used_last_run": 0,
- "health_score": 0,
- "health_score_partial": true,
- "last_run_at": "2019-08-24T14:15:22Z",
- "next_run_at": "2019-08-24T14:15:22Z",
- "converted_from_project_id": "9386c646-62cd-4751-bf95-9f351d1ac816",
- "source_trial_project_id": "584e67a9-08d0-421e-9e08-38af1ba24ad0",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}| project_id required | string <uuid> |
{- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "type": "prospect",
- "status": "string",
- "name": "string",
- "label": "string",
- "business_profile": { },
- "keyword": "string",
- "prompt": "string",
- "target_location": "string",
- "enabled_features": [
- "string"
], - "feature_configs": { },
- "feature_refs": { },
- "schedule": { },
- "credits_used_total": 0,
- "credits_used_last_run": 0,
- "health_score": 0,
- "health_score_partial": true,
- "last_run_at": "2019-08-24T14:15:22Z",
- "next_run_at": "2019-08-24T14:15:22Z",
- "converted_from_project_id": "9386c646-62cd-4751-bf95-9f351d1ac816",
- "source_trial_project_id": "584e67a9-08d0-421e-9e08-38af1ba24ad0",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}Narrative opening of the report: an executive diagnosis plus What's Going Well, Keep an Eye On, Needs Attention and a Priority Action Plan, all derived from the project's own scan data.
Written once when the project's run settles, so repeated calls
return the same text. Responds with null while the run is
still in progress, or when generation failed — the report falls
back to its computed summary in that case.
| project_id required | string <uuid> |
{- "executive_diagnosis": "string",
- "going_well": [
- {
- "channel": "AI",
- "title": "string",
- "detail": "string",
- "impact": "string",
- "next_step": "string",
- "metric": "string"
}
], - "keep_eye_on": [
- {
- "channel": "AI",
- "title": "string",
- "detail": "string",
- "impact": "string",
- "next_step": "string",
- "metric": "string"
}
], - "needs_attention": [
- {
- "channel": "AI",
- "title": "string",
- "detail": "string",
- "impact": "string",
- "next_step": "string",
- "metric": "string"
}
], - "next_best_action": [
- {
- "channel": "AI",
- "title": "string",
- "detail": "string",
- "what_to_do": "string",
- "why_now": "string",
- "how_to_measure": "string",
- "priority": "Highest"
}
]
}Agency-facing report endpoint. Currently returns the same payload
as GET /v1/projects/{project_id}/report-snapshots.
| project_id required | string <uuid> |
{ }