Local Dominator API - OpenAPI 3.1 (v1)

Download OpenAPI specification:

Local Dominator

Scan Grid Configuration

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).

List scans

Authorizations:
BearerAuth
query Parameters
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 that occurred on or after the specified start date (in YYYY-MM-DD format).

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

Responses

Response samples

Content type
application/json
{ }

Perform a scan

Initiates a scan based on the specified parameters.

Authorizations:
BearerAuth
Request Body schema: application/json
required
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, ChIJgUbEo8cfqokR5lP9_Wh_DaM.

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 (desktop, mobile, both). googleLocalFinder — Local Pack (Local Finder); desktop-only in practice (see serp_device). Defaults to googleMaps.

serp_device
string
Default: "desktop"
Enum: "desktop" "mobile" "both"

SERP device for provider requests. On googleMaps: desktop, mobile, or both (one scan_uuid; both collects desktop and mobile heatmaps per keyword). On googleLocalFinder: only desktop runs — mobile and both are accepted but do not schedule a mobile leg (same as desktop, one heatmap per keyword). Defaults to desktop when omitted.

Responses

Request samples

Content type
application/json
{
  • "latitude": 40.712776,
  • "longitude": -74.005974,
  • "shape": "circle",
  • "distance": 1000,
  • "google_place_id": "ChIJgUbEo8cfqokR5lP9_Wh_DaM",
  • "grid_size": 13,
  • "search_terms": [
    ],
  • "preschedule_analysis": true,
  • "resource_category": "googleLocalFinder",
  • "serp_device": "desktop"
}

Response samples

Content type
application/json
{
  • "message": "Scan creation started.",
  • "scan_uuid": "xPH5x_SZcmcf8E7FROCNQ"
}

Retrieve scan details

Returns the details of the scan specified by scan_id.

Authorizations:
BearerAuth
path Parameters
scan_id
required
string

The ID of the scan

Responses

Response samples

Content type
application/json
[ ]

Delete a scan

Authorizations:
BearerAuth
path Parameters
scan_id
required
string

The ID of the scan

Responses

Scan results

The details of each business in every point in the scan.

Authorizations:
BearerAuth
path Parameters
scan_id
required
string

The ID of the scan

Responses

Response samples

Content type
application/json
[ ]

Analyze a scan

Initiates or retrieves post-run insights for a scan: ranking context, competitors, and optimization-oriented notes derived from the scan grid.

Authorizations:
BearerAuth
path Parameters
scan_id
required
string

The ID of the scan to analyze

Responses

Response samples

Content type
application/json
{
  • "scanId": "xPH5x_SZcmcf8E7FROCNQ",
  • "message": "Group was successfully analyzed",
  • "analysis": {
    }
}

Scheduled scan campaigns

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).

List scheduled scan campaigns

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).

Authorizations:
BearerAuth
query Parameters
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 labels value, or repeat ?labels=... (form style) — same as the list query schema in the API. The scan must still match on all of the given labels.

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 orderProperty as scheduled_scans list queries. Aliases (e.g. lastScan = last_run_date) are accepted for client convenience.

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).

Responses

Response samples

Content type
application/json
{ }

Create scheduled scan campaign

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.

Authorizations:
BearerAuth
Request Body schema: application/json
required
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: ^https?://, max 2048 chars). POST when each scan run completes successfully (same style as one-time POST /v1/scans webhook_url). Applies until changed or cleared via PATCH.

serp_device
string
Enum: "desktop" "mobile"

SERP device for provider requests. Defaults to desktop when omitted.

Responses

Request samples

Content type
application/json
{
  • "google_place_id": "ChIJ03jJ2Omr2YgRNkx36YLd3q4",
  • "latitude": 26.0481,
  • "longitude": -80.1819,
  • "shape": "circle",
  • "distance": 1,
  • "grid_size": 1,
  • "search_terms": [
    ],
  • "scheduling": "0 9 * * 1",
  • "notify": true,
  • "run_now": true,
  • "labels": [
    ],
  • "skippable": true,
  • "preschedule_analysis": true,
  • "alias": "string",
  • "resource_category": "googleMaps",
  • "webhook_url": "http://example.com",
  • "serp_device": "desktop"
}

