contactctl Try ContactCTL

We finally fixed contact data for agents

Every contact-data tool was built for a human at a dashboard — tabs, filters, export buttons. Your agent doesn’t click. It runs commands. So we built ContactCTL: find, verify, and enrich B2B contacts from the shell, with honest exit codes and lookups that cost nothing when they miss.

Start finding contacts

€9/mo to start. Cancel anytime. Your agent does the rest.

claude — ~/outbound
$ ctc find "Jane Smith" acme.com --json
{
"status": "found",
"contacts": [{
"type": "work_email",
"value": "jane.smith@acme.com",
"status": "deliverable",
"confidence": 0.98
}],
"cost": { "actual_credits": 1 }
}
 
$ ctc phone "Jane Smith" acme.com --estimate
estimated_credits: 10
# charged only if found — a miss is free

Deliverability is a number,
not a paragraph.

ctc verify checks an address before you ever send, then hands down The Verdict as an exit code: 0 deliverable, 2 undeliverable, 3 risky, 6 invalid. Your agent branches on $? — no parsing, no vibes, no maybe.

verdict — ctc verify
$ ctc verify jane.smith@acme.com --json
{ "email": "jane.smith@acme.com", "status": "deliverable", "confidence": 0.99, … }
$ echo $?
0
 
$ ctc verify billing@dead-startup.example --json
{ "email": "billing@dead-startup.example", "status": "undeliverable", … }
$ echo $?
2
 
$ ctc verify hello@catchall-corp.example --json
{ "email": "hello@catchall-corp.example", "status": "risky", "confidence": 0.41, … }
$ echo $?
3
exit 0 deliverable exit 2 undeliverable exit 3 risky exit 6 invalid

Stop guessing addresses.
Ask The Resolver.

The Resolver is one command: ctc find — a name and a company in, a verified work email out. Behind it, a waterfall sweeps 20+ premium sources; in front, one price: 1 credit, charged only when something is found. Need the personal address instead? --personal, 3 credits, same deal. And when you hold the email but not the person, ctc whois runs it in reverse — the person and company behind an address, 1 credit, miss free too.

resolver — ctc find
$ ctc find "Jane Smith" acme.com --json
{
"status": "found",
"contacts": [{
"type": "work_email",
"value": "jane.smith@acme.com",
"status": "deliverable",
"confidence": 0.98
}],
"cost": { "actual_credits": 1 }
}
 
$ ctc find "Jan Smyth" acme.com --json
{ "status": "not_found", "cost": { "actual_credits": 0 } }
$ echo $?
2
# the miss is free — retry with another spelling, guilt-free
 
$ ctc whois jane.smith@acme.com --json
{ "person": { "name": "Jane Smith", "title": "VP Marketing" },
"company": { "domain": "acme.com" }, "cost": { "actual_credits": 1 } }

When email isn’t enough,
pick up The Direct Line.

Mobile numbers are the expensive ask, so The Direct Line is quarantined on purpose: ctc phone is its own verb at 10 credits — nothing else ever escalates into a phone lookup by accident. Your agent puts the price on the table first with --estimate, and a miss still costs exactly nothing.

direct line — ctc phone
$ ctc phone "Jane Smith" acme.com --estimate
estimated_credits: 10
# the price on the table before a single credit moves
 
$ ctc phone "Jane Smith" acme.com --json
{
"status": "found",
"contacts": [{
"type": "mobile_phone",
"value": "+1 415 555 0148",
"confidence": 0.95
}],
"cost": { "actual_credits": 10 }
}

Don’t babysit ten thousand rows.
Load The Conveyor.

One row or a whole file — same verb. The Conveyor is batch mode on verify, find, phone, and whois: CSV or TSV in, CSV out — ctc verify list.csv clean.csv at 0.02 credits a row — with row order preserved and a per-row error column, so one bad row never sinks the file. --dry-run preflights readiness and worst-case cost with zero spend, --max-cost stops the job before it crosses your budget, and big files ship in chunks of 100 rows. When in and out are the same file, the overwrite is atomic — finished or untouched, never half. Misses stay free on find, phone, and whois rows.

conveyor — batch mode
$ ctc find prospects.csv enriched.csv --dry-run
rows detected 2,418
rows ready 2,406 (12 missing a domain)
worst case 2,406 credits — every miss is free
# preflight: readiness + estimate, zero spend
 
$ ctc find prospects.csv enriched.csv
chunk 01/25 100 rows 91 found · 9 misses (free)
chunk 02/25 100 rows 88 found · 12 misses (free)
chunk 25/25 6 rows 5 found · 1 miss (free)
done 2,193 found · 213 misses (free) · 12 row errors · order kept
 
$ head -3 enriched.csv
name,domain,work_email,status,confidence,contactctl_error,contactctl_detail
Jane Smith,acme.com,jane.smith@acme.com,deliverable,0.98,,run_01HQ…
Sam Ruiz,vandelay.example,,not_found,,,run_01HQ…

Don’t browse a database.
Ask for The Shortlist.

