KYB API and MCP
Structured JSON. Per-field confidence. A source for every finding.
Registry record, directors and owners, sanctions, PEP, leaks and adverse media on any company, from your onboarding flow or your AI agent.
For AI Agents
NewOne-line install. Works with Claude Desktop, Claude Code, Cursor, Windsurf, and any MCP-compatible client.
Tools your AI assistant can call
kyb_findingsFast KYB check: structured findings with sources€0.50kyb_run_submitSubmit a full KYB report, returns a run_id€6.00adverse_mediaNegative news, complaints and reviews€0.50check_report_statusPoll a report submitted with kyb_run_submitfreeaccount_balanceBalance and free calls remainingfreeYour first API call is free. After that, each call is paid from your prepaid balance at the prices above.
Claude Desktop, Cursor, Windsurf
{
"mcpServers": {
"sonar": {
"command": "uvx",
"args": ["getsonar-mcp"],
"env": {
"SONAR_API_KEY": "sk-sonar-YOUR_KEY_HERE"
}
}
}
}~/Library/Application Support/Claude/claude_desktop_config.json%APPDATA%\Claude\claude_desktop_config.json~/.cursor/mcp.json~/.codeium/windsurf/mcp_config.jsonClaude Code
claude mcp add sonar --env SONAR_API_KEY=sk-sonar-YOUR_KEY_HERE -- uvx getsonar-mcpSame API, two ways to call it: directly from your code (curl / Python / Node — see Quickstart below) or as MCP tools the LLM picks autonomously.
Quickstart
One API call, no SDK required.
Sign up and create an API key in Settings. Your first call is free, no card needed.
curl -X POST https://api.getsonar.report/v1/agents/kyb-diligence/findings \
-H "X-API-Key: sk-sonar-your-key-here" \
-H "Content-Type: application/json" \
-d '{"company_name": "Tesco PLC", "website": "https://www.tesco.com"}'{
"request_id": "req_abc123",
"agent": "kyb-diligence",
"company": { "name": "Tesco PLC", "domain": "tesco.com" },
"status": "completed",
"fields": {
"corporate_registration": {
"findings": [{
"value": "TESCO PLC, company no. 00445790, Active, incorporated 27 Nov 1947",
"confidence": "HIGH",
"sources": [{
"provider": "Companies House",
"url": "https://find-and-update.company-information.service.gov.uk/company/00445790",
"excerpt": "Name: TESCO PLC. Reg No: 00445790. Status: Active...",
"retrieved_at": "2026-10-18T09:00:44Z"
}]
}],
"confidence": "HIGH",
"confidence_score": 0.92
},
"sanctions_screening": {
"findings": [{
"value": "No matches across 8 sanctions lists",
"confidence": "MEDIUM",
"sources": [{ "provider": "Sanctions Lists", "url": "" }]
}],
"confidence": "MEDIUM",
"confidence_score": 0.65
}
},
"sources_checked": 23,
"fields_found": ["corporate_registration", "directors_officers", "adverse_media", ...],
"fields_not_found": ["ownership_structure"],
"processing_time_ms": 42150
}website with a full https:// URL. For generic-noun company names ("Next", "Apple", "Shell"), the website is what disambiguates the right entity from unrelated companies that share the brand word. Without it, expect noisy news./findings when your agent does the reasoning : cheaper (€0.50), synchronous, returns raw structured signals and confidence scores./run for the full AI report : €6.00, async, returns a run_id; poll the run-id endpoint until status="completed".website field must be http:// or https://. Bare hostnames, file://, and private/loopback IPs are rejected with HTTP 400.?pretty=1 to any URL to receive indented JSON. Default stays compact (industry convention) so production clients don't pay the bandwidth cost. Example: /v1/usage?pretty=1.Endpoints
/v1/agents/{type}/findingsStructured Findings
Synchronous. Returns structured findings with per-field confidence scores and source citations, without a written summary. For when your own code or agent makes the call.
Details
Response time: 30–90 seconds
Cost: €0.50 per call
No Claude AI required
/v1/agents/{type}/runFull AI Report
Async. Returns 202 with a run_id; poll for the result. Adds the written KYB report: verdict per check, summary and risk flags.
Details
Response time: 60–180 seconds
Cost: €6 per call
Full narrative + structured findings
Supporting Endpoints
/v1/agents/{type}/run/{run_id}/v1/auth/keys/v1/auth/keys/v1/auth/keys/{id}/v1/usageAgent Types
kyb-diligence runs the full screen. adverse-media runs the press and legal-coverage search alone, for a quick check or ongoing monitoring of a name.
kyb-diligence50+ sourcesCorporate registration, directors & officers, ownership / UBO structure, sanctions + PEP screening (8 lists + OpenSanctions), 10-year adverse media, registry crossmatch, regulatory status, and 12-row PASS/REVIEW/FLAG verification check summary.
Output Fields
Companies House (UK), GLEIF/LEI, OpenSanctions, OpenSanctions PEP, ICIJ Offshore Leaks, FCA Register, Gazette UK, EPA ECHO, SEC EDGAR, KVK (NL), SIRENE (FR), KRS (PL), ARES (CZ), BrasilAPI, ACRA (SG), KBO (BE), Brreg (NO), CRO (IE), CVR (DK), PRH (FI), Wayback Machine, Brave Search, and more.
adverse-mediaPress and review sourcesLegal, regulatory and press coverage on any entity, with a 10-year window tuned for compliance canaries, so historical fraud and enforcement coverage surfaces.
Output Fields
Google News, Brave Search, adverse-media search with legal and regulatory terms, Clutch.co, and more.
Compliance Accuracy
Every response includes a field_status map with three values. The distinction between checked_clean (we screened, no hits) and not_run (this source is not enabled for this agent type) is the difference between PASS and "we never looked." This is what compliance officers audit. We surface it explicitly so your agent does not confuse the two.
foundSources fired and emitted at least one matching signal. Surface as a finding.
checked_cleanSources fired and returned zero hits. Surface as a clean result with confidence.
not_runSource not enabled for this agent type. Surface as a data gap, NEVER as "clean."
Example response (Tesco PLC, kyb-diligence)
{
"field_status": {
"corporate_registration": "found", // ✓ Companies House returned a match
"sanctions_screening": "checked_clean", // ✓ screened across 8 lists, no hits
"directors_officers": "found",
"ownership_structure": "checked_clean", // ✓ no PSC records (typical for listed PLC)
"pep_screening": "checked_clean",
"adverse_media": "found", // ✓ historical 10-yr coverage included
"ubo_network": "checked_clean",
"reputation": "not_run" // ⚠ source unavailable, NOT a clean result
}
}Why this matters: An audit asks: "did you screen this counterparty against sanctions?" checked_clean is a defensible yes. not_run means "we did not check". Treating the two the same is the most common KYB tooling failure, so SONAR keeps them apart in every response.
UK beneficial-ownership index
13.5M-record local mirror of the UK Companies House PSC register, refreshed every Sunday from the official CH bulk-data snapshot. Sub-100ms reverse-UBO and cross-company lookups — no per-call rate limit, no 200-500ms live-API tax, full historical coverage back to 2016.
Historical Coverage
Many KYB tools search 30 days or one year of news. Compliance teams test with Wirecard, Theranos and FTX, the largest scandals of the last decade, and a short window silently drops them and returns a false clean. SONAR searches 10 years for KYB and adverse-media checks because that is what passes the test.
Canary subjects we surface correctly
Authentication
Every request requires an API key in the X-API-Key header. Keys start with sk-sonar- and are tied to your SONAR account.
# Include your API key in every request
curl -H "X-API-Key: sk-sonar-a1b2c3d4e5f6g7h8..." \
https://api.getsonar.report/v1/agents/kyb-diligence/findingsRate limits
Returned in every response via X-RateLimit-* headers. Limit: 10 requests a minute per key.
Error codes
401 (invalid key), 402 (balance too low, top up), 429 (rate limited), 404 (unknown agent), 500 (processing failed).
Key management
Create, list, and revoke keys via the /v1/auth/keys endpoints. Max 5 active keys per account.
Data Quality
Every field in the response includes a confidence score based on source reliability, data freshness, and cross-source corroboration. Build your own thresholds.
Score 0.75+
2+ independent authoritative sources agree (national registers, GLEIF, SEC filings)
Score 0.50–0.75
1 authoritative source or 2+ secondary sources agree
Score <0.50
Single secondary source only, or sources partially conflict
Code Examples
Python
import requests
response = requests.post(
"https://api.getsonar.report/v1/agents/kyb-diligence/findings",
headers={
"X-API-Key": "sk-sonar-your-key-here",
"Content-Type": "application/json",
},
json={
"company_name": "Wirecard AG",
"website": "wirecard.com",
},
)
data = response.json()
# Check sanctions screening
sanctions = data["fields"]["sanctions_screening"]
if sanctions["confidence"] == "HIGH":
print(f"ALERT: {len(sanctions['findings'])} sanctions flags found")
# Check overall risk
for field_name, field_data in data["fields"].items():
if field_data["findings"]:
print(f"{field_name}: {field_data['confidence']} confidence")JavaScript / Node.js
const response = await fetch(
"https://api.getsonar.report/v1/agents/adverse-media/findings",
{
method: "POST",
headers: {
"X-API-Key": "sk-sonar-your-key-here",
"Content-Type": "application/json",
},
body: JSON.stringify({
company_name: "Acme Corp",
}),
}
);
const data = await response.json();
// Filter high-confidence findings only
const highConfidence = Object.entries(data.fields)
.filter(([_, field]) => field.confidence === "HIGH")
.flatMap(([_, field]) => field.findings);
console.log(`Found ${highConfidence.length} high-confidence findings`);Async Report (curl)
# 1. Submit the run
curl -X POST https://api.getsonar.report/v1/agents/kyb-diligence/run \
-H "X-API-Key: sk-sonar-your-key-here" \
-H "Content-Type: application/json" \
-d '{"company_name": "Tesco PLC", "website": "https://www.tesco.com"}'
# Response: {"run_id": "abc123", "status": "pending", "poll_url": "/v1/agents/..."}
# 2. Poll for results
curl https://api.getsonar.report/v1/agents/kyb-diligence/run/abc123 \
-H "X-API-Key: sk-sonar-your-key-here"
# Response when complete: {"status": "completed", "result": {...}, "report": {...}}LangChain — wrap as a tool
from langchain_core.tools import tool
import requests
SONAR_KEY = "sk-sonar-your-key-here"
@tool
def kyb_screen(company_name: str, website: str = "") -> dict:
"""Run a KYB compliance screening on a company.
Returns sanctions, FCA registration, directors, UBOs, adverse media."""
r = requests.post(
"https://api.getsonar.report/v1/agents/kyb-diligence/findings",
headers={"X-API-Key": SONAR_KEY},
json={"company_name": company_name, "website": website},
timeout=120,
)
return r.json()
# Now bind to your agent (works with any LangChain LLM):
from langchain_anthropic import ChatAnthropic
llm = ChatAnthropic(model="claude-sonnet-5-5").bind_tools([kyb_screen])
response = llm.invoke("Run a KYB check on Tesco PLC and tell me what's risky.")Anthropic Python SDK — native tool use
from anthropic import Anthropic
import requests
SONAR_KEY = "sk-sonar-your-key-here"
client = Anthropic()
response = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=16000,
tools=[{
"name": "kyb_screen",
"description": "KYB compliance screening: sanctions, FCA, directors, UBOs, adverse media.",
"input_schema": {
"type": "object",
"properties": {
"company_name": {"type": "string"},
"website": {"type": "string"},
},
"required": ["company_name"],
},
}],
messages=[{"role": "user", "content": "Screen Tesco PLC."}],
)
# When Claude calls the tool, dispatch to SONAR:
for block in response.content:
if block.type == "tool_use" and block.name == "kyb_screen":
result = requests.post(
"https://api.getsonar.report/v1/agents/kyb-diligence/findings",
headers={"X-API-Key": SONAR_KEY},
json=block.input,
).json()
# ...continue the conversation with the resultOpenAI — function calling
from openai import OpenAI
import requests, json
SONAR_KEY = "sk-sonar-your-key-here"
client = OpenAI()
tools = [{
"type": "function",
"function": {
"name": "kyb_screen",
"description": "KYB screening: sanctions, FCA, directors, UBOs, adverse media.",
"parameters": {
"type": "object",
"properties": {
"company_name": {"type": "string"},
"website": {"type": "string"},
},
"required": ["company_name"],
},
},
}]
response = client.chat.completions.create(
model="gpt-4.1",
tools=tools,
messages=[{"role": "user", "content": "Screen Tesco PLC."}],
)
# Dispatch the tool call to SONAR
call = response.choices[0].message.tool_calls[0]
args = json.loads(call.function.arguments)
result = requests.post(
"https://api.getsonar.report/v1/agents/kyb-diligence/findings",
headers={"X-API-Key": SONAR_KEY},
json=args,
).json()What is the difference between /findings and /run?
/findings returns structured findings with confidence scores in 30 to 90 seconds, without a written summary. /run adds the written KYB report (verdict per check, summary, risk flags) and takes 60 to 180 seconds.
How is confidence scored?
Each field gets HIGH, MEDIUM or LOW based on source reliability, data freshness and cross-source agreement. Official registers (Companies House, GLEIF, SEC) score highest. Independent sources confirming the same fact raise confidence.
What format does the API return?
JSON with per-field confidence scores and source citations. Every finding carries the provider name, source URL, a text excerpt and the retrieval timestamp.
How long does a request take?
/findings takes 30 to 90 seconds (source collection only). /run takes 60 to 180 seconds (collection plus the written report) and returns a run_id at once, which you poll.
Is there a sandbox?
Your free first call runs against real data: use it to verify your integration, then top up your balance to continue. There is no separate sandbox environment.
What are the rate limits?
10 requests a minute per API key, on every plan. Rate-limit headers come with every response.
Your first call is free. No card required.
Screen any company from your own code.