Response samples

Content type
application/json
{
  • "message": "Scheduled scan created successfully.",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08"
}

Start a scheduled scan campaign run now

Starts a new scan run immediately (manual execution). Processing is asynchronous; 200 means the run was accepted.

Authorizations:
BearerAuth
path Parameters
scheduled_scan_id
required
string <uuid>

Opaque scheduled scan identifier (same id as in list/detail responses).

Responses

Response samples

Content type
application/json
{
  • "message": "Run started."
}

Get scheduled scan campaign and runs

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).

Authorizations:
BearerAuth
path Parameters
scheduled_scan_id
required
string <uuid>

Opaque scheduled scan identifier (same id as in GET /v1/scheduled-scans list items).

query Parameters
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 dateRange filter. MAX = all runs. When omitted, 3M is used (last three months). image_link, dynamic_url, and campaign_link on the campaign object are derived from the newest notified run inside that filtered set that has resolvable per-row share URLs.

Responses

Response samples

Content type
application/json
{ }

Update scheduled scan campaign

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).

Authorizations:
BearerAuth
path Parameters
scheduled_scan_id
required
string <uuid>

Opaque scheduled scan identifier (same id as in GET /v1/scheduled-scans list items).

Request Body schema: application/json
required
non-empty
Array of strings or objects (ScheduledScanPatchSearchTerm) non-empty

Same rules as search_terms on create: array of strings and/or { term, status? } objects; status is optional and defaults to active; duplicates are rejected.

scheduling
string

Five-field cron expression; must be accepted by the API (400 if invalid).

skippable
boolean

When true, biweekly scheduling uses selected_day_of_week / selected_hour / selected_minute to refine next_run_at. When false, omit those three fields unless you will set skippable to true; they do not affect next_run_at while false.

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 scheduled-scans-validation for PATCH). Receives a POST when each run completes.

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 true. Ignored for next_run_at when skippable is false — omit in that case.

selected_hour
integer [ 0 .. 23 ]

Local hour (scheduled scan timezone) for biweekly next_run_at when skippable is true. Ignored when skippable is false — omit in that case.

selected_minute
integer [ 0 .. 59 ]

Local minute for biweekly next_run_at when skippable is true. Ignored when skippable is false — omit in that case.

Responses

Request samples

Content type
application/json
{
  • "search_terms": [
    ],
  • "scheduling": "0 9 * * 1",
  • "notify": true
}

Response samples

Content type
application/json
{
  • "message": "Scheduled scan updated successfully.",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08"
}

Delete scheduled scan campaign

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.

Authorizations:
BearerAuth
path Parameters
scheduled_scan_id
required
string <uuid>

Opaque scheduled scan identifier (same id as in GET /v1/scheduled-scans list items).

Responses

Response samples

Content type
application/json
{
  • "message": "Scheduled scan deleted successfully."
}

Delete scheduled scan campaign runs by run creation date

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.

Authorizations:
BearerAuth
path Parameters
scheduled_scan_id
required
string <uuid>

Opaque scheduled scan identifier.

query Parameters
creation_date
required
string <date>

Filter: UTC calendar day (YYYY-MM-DD). Matches the date part of each scan run’s created_at.

Responses

Response samples

Content type
application/json
{
  • "message": "Scheduled scan runs deleted for the selected run-creation date.",
  • "creation_date": "2025-04-14",
  • "runs_deleted": 2
}

Scheduled scan campaign runs

Scheduled scansruns: 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.

Get compressed results for a scheduled scan campaign run

Returns compressed grid results for this scan run (all keywords; camelCase keys and compressed_grid).

Authorizations:
BearerAuth
path Parameters
run_id
required
string <uuid>

Scan run identifier (run_uuid).

Responses

Response samples

Content type
application/json
[ ]

List scan rows for a scheduled scan campaign run

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}).

Authorizations:
BearerAuth
path Parameters
run_id
required
string <uuid>

