One endpoint.
Same graph as the site.
Bearer-key REST API over the identity graph that powers reverse-lookup, append, and batch. 1 credit per hit, misses refunded automatically.
One header. One key.
Every request carries a Bearer key in the Authorization header (a ?key= query param works too, for quick testing). Live keys look like tcs_live_<32 hex chars>; test keys use thetcs_test_ prefix and never bill.
Authorization: Bearer tcs_live_5f2c9a1e7b4d3f6081a2c9e0f4b7d1a3POST /api/v1/lookup
One endpoint, three input shapes. Send a phone, an email, or a name+address — get back the household we resolved. Billing is identical to the website: 1 credit per hit, and a miss (no match) is refunded automatically, so a burst of misses never drains your balance.
Optional: tier — "basic" returns household + best phone + best email; "full" (default on the site) returns all phones/emails, full demographics, and DNC flags.
curl -X POST https://twocentskips.com/api/v1/lookup \
-H "Authorization: Bearer tcs_live_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"key": "phone",
"value": "5551234567",
"tier": "full"
}'{
"ok": true,
"hit": true,
"results": [{
"first": "Jamie",
"last": "Rivera",
"address": "1500 Broadway",
"city": "Springfield",
"state": "IL",
"zip": "62701",
"phones": [{
"number": "5551234567",
"carrier": "Verizon Wireless",
"line_type": "M",
"dnc": false,
"last_seen": "2026-06-02"
}],
"emails": [{ "address": "jamie@example.com" }],
"home_owner": true,
"income_range": "$75k-$100k"
}],
"meta": { "latency_ms": 41, "cached": false, "request_id": "req_9f2c..." }
}{ "ok": true, "hit": false, "results": [], "meta": { "latency_ms": 38, "cached": false, "request_id": "req_a01b..." } }Every response includes an x-credits-remaining header with your post-call balance — check it instead of a separate account-balance call if you're calling in a tight loop.
What can come back besides a hit
| Status | error.code | Meaning |
|---|---|---|
| 400 | bad_json | Request body isn't valid JSON. |
| 400 | bad_input | Missing required 'key' or 'value' field. |
| 401 | no_key | No Authorization header or ?key= param present. |
| 401 | invalid_key | Key doesn't exist, is inactive, or was revoked. |
| 402 | insufficient_credits | API key has 0 credits remaining. Top up to continue. |
| 503 | service_unavailable | Auth/database is temporarily down. Safe to retry. |
Errors always come back as { "ok": false, "error": { "code", "message" } }. 401 and 402 don't touch your credit balance. We don't currently publish a hard per-second rate limit — keep your concurrency reasonable, and email us if you need a guaranteed throughput SLA.
Node and Python
const res = await fetch("https://twocentskips.com/api/v1/lookup", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.TCS_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
key: "name_addr",
value: {
first: "Jamie",
last: "Rivera",
address: "1500 Broadway",
city: "Springfield",
state: "IL",
zip: "62701",
},
tier: "full",
}),
});
const data = await res.json();
if (data.ok && data.hit) {
console.log(data.results[0].phones);
}import os, requests
resp = requests.post(
"https://twocentskips.com/api/v1/lookup",
headers={"Authorization": f"Bearer {os.environ['TCS_API_KEY']}"},
json={"key": "email", "value": "jane@example.com", "tier": "full"},
)
data = resp.json()
if data["ok"] and data["hit"]:
print(data["results"][0]["phones"])
else:
print("no match — not billed")Thousands of rows at once
For CSV volume, the fastest path today is the web UI at /batch — upload up to 50,000 rows, and results come back as an enriched CSV via a permanent email-verified link. Same per-hit pricing as single lookups. If you need programmatic bulk submission wired directly into your pipeline, email us — we can talk through what that looks like for your volume.
Get an API key, start calling.
No monthly minimum, no separate developer plan — API calls draw from the same per-hit pricing as the website.