Reference

The ctscout.dev API and MCP server, endpoint by endpoint

Every endpoint below is served from the same Worker. Keyed routes take X-API-Key; a free key comes from the box on the front page, no account. Contract changes are dated in the API changelog and surfaced as the X-API-Version header on /scan.

Search: POST /scan

# Search by company name
curl -X POST https://ctscout.dev/scan \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_KEY" \
  -d '{"company_name": "Goldman Sachs"}'

# Search by domain
curl ... -d '{"seed_domain": ["gs.com"]}'

# Both (OR logic)
curl ... -d '{"company_name": "Goldman Sachs", "seed_domain": ["gs.com"]}'

A result row is an apex domain with the organization its OV/EV certificates name, a certificate count and a subdomain count. Free keys see the top 5 rows over the last 90 days; Pro keys the top 25 over up to 12 months (tiers). A query counts against the tier's lookup quota only when it returns at least one row; remaining_quota on a batch and GET /keys/usage report what is left in the period (a day on Free, a month on Pro). A batch is refused whole when it could overrun what is left, as if every query would succeed; if another call takes the remaining quota while it runs, it is refused rather than part-charged. A separate per-day request guard, set above the quota and charged for refused requests too, stops runaway scripts.

Batch: POST /scan/batch

# Up to 10 queries per request, single auth; each query that returns a result counts as one lookup
curl -X POST https://ctscout.dev/scan/batch \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_KEY" \
  -d '{
    "queries": [
      {"company_name": "Goldman Sachs", "strict_match_org_only": true},
      {"company_name": "Morgan Stanley", "strict_match_org_only": true},
      {"seed_domain": ["gs.com"]}
    ]
  }'
# Response: { "results": [ {"query": {...}, "domains": [...], ...}, ... ],
#             "remaining_quota": 7 }

Batch semantics: one auth check, per-query parallel execution (up to 10 in flight), and one lookup charged per query that returns at least one row, recorded before the batch is returned; an empty answer costs nothing. The batch is checked up front as if every query will succeed: it is refused whole with a 429 when len(queries) exceeds the lookups left in the period, and the per-day request guard is charged len(queries) whether the batch runs or not. The whole batch returns 400, with nothing charged, if any query fails validation or if len > 10. The MCP equivalent is ctscout_search_company_batch, available through both hosted transports and the local package, with the same ten-query limit. Use strict_match_org_only: true on each query when you want certificate-organization matching without semantic fallback.

Deep-dive jobs (Pro, asynchronous)

# Queue a deep-dive: same body as /scan (company_name and/or up to 10 seed_domain)
curl -X POST https://ctscout.dev/jobs \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_PRO_KEY" \
  -d '{"company_name": "Goldman Sachs"}'
# 202 { "job_id": "…", "status": "queued", "submitted_at": "…", "poll": "/jobs/…" }

# Poll (backoff: 30s → 5min). `result` appears once status is "done".
curl -H "X-API-Key: YOUR_PRO_KEY" https://ctscout.dev/jobs/JOB_ID
# { "job_id", "kind", "status": "queued|running|done|failed",
#   "submitted_at", "started_at", "finished_at", "result"?, "error"? }

# Your last 50 jobs
curl -H "X-API-Key: YOUR_PRO_KEY" https://ctscout.dev/jobs

A job is picked up by a batch worker (roughly every 10 minutes) and runs the Pro enrichment off the request path — DNS, RDAP, IP/ASN, homepage-metadata and favicon corroboration, with snapshot, signals_attempted, signal_health and confidence_band computed in one place. Result fields match Pro /scan plus those four. 20 jobs per key per day; jobs never count as lookups. Only the submitting key can read a job. VLM verdicts are not included yet. Free keys get a 403 — the synchronous /scan path stays edge-only for every tier.

LEI and vendor customers (research product)

# One LEI: GLEIF legal name, country, ISIN count, first/last seen, confirmed vendors,
# a capped domain sample, and the URL of its answer page. No key.
curl https://ctscout.dev/lei/2138006IPPRD4N6XLT30

# Name to LEI (exact, then normalized; capped at 20). No key.
curl "https://ctscout.dev/lei?name=ABSA%20Group"
# { "query": "…", "name_match": "exact" | "normalized" | "none", "leis": [...], "lei_count": 1, "limit": 20, "truncated": false, ... }