Scan run identifier (run_uuid).

Responses

Response samples

Content type
application/json
[ ]

Delete a scheduled scan campaign run

Deletes this scan run (soft-delete). Does not delete the scheduled scan.

Authorizations:
BearerAuth
path Parameters
run_id
required
string <uuid>

Scan run identifier (run_uuid).

Responses

Response samples

Content type
application/json
{
  • "message": "Scheduled scan run deleted successfully."
}

Listings Manual Submissions API

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.

Create a listings scan (prospect)

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.

Authorizations:
BearerAuth
Request Body schema: application/json
required
business_name
required
string
address
required
string
city
string
state
string
zip
string
phone
string
website
string <uri>
country
string
Default: "US"

Responses

Request samples

Content type
application/json
{
  • "business_name": "Acme Inc",
  • "address": "123 Main St",
  • "city": "New York",
  • "state": "NY",
  • "zip": "10001",
  • "phone": "+1234567890",
  • "website": "https://acme.com",
  • "country": "US"
}

Response samples

Content type
application/json
{
  • "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  • "order_number": "ABC123",
  • "status": "pending"
}

List your listings scans

Authorizations:
BearerAuth
query Parameters
page
integer
Default: 1
limit
integer <= 100
Default: 20

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "page": 1,
  • "limit": 20,
  • "total": 47
}

Get a prospect and its scan results

Authorizations:
BearerAuth
path Parameters
search_id
required
string

Prospect id returned from POST /v1/listings/prospect

Responses

Response samples

Content type
application/json
{
  • "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": [
    ]
}

Order a listings fix for a prospect

Charges credits from the caller's wallet and submits a fix order for the given prospect.

  • Plan "50" costs 55,650 credits and fixes up to 50 directories.
  • Plan "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.

Authorizations:
BearerAuth
Request Body schema: application/json
required
search_id
required
string
plan
required
string
Enum: "50" "100"

Responses

Request samples

Content type
application/json
{
  • "search_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  • "plan": "50"
}

Response samples

Content type
application/json
{
  • "order_id": "string",
  • "order_number": "string",
  • "status": "ordered",
  • "plan": "50",
  • "credits_charged": 55650
}

Get fix order progress

Authorizations:
BearerAuth
path Parameters
search_id
required
string

Responses

Response samples

Content type
application/json
{
  • "search_id": "string",
  • "order_number": "string",
  • "campaign_progress": "ordered",
  • "plan": "50",
  • "progress": {
    }
}

AI Tracker Public API

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.

Get AI Tracker account summary

Authorizations:
BearerAuth

Responses

Response samples

Content type
application/json
{
  • "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
}

List AI Tracker campaigns

Authorizations:
BearerAuth

Responses

Response samples

Content type
application/json
{
  • "campaigns": [
    ]
}

Preview AI Tracker prompts (no billing)

Generates prompt previews for the given keywords. Does not create a campaign or charge credits. Same rate limits as campaign commit.

Authorizations:
BearerAuth
Request Body schema: application/json
required
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

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "domain": "string",
  • "location": "string",
  • "engines": [
    ],
  • "keywords": [
    ]
}

Response samples

Content type
application/json
{ }

Commit AI Tracker campaign (charges credits)

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.

Authorizations:
BearerAuth
Request Body schema: application/json
required
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

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "domain": "string",
  • "location": "string",
  • "engines": [
    ],
  • "prompts": [
    ]
}

Response samples

Content type
application/json
{
  • "id": "string",
  • "name": "string",
  • "domain": "string",
  • "status": "string",
  • "prompts_count": 0,
  • "billable_prompts": 0,
  • "credits_per_prompt": 100,
  • "credits_charged": 0
}

Get AI Tracker campaign results

Authorizations:
BearerAuth
path Parameters
id
required
string
query Parameters
limit
integer <= 500
offset
integer

Responses

Response samples

Content type
application/json
{
  • "total": 0,
  • "limit": 0,
  • "offset": 0,
  • "results": [
    ]
}

Get AI Tracker campaign rankings

Authorizations:
BearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "rankings": [
    ]
}

