Version: v1https://lookupyou.com/api/v1Download OpenAPI

API documentation

The API lets you submit searches programmatically and receive a structured answer. Base URL — https://lookupyou.com/api/v1, JSON in and out, all dates in responses are ISO 8601 (UTC). Below is a live reference: it is built from the same OpenAPI schema the service serves, so it matches the real behaviour.

Keys, balance, packages and request history live in the dashboard. Open dashboard

Authentication

Every request carries an API key in the Authorization header. The key is created in the dashboard and shown once — save it right away, it cannot be recovered. Afterwards the UI shows only a fragment like lp_live_a7f3…9c21.

Authorization: Bearer lp_live_a7f3f0c98b1d4e7aa2b5c8d19c21

A key can be limited by lifetime and by a list of IP addresses. If a key is compromised, revoke it in the dashboard — access stops immediately: requests get 401 and are not charged. Manage keys

First request

A minimal search in fast mode — the program waits for the result on the same connection. The answer usually takes 6–12 seconds; longer under queue.

curl -X POST https://lookupyou.com/api/v1/search \
  -H "Authorization: Bearer lp_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"product":"ssn_bg","first_name":"John","last_name":"Miller","hint":"1026 Maple Lane","mode":"sync"}'

Response · 200 OK

{
  "request_id": "req_8f21c4",
  "client_ref": "order-1187",
  "product": "ssn_bg",
  "mode": "sync",
  "status": "completed",
  "query": { "first_name": "John", "last_name": "Miller", "hint": "1026 Maple Lane", "dob": null },
  "charged": true,
  "charge": { "source": "package", "amount_cents": 0, "requests": 1 },
  "cached": false,
  "error": null,
  "queue_position": 0,
  "duration_ms": 7840,
  "created_at": "2026-08-17T11:07:12Z",
  "finished_at": "2026-08-17T11:07:20Z",
  "result_expires_at": "2026-11-15T11:07:20Z",
  "result": {
    "found": true,
    "matches_count": 1,
    "records": [{ "full_name": "MILLER, JOHN A", "ssn": "445-72-4417", "dob": "3/14/1979", "age": 47, "addresses": [ … ], "phones": [ … ] }]
  }
}

product, first_name and last_name are required, plus at least one clarifier: hint (street without ZIP, city or state) or dob (date of birth MM/DD/YYYY). Without a clarifier the source does not search — 422, nothing charged.

Sync and async mode

The data source allows one active session, so searches run one after another. The mode field decides how you get the result:

  • sync (default) — the connection is held until the result is ready. Suits single searches triggered by a user action. Our timeout is 5 minutes.
  • async — the API answers 202 Accepted with the queue position right away, and the result is delivered to your webhook as search.completed or search.failed. It can also be fetched any time via GET /requests/{id}. The only convenient option for a stream of requests.

Queue and waiting

The 202 response has queue_position and eta_seconds — how many requests are ahead and roughly how long to wait; sync responses carry X-Queue-Position. The current queue length is served by GET /queue. The queue is fair: no plan has priority.

No more than 8 of your requests can be in progress at once; beyond that — 429 too_many_running. For a stream this is not a problem: send the next one when you get an answer or a notification.

Add-ons for a found person

If an SSN + DOB search found a person, the driver licence (DL) and the MVR can be taken from the same record without a second call to the source — cheaper and instant: POST /requests/{id}/addons with the addon field. If the record lacks that part, the answer has status not_found and nothing is charged. Buying the same add-on again is free from the saved answer.

Add-onAfter which productWhat you getPrice
dl_after_ssnssn_bgLicence number and issuing state$1.00
mvr_after_ssnssn_bgLicence issue and expiry dates, sometimes sex, race, height$7.50
mvr_after_dldlThe same — for the licence number already bought$5.00
The credit report (CR and CS) is not available via the API yet — 409 addon_unavailable. It is handled by support.

Endpoints

All paths are relative to the base URL. Every endpoint requires the Authorization: Bearer <key> header.

POST/searchRun a search

