Reference
API Documentation
Programmatic access to skip tracing and list management over HTTPS.
llms.txt — for AI coding assistantsBase URL
https://api.titanskip.com/v1Authentication
curl https://api.titanskip.com/v1/traces \
-H "Authorization: Bearer tsk_live_your_key_here" \
-H "Content-Type: application/json"Never expose your key in client-side code. Keep it secret and secure to prevent unauthorized access. Rotate keys immediately if you suspect compromise.
Get account info
https://api.titanskip.com/v1/account120 / minuteReturns the authenticated user's account info: credits balance, billing mode, and subscription/metered state. Use this to show a 'credits remaining' indicator or to pre-flight a submission before calling POST /traces.
| Parameter | Type | Required | Description |
|---|
GET /account{
"id": "9fa9e14b-6187-473c-8b5f-0b097823886f",
"email": "[email protected]",
"billing_mode": "prepaid",
"credits": 12450,
"subscription": {
"plan_id": "starter_v1",
"plan_name": "Starter",
"credits_limit": 25000,
"credits_used": 12550,
"current_period_end": "2026-06-01T00: 00: 00.000Z"
},
"metered": null
}Get pricing
https://api.titanskip.com/v1/pricing120 / minuteReturns the current per-row credit rates for each job type. Rates can change — always fetch before computing cost estimates client-side. Rates are global; they do not vary per API key.
| Parameter | Type | Required | Description |
|---|
GET /pricing{
"rates": {
"normal": 1,
"enhanced": 2,
"llc": 3,
"dnc": 1
}
}List trace jobs
https://api.titanskip.com/v1/traces1,200 / minuteReturns a paginated list of trace jobs for the authenticated user. Includes pagination metadata and a downloadable CSV URL for each trace.
| Parameter | Type | Required | Description |
|---|---|---|---|
page | integer | optional | Page number to fetch. Default: 1. |
limit | integer | optional | Records per page. Default: 10. Maximum: 100. |
type | string | optional | Filter by trace type: 'bulk' (CSV uploads) or 'single' (synchronous lookups). |
GET /traces?page=1&limit=10{
"_metadata": {
"page": 1,
"per_page": 10,
"page_count": 3,
"total_count": 24
},
"traces": [
{
"id": "trc_19568a12b3c7d4e8f5a6b7c8d9e",
"status": "completed",
"mode": "normal",
"include_llcs": true,
"total_rows": 1000,
"entity_rows": 40,
"match_count": 870,
"no_match_count": 110,
"error_count": 20,
"credits_charged": 1120,
"credits_refunded": 130,
"name": "owners",
"download_url": "https://api.titanskip.com/v1/traces/trc_19568a12b3c7d4e8f5a6b7c8d9e/download",
"created_at": "2026-03-17T16: 00: 00.000Z",
"completed_at": "2026-03-17T16: 03: 42.000Z"
}
]
}Create a trace job
https://api.titanskip.com/v1/traces120 / minuteUploads a CSV for bulk skip tracing and starts an async job. Provide a column_mapping pointing our keys at your CSV headers. Requirements depend on mode: normal needs first_name + last_name, enhanced ignores names. Every job needs at least one full address group (property OR mailing), and address groups are all-or-nothing — map all four fields of a group or none. Credits are charged up front per billable row and refunded for no-matches/errors after completion.
Field requirements
normalRequires first_name + last_name, plus one full address group (property OR mailing).
enhancedRequires one full address group (property OR mailing). Names are ignored.
Address groups are all-or-nothing: include all four fields of a group, or none. Every job needs at least one complete group.
| Parameter | Type | Required | Description |
|---|---|---|---|
file | file | required | CSV file of records to trace (multipart/form-data). Max 50 MB. |
mode | string | optional | `normal` (default) — name + address lookup; requires first_name + last_name. `enhanced` — address-only deeper lookup; names are ignored. Either way, at least one full address group is required. |
column_mapping | JSON string | required | Maps our field keys to your CSV header names — e.g. {"first_name":"First Name"}. See the per-key badges: names are required in normal mode; each address group is all-or-nothing and at least one full group (property or mailing) is required. |
include_llcs | boolean | optional | Also trace company/LLC rows. In `normal` mode they run as a parallel sub-job billed at the LLC rate; in `enhanced` mode they're folded into the main job. Default: `false`. |
curl -X POST https://api.titanskip.com/v1/traces \
-H "Authorization: Bearer tsk_live_your_key_here" \
-F "[email protected]" \
-F "mode=normal" \
-F "include_llcs=true" \
-F 'column_mapping={
"first_name": "First Name",
"last_name": "Last Name",
"property_street": "Property Address",
"property_city": "Property City",
"property_state": "Property State",
"property_zip": "Property Zip"
}'{
"success": true,
"id": "trc_19568a12b3c7d4e8f5a6b7c8d9e",
"status": "processing",
"mode": "normal",
"include_llcs": true,
"total_rows": 1000,
"entity_rows": 40,
"cost": 1120,
"credits_charged": 1120,
"columns": {
"first_name": "First Name",
"last_name": "Last Name",
"property_street": "Property Address",
"property_city": "Property City",
"property_state": "Property State",
"property_zip": "Property Zip"
}
}Validation errors
400A rejected mapping returns HTTP 400 with a human-readable error and a details array listing every problem at once — so you can fix them all in one pass. Unknown keys include a “did you mean” hint.
{
"error": "Normal mode is missing required fields: \"first_name\", \"last_name\". At least one full address group is required: map all of property_street/city/state/zip, or all of mailing_street/city/state/zip.",
"details": [
"Normal mode is missing required fields: \"first_name\", \"last_name\".",
"At least one full address group is required: map all of property_street/city/state/zip, or all of mailing_street/city/state/zip."
]
}{
"error": "Unknown field \"address\" — did you mean \"property_street\"?",
"details": [
"Unknown field \"address\" — did you mean \"property_street\"?"
]
}{
"error": "Property address is all-or-nothing — you mapped \"property_street\", \"property_city\", \"property_state\" but are missing \"property_zip\".",
"details": [
"Property address is all-or-nothing — you mapped \"property_street\", \"property_city\", \"property_state\" but are missing \"property_zip\"."
]
}{
"error": "These mapped columns are not in the CSV: \"Street Addr\" (mapped to property_street). Available headers: First Name, Last Name, Address, City, State, Zip.",
"details": [
"These mapped columns are not in the CSV: \"Street Addr\" (mapped to property_street). Available headers: First Name, Last Name, Address, City, State, Zip."
]
}Get a trace job
https://api.titanskip.com/v1/traces/:id1,200 / minuteReturns a single trace job by ID. The shape matches each item in GET /traces and the trace.* webhook payload's data field, so the same parser works for polling and webhooks.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | required | Trace job ID (prefixed trc_). |
GET /traces/trc_19568a12b3c7d4e8f5a6b7c8d9e{
"id": "trc_19568a12b3c7d4e8f5a6b7c8d9e",
"status": "completed",
"mode": "normal",
"include_llcs": true,
"total_rows": 1000,
"entity_rows": 40,
"match_count": 870,
"no_match_count": 110,
"error_count": 20,
"credits_charged": 1080,
"credits_refunded": 154,
"name": "owners",
"download_url": "https://api.titanskip.com/v1/traces/trc_19568a12b3c7d4e8f5a6b7c8d9e/download",
"created_at": "2026-03-17T16: 00: 00.000Z",
"completed_at": "2026-03-17T16: 03: 42.000Z",
"breakdown": {
"individuals": { "rate": 1, "rows": 960, "match": 842, "no_match": 100, "error": 18, "credits_charged": 960, "credits_refunded": 118 },
"llcs": { "rate": 3, "rows": 40, "match": 28, "no_match": 10, "error": 2, "credits_charged": 120, "credits_refunded": 36 }
}
}Single record trace (synchronous)
https://api.titanskip.com/v1/traces/single120 / minuteSkip-trace a single record and get the result back inline — no CSV, no job, no webhook. Charges 1 credit at the normal rate (or the LLC rate if `is_entity` is true). Use for one-off lookups or manual research from your own app.
| Parameter | Type | Required | Description |
|---|---|---|---|
first_name | string | required | Owner first name, OR full company / LLC name (e.g. "ACME Holdings LLC"). |
last_name | string | optional | Owner last name. Leave blank for companies. |
address | string | required | Property street address. |
city | string | required | Property city. |
state | string | required | Property state (2-letter code). |
zip | string | required | Property zip / postal code. |
mailing_address | string | optional | Mailing street address. Optional but improves match quality. |
mailing_city | string | optional | Mailing city. Required if `mailing_address` is set. |
mailing_state | string | optional | Mailing state. Required if `mailing_address` is set. |
mailing_zip | string | optional | Mailing zip. Required if `mailing_address` is set. |
is_entity | boolean | optional | Force LLC pricing. Auto-detected if `first_name` contains a company suffix (LLC, INC, CORP, TRUST, etc.). Default: `false`. |
POST /traces/single
Content-Type: application/json
{
"first_name": "Jordan",
"last_name": "Reeves",
"address": "1842 Oak Ridge Dr",
"city": "Phoenix",
"state": "AZ",
"zip": "85032"
}{
"matched": true,
"first_name": "Jordan",
"last_name": "Reeves",
"phones": [
{ "number": "+1 (602) 555-0182", "type": "mobile" },
{ "number": "+1 (480) 555-9921", "type": "landline" }
],
"emails": ["[email protected]"],
"credits": 1,
"credits_refunded": 0,
"is_entity": false
}List DNC jobs
https://api.titanskip.com/v1/dnc1,200 / minuteReturns a paginated list of DNC + TCPA jobs for the authenticated user. Each job represents one uploaded phone list scrubbed against the Do Not Call registry and the TCPA litigator list.
| Parameter | Type | Required | Description |
|---|---|---|---|
page | integer | optional | Page number to fetch. Default: 1. |
limit | integer | optional | Records per page. Default: 10. Maximum: 100. |
GET /dnc?page=1&limit=10{
"_metadata": {
"page": 1,
"per_page": 10,
"page_count": 2,
"total_count": 14
},
"jobs": [
{
"id": "dnc_19568a12b3c7d4e8f5a6b7c8d9e",
"name": "Lead list - May 20 2026",
"status": "completed",
"total_rows": 5000,
"match_count": 4980,
"error_count": 20,
"credits_charged": 5000,
"credits_refunded": 20,
"download_url": "https://api.titanskip.com/v1/dnc/dnc_19568a12b3c7d4e8f5a6b7c8d9e/download",
"created_at": "2026-05-20T14: 30: 00.000Z",
"completed_at": "2026-05-20T14: 33: 12.000Z"
}
]
}Create a DNC job
https://api.titanskip.com/v1/dnc120 / minuteUploads a CSV containing phone numbers and scrubs them against the Do Not Call registry and the TCPA litigator list. Billed per phone at the dnc rate (see GET /pricing). Returns immediately with the new job ID — completion is asynchronous (webhook or poll).
| Parameter | Type | Required | Description |
|---|---|---|---|
file | file | required | CSV file containing one phone number per row. Max size: 50 MB. |
column_mapping | string | required | JSON string mapping our 'phone' key to your CSV column header — e.g. {"phone":"Phone Number"}. Numbers can be in any format; we normalize before sending upstream. |
POST /dnc
Content-Type: multipart/form-data
file=phones.csv
column_mapping={"phone": "Phone Number"}{
"id": "dnc_19568a12b3c7d4e8f5a6b7c8d9e",
"name": "phones",
"status": "processing",
"total_rows": 5000,
"rate": 1,
"credits_charged": 5000
}Get a DNC job
https://api.titanskip.com/v1/dnc/:id1,200 / minuteReturns a single DNC job by ID for the authenticated user. The shape matches the webhook payload's data field, so the same parser works for both polling and webhook receipt.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | required | DNC job ID (prefixed dnc_). |
GET /dnc/dnc_19568a12b3c7d4e8f5a6b7c8d9e{
"id": "dnc_19568a12b3c7d4e8f5a6b7c8d9e",
"status": "completed",
"total_rows": 5000,
"match_count": 4980,
"error_count": 20,
"rate": 1,
"credits_charged": 5000,
"credits_refunded": 20,
"download_url": "https://api.titanskip.com/v1/dnc/dnc_19568a12b3c7d4e8f5a6b7c8d9e/download",
"created_at": "2026-05-20T14: 30: 00.000Z",
"completed_at": "2026-05-20T14: 33: 12.000Z",
"error_message": null
}Retry the trace completion webhook
https://api.titanskip.com/v1/traces/:id/retry-webhook120 / minuteRequeues a fresh delivery to the webhook URL configured on the API key that created this trace. Useful when your endpoint was down during the original delivery. The trace state is not recomputed and credits are not re-evaluated — only the outbound notification is re-fired.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | required | Trace ID (in the URL path). Must be a completed or failed trace owned by your API key. |
POST /traces/trc_19568a12b3c7d4e8f5a6b7c8d9e/retry-webhook{
"delivery_id": "whd_19568a12b3c7d4e8f5a6b7c8d9e",
"attempt": 1,
"status": "pending"
}Retry the DNC completion webhook
https://api.titanskip.com/v1/dnc/:id/retry-webhook120 / minuteRequeues a fresh delivery to the webhook URL configured on the API key that created this DNC check. Useful when your endpoint was down during the original delivery. The DNC job state is not recomputed and credits are not re-evaluated — only the outbound notification is re-fired.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | required | DNC job ID (in the URL path). Must be a completed or failed job owned by your API key. |
POST /dnc/dnc_19568a12b3c7d4e8f5a6b7c8d9e/retry-webhook{
"delivery_id": "whd_19568a12b3c7d4e8f5a6b7c8d9e",
"attempt": 1,
"status": "pending"
}Completion webhook
https://api.titanskip.com/v1(your webhook URL)We POST to your configured webhook URL when an asynchronous job finalises (a bulk trace or DNC check). Every event uses the same typed envelope — { event, data } — so you can route in a single switch. The `data` field mirrors the exact shape that GET /v1/<type>/:id returns. Single-record traces are synchronous and do not fire webhooks.
| Parameter | Type | Required | Description |
|---|---|---|---|
X-TitanSkip-Event | header | required | Event name: trace.completed, trace.failed, dnc.completed, or dnc.failed. Mirrors `event` in the body. |
X-TitanSkip-Signature | header | required | HMAC-SHA256 of the raw request body, hex-encoded. Signed with the webhook secret from your API Keys page. |
X-TitanSkip-Delivery-Id | header | required | Unique ID for this delivery attempt. Dedupe on this for at-most-once handling. |
X-TitanSkip-Attempt | header | required | Attempt number (1–5). Retries share the resource ID inside `data` as the stable dedupe key. |
// trace.completed (bulk skip trace)
// On trace.failed the same shape is sent with event="trace.failed",
// data.status="failed", download_url=null, and error_message populated.
{
"event": "trace.completed",
"data": {
"id": "trc_19568a12b3c7d4e8f5a6b7c8d9e",
"status": "completed",
"mode": "normal",
"include_llcs": true,
"total_rows": 23000,
"entity_rows": 400,
"match_count": 22100,
"no_match_count": 750,
"error_count": 150,
"credits_charged": 23800,
"credits_refunded": 1100,
"download_url": "https://api.titanskip.com/v1/traces/trc_19568a12b3c7d4e8f5a6b7c8d9e/download",
"created_at": "2026-03-20T14: 30: 00.000Z",
"completed_at": "2026-03-20T15: 00: 00.000Z",
"error_message": null,
"breakdown": {
"individuals": { "rate": 1, "rows": 22600, "match": 21800, "no_match": 700, "error": 100, "credits_charged": 22600, "credits_refunded": 800 },
"llcs": { "rate": 3, "rows": 400, "match": 300, "no_match": 50, "error": 50, "credits_charged": 1200, "credits_refunded": 300 }
}
}
}
// dnc.completed (DNC + TCPA lookup)
// On dnc.failed the same shape is sent with event="dnc.failed",
// data.status="failed", download_url=null, and error_message populated.
{
"event": "dnc.completed",
"data": {
"id": "dnc_19568a12b3c7d4e8f5a6b7c8d9e",
"status": "completed",
"total_rows": 5000,
"match_count": 4980,
"error_count": 20,
"rate": 1,
"credits_charged": 5000,
"credits_refunded": 20,
"download_url": "https://api.titanskip.com/v1/dnc/dnc_19568a12b3c7d4e8f5a6b7c8d9e/download",
"created_at": "2026-03-20T14: 30: 00.000Z",
"completed_at": "2026-03-20T15: 00: 00.000Z",
"error_message": null
}
}// Reply with any 2xx within 10 seconds.
// Non-2xx triggers up to 5 retries (1s, 5s, 30s, 5min, 30min).
HTTP/1.1 200 OK
Content-Type: application/json
{ "received": true }Error codes
All errors return error.code and error.message. Validation errors add an error.details array.
| Code | Name | Description |
|---|---|---|
| 400 | Bad Request | Missing or malformed request parameters. Check the error.details field for field-level messages. |
| 401 | Unauthorized | Missing or invalid API key. Send it as `Authorization: Bearer tsk_live_...`. |
| 402 | Payment Required | Your account has insufficient credits. Top up your balance to continue making requests. |
| 403 | Forbidden | Your API key does not have permission to access this resource. |
| 404 | Not Found | The requested resource does not exist. Check the ID or path parameter. |
| 422 | Unprocessable | The request was well-formed but contained semantic errors, such as exceeding the record limit. |
| 429 | Rate Limited | You have exceeded your request rate limit. Retry after the duration indicated in the Retry-After header. |
| 500 | Server Error | An unexpected error occurred on our end. These are logged and automatically investigated. |
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed.",
"details": [
{ "field": "records[0].address", "message": "Address is required." },
{ "field": "records[1].last_name", "message": "Last name is required." }
]
}
}