Get committed keywords and prompts for a campaign

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.

Authorizations:
BearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "campaign_id": "string",
  • "keywords": [
    ],
  • "prompts": [
    ]
}

Preview prompts for new keywords on a campaign (no billing)

Authorizations:
BearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
keywords
required
Array of strings non-empty

Responses

Request samples

Content type
application/json
{
  • "keywords": [
    ]
}

Response samples

Content type
application/json
{ }

Commit new keywords/prompts on a campaign (charges credits)

Authorizations:
BearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
required
Array of objects [ 1 .. 50 ] items

Responses

Request samples

Content type
application/json
{
  • "prompts": [
    ]
}

Response samples

Content type
application/json
{
  • "billable_prompts": 0,
  • "credits_per_prompt": 0,
  • "credits_charged": 0
}

Get active AI Tracker campaign share link

Returns the active public share link for a campaign, or null fields when none exists. Does not bill credits.

Authorizations:
BearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "shareToken": "string",
  • "shareUrl": "string"
}

Create or get active AI Tracker campaign share link

Creates a share link for the campaign if none is active, or returns the existing one. Set useWhiteLabel=true to return white-label share URL when the team has a verified white-label domain.

Authorizations:
BearerAuth
path Parameters
id
required
string
Request Body schema: application/json
optional
useWhiteLabel
boolean

Return white-label share URL when available

Responses

Request samples

Content type
application/json
{
  • "useWhiteLabel": true
}

Response samples

Content type
application/json
{
  • "shareToken": "string",
  • "shareUrl": "string",
  • "upstreamShareUrl": "string",
  • "whiteLabelShareUrl": "string",
  • "isWhiteLabel": true,
  • "whiteLabelDomain": "string"
}

Deactivate AI Tracker campaign share link

Deactivates the active share link for the campaign.

Authorizations:
BearerAuth
path Parameters
id
required
string

Responses

Trigger AI Tracker rerun (charges credits)

Authorizations:
BearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "triggered": true,
  • "campaign_id": "string",
  • "scan_jobs_count": 0,
  • "status": "string",
  • "billable_prompts": 0,
  • "credits_per_prompt": 100,
  • "credits_charged": 0
}

Review Velocity Public API

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.

Review Velocity identity and API limits

Authorizations:
BearerAuth

Responses

Response samples

Content type
application/json
{ }

Google Places autocomplete (upstream proxy)

Authorizations:
BearerAuth
query Parameters
q
required
string
limit
integer

Responses

Resolve a Google business from place id or search query

Authorizations:
BearerAuth
Request Body schema: application/json
required
object

Responses

Request samples

Content type
application/json
{ }

Update business metadata

Authorizations:
BearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
object

Responses

Request samples

Content type
application/json
{ }

Discover competitor candidates near a business

Authorizations:
BearerAuth
Request Body schema: application/json
required
campaign_id
required
string <uuid>

Existing campaign UUID; anchor is its main business

Responses

Request samples

Content type
application/json
{
  • "campaign_id": "78614b6c-fe7c-41e2-8e25-c9b3a3c91904"
}

Generate an AI reply draft for a review

Billed synchronously (1 credit). Returns billing.status=completed on success.

Authorizations:
BearerAuth
path Parameters
id
required
string
Request Body schema: application/json
tone
string
language
string
max_chars
integer

Responses

Request samples

Content type
application/json
{
  • "tone": "string",
  • "language": "string",
  • "max_chars": 0
}

Response samples

Content type
application/json
{
  • "billing": {
    }
}

List Review Velocity campaigns

Authorizations:
BearerAuth
query Parameters
limit
string
offset
string

Responses

Create campaign (one-shot)

Authorizations:
BearerAuth
Request Body schema: application/json
required
object

Responses

Request samples

Content type
application/json
{ }

Preview campaign create (preview token)

Authorizations:
BearerAuth
Request Body schema: application/json
required
name
required
string
required
object

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "main_business": {
    }
}

Commit campaign create from preview token

Authorizations:
BearerAuth
Request Body schema: application/json
required
preview_token
required
string

