Bearer-auth REST API for the PRISM multi-stage skip tracer. Submit a seed, poll for the dossier, plug into any HTTP client. The same engine that powers the Lookup tab, exposed for headless / programmatic use.
GET/api/v1/healthservice heartbeat (no auth)GET/api/v1/accountkey info · balance · rate budgetsGET/api/v1/usageper-key job + credit usagePOST/api/v1/tracersubmit one traceGET/api/v1/tracerlist jobs · cursor pagination · status filterGET/api/v1/tracer/{id}poll a single jobDELETE/api/v1/tracer/{id}cancel + refundPOST/api/v1/tracer/batchsubmit up to 50 traces in one callEvery request needs a key in the Authorization header. Generate one in Settings → API keys. Plaintext is shown once on creation; only its sha256 is stored. Revoking a key invalidates it instantly.
Authorization: Bearer prsm_live_abc123… Content-Type: application/json
/api/v1/healthis the only endpoint that doesn't require auth.
Sliding-window in-memory limiter on every endpoint, keyed on your api key id. Exceed and you get 429 with Retry-After. Layered on top of the edge per-IP cap (120 req/min).
writePOST /tracer · DELETE /tracer/{id}60 / minute / keybatchPOST /tracer/batch10 / minute / key (each call may submit ≤50)readGET /tracer · /tracer/{id} · /account · /usage600 / minute / keyhealthGET /health240 / minute / IP (unauth)Every successful response returns X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (unix seconds). 429 responses also return Retry-After in seconds.
Returns immediately with a job id and status: queued. The engine runs asynchronously. Poll the status endpoint until the job terminates.
curl -X POST https://prism-tools.vip/api/v1/tracer \
-H "Authorization: Bearer prsm_live_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: my-unique-id-123" \
-d '{
"seed": { "email": "jane@example.com" },
"depth": 1,
"useAi": true,
"cluster": { "kind": "managed", "id": "managed:sonnet+search" },
"webhookUrl": "https://your.app/prism-webhook"
}'Response (HTTP 202):
{
"job": { "id": "8f2c…", "status": "queued", "seed": {...}, "depth": 1, "use_ai": true, ... },
"charged": 4,
"balance": 46,
"poll_url": "/api/v1/tracer/8f2c…"
}Send Idempotency-Key: <opaque-string> on any POST. PRISM caches the response for 24 hours scoped to (api_key, idempotency_key). Replays return the cached body without re-executing — safe for client retries on timeout / network blip.
# initial
curl -X POST https://prism-tools.vip/api/v1/tracer \
-H "Authorization: Bearer prsm_live_…" \
-H "Idempotency-Key: trace-2026-05-06-abc" \
... → 202 { job: {...}, charged: 1 }
# retry within 24h with the same key
curl -X POST https://prism-tools.vip/api/v1/tracer \
-H "Authorization: Bearer prsm_live_…" \
-H "Idempotency-Key: trace-2026-05-06-abc" \
... → 202 (cached) { job: {same id}, ... } # no second debitResponse carries Idempotent-Replayed: true on cache hits.
The seed object accepts at least one of the following selectors. Combine multiple to disambiguate.
emailj.doe@example.comdirect skip-tracephone+1 415 555 0100direct skip-tracenameJane Doepair with city/stateaddress1600 Amphitheatre Pkwy, Mountain View CA 94043single lineusernamejdoe87pivots through github / gravatardomainexample.compivots through whois / dns / rdapip8.8.8.8pivots through ip-api / asndob1987-09-14narrows ambiguous matchesssn_last44823narrows on identity overlapcity / state / zipOrem · UT · 84058address componentsControl which AI cluster (or none) cross-references and organizes the dossier. The cluster choice multiplies the per-trace cost; see /account for your balance.
{ kind: "managed", id: "off" }0×AI off · heuristic correlator only{ kind: "managed", id: "managed:haiku" }1×Haiku 4.5 · structure only{ kind: "managed", id: "managed:haiku+search" }2×Haiku 4.5 + web search{ kind: "managed", id: "managed:sonnet+search" }4×Sonnet 4.6 + web search{ kind: "byo", configId: <int> }1×Bring Your Own LLM (free uplift) — config in /settingsCancel a job that hasn't terminated. Refunds the credit debit (idempotent). Returns 410 if the job is already done, failed, orcancelled.
curl -X DELETE https://prism-tools.vip/api/v1/tracer/8f2c… \
-H "Authorization: Bearer prsm_live_…"
# 200 { job: {...status: "cancelled"}, refunded: 4 }Up to 50 traces per call. Each item is independently debited and queued. Results pair back to inputs by index. Honors Idempotency-Key for the whole batch.
curl -X POST https://prism-tools.vip/api/v1/tracer/batch \
-H "Authorization: Bearer prsm_live_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: nightly-import-2026-05-06" \
-d '{
"items": [
{ "seed": { "email": "a@x.com" }, "depth": 1 },
{ "seed": { "phone": "+14155550100" } },
{ "seed": { "name": "Jane Doe", "address": "Boston, MA" } }
]
}'
# 202
{
"submitted": 3, "failed": 0,
"results": [
{ "ok": true, "index": 0, "job": {...}, "charged": 2, "balance": 98, "poll_url": "..." },
{ "ok": true, "index": 1, "job": {...}, "charged": 2, "balance": 96, "poll_url": "..." },
{ "ok": true, "index": 2, "job": {...}, "charged": 2, "balance": 94, "poll_url": "..." }
]
}Returns the job in its current state. result is null until done.
curl https://prism-tools.vip/api/v1/tracer/8f2c… \ -H "Authorization: Bearer prsm_live_…"
Cursor pagination via before=<ISO>. Status filter via status=.
curl "https://prism-tools.vip/api/v1/tracer?status=done&limit=50" \ -H "Authorization: Bearer prsm_live_…" # response includes meta.next_cursor (ISO timestamp); pass it as ?before=… for the next page
Useful at app boot — lists key info, balance, rate-limit budget, and the webhook secret derivation hint.
curl https://prism-tools.vip/api/v1/account \ -H "Authorization: Bearer prsm_live_…"
Counts by status across rolling 24h / 7d windows, credits spent, average duration of completed jobs. For dashboards.
curl https://prism-tools.vip/api/v1/usage \
-H "Authorization: Bearer prsm_live_…"
{
"usage": {
"jobs_24h": { "queued": 3, "running": 1, "done": 41, "failed": 2 },
"jobs_7d": { "done": 287, "failed": 14, ... },
"credits_spent": { "day": 47, "week": 312 },
"duration_done_7d": { "avg_ms": 312000, "max_ms": 891000, "n": 287 }
}
}Pass webhookUrl on submit (single or batch) and PRISM will POST the terminal job (done / failed) to that URL with retries (3 attempts, exponential backoff, 30s per-attempt timeout). HTTPS only; SSRF-blocked against private/loopback/cloud-metadata hosts.
Headers PRISM sends:
POST https://your.app/prism-webhook
content-type: application/json
user-agent: PRISM-Webhook/1.0
x-prism-event: trace.done | trace.failed
x-prism-event-id: <job uuid> (idempotency)
x-prism-timestamp: 1714972800
x-prism-signature: t=1714972800,v1=<hex_hmac>
{
"event": "trace.done",
"job_id": "8f2c…",
"status": "done",
"seed": {...},
"duration_ms": 287214,
"records_count": 7,
"result": {...},
"error": null,
"finished_at": "..."
}Verifying the signature: the secret is sha256(your_api_key). PRISM signs <timestamp>.<rawBody> with HMAC-SHA-256. Reject events older than ~5 minutes.
JavaScript verifier:
import crypto from "node:crypto";
function verifyPrismWebhook(rawBody, header, apiKey, toleranceSec = 300) {
if (!header) return false;
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const t = Number(parts.t);
if (!Number.isFinite(t)) return false;
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - t) > toleranceSec) return false;
const secret = crypto.createHash("sha256").update(apiKey).digest("hex");
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
try {
return crypto.timingSafeEqual(Buffer.from(parts.v1, "hex"), Buffer.from(expected, "hex"));
} catch { return false; }
}Python verifier:
import hashlib, hmac, time
def verify_prism_webhook(raw_body: bytes, header: str, api_key: str, tolerance_sec: int = 300) -> bool:
if not header: return False
parts = dict(p.split("=", 1) for p in header.split(","))
t = int(parts.get("t", 0))
if abs(time.time() - t) > tolerance_sec: return False
secret = hashlib.sha256(api_key.encode()).hexdigest()
expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(parts.get("v1", ""), expected)queued — accepted, debit posted, awaiting workerrunning — engine is fanning queries through the BFS frontierdone — result populated; final statefailed — error set; debit refunded automaticallycancelled — operator cancelled; debit refunded202acceptedtrace queued; poll the returned id400bad_requestmissing / malformed JSON, empty seed401unauthorizedmissing / invalid / revoked bearer402insufficient_creditstop up to continue · response includes balance + cost404not_foundpolling a job that doesn't belong to this key410already_terminalDELETE on a job that already finished/failed/cancelled413batch_too_largePOST /tracer/batch with >50 items429rate_limitedtier specified in body · check Retry-After500internal_errorengine fault · job auto-fails + refunds