SignalSumo

API Reference

Integrate SignalSumo's SEO data directly into your own tools, dashboards, and workflows.

Pro Agency API access is available on the Pro and Agency plans

Quickstart

Three steps to your first response.

1. Create an API key

On a Pro or Agency plan, generate a key at API Keys and copy it — it's shown only once.

2. Verify the connection

curl -H "Authorization: Bearer ss_live_your_key_here" \
     "https://signalsumo.com/api/v1/usage"

A 200 response with your plan and remaining calls means you're connected.

3. Make your first data call

curl -X POST \
     -H "Authorization: Bearer ss_live_your_key_here" \
     -H "Content-Type: application/json" \
     -d '{"keyword":"seo audit tool","limit":5}' \
     "https://signalsumo.com/api/v1/keyword-research"
That's it. Every endpoint uses the same Authorization: Bearer header and returns the same JSON envelope — browse the endpoints below for parameters and response fields.

Overview

All API endpoints are served under:

https://signalsumo.com/api/v1/

Requests and responses use JSON. Every response follows the same envelope:

{
  "success": true,
  "data":    { ... },
  "meta":    { "timestamp": "2026-06-26T10:00:00+00:00" }
}

Errors follow the same shape with success: false and an error object instead of data.

Endpoints fall into two groups, and the difference is what they cost you:

  • Research endpoints — /backlinks, /keyword-research, /site-audit — fetch fresh data from outside SignalSumo. They draw feature quota or credits. See Plans & Billing.
  • Your-data endpoints — everything under /rank, /gsc and /ai-visibility — read what your account has already collected. They are plain database reads: they cost no credits and no feature quota, and they are never themselves refused for being over the monthly cap.
Your-data endpoints are free of credits and feature quota, but each call is still recorded in your monthly API call total — and that total is what gates the research endpoints. A long exploratory session can therefore use up the allowance your billed calls need. This applies equally to the MCP connector, which runs on the same meter.

Authentication

Pass your API key in the Authorization header on every request:

Authorization: Bearer ss_live_your_key_here

Alternatively, you may pass it as a query parameter (not recommended for production):

GET /api/v1/usage?api_key=ss_live_your_key_here
Generate and manage your API keys at signalsumo.com/api-keys. Each key is shown only once at creation time — store it securely.

The Authorization: Bearer header is the recommended method. The API also sends permissive CORS headers, so it can be called directly from browser-based tools. Keep secret keys server-side in production.

Plans & Billing

API access is available on the Pro and Agency plans. Two independent meters govern usage — a call is only served when both allow it.

1. Monthly API calls

Every request (including cache hits and errors that reach an endpoint) counts as one API call.

PlanMonthly API callsResets
Pro201st of each month
Agency1001st of each month

Exceeding this returns HTTP 429 (RATE_LIMITED) with a resets_at field in meta. Check your live limit and usage any time via GET /usage.

2. Per-feature quota & credits

Beyond the call meter, the data endpoints each draw from their own monthly feature quota (the same allowance your logged-in dashboard uses). How each behaves when that quota runs out:

EndpointWhen feature quota is exhausted
/keyword-researchReturns 429 QUOTA_EXCEEDED. Never spends credits — safe to loop.
/backlinksFalls back to 1 credit per request. A deep query (page > 1 or limit > 100) always costs 1 credit. No credits → 402 PAYMENT_REQUIRED.
/site-auditReturns 429 QUOTA_EXCEEDED when the monthly crawl count or the rolling page budget is used up.
Cache hits are free of feature quota. Results are cached and shared with your dashboard, so a domain or keyword already looked up recently returns instantly, marked "cached": true in meta, and draws no feature quota or credits (it still counts as one API call).

3. Requests per minute

Separately from the monthly meters, your plan sets how many requests a minute you can make. This bounds a runaway loop or a leaked key to something recoverable — a monthly allowance says nothing about spending all of it inside an hour.

PlanRequests per minute
Pro20
Agency60

Each endpoint counts separately, so working through your backlink allowance never locks you out of keyword research. Exceeding it returns 429 with retry_after (seconds) and limit in meta. Refused requests are not counted, so waiting out retry_after always clears it.

Identical requests are answered once. If several of your requests ask the same question at the same moment, one of them is fetched and the rest are served that same result — charged once, not once each. If the fetch is still running when yours arrives, you get 409 ALREADY_RUNNING; retry in a few seconds and it will be a cache hit.