Responses

Request samples

Content type
application/json
{
  • "preview_token": "string"
}

Get campaign

Authorizations:
BearerAuth
path Parameters
id
required
string
query Parameters
expand
string
Value: "details"

Responses

Update campaign

Authorizations:
BearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
object

Responses

Request samples

Content type
application/json
{ }

Delete campaign

Authorizations:
BearerAuth
path Parameters
id
required
string

Responses

List businesses attached to a campaign

Authorizations:
BearerAuth
path Parameters
id
required
string

Responses

List campaign competitors

Authorizations:
BearerAuth
path Parameters
id
required
string

Responses

Add a competitor to a campaign

Authorizations:
BearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
object

Responses

Request samples

Content type
application/json
{ }

Remove a competitor from a campaign

Authorizations:
BearerAuth
path Parameters
id
required
string
businessId
required
string

Responses

List scraped reviews for a campaign

Authorizations:
BearerAuth
path Parameters
id
required
string
query Parameters
limit
string
offset
string
since
string
min_rating
string
max_rating
string

Responses

Poll scrape job progress (finalizes billing when terminal)

Authorizations:
BearerAuth
path Parameters
id
required
string
query Parameters
limit
string
offset
string

Responses

Response samples

Content type
application/json
{
  • "billing": {
    }
}

Get velocity analysis (poll target after POST analyze)

Authorizations:
BearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "status": "pending",
  • "analysis_type": "velocity",
  • "campaign_id": "string",
  • "started_at": "2019-08-24T14:15:22Z",
  • "poll": "string",
  • "poll_after_ms": 5000,
  • "billing": {
    }
}

Get reputation analysis (poll target after POST analyze)

Authorizations:
BearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "status": "pending",
  • "analysis_type": "velocity",
  • "campaign_id": "string",
  • "started_at": "2019-08-24T14:15:22Z",
  • "poll": "string",
  • "poll_after_ms": 5000,
  • "billing": {
    }
}

List feedback collection links for a campaign

Authorizations:
BearerAuth
path Parameters
id
required
string

Responses

List feedback form responses

Authorizations:
BearerAuth
path Parameters
id
required
string
query Parameters
limit
string
offset
string

Responses

Preview scrape credits estimate

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.

Authorizations:
BearerAuth
path Parameters
id
required
string
Request Body schema: application/json
estimated_total_reviews
integer

Optional hint for the pre-upstream credit hold.

Responses

Request samples

Content type
application/json
{
  • "estimated_total_reviews": 0
}

Response samples

Content type
application/json
{
  • "preview_token": "string",
  • "estimated_total_reviews": 0,
  • "billing": {
    }
}

Commit scrape run

Authorizations:
BearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
preview_token
required
string

Responses

Request samples

Content type
application/json
{
  • "preview_token": "string"
}

Response samples

Content type
application/json
{
  • "billing": {
    }
}

Run velocity LLM analysis

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.

Authorizations:
BearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "status": "pending",
  • "analysis_type": "velocity",
  • "campaign_id": "string",
  • "started_at": "2019-08-24T14:15:22Z",
  • "poll": "string",
  • "poll_after_ms": 5000,
  • "billing": {
    }
}

Run reputation LLM analysis

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.

Authorizations:
BearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "status": "pending",
  • "analysis_type": "velocity",
  • "campaign_id": "string",
  • "started_at": "2019-08-24T14:15:22Z",
  • "poll": "string",
  • "poll_after_ms": 5000,
  • "billing": {
    }
}

Enable public share link

Authorizations:
BearerAuth
path Parameters
id
required
string

Responses

Rotate campaign share token

Authorizations:
BearerAuth
path Parameters
id
required
string

Responses

Site Audit Public API

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.

Partner account summary (sites, audits, active audits)

Authorizations:
BearerAuth

Responses

List sites

Authorizations:
BearerAuth
query Parameters
limit
string
offset
string

Responses

Create a site

Authorizations:
BearerAuth
Request Body schema: application/json
domain
required
string
name
string
start_url
string
settings
object