# One vendor: candidate and DNS-confirmed customer counts, country distribution, co-use, a capped sample
curl https://ctscout.dev/vendors/salesforce

# The enumeration — any active key: Free gets the first 100 rows of each list with the true counts and truncated: true
# when rows were dropped, Pro the whole list
curl -H "X-API-Key: YOUR_KEY" https://ctscout.dev/vendors/salesforce/customers
# { "confirmed": [{"apex": "…", "attributed_to": "…"|null, "lei": "…"|null}], "candidates": rows of the same shape,
#   "counts": {"candidates": N, "confirmed": N}, "capped": false,        # capped: true = candidates is a hash-chosen subset
#   "truncated": false,                                                  # true only when a Free slice dropped rows
#   "free_slice": {"rows": 100, "full_list": "pro"} | null, ... }        # null on Pro

These serve precomputed objects from the weekly research refresh, not the /scan warehouse: nothing is matched or counted at request time. Every response carries product_version, as_of and snapshot_dates (GLEIF, Wikidata, ISIN, ELF, PSL), and uses the notes' vocabulary — attributed, candidate, confirmed, never "owns". Counts and samples are free; the customer list is the keyed boundary. Until the product is published the routes answer 503, never a guess. The same objects back the human-readable pages: one per organization at /research/companies/<lei>, one per confirmed vendor at /research/vendors/<slug>.

MCP

Use ctscout from Claude Code, Claude Desktop, Cursor, or any client that speaks the Model Context Protocol. Tools: ctscout_search_company, ctscout_search_company_batch, ctscout_lookup_domain, the research-product reads ctscout_lookup_lei / ctscout_vendor_customers, and for Pro keys the async pair ctscout_submit_deep_dive / ctscout_get_job. The hosted server and the npm package register the same tool contract (ctscout-mcp-server/contract), so both transports expose one surface.

First, get a free key from the front page. Replace YOUR_KEY in the setup for your client below. Hosted HTTP needs no local CTScout package; the Desktop stdio option requires Node.js and npm.

Claude Code: hosted HTTP

With Claude Code installed, run this command in your terminal:

claude mcp add --header "X-API-Key: YOUR_KEY" --transport http --scope user ctscout https://ctscout.dev/mcp

Start or restart Claude Code, then use /mcp to check that ctscout connects. See the Claude Code MCP setup guide.

Cursor: hosted HTTP

Merge this entry into mcpServers in ~/.cursor/mcp.json for all projects, or .cursor/mcp.json inside one project. Keep any existing servers:

{
  "mcpServers": {
    "ctscout": {
      "url": "https://ctscout.dev/mcp",
      "headers": { "X-API-Key": "YOUR_KEY" }
    }
  }
}

Check the server in Cursor's MCP settings. See the Cursor MCP setup guide. Clients that require legacy SSE can use https://ctscout.dev/sse with their client's SSE transport setting and the same key header.

Claude Desktop: local stdio client

Install Node.js LTS (24 LTS is supported), which includes npm. Open a new terminal and check node --version and npm --version. No npm account or npm login is needed to run this public package.

In Claude Desktop, open Settings → Developer → Edit Config. Merge this entry into mcpServers, preserving any existing servers. The file is ~/Library/Application Support/Claude/claude_desktop_config.json on macOS or %APPDATA%\Claude\claude_desktop_config.json on Windows:

{
  "mcpServers": {
    "ctscout": {
      "command": "npx",
      "args": ["-y", "ctscout-mcp-server@0.6.1"],
      "env": { "CTSCOUT_API_KEY": "YOUR_KEY" }
    }
  }
}

Quit and reopen Claude Desktop to load the server. This launches a local compatibility client that calls ctscout.dev; it still needs internet access. See the Desktop local-server setup and troubleshooting guide.

Then ask Claude: "Use ctscout to find domains attributed to Cloudflare."

Source: github.com/minghsuy/ctscout-mcp (MIT). For Claude Code CLI configs and full setup, see the README.

Scope

Built for account-to-domain enrichment, subsidiary discovery, and SAN-cohort analysis from LLM-driven workflows. DV-only infrastructure (Let's Encrypt, ZeroSSL, cloud-native shops) is out of scope by design. Not a cyber-risk-scoring tool — see LIMITATIONS. Tiers and quotas: front page.