Locations & Languages

/keyword-research accepts a location_code and language_code to localize results. Both are optional — the defaults are the United States (2840) and English (en).

location_code

A numeric market code. Common values:

CodeMarketCodeMarket
2840United States2276Germany
2826United Kingdom2250France
2124Canada2724Spain
2036Australia2380Italy
2356India2528Netherlands
2392Japan2076Brazil

Over 90 markets are supported. An unrecognized code returns 422 UNSUPPORTED_LOCATION rather than falling back to the United States — being billed for American data you did not ask for is worse than being told the code is wrong. Call GET /api/v1/keyword-research/locations for the full list.

language_code

A two-letter language code. Supported values:

CodeLanguageCodeLanguage
enEnglishptPortuguese
esSpanishnlDutch
frFrenchruRussian
deGermanjaJapanese
itItalianzhChinese
Only the languages listed above are supported. Any other language_code falls back to English rather than returning an error — so double-check the code if results look unexpectedly English.

Error Codes

HTTPcodeMeaning
400BAD_REQUESTMalformed request syntax
401UNAUTHORIZEDMissing or invalid API key
403FORBIDDENAPI access not available on your plan (Pro or Agency required)
404NOT_FOUNDEndpoint or resource not found
402PAYMENT_REQUIREDFeature quota exhausted and no credits remaining (backlinks)
422VALIDATION_ERRORRequired parameter missing or invalid value
409ALREADY_RUNNINGThe same request is already being fetched for your account — retry shortly and it will be a cache hit. Nothing was charged
429RATE_LIMITED / QUOTA_EXCEEDEDMonthly call limit, per-minute pacing or feature quota hit — see resets_at or retry_after in meta
502UPSTREAM_ERRORNo data returned by the upstream data provider
500SERVER_ERRORInternal error — safe to retry

MCP & LLM Clients

If your goal is to let an assistant like Claude read this data conversationally, you do not need to write a client. SignalSumo speaks the Model Context Protocol, which exposes the endpoints below as tools an LLM can call directly.

RouteAuthBest for
Hosted connectorOAuth 2.1 — no key to copyClaude and other MCP clients. Nothing to install, always current.
@signalsumo/mcp on npmAPI key in the client configLocal installs, self-hosted setups, or running against your own environment.
This REST APIAuthorization: BearerYour own scripts, dashboards and back-end integrations.

All three read the same data through the same endpoints and the same plan limits — they differ only in how the caller authenticates. The npm package uses slightly different tool names from the hosted connector; its README is the reference for those. Source: github.com/signalsumo/mcp.

MCP access is governed by its own plan flag, separate from REST API access. Check your plan if a client authenticates but every tool call is refused.

GET /api/v1/usage

GET /api/v1/usage

Returns your API quota usage for the current calendar month.

Example Request

curl -H "Authorization: Bearer ss_live_xxxx" \
     "https://signalsumo.com/api/v1/usage"

Example Response

{
  "success": true,
  "data": {
    "plan": "agency",
    "api_calls_used": 42,
    "api_calls_limit": 100,
    "unlimited": false,
    "resets_at": "2026-08-01"
  },
  "meta": {
    "timestamp": "2026-06-26T10:00:00+00:00",
    "key_name": "Production App"
  }
}
GET /api/v1/backlinks?domain={domain}

Returns a domain's backlink profile: a summary overview, distribution insights, and an enriched list of individual backlinks. Results are cached and shared with your dashboard (Pro: 2 days, Agency: 7 days); a cache hit is marked "cached": true and costs no feature quota.

This is the same data the Backlink Checker shows in the app, and the cache is shared — a domain you have already looked at there returns instantly here, free.