Responses

Request samples

Content type
application/json
{
  • "domain": "string",
  • "name": "string",
  • "start_url": "string",
  • "settings": { }
}

Get site by id

Authorizations:
BearerAuth
path Parameters
id
required
string

Responses

Update site metadata

Authorizations:
BearerAuth
path Parameters
id
required
string
Request Body schema: application/json
object

Responses

Request samples

Content type
application/json
{ }

Delete site and related audits

Authorizations:
BearerAuth
path Parameters
id
required
string

Responses

List audits

Authorizations:
BearerAuth
query Parameters
site_id
string
status
string
limit
string
offset
string

Responses

Start a site or landing-page audit

Authorizations:
BearerAuth
Request Body schema: application/json
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

Responses

Request samples

Content type
application/json
{
  • "site_id": "string",
  • "mode": "site_audit",
  • "max_pages": 0,
  • "max_depth": 0,
  • "exclude_noindex": true,
  • "start_url": "string",
  • "target_keyword": "string",
  • "competitor_urls": [
    ]
}

Response samples

Content type
application/json
{
  • "billing": {
    }
}

Get audit by id (poll until complete; finalizes billing)

Authorizations:
BearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "billing": {
    }
}

Delete audit

Authorizations:
BearerAuth
path Parameters
id
required
string

Responses

Rerun audit with same settings

Authorizations:
BearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "billing": {
    }
}

List audit issues (finalizes billing when audit is terminal)

Authorizations:
BearerAuth
path Parameters
id
required
string
query Parameters
severity
string
category
string
limit
string
offset
string

Responses

List crawled pages for audit

Authorizations:
BearerAuth
path Parameters
id
required
string

Responses

List audit images

Authorizations:
BearerAuth
path Parameters
id
required
string

Responses

List internal link anchors

Authorizations:
BearerAuth
path Parameters
id
required
string

Responses

SERP Public API

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).

Get SERP account limits and credit summary

Authorizations:
BearerAuth

Responses

Search geo locations for SERP campaigns

Authorizations:
BearerAuth
query Parameters
search
required
string
country_code
string
location_type
string
limit
string

Responses

List SERP campaigns

Authorizations:
BearerAuth
query Parameters
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"

Responses

Create a SERP campaign

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.

Authorizations:
BearerAuth
Request Body schema: application/json
required
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

Responses

Request samples

Content type
application/json
{
  • "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": [
    ],
  • "business_name": "string",
  • "metadata": { }
}

Get SERP campaign details

Authorizations:
BearerAuth
path Parameters
id
required
string <uuid>

Responses

Update SERP campaign

Authorizations:
BearerAuth
path Parameters
id
required
string <uuid>
Request Body schema: application/json
required
non-empty
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"

Responses

Request samples

Content type
application/json
{
  • "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": [
    ],
  • "business_name": "string",
  • "metadata": { },
  • "status": "active"
}

Delete SERP campaign

Authorizations:
BearerAuth
path Parameters
id
required
string <uuid>

Responses

Trigger a SERP campaign rescan (charges credits)

Authorizations:
BearerAuth
path Parameters
id
required
string <uuid>

Responses

Check whether a campaign scan is in progress

Authorizations:
BearerAuth
path Parameters
id
required
string <uuid>

Responses

Response samples

Content type
application/json
{
  • "is_processing": true
}

List keywords in a SERP campaign

Authorizations:
BearerAuth
path Parameters
id
required
string <uuid>
query Parameters
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"

Responses

Add keywords to a SERP campaign

Authorizations:
BearerAuth
path Parameters
id
required
string <uuid>
Request Body schema: application/json
required
required
Array of objects [ 1 .. 100 ] items
devices
Array of strings
Items Enum: "desktop" "mobile"

Responses

Request samples

Content type
application/json
{
  • "keywords": [
    ],
  • "devices": [
    ]
}

Get SERP result snapshot for a keyword

Authorizations:
BearerAuth
path Parameters
id
required
string <uuid>
keyword_id
required
string <uuid>
query Parameters
date_from
string <date>
date_to
string <date>

