Developer API

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.

<50ms P50 971M phones · 380M+ profiles
Authentication

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.

Header
Authorization: Bearer tcs_live_5f2c9a1e7b4d3f6081a2c9e0f4b7d1a3
Getting a key: API keys are provisioned by us, not self-serve yet. Email hello@twocentskips.com with your expected volume and we'll set up a key and starting credit balance same-day.
The endpoint

POST /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.

key: "phone"
value: "5551234567"
Any US phone format. Digits are normalized server-side.
key: "email"
value: "jane@example.com"
Resolves to the household behind the inbox.
key: "name_addr"
value: { "first", "last", "address", "city", "state", "zip" }
Full street address + name. middle is optional.

Optional: tier"basic" returns household + best phone + best email; "full" (default on the site) returns all phones/emails, full demographics, and DNC flags.

Request
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"
  }'
Response — 200, hit
{
  "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..." }
}
Response — 200, miss (not billed)
{ "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.

Error codes

What can come back besides a hit

Statuserror.codeMeaning
400bad_jsonRequest body isn't valid JSON.
400bad_inputMissing required 'key' or 'value' field.
401no_keyNo Authorization header or ?key= param present.
401invalid_keyKey doesn't exist, is inactive, or was revoked.
402insufficient_creditsAPI key has 0 credits remaining. Top up to continue.
503service_unavailableAuth/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.

Code samples

Node and Python

Node.js (fetch)
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);
}
Python (requests)
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")
Bulk volume

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.

Ready to integrate?

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.