Skip to main content

KYB API and MCP

KYB checks as an API.

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

New

Plug SONAR into your AI agent.

One-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.50
  • kyb_run_submitSubmit a full KYB report, returns a run_id€6.00
  • adverse_mediaNegative news, complaints and reviews€0.50
  • check_report_statusPoll a report submitted with kyb_run_submitfree
  • account_balanceBalance and free calls remainingfree

Your 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"
      }
    }
  }
}
  • Claude Desktop (macOS): ~/Library/Application Support/Claude/claude_desktop_config.json
  • Claude Desktop (Windows): %APPDATA%\Claude\claude_desktop_config.json
  • Cursor: ~/.cursor/mcp.json
  • Windsurf: ~/.codeium/windsurf/mcp_config.json

Claude Code

claude mcp add sonar --env SONAR_API_KEY=sk-sonar-YOUR_KEY_HERE -- uvx getsonar-mcp

Same 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

Your first KYB check in three steps

One API call, no SDK required.

1

Get your API key

Sign up and create an API key in Settings. Your first call is free, no card needed.

2

Call the API

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"}'
3

Read the structured result

{
  "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
}

Tips for clean results

  • Always pass 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.
  • Use /findings when your agent does the reasoning : cheaper (€0.50), synchronous, returns raw structured signals and confidence scores.
  • Use /run for the full AI report : €6.00, async, returns a run_id; poll the run-id endpoint until status="completed".
  • First call free: your first call is free, whatever the endpoint. After that each call is paid from your prepaid balance (top up from €5). The same balance pays for reports in the web app, and Team or Agency plans add a monthly allowance of web reports.
  • SSRF guard: the website field must be http:// or https://. Bare hostnames, file://, and private/loopback IPs are rejected with HTTP 400.
  • Pretty output for terminal exploration: append ?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

Two endpoints per agent

POST/v1/agents/{type}/findings

Structured 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

POST/v1/agents/{type}/run

Full 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

GET/v1/agents/{type}/run/{run_id}
POST/v1/auth/keys
GET/v1/auth/keys
DELETE/v1/auth/keys/{id}
GET/v1/usage

Agent Types

Two agents, one API

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 Due Diligence

kyb-diligence50+ sources

Corporate 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

corporate_registrationdirectors_officersownership_structureubo_networkregistry_crossmatchverification_checkssanctions_screeningpep_screeningadverse_mediafinancial_signalsregulatory_statusdomain_historyreputation

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 Media

adverse-mediaPress and review sources

Legal, 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

adverse_newsnegative_reviewscomplaintssocial_media_negative

Google News, Brave Search, adverse-media search with legal and regulatory terms, Clutch.co, and more.

Compliance Accuracy

field_status: clean versus not checked

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.

found

Sources fired and emitted at least one matching signal. Surface as a finding.

checked_clean

Sources fired and returned zero hits. Surface as a clean result with confidence.

not_run

Source 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

A 10-year adverse-media window, by design

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

  • ✓Wirecard AG — Bloomberg/FT/Reuters fraud coverage from 2020 collapse
  • ✓Theranos — SEC enforcement + criminal trial coverage (2015–2018)
  • ✓FTX — bankruptcy + SBF trial coverage (2022–2024)
  • ✓Tesco PLC — accounting scandal coverage (2014)

Authentication

API key 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/findings

Rate 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

Per-field confidence scoring

Every field in the response includes a confidence score based on source reliability, data freshness, and cross-source corroboration. Build your own thresholds.

HIGH

Score 0.75+

2+ independent authoritative sources agree (national registers, GLEIF, SEC filings)

MEDIUM

Score 0.50–0.75

1 authoritative source or 2+ secondary sources agree

LOW

Score <0.50

Single secondary source only, or sources partially conflict

Code Examples

Works with any language

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 result

OpenAI — 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()

Frequently asked questions

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.

Ready to build?

Your first call is free. No card required.

Screen any company from your own code.