Responses

Update keyword

Authorizations:
BearerAuth
path Parameters
id
required
string <uuid>
keyword_id
required
string <uuid>
Request Body schema: application/json
required
non-empty
keyword
string
position
integer
url
string
search_volume
integer
difficulty
integer

Responses

Request samples

Content type
application/json
{
  • "keyword": "string",
  • "position": 0,
  • "url": "string",
  • "search_volume": 0,
  • "difficulty": 0
}

Delete keyword

Authorizations:
BearerAuth
path Parameters
id
required
string <uuid>
keyword_id
required
string <uuid>

Responses

Get geo location by code

Authorizations:
BearerAuth
path Parameters
location_code
required
string^[0-9]+$

Responses

Batch processing status for campaigns

Authorizations:
BearerAuth
query Parameters
campaign_ids
string

Comma-separated UUIDs or repeated query params

Responses

Duplicate SERP campaign

Authorizations:
BearerAuth
path Parameters
id
required
string <uuid>

Responses

Cancel in-progress campaign scan

Authorizations:
BearerAuth
path Parameters
id
required
string <uuid>

Responses

Bulk delete campaigns

Bulk operations use POST with a JSON body. For deleting a single campaign, prefer DELETE /v1/serp/campaigns/{id}.

Authorizations:
BearerAuth
Request Body schema: application/json
required
campaign_ids
required
Array of strings <uuid> [ items <uuid > ]

Responses

Request samples

Content type
application/json
{
  • "campaign_ids": [
    ]
}

Bulk pause campaigns

Authorizations:
BearerAuth
Request Body schema: application/json
required
campaign_ids
required
Array of strings <uuid> [ items <uuid > ]

Responses

Request samples

Content type
application/json
{
  • "campaign_ids": [
    ]
}

Bulk resume campaigns

Authorizations:
BearerAuth
Request Body schema: application/json
required
campaign_ids
required
Array of strings <uuid> [ items <uuid > ]

Responses

Request samples

Content type
application/json
{
  • "campaign_ids": [
    ]
}

Bulk rescan campaigns

Authorizations:
BearerAuth
Request Body schema: application/json
required
campaign_ids
required
Array of strings <uuid> [ items <uuid > ]

Responses

Request samples

Content type
application/json
{
  • "campaign_ids": [
    ]
}

Get campaign schedule

Authorizations:
BearerAuth
path Parameters
id
required
string <uuid>

Responses

Create or update campaign schedule

Authorizations:
BearerAuth
path Parameters
id
required
string <uuid>
Request Body schema: application/json
required
type
required
string
Enum: "daily" "weekly" "monthly" "custom" "manual"

Responses

Request samples

Content type
application/json
{
  • "type": "daily"
}

Disable campaign schedule

Authorizations:
BearerAuth
path Parameters
id
required
string <uuid>

Responses

Average rank history for selected keywords

Authorizations:
BearerAuth
path Parameters
id
required
string <uuid>
Request Body schema: application/json
required
keyword_ids
required
Array of strings <uuid> [ items <uuid > ]
device
required
string
Enum: "desktop" "mobile"

Responses

Request samples

Content type
application/json
{
  • "keyword_ids": [
    ],
  • "device": "desktop"
}

Rescan single keyword

Authorizations:
BearerAuth
path Parameters
id
required
string <uuid>
keyword_id
required
string <uuid>

Responses

Bulk rescan keywords in campaign

Authorizations:
BearerAuth
path Parameters
id
required
string <uuid>
Request Body schema: application/json
required
keyword_ids
required
Array of strings <uuid> [ items <uuid > ]

Responses

Request samples

Content type
application/json
{
  • "keyword_ids": [
    ]
}

Bulk delete keywords in campaign

Authorizations:
BearerAuth
path Parameters
id
required
string <uuid>
Request Body schema: application/json
required
keyword_ids
required
Array of strings <uuid> [ items <uuid > ]

Responses

Request samples

Content type
application/json
{
  • "keyword_ids": [
    ]
}

Overview analytics across campaigns

