# DataSkip Developers: Skip Tracing API, CLI, and SDK

> Machine-readable mirror of https://dataskip.io/developers for AI agents. Rendered page: https://dataskip.io/developers · API overview: https://dataskip.io/skip-tracing-api.md · Full site summary: https://dataskip.io/llms.txt

Turn a US property address into the owner's name, phone numbers with DNC flags, emails, and mailing address. One npm package gives you a CLI and a typed JS/TS SDK; the REST API works from any language. Same flat 4 cents per match as the dashboard, and misses are always free.

## npm package `skiptrace`

- Install: `npm install -g skiptrace` (Node 18+, Apache-2.0; package page: https://www.npmjs.com/package/skiptrace)
- CLI: `skiptrace "123 Example St, Phoenix AZ 85001"`
- CLI exit codes: 0 match, 1 miss, 2 usage error, 3 API error
- SDK: `import { SkipTrace } from 'skiptrace'` then `await st.skip(address)`, or `await st.skipBulk([...addresses])` for up to 100 addresses in one request (strings or structured fields, mixable; results in input order; also available as a one-shot `skipBulk(addresses, { apiKey })` export, fully typed as `BulkLookupResponse`)

## REST API

- Endpoint: POST https://app.dataskip.io/api/v1/skip-trace
- OpenAPI 3.1 spec for every endpoint below (operationIds, typed schemas, error shape): https://dataskip.io/openapi.json
- Auth: `Authorization: Bearer <api key>` (create keys in the dashboard at https://app.dataskip.io under Settings, API)
- Pricing: 4 cents per matched lookup, misses are always free (a miss returns HTTP 200 with `found: false`)
- Errors are JSON with an `error` message: 400 names the invalid field, 401 is a bad or revoked key, 402 means insufficient balance (`balanceRequired` in cents), 429 is a rate limit (honor `Retry-After`)
- Rate limit: 500 single lookups per minute per account; use the bulk endpoint or the CSV pipeline for lists

## Bulk lookup endpoint (up to 100 addresses)

- Endpoint: POST https://app.dataskip.io/api/v1/skip-trace-bulk
- Body: `{ "addresses": [{ "address": "...", "city": "...", "state": "...", "zip": "..." }, ...] }` with 1 to 100 entries; only `address` is required per entry
- Pricing matches single lookups: 4 cents per match, misses free. The worst case (every entry matching) is charged up front; non-matches are refunded automatically when the request completes, so the net charge is matches only. Both entries appear in the transaction history
- 402 with `balanceRequired` means the balance cannot cover the batch (all entries at the per-lookup rate)
- Response: `{ "success": true, "total": N, "matched": M, "totalCharged": cents, "results": [...] }`; `results` is in request order and each entry has the single-lookup shape (`found`, `charged`, `contact`, `phones`, `emails`)
- Rate limit: 250 bulk requests per minute per account (100 addresses each); 429 responses carry a Retry-After header
- SDK: `await st.skipBulk([...])`

## Bulk API (CSV jobs)

For lists beyond the bulk endpoint's 100-address cap, the CSV pipeline processes up to 250,000 records in one job and never charges before the previewed cost is confirmed.

1. `POST /api/user/skip-trace` creates a job. Body: `{ "filename": "leads.csv", "columnMapping": { "address": "Property Address", "city": "City", "state": "State", "zip": "Zip" } }` (the address, city, state, and zip columns must all be mapped; `firstName` and `lastName` columns are optional). Returns `jobId`, `uploadUrl`, and `uploadMethod` (`presigned` or `direct`).
2. Upload the raw CSV. Presigned: PUT to `uploadUrl` with `Content-Type: text/csv`, then `POST /api/user/skip-trace/{jobId}/uploaded`. Direct: POST the file as multipart form data (field name `file`).
3. Poll `GET /api/user/skip-trace/{jobId}` until status is `preview`: it reports `totalRows`, `matchedRows`, and `totalChargeCents`.
4. `POST /api/user/skip-trace/{jobId}/confirm` to charge (matched rows only, 4 cents each; 402 if the balance cannot cover it), or `POST /api/user/skip-trace/{jobId}/cancel` to abort with no charge.
5. Poll until `completed`, then download the enriched CSV from `downloadUrl`. Matched rows gain owner names, phones, and emails; unmatched rows come back unchanged.

`GET /api/user/skip-trace` lists the account's 50 most recent jobs. Job statuses: pending, uploaded, processing, preview, confirmed, generating, completed, failed, cancelled.

## Errors

Every error response is JSON with an `error` string that names the problem (on 400, the invalid field). A 402 adds `balanceRequired` (cents needed to run the request). A miss is not an error: HTTP 200 with `found: false`.

| Status | Meaning | What to do |
| --- | --- | --- |
| 400 | Body is not JSON, or a required field is missing or malformed | Fix the named field and retry |
| 401 | Missing, malformed, invalid, or revoked API key, or inactive account | Send `Authorization: Bearer <key>`; create keys in the dashboard under Settings, API |
| 402 | Balance cannot cover the request (`balanceRequired` in cents) | Top up at https://app.dataskip.io, then retry |
| 404 | CSV jobs only: no job with that `jobId` on this account | Check the id returned when the job was created |
| 409 | CSV jobs only: the job's status does not allow the action | Poll the job and act on its current status |
| 429 | Rate limited (`Retry-After` header, seconds) | Wait for `Retry-After` |
| 500 | Server error; a failed lookup is never charged | Retry later, then email support@dataskip.io |

Call only the documented methods: the lookup endpoints are POST only, and an unsupported method or an unknown path returns the application's own 405 or 404 page rather than an API error body. Check the path and method against https://dataskip.io/openapi.json before calling.

## Rate limits

- POST /api/v1/skip-trace: 500 lookups per minute per account
- POST /api/v1/skip-trace-bulk: 250 requests per minute per account (100 addresses each)
- Single and bulk endpoints: misses are always free; you only pay for matches
- POST /api/contact: 5 messages per 15 minutes per IP
- A 429 carries `Retry-After` in seconds: wait that long, then retry. Never loop the single endpoint over a list

## Versioning and deprecation policy

- The API is versioned in the URL path; `/api/v1` is the current stable version and https://dataskip.io/openapi.json describes it
- Backward-compatible changes (new endpoints, new optional request fields, new response fields) ship in place without a version change; ignore fields you do not recognize
- Breaking changes ship as a new path version and never change how v1 behaves
- A retirement is announced at least 6 months before the retirement date. During the notice period every response from the affected version carries a `Deprecation` header (RFC 9745) and a `Sunset` header (RFC 8594) with the retirement date, plus `Link: <announcement>; rel="deprecation"`. The dates are published at https://dataskip.io/developers#versioning, here, and in https://dataskip.io/llms.txt
- Nothing is deprecated today

## Developer resources (predictable URLs)

- API documentation: https://dataskip.io/developers (aliases: https://dataskip.io/docs, https://dataskip.io/api-docs); this markdown mirror: https://dataskip.io/developers.md
- OpenAPI 3.1 spec: https://dataskip.io/openapi.json (alias: https://dataskip.io/swagger.json)
- API catalog (RFC 9727 linkset): https://dataskip.io/.well-known/api-catalog
- Sections: https://dataskip.io/developers#cli · #sdk · #rest · #bulk · #errors · #rate-limits · #versioning
- CLI and SDK: npm package `skiptrace`, https://www.npmjs.com/package/skiptrace
- Support endpoint: POST https://app.dataskip.io/api/contact (public, no auth)
- Webhooks: none; poll `GET /api/user/skip-trace/{jobId}` for job progress

## Data dictionary (returned per hit)

- Owner name: first, middle, last, and full name
- Property address: street, city, state, ZIP
- Mailing address: included when it differs from the property address (absentee owner signal)
- Mobile phones: up to 10 per hit
- Landlines: up to 9 per hit
- Emails: up to 28 per hit
- DNC flag: returned per phone number (Do Not Call registry status)

A miss returns HTTP 200 with `found: false` and costs nothing. There is no partial charge: a record either hits (4 cents) or misses (free).

## Facts

- Hits return owner contact data: names, up to 10 mobile phones, up to 9 landlines, up to 28 emails, property and mailing address, DNC flags on phones
- No minimum order, no subscription required, flat rate at any volume
- US properties only, all 50 states
- Typical lists see a 98.9% hit rate

Rates: https://dataskip.io/pricing · Docs page: https://dataskip.io/developers · Sign up: https://app.dataskip.io/signup