Sync mode holds the connection until the source answers (usually 6–12 seconds, longer under queue). Async mode returns 202 immediately; the result is delivered to your webhook and stays available via GET /requests/{id}. Only a completed search is charged: a validation error, an unavailable source or a rate-limit rejection cost nothing.

Request body

FieldTypeDescriptionExample
productrequiredssn_bg | dl | mvrssn_bg — SSN + DOB (package or balance), dl / mvr — balance only
first_namerequiredstringJohn
last_namerequiredstringMiller
hintstringStreet without ZIP, city or state. Required unless dob is given1026 Maple Lane
dobstringMM/DD/YYYY. Required unless hint is given03/14/1979
modesync | async (default: sync)
client_refstringYour identifier, echoed back in responses and webhooks

Responses

200Search finished (found or not found)Request
202Accepted into the queue (async mode)Accepted
401Missing, invalid, revoked or expired API keyError
402Not enough funds: no package requests left and balance is below the product priceError
403IP address is not allowed for this key, or the account is suspendedError
422Validation error — the search was not sent to the sourceError
429Rate limit: 60 requests per minute per account, or too many requests running at onceError
503Source did not answer; nothing was chargedError
GET/requestsList recent requests

Parameters

NameInTypeDescription
limitqueryinteger
beforequerystringReturn requests created before this ISO timestamp (pagination)
statusqueryqueued | running | completed | not_found | failed | rejected

Responses

200Requests without result bodies
401UnauthorizedError
GET/requests/{id}Get a request with its result

Parameters

NameInTypeDescription
idrequiredpathstring

Responses

200RequestRequest
401UnauthorizedError
404Unknown request idError
GET/requests/{id}/reportFull report as plain text

Parameters

NameInTypeDescription
idrequiredpathstring

Responses

200text/plain reporttext/plain
404No result for this request (not finished, failed, or expired)Error
POST/requests/{id}/addonsBuy an add-on from a saved result

Driver licence or MVR taken from the record already found — no second call to the source, so it is cheaper and instant. If the record has no such data, the answer is `not_found` and nothing is charged. Already bought add-ons are returned again for free.

Parameters

NameInTypeDescription
idrequiredpathstring

Request body

FieldTypeDescriptionExample
addonrequireddl_after_ssn | mvr_after_ssn | mvr_after_dl

Responses

200Add-on resultRequest
402Balance is below the add-on priceError
404Unknown request idError
409Add-on is not applicable to this request, or the credit report is not available via API yetError
GET/accountBalance, packages and prices

Responses

200Account stateAccount
401UnauthorizedError
GET/queueQueue length at the source

Responses

200Queue

Objects

Fields of response objects. Types follow JSON Schema; nullable fields may carry no data.