Authorizations:
BearerAuth

Responses

Campaign analytics with optional date range

Authorizations:
BearerAuth
path Parameters
campaign_id
required
string <uuid>
query Parameters
date_from
string <date>
date_to
string <date>

Responses

Keyword analytics with optional date range

Authorizations:
BearerAuth
path Parameters
keyword_id
required
string <uuid>
query Parameters
date_from
string <date>
date_to
string <date>

Responses

List SERP labels

Authorizations:
BearerAuth

Responses

Create label

Authorizations:
BearerAuth
Request Body schema: application/json
required
name
required
string
color
string

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "color": "string"
}

Validate domain for SERP campaign

Authorizations:
BearerAuth
Request Body schema: application/json
required
domain
required
string
relaxed_fetch
boolean

Responses

Request samples

Content type
application/json
{
  • "domain": "string",
  • "relaxed_fetch": true
}

Enrich domain metadata and keyword ideas

Authorizations:
BearerAuth
Request Body schema: application/json
required
domain
required
string

Responses

Request samples

Content type
application/json
{
  • "domain": "string"
}

Enrich Google Business Profile keyword ideas

Authorizations:
BearerAuth
Request Body schema: application/json
cid
string
place_id
string
location_name
string
business_name
string
category
string
url
string
address
string

Responses

Request samples

Content type
application/json
{
  • "cid": "string",
  • "place_id": "string",
  • "location_name": "string",
  • "business_name": "string",
  • "category": "string",
  • "url": "string",
  • "address": "string"
}

Read cached domain insight for the authenticated user

Authorizations:
BearerAuth
query Parameters
domain
required
string

Responses

Generate keyword recommendations from a seed keyword

Authorizations:
BearerAuth
Request Body schema: application/json
required
keyword
required
string
location_code
integer
location_name
string
language_code
string
limit
integer [ 1 .. 100 ]
min_search_volume
integer
max_search_volume
integer

Responses

Request samples

Content type
application/json
{
  • "keyword": "string",
  • "location_code": 0,
  • "location_name": "string",
  • "language_code": "string",
  • "limit": 1,
  • "min_search_volume": 0,
  • "max_search_volume": 0
}

Update label

Authorizations:
BearerAuth
path Parameters
label_id
required
string <uuid>
Request Body schema: application/json
name
string
color
string

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "color": "string"
}

Delete label

Authorizations:
BearerAuth
path Parameters
label_id
required
string <uuid>

Responses

Attach labels to campaign

Authorizations:
BearerAuth
path Parameters
campaign_id
required
string <uuid>
Request Body schema: application/json
required
label_ids
required
Array of strings <uuid> [ items <uuid > ]

Responses

Request samples

Content type
application/json
{
  • "label_ids": [
    ]
}

Detach labels from campaign

Authorizations:
BearerAuth
path Parameters
campaign_id
required
string <uuid>
Request Body schema: application/json
required
label_ids
required
Array of strings <uuid> [ items <uuid > ]

Responses

Request samples

Content type
application/json
{
  • "label_ids": [
    ]
}

List share links for campaign

Authorizations:
BearerAuth
path Parameters
campaign_id
required
string <uuid>

Responses

Create share link for campaign

Authorizations:
BearerAuth
path Parameters
campaign_id
required
string <uuid>
Request Body schema: application/json
expires_in_days
integer
max_views
integer

Responses

Request samples

Content type
application/json
{
  • "expires_in_days": 0,
  • "max_views": 0
}

Revoke share link

Authorizations:
BearerAuth
path Parameters
share_id
required
string <uuid>

Responses

Request CSV or PDF export job

Authorizations:
BearerAuth
Request Body schema: application/json
required
export_type
required
string
Enum: "csv" "pdf"
campaign_id
string <uuid>

Responses

Request samples

Content type
application/json
{
  • "export_type": "csv",
  • "campaign_id": "78614b6c-fe7c-41e2-8e25-c9b3a3c91904"
}

Get export job status

Authorizations:
BearerAuth
path Parameters
export_id
required
string <uuid>

Responses