You don’t get a seat-limited database browser; you get The Shortlist. ctc search people and ctc search companies take honest filters — domain, titles, industry, headcount — and bill 0.25 credits per search, not per row. And because guessing filter syntax wastes agent turns, ctc search filters prints every accepted field and value: free, locally.

shortlist — ctc search
$ ctc search people --domain cloudflare.com --titles "VP Marketing" --json
{
"results": [
{ "name": "████ ████", "title": "VP Marketing", "linkedin": "…" },
{ "name": "████ ███", "title": "VP Product Marketing", "linkedin": "…" }
],
"cost": { "actual_credits": 0.25 }
}
# real people in the real output — names redacted on this page
 
$ ctc search filters
# every accepted field and value — free, runs locally

Clone your best customer.
Meet The Doppelgänger.

Your best customer is a query. Hand The Doppelgänger a domain and it returns companies that look like it; hand it a LinkedIn profile and it returns similar people. Billing is per returned row — 0.35 credits each — so --limit 5 costs at most 1.75 credits, priced before anything runs.

doppelgänger — ctc lookalike
$ ctc lookalike hubspot.com --limit 5 --json
{
"results": [
{ "domain": "klaviyo.com", "similarity": 0.91 },
{ "domain": "activecampaign.com", "similarity": 0.89 },
{ "domain": "braze.com", "similarity": 0.87 },
{ "domain": "customer.io", "similarity": 0.85 },
{ "domain": "iterable.com", "similarity": 0.84 }
],
"cost": { "actual_credits": 1.75 }
}
# 5 returned rows × 0.35 · a LinkedIn URL seed returns similar people

The Estimate before.
The Ledger after.

Cost control is the same pattern twice. Before a run, The Estimate: every paid command takes --estimate — printed locally, zero spend — and --max-cost, a hard wall the job cannot cross. After it, The Ledger: ctc usage prints your balance and every line of spend — plan credits first, then top-ups, oldest first; misses logged, never billed.

cost control — estimate & ledger
$ ctc find leads.csv enriched.csv --dry-run
rows detected 4,812
rows ready 4,790 (22 missing name or domain)
worst case 4,790 credits — every miss is free
 
$ ctc find leads.csv enriched.csv --max-cost 1200
stopped before exceeding --max-cost 1200 (exit 4)
1,164 found · 213 misses (free) · completed rows written
 
$ ctc usage
plan Growth · 1,000 credits/mo
balance 604.86 plan · 250 top-up
# plan credits are spent first, then top-ups, oldest first

Schemas in every window,
or sixty lines — once.

An MCP server is an honest trade: its tool schemas ride along in every context window, and a server runs between your agent and the work. ContactCTL takes the other deal — one binary plus a ≈60-line guide. Your agent runs ctc instructions, reads it once, and knows the whole tool: zero standing context cost, no server hop, and it works anywhere a shell runs. Which is exactly why it composes happily alongside MCP stacks — not against them.

instructions — the whole onboarding
$ ctc instructions
CONTACTCTL — INSTRUCTIONS FOR AGENTS (excerpt)
 
One command per job:
ctc verify <email> 0.02 credits / verification
ctc find <name> <domain> 1 credit / result found
ctc phone <name> <domain> 10 credits / result found
ctc whois <email> 1 credit / result found
 
find, phone, and whois charge only when a result is found.
Every command takes --json, --estimate, and --max-cost.
No interactive prompts, ever.
 
# ≈60 lines total — read once, known for good

No press release —
a note from the maker.

Hey everyone —

I’m Miha, and I make ContactCTL.

For two decades, contact data has lived behind dashboards. Log in, click around, build a list, export a CSV, upload it somewhere else. Fine — when a human was doing the work.

But the people I build for don’t do that work anymore. Their agents do. And an agent driving a browser dashboard is a sad thing: slow, brittle, screenshotting its way through menus designed for mice.

The fashionable fix is an MCP server. We went the other way. A native CLI starts instantly, costs zero context window, and composes with pipes, files, and cron like every good tool since 1978. Your agent reads contactctl instructions once — about 60 lines — and knows the entire product. No schemas squatting in its context. (And if you run MCP stacks, the CLI sits happily alongside them.)

So that’s what ContactCTL is: contact data as a well-behaved Unix citizen. One command per job. Exit codes you can branch on. Misses that cost nothing. A bill you can read.

The terminal was never legacy. It was waiting.

Miha, founder

Three plans. One meter.

€9 for 150, €55 for 1,000, €250 for 5,000 credits a month — that’s the meter.

Starter

€9/mo

150 credits / month

For one agent doing real work.

one month ≈

found work emails
150
verifications
7,500
mobile numbers
15

top-ups €0.060/credit · min €5

Scale

€250/mo

5,000 credits / month

For agencies and pipelines that run daily.

one month ≈

found work emails
5,000
verifications
250,000
mobile numbers
500

top-ups €0.050/credit · min €5

No free trial — Starter is the trial: plan credits reset monthly, never roll over, and spend first. Top-ups never expire while you’re subscribed (parked 12 months if you cancel), and a miss never costs a credit.

Pick a plan

Full pricing breakdown →

Cancel anytime — CtrlC for subscriptions.