Error
FieldTypeDescription
errorstring · Example: payment_required
messagestring
charged"false"
retry_afterintegerseconds, only with 429
SearchInput
FieldTypeDescription
productrequiredssn_bg | dl | mvrssn_bg — SSN + DOB (package or balance), dl / mvr — balance only
first_namerequiredstring · Example: John
last_namerequiredstring · Example: Miller
hintstringStreet without ZIP, city or state. Required unless dob is given · Example: 1026 Maple Lane
dobstringMM/DD/YYYY. Required unless hint is given · Example: 03/14/1979
modesync | async
client_refstringYour identifier, echoed back in responses and webhooks
Accepted
FieldTypeDescription
request_idstring · Example: req_8f21c4
status"queued"
queue_positioninteger
eta_secondsinteger
Request
FieldTypeDescription
request_idstring
client_refstring | null
productstring
modesync | async
statusqueued | running | completed | not_found | failed | rejected
queryobject
query.first_namestring
query.last_namestring
query.hintstring | null
query.dobstring | null
chargedboolean
chargeobject
charge.sourcepackage | balance | none
charge.amount_centsinteger
charge.requestsinteger
charge.reasonstring | null
cachedbooleanSame query within 5 minutes — served from the saved answer for free
errorobject | null
error.codestring
error.messagestring | null
queue_positioninteger | null
duration_msinteger | null
created_atstring
finished_atstring | null
result_expires_atstring | nullResults are kept for 90 days
resultResult | null
Result
FieldTypeDescription
foundboolean
matches_countinteger
recordsPerson[]
PersonFor ssn_bg: identity, addresses, phones, emails, relatives, civil records. For dl: name and licence numbers. For mvr: licences with dates.
FieldTypeDescription
full_namestring
ssnstring | null
dobstring | null · Example: 3/14/1979
ageinteger | null
genderstring | null
akastring[]
addressesobject[]
addresses[].rawstring
addresses[].citystring | null
addresses[].statestring | null
addresses[].zip_codestring | null
addresses[].date_fromstring | null
addresses[].date_tostring | null
addresses[].is_currentboolean
phonesobject[]
phones[].numberstring
phones[].formattedstring
phones[].phone_typestring | null
phones[].carrierstring | null
emailsstring[]
relativesobject[]
relatives[].full_namestring
relatives[].relationship_typestring | null
relatives[].dob_yearstring | null
relatives[].current_ageinteger | null
civil_recordsobject | null
civil_records.bankruptciesinteger
civil_records.liensinteger
civil_records.judgmentsinteger
driver_licensesobject[]
driver_licenses[].numberstring
driver_licenses[].statestring | null
driver_licenses[].issue_datestring | null
driver_licenses[].expiration_datestring | null
driver_licenses[].genderstring | null
driver_licenses[].racestring | null
driver_licenses[].heightstring | null
Account
FieldTypeDescription
nicknamestring
balance_centsinteger
available_centsintegerbalance minus reservations of running searches
package_requests_leftinteger
packagesobject[]
packages[].idstring
packages[].namestring
packages[].requests_leftinteger
packages[].requests_totalinteger
packages[].expires_atstring
pricesobjectyour price per product/add-on, cents
limitsobject
limits.requests_per_minuteinteger
limits.concurrent_requestsinteger

Response codes

Every error body looks the same: error (machine-readable code), message (text), charged: false. With 429 there is also retry_after in seconds and a Retry-After header.

CodeerrorMeaningRequest charged
200Search finished: status completed (found) or not_found (no matches)Yes — a completed search
202Accepted into the queue (mode: async)No — charged on execution
401unauthorizedNo key, wrong key, revoked or expiredNo
402payment_requiredNo package requests and the balance is below the product priceNo
403forbiddenIP not allowed for the key, or the account is suspendedNo
404not_foundUnknown request_idNo
409addon_unavailableAdd-on not applicable to this request, or not available via API yetNo
422validation_errorParameters rejected: no clarifier, ZIP in the address, non-Latin letters, bad dateNo
429rate_limited / too_many_runningMore than 60 requests per minute per account, or too many requests in progressNo
503source_unavailableThe data source did not answerNo
A special case — a person was found but the record lacks the required part (SSN without a full date of birth, DL without a number, MVR without dates). Such an answer comes as not_found and is never charged.

Webhook notification format

The URL and secret are set in the dashboard. A notification is a POST with a JSON body: the same Request object GET /requests/{id} returns, plus an event field. A 2xx response counts as delivered, otherwise retries follow. Answer quickly — the timeout is 5 seconds, do the processing in the background.

Events

  • search.completedsearch finished, result ready (including not_found)
  • search.failedsearch did not take place, nothing charged
  • package.lowrequests below the threshold from notification settings
  • package.expiringpackage expires soon

Headers: X-Signature — signature, X-Event — event, X-Delivery-Id — delivery id (for deduplication), X-Request-Id, X-Timestamp.

POST https://your-service.com/hooks/search
Content-Type: application/json
X-Signature: sha256=6b1c…f0a2
X-Event: search.completed
X-Delivery-Id: 4c1d0e9a-…
X-Request-Id: req_8f21c4
X-Timestamp: 1786901240

{
  "event": "search.completed",
  "request_id": "req_8f21c4",
  "client_ref": "order-1187",
  "product": "prod_ssn_bg",
  "status": "completed",
  "charged": true,
  "error": null,
  "finished_at": "2026-08-17T11:07:20Z",
  "result": { "found": true, "matches_count": 1, "records": [ … ] }
}