A standard request draws your monthly backlink-search quota (then 1 credit if that's exhausted). A deep request — page > 1 or limit > 100 — always costs 1 credit. See Plans & Billing.

Query Parameters

ParameterTypeRequiredDescription
domainstringYesRoot domain, e.g. example.com (a full URL is also accepted and normalized)
pageintegerNoPage number, default 1
limitintegerNoResults per page, 1–200, default 100

Example Request

curl -H "Authorization: Bearer ss_live_xxxx" \
     "https://signalsumo.com/api/v1/backlinks?domain=example.com&limit=2"

Example Response

{
  "success": true,
  "data": {
    "domain": "example.com",
    "total_backlinks": 48210,
    "referring_domains": 1824,
    "page": 1,
    "limit": 2,
    "overview": {
      "domain_rank": 71,
      "total_backlinks": 48210,
      "referring_domains": 1824,
      "referring_domains_nofollow": 640,
      "referring_main_domains": 1610,
      "referring_pages": 39044,
      "referring_ips": 1502,
      "referring_subnets": 1210,
      "dofollow_backlinks": 1184,
      "broken_backlinks": 220,
      "broken_pages": 96,
      "spam_score": 8,
      "first_seen": "2018-05-11 09:20:00",
      "lost_date": null
    },
    "insights": {
      "referring_links_types":          { "anchor": 30140, "image": 9800 },
      "referring_links_attributes":     { "noopener": 12000, "nofollow": 8400 },
      "referring_links_platform_types": { "blogs": 900, "cms": 640, "news": 210 },
      "referring_links_tld":            { "com": 1200, "org": 180 },
      "referring_links_countries":      { "US": 800, "GB": 210 },
      "referring_subnets": 1210,
      "referring_main_domains": 1610
    },
    "backlinks": [
      {
        "domain_from": "blog.example-news.com",
        "url_from": "https://blog.example-news.com/best-seo-tools",
        "url_to": "https://example.com/",
        "anchor": "SignalSumo",
        "dofollow": 1,
        "spam_score": 4,
        "first_seen": "2021-03-10 12:00:00",
        "last_seen": "2026-06-20 04:00:00",
        "semantic_location": "article",
        "platform_type": "blogs",
        "country": "US",
        "is_broken": 0,
        "domain_from_rank": 62,
        "page_from_rank": 41,
        "is_lost": 0,
        "lost_date": null,
        "is_new": 0,
        "item_type": "anchor",
        "page_from_title": "The Best SEO Tools in 2026",
        "page_from_status_code": 200,
        "url_to_status_code": 200,
        "domain_from_ip": "192.0.2.10",
        "tld_from": "com",
        "links_count": 2,
        "is_indirect_link": 0,
        "url_from_https": 1,
        "attributes": ["noopener"],
        "text_pre": "we recommend",
        "text_post": "for site audits",
        "kw_top3": 4,
        "kw_top10": 12,
        "kw_top100": 88
      }
    ]
  },
  "meta": { "timestamp": "2026-06-26T10:00:00+00:00", "cached": false }
}

Backlink Fields

FieldDescription
domain_from / url_fromThe linking site and the exact page the link is on
url_toThe page on your domain being linked to
anchorAnchor text of the link
dofollow1 = dofollow, 0 = nofollow
spam_scoreSpam score of the linking page (0–100)
domain_from_rank / page_from_rankAuthority of the linking domain / page, normalized 0–100
is_new / is_lost / is_brokenLink lifecycle flags (1/0)
first_seen / last_seen / lost_dateWhen the link was first seen, last seen, and (if applicable) lost
platform_type / country / tld_fromLinking site's platform, country, and TLD
item_typeLink form: anchor, image, redirect, …
page_from_status_code / url_to_status_codeHTTP status of the linking page / the linked page
attributesLink rel attributes, e.g. ["noopener","nofollow"]
kw_top3 / kw_top10 / kw_top100Keywords the linking page ranks for in the top 3 / 10 / 100

POST /api/v1/keyword-research

POST /api/v1/keyword-research

Returns a full metric set for a seed keyword — search volume, CPC, competition, difficulty, search intent, a 12-month volume trend, SERP features and more — plus a ranked list of related keywords carrying the same fields. Synchronous — responds immediately.

These are the metrics the Keyword Research Tool displays. Once you have picked the keywords worth pursuing, the Rank Tracker follows their positions daily — and /rank/keywords reads those positions back out, free.

This endpoint draws your monthly keyword-search quota and never spends credits: once the quota is used up it returns 429 QUOTA_EXCEEDED, so it's safe to call in a loop. Recent lookups are served from cache (marked "cached": true) at no quota cost.

Request Body (JSON)

FieldTypeRequiredDescription
keywordstringYesSeed keyword, max 200 chars
location_codeintegerNoLocation code, default 2840 (United States)
language_codestringNoLanguage code, default en
limitintegerNoMax related keywords returned, 1–100, default 10

Example Request

curl -X POST \
     -H "Authorization: Bearer ss_live_xxxx" \
     -H "Content-Type: application/json" \
     -d '{"keyword":"seo audit tool","limit":5}' \
     "https://signalsumo.com/api/v1/keyword-research"

Example Response

{
  "success": true,
  "data": {
    "keyword": "seo audit tool",
    "location_code": 2840,
    "language_code": "en",
    "total_count": 80,
    "overview": {
      "search_volume": 2900,
      "cpc": 19.07,
      "competition": 0.11,
      "competition_level": "LOW",
      "difficulty": 77,
      "intent": ["Commercial"],
      "trend": [ { "y": 2026, "m": 5, "v": 2400 }, { "y": 2026, "m": 6, "v": 1600 } ],
      "serp_features": ["organic"],
      "growth": { "m": -33, "q": -16, "y": -64 },
      "bid_low": 4.72,
      "bid_high": 19.10,
      "avg_backlinks": 5087,
      "avg_ref_domains": 970,
      "comp_domain_rank": 486,
      "se_results": 41000000
    },
    "related_keywords": [
      {
        "keyword": "free seo audit tool",
        "search_volume": 1900,
        "cpc": 3.10,
        "competition": 0.68,
        "competition_level": "MEDIUM",
        "difficulty": 54,
        "intent": ["Commercial", "Transactional"],
        "trend": [ { "y": 2026, "m": 6, "v": 1900 } ],
        "serp_features": ["organic", "people_also_ask"],
        "growth": { "m": 4, "q": -2, "y": 11 },
        "bid_low": 1.20,
        "bid_high": 4.90,
        "avg_backlinks": 210,
        "avg_ref_domains": 88,
        "comp_domain_rank": 402,
        "se_results": 12000000,
        "related": ["website audit tool", "seo checker free"],
        "match_bucket": "similar"
      }
    ]
  },
  "meta": { "timestamp": "2026-06-26T10:00:00+00:00" }
}

Keyword Fields

The overview (seed keyword) and every related_keywords row share these fields. Any field may be null when the upstream data is unavailable.

FieldDescription
search_volumeAverage monthly searches
cpcAverage cost-per-click (USD)
competitionPaid competition, 0–1
competition_levelLOW / MEDIUM / HIGH
difficultyOrganic ranking difficulty, 0–100 (null if not yet computed)
intentSearch intent, e.g. ["Commercial"]
trend12-month volume history — { y, m, v } per month
growthVolume change %: m monthly, q quarterly, y yearly
serp_featuresSERP feature types present, e.g. ["organic","people_also_ask"]
bid_low / bid_highTop-of-page bid range (USD)
avg_backlinks / avg_ref_domainsAverage backlinks / referring domains of the pages ranking now
comp_domain_rankAverage authority (0–1000) of the pages ranking now
se_resultsTotal competing results for the keyword
relatedUp to 8 related sub-keywords (related rows only)
match_bucketRelation to the seed: similar, related, or question

POST /api/v1/site-audit

POST /api/v1/site-audit

Runs the same crawl as the Website Audit Tool — broken links, redirect chains, missing titles and canonical problems, page by page.

Site audits are asynchronous. This endpoint returns a job_id immediately. Poll GET /api/v1/jobs/{job_id} for results. Typical crawl time: 1–5 minutes.

Request Body (JSON)

FieldTypeRequiredDescription
urlstringYesFull URL to crawl, e.g. https://example.com
max_pagesintegerNoMax pages to crawl (default 100). Capped by your plan's per-crawl limit and your remaining monthly page budget.
depthintegerNoCrawl depth, 1–5, default 3

Example Request

curl -X POST \
     -H "Authorization: Bearer ss_live_xxxx" \
     -H "Content-Type: application/json" \
     -d '{"url":"https://example.com","max_pages":50}' \
     "https://signalsumo.com/api/v1/site-audit"

Response (202 Accepted)

{
  "success": true,
  "job_id": "a3f9b2c1d4e5f6a7b8c9d0e1f2a3b4c5",
  "status": "queued",
  "poll": "https://signalsumo.com/api/v1/jobs/a3f9b2c1d4e5f6a7b8c9d0e1f2a3b4c5",
  "meta": { "timestamp": "2026-06-26T10:00:00+00:00" }
}

GET /api/v1/jobs/{job_id}

GET /api/v1/jobs/{job_id}

Poll the status of an async job. Poll every 10–15 seconds until status is complete or failed.

Status Values

StatusMeaning
queuedJob accepted, not yet started
runningCrawl in progress — check progress (0–100)
completeDone — data field contains results
failedCrawl failed — error field has the reason

Example Request

curl -H "Authorization: Bearer ss_live_xxxx" \
     "https://signalsumo.com/api/v1/jobs/a3f9b2c1d4e5f6a7b8c9d0e1f2a3b4c5"

Response — Running

{
  "success": true,
  "data": {
    "job_id": "a3f9b2c1d4e5f6a7b8c9d0e1f2a3b4c5",
    "endpoint": "site-audit",
    "status": "running",
    "progress": 42,
    "created_at": "2026-06-26 10:00:00",
    "updated_at": "2026-06-26 10:01:30",
    "completed_at": null
  },
  "meta": { "timestamp": "2026-06-26T10:01:30+00:00" }
}

Response — Complete

{
  "success": true,
  "data": {
    "job_id": "a3f9b2c1d4e5f6a7b8c9d0e1f2a3b4c5",
    "endpoint": "site-audit",
    "status": "complete",
    "progress": 100,
    "created_at": "2026-06-26 10:00:00",
    "updated_at": "2026-06-26 10:03:18",
    "completed_at": "2026-06-26 10:03:18",
    "data": {
      "audit_id": "AUDIT-A1B2C3-4567",
      "domain": "https://example.com",
      "pages_crawled": 47,
      "total_score": 78,
      "geo_score": 71,
      "issues": { "critical": 3, "warning": 12, "total": 15 },
      "report_url": "https://signalsumo.com/report?id=AUDIT-A1B2C3-4567"
    }
  },
  "meta": { "timestamp": "2026-06-26T10:03:18+00:00" }
}

GET /api/v1/rank/keywords

GET /api/v1/rank/keywords

Every keyword your account tracks, each with the market and device it was measured in and its most recent position. This is the data behind the Rank Tracker — no crawl is triggered and nothing is charged.

country, language and device are returned on every row on purpose. A position is meaningless without them — comparing a desktop US ranking against a mobile UK one produces a decline that never happened.

Query Parameters

ParameterTypeRequiredDescription
project_idintegerNoRestrict to one project. Omit to return keywords across every project you own.
limitintegerNo1–500, default 100

Example Request

curl -H "Authorization: Bearer ss_live_xxxx" \
     "https://signalsumo.com/api/v1/rank/keywords?limit=1"

Example Response

{
  "success": true,
  "data": {
    "keywords": [
      {
        "keyword_id": 4821,
        "keyword": "seo audit tool",
        "difficulty": 62,
        "search_volume": 8100,
        "cpc": 12.4,
        "country": "US",
        "language": "en",
        "device": "desktop",
        "status": "active",
        "project_id": 17,
        "project_domain": "example.com",
        "project_name": "Example Site",
        "last_checked": "2026-08-19",
        "current_position": 8,
        "previous_position": 11,
        "position_change": 3,
        "landing_page": "https://example.com/tools/audit",
        "gsc_clicks": 143,
        "gsc_impressions": 5210,
        "gsc_ctr": 2.74,
        "local_pack": false,
        "featured_snippet": true,
        "ai_overview": false
      }
    ],
    "count": 1
  },
  "meta": { "project_id": null, "limit": 1 }
}

position_change is positive when the keyword moved up. A keyword that has never been checked returns null for every latest.* field rather than 0 — position zero would read as "ranked first".

GET /api/v1/rank/history

GET /api/v1/rank/history?keyword_id={id}

Daily position history for one tracked keyword, with the URL that ranked and the SERP features present on each check. Includes a precomputed trend summary.

Query Parameters

ParameterTypeRequiredDescription
keyword_idintegerYesFrom /rank/keywords. A keyword you do not own returns 404 NOT_FOUND.
daysintegerNo1–365, default 90

Example Request

curl -H "Authorization: Bearer ss_live_xxxx" \
     "https://signalsumo.com/api/v1/rank/history?keyword_id=4821&days=30"

Example Response

{
  "success": true,
  "data": {
    "keyword": {
      "id": 4821,
      "keyword": "seo audit tool",
      "project_id": 17,
      "domain": "example.com",
      "project": "Example Site"
    },
    "history": [
      {
        "check_date": "2026-08-19",
        "position": 8,
        "previous_position": 11,
        "change": 3,
        "landing_page": "https://example.com/tools/audit",
        "gsc_clicks": 143,
        "gsc_impressions": 5210,
        "gsc_ctr": 2.74,
        "gsc_average_position": 8.6,
        "featured_snippet": true,
        "ai_overview": false,
        "people_also_ask": true,
        "local_pack": false,
        "images": false,
        "videos": false,
        "shopping": false,
        "knowledge_graph": false,
        "sitelinks": true
      }
    ],
    "trend": { "first": 14, "latest": 8, "best": 6, "worst": 15 }
  },
  "meta": { "days_requested": 30, "points": 1 }
}

Reading trend

FieldMeaning
firstPosition at the oldest point in the window
latestPosition at the most recent check
bestLowest number reached (best ranking)
worstHighest number reached (worst ranking)

history is ordered newest first. best and worst ignore unranked checks, so they are null when the keyword never ranked in the window.

GET /api/v1/gsc/properties

GET /api/v1/gsc/properties

The Google Search Console properties connected to your account, as shown in Search Console Insights. Takes no parameters.

This reads SignalSumo's synced copy — it does not call Google. last_synced_at tells you how fresh that copy is.

Example Request

curl -H "Authorization: Bearer ss_live_xxxx" \
     "https://signalsumo.com/api/v1/gsc/properties"

Example Response

{
  "success": true,
  "data": {
    "properties": [
      {
        "property_id": 31,
        "property_uri": "sc-domain:example.com",
        "display_name": "example.com",
        "status": "active",
        "last_synced_at": "2026-08-20 04:15:02",
        "last_sync_status": "ok",
        "google_email": "[email protected]",
        "is_sandbox": false
      }
    ],
    "count": 1
  }
}

GET /api/v1/gsc/queries

GET /api/v1/gsc/queries?property_id={id}

Clicks, impressions, CTR and average position for a synced property, grouped by whichever dimension you ask for. Despite the name it is not limited to queries — set dim to break the same numbers down by page, country, device or date.

Query Parameters

ParameterTypeRequiredDescription
property_idintegerYesFrom /gsc/properties. A property you do not own returns 404 NOT_FOUND.
dimstringNoquery (default), page, country, device, date, searchAppearance
fromstringNoYYYY-MM-DD, default 28 days ago
tostringNoYYYY-MM-DD, default today
sortstringNoclicks (default), impressions, ctr, position, key
dirstringNodesc (default) or asc
limitintegerNo1–500, default 100
A malformed date returns 422 VALIDATION_ERROR, but an unrecognised dim or sort value silently falls back to the default rather than erroring. Check the dim echoed back in data before trusting a breakdown.

Example Request

curl -H "Authorization: Bearer ss_live_xxxx" \
     "https://signalsumo.com/api/v1/gsc/queries?property_id=31&dim=query&limit=2"

Example Response

{
  "success": true,
  "data": {
    "property_id": 31,
    "dim": "query",
    "from": "2026-07-23",
    "to": "2026-08-20",
    "totals": {
      "clicks": 4820,
      "impressions": 216400,
      "ctr": 2.23,
      "position": 18.4
    },
    "rows": [
      { "key": "seo audit tool", "clicks": 143, "impressions": 5210, "ctr": 2.74, "position": 8.6 },
      { "key": "free seo checker", "clicks": 98,  "impressions": 7740, "ctr": 1.27, "position": 14.2 }
    ],
    "total_rows": 1842
  },
  "meta": { "sort": "clicks", "dir": "desc", "limit": 2 }
}

total_rows is the number of distinct keys in the period, not the number returned — use it to decide whether to raise limit. totals always covers the whole period regardless of limit.

GET /api/v1/ai-visibility/projects

GET /api/v1/ai-visibility/projects

The brands you track across AI answer engines, one row per project, with the current visibility score and its movement. Takes no parameters. Powered by the AI Visibility Checker.

Scores are collected by scheduled scans, so this endpoint never calls an AI provider — it returns what the last scan stored.

Example Request

curl -H "Authorization: Bearer ss_live_xxxx" \
     "https://signalsumo.com/api/v1/ai-visibility/projects"

Example Response

{
  "success": true,
  "data": {
    "projects": [
      {
        "id": 9,
        "domain": "example.com",
        "brand_name": "Example",
        "target_country": "US",
        "target_language": "en",
        "ai_engine": "all",
        "tracking_frequency": "weekly",
        "status": "active",
        "visibility_score": 41.8,
        "previous_visibility_score": 37.2,
        "score_change": 4.6,
        "created_at": "2026-05-02 11:20:44",
        "updated_at": "2026-08-18 06:02:11"
      }
    ],
    "count": 1
  }
}

score_change is null until a project has been scanned at least twice — a first scan has nothing to compare against, and reporting 0 would read as "no movement".

GET /api/v1/ai-visibility/share-of-voice

GET /api/v1/ai-visibility/share-of-voice?project_id={id}

How often AI assistants name your brand versus the competitors tracked alongside it, plus the full metric breakdown from the most recent scan.

Query Parameters

ParameterTypeRequiredDescription
project_idintegerYesFrom /ai-visibility/projects. A project you do not own returns 404 NOT_FOUND.

Example Request

curl -H "Authorization: Bearer ss_live_xxxx" \
     "https://signalsumo.com/api/v1/ai-visibility/share-of-voice?project_id=9"

Example Response

{
  "success": true,
  "data": {
    "project": {
      "id": 9,
      "domain": "example.com",
      "brand_name": "Example",
      "target_country": "US",
      "ai_engine": "all",
      "visibility_score": 41.8,
      "previous_visibility_score": 37.2
    },
    "latest_snapshot": {
      "geo_score": 41.8,
      "mention_rate": 38.0,
      "avg_position": 2.4,
      "sentiment_avg": 0.62,
      "citation_rate": 21.5,
      "competition_score": 58.3,
      "prompts_evaluated": 50,
      "snapshot_date": "2026-08-18"
    },
    "share_of_voice": {
      "brand_mentions": 19,
      "competitor_mentions": 74,
      "total_mentions": 93,
      "brand_share_pct": 20.43
    },
    "competitors": [
      {
        "competitor_name": "Competitor One",
        "competitor_domain": "competitor1.com",
        "mention_count": 31,
        "visibility_score": 52.1,
        "avg_position": 1.9,
        "updated_at": "2026-08-18 04:41:07"
      }
    ]
  }
}

Two different percentages

These are easy to confuse, and they answer different questions:

FieldQuestion it answers
mention_rateOf the prompts we tested, what share mentioned you at all? — coverage.
brand_share_pctOf every brand mention across you and your tracked competitors, what share was yours? — competitive position.

latest_snapshot is null before a project's first scan completes. competitors returns at most 20, ordered by mention count. brand_share_pct is null when no mentions have been recorded on either side — a share of nothing is not zero percent.

GET /api/v1/keyword-research/locations

GET /api/v1/keyword-research/locations

Every market POST /keyword-research accepts, so you can discover a location_code instead of guessing one and earning a 422. Takes no parameters.

This is deliberately not the full market list. Keyword research runs on the keyword database, which covers fewer markets than SERP work does — publishing the wider list here would only move the failure one call later.

Example Request

curl -H "Authorization: Bearer ss_live_xxxx" \
     "https://signalsumo.com/api/v1/keyword-research/locations"

Example Response

{
  "success": true,
  "data": {
    "locations": [
      { "location_code": 2840, "location_name": "United States", "country_iso": "US", "language_code": "en" },
      { "location_code": 2826, "location_name": "United Kingdom", "country_iso": "GB", "language_code": "en" }
    ],
    "count": 94,
    "note": "These are the markets covered by the keyword database. Pass location_code to POST /keyword-research."
  }
}

Changelog

VersionDateChanges
v1.2Aug 2026Documented the seven read-only endpoints that were already live: /rank/keywords, /rank/history, /gsc/properties, /gsc/queries, /ai-visibility/projects, /ai-visibility/share-of-voice and /keyword-research/locations. No behaviour changed — these were callable before, just undocumented. Added MCP and LLM client guidance.
v1.1Jul 2026Keyword & backlink responses expanded to full metric sets (difficulty, intent, trend, SERP features, enriched backlink fields, overview & insights). Site-audit results add geo_score and issue counts. Added location/language reference and billing details.
v1.0Jun 2026Initial release — usage, backlinks, keyword-research, site-audit, jobs

Questions? Email [email protected]