Signature check

X-Signature is sha256= followed by HMAC-SHA256 of the raw request body, keyed with the webhook secret from the dashboard. Compute the HMAC over the body bytes before parsing JSON and compare in constant time.

Delivery retries

If your endpoint answered non-2xx or did not answer within 5 seconds, delivery is retried after 1, 5, 15 and 60 minutes — four attempts in total. Every attempt is visible in the dashboard, and undelivered ones can be resent manually there. After three failures in a row an email is sent. Results are not lost — they are available via GET /requests/{id}.

When a request is charged

A charge happens only for a completed search: the source answered and the answer was passed to you. No matches is also a completed search — the work was done. Parameter errors, an unavailable source, a rate-limit rejection and a revoked key charge nothing. Every response has the charged field and a charge object with the source of the charge.

A package request is an ssn_bg search: the package with the earliest expiry is used first, then the balance at retail price. Products dl and mvr and add-ons are always charged from the balance.

The same request (product and every field match) within 5 minutes is served from the saved answer: cached: true, nothing charged.

The result is kept for 90 days (result_expires_at), then deleted; request metadata stays in the history.

Rate limiting

One limit for all plans — 60 requests per minute per account and 8 requests in progress at once. It is not for sale, not raised for money, and exists only so that one client’s looping program cannot occupy the whole queue. The current state comes back in the headers of every response:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 57
X-RateLimit-Reset: 34
X-Request-Id: req_8f21c4
X-Queue-Position: 3

Code examples

Fast mode in three languages. Put in your own key and URL.

curl

curl -X POST https://lookupyou.com/api/v1/search \
  -H "Authorization: Bearer lp_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"product":"ssn_bg","first_name":"John","last_name":"Miller","hint":"1026 Maple Lane","mode":"sync"}'

Python

import requests

r = requests.post(
    "https://lookupyou.com/api/v1/search",
    headers={"Authorization": "Bearer lp_live_YOUR_KEY"},
    json={"product": "ssn_bg", "first_name": "John", "last_name": "Miller", "hint": "1026 Maple Lane"},
    timeout=300,
)
data = r.json()
if r.status_code == 200 and data["status"] == "completed":
    print(data["result"]["records"][0]["ssn"])
else:
    print(r.status_code, data.get("error"), data.get("message"))

Node.js

const res = await fetch("https://lookupyou.com/api/v1/search", {
  method: "POST",
  headers: { Authorization: "Bearer lp_live_YOUR_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ product: "ssn_bg", first_name: "John", last_name: "Miller", hint: "1026 Maple Lane" }),
});
const data = await res.json();
if (res.ok && data.status === "completed") console.log(data.result.records[0].ssn);
else console.log(res.status, data.error, data.message);

Async mode and webhook

Submitting to the queue and verifying the notification signature on your server.

// 1. отправка в очередь
const accepted = await fetch("https://lookupyou.com/api/v1/search", {
  method: "POST",
  headers: { Authorization: "Bearer lp_live_YOUR_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ product: "ssn_bg", first_name: "John", last_name: "Miller", hint: "TX", mode: "async", client_ref: "order-1187" }),
}).then((r) => r.json());
// { request_id: "req_8f21c4", status: "queued", queue_position: 3, eta_seconds: 32 }

// 2. приём вебхука (Express)
import crypto from "node:crypto";
app.post("/hooks/search", express.raw({ type: "application/json" }), (req, res) => {
  const expected = "sha256=" + crypto.createHmac("sha256", WEBHOOK_SECRET).update(req.body).digest("hex");
  const given = req.get("X-Signature") ?? "";
  if (given.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected))) return res.sendStatus(401);
  const event = JSON.parse(req.body);   // { event: "search.completed", request_id, client_ref, status, charged, result }
  res.sendStatus(200);                   // ответить сразу, обработку — фоном
  setImmediate(() => handle(event));
});

Changelog

  • v1.0ssn_bg / dl / mvr search in sync and async modes, DL and MVR add-ons, signed webhooks with retries, request history, balance and packages, OpenAPI schema.