API reference · version 1

One endpoint for every check

POST /v1/lookup is the public lookup endpoint. Send an identifier; Orisift detects its type, runs the checks, and returns a verdict with the evidence and the limits behind it.

Authentication

Send your key as a bearer token. Keys are created in the dashboard and shown once; only a hash is stored, so a lost key is rotated rather than recovered. Keep it on your server. Anyone holding the key can spend your account’s credits. In the dashboard, select New key, save the revealed value securely, then select I have saved it. Revoke stops a key immediately. Rotate creates a replacement and leaves the previous key working for 60 minutes. Up to 20 active keys are allowed. Keys have no configurable scopes; each key can run all four lookup types.

Authorization header
Authorization: Bearer tl_live_xxxxxxxxxxxxxxxxxxxxxxxx

The API is server-to-server. It sends no CORS headers, so a browser cannot call it across origins. Do not put a key in frontend code, browser storage or a public repository. The Orisift dashboard uses a separate session-cookie and CSRF flow at /api/lookup; those cookies and response fields are not the public API contract.

Run a lookup

The only required field is input. Optional: type to skip detection, country for national-format phone numbers, and retain_input to store the submitted form against the lookup (off by default). Normalised identifiers are still stored; read Data handling.

  • input: string, 1 to 320 characters before trimming. Use one identifier per request.
  • type: optional phone, email, ip or domain. This selects a checker; it does not guarantee a valid value.
  • country: optional two-letter country code, case-insensitive. Use the phone number’s country, not the caller’s location. Unsupported codes leave a phone check unresolved, even if the input has an international calling code.
  • retain_input: optional boolean, default false. Read Data handling before submitting personal data.
  • Send JSON with Content-Type: application/json. The examples use reserved values and may return a rejected verdict. Copying an example does nothing; running it with your key consumes the stated credits.
POST /v1/lookup
# Bash/zsh. Set ORISIFT_API_KEY securely in your server environment first.
# Save a fresh key per logical lookup; reuse it only when retrying that lookup.
IDEMPOTENCY_KEY="$(uuidgen)"
curl --fail-with-body -X POST "https://orisift.com/v1/lookup" \
  -H "Authorization: Bearer $ORISIFT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -d '{"input":"2001:db8::1"}'
Node
// Node.js 22+, on your server. Save as lookup.mjs.
const apiKey = process.env.ORISIFT_API_KEY;
if (!apiKey) throw new Error("Set ORISIFT_API_KEY on the server first.");
// Save this value with your job and reuse it for retries of the same lookup.
const idempotencyKey = crypto.randomUUID();
const res = await fetch("https://orisift.com/v1/lookup", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${apiKey}`,
    "Content-Type": "application/json",
    "Idempotency-Key": idempotencyKey,
  },
  body: JSON.stringify({"input":"2001:db8::1"}),
});
const body = await res.json();
if (!res.ok) {
  console.error(res.status, body.error?.type, body.error?.request_id);
  throw new Error(body.error?.message ?? "Lookup failed");
}
// Decide using capabilities and limits as well as the overall verdict.
// insufficient_evidence and a null score require review, not automatic rejection.
console.log({
  verdict: body.verdict,
  riskScore: body.risk_score,
  capabilities: body.capabilities,
  limitations: body.coverage_limitations,
  ai: body.analysis.ai,
});

Country handling

A number in international form carries its own country, so nothing extra is needed. A number in national format is ambiguous, and Orisift will not guess: the same ten digits are a valid mobile in one country and nonsense in another. Without a country you get insufficient_evidence and a country_required coverage code, and a null risk score. This completed lookup still costs one credit. The dashboard asks you to choose a country before submitting.

National format
# Reserved UK example. This is not a real subscriber number.
curl --fail-with-body -X POST "https://orisift.com/v1/lookup" \
  -H "Authorization: Bearer $ORISIFT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"input":"020 7946 0000","country":"GB"}'

Response

This example comes from a deterministic check of the reserved documentation address 2001:db8::1. It is rejected because it cannot be public client traffic. The lookup ID, time and latency are illustrative; the remaining fields match the reference response. A model name can be present while AI is disabled. Read analysis.ai to tell whether AI ran. Your Settings opt-out applies to future dashboard and API lookups. Anonymous previews never use AI.

200 OK
{
  "object": "lookup",
  "api_version": "1",
  "id": "lk_example",
  "created": "2026-09-23T00:00:00.000Z",
  "type": "ip",
  "country": null,
  "normalized": "2001:db8::1",
  "verdict": "rejected",
  "risk_score": 60,
  "evidence_coverage": 95,
  "headline": "This IP address will not work",
  "summary": "Documentation addresses are never routable on the public internet. That is conclusive, so the IP address cannot be used.",
  "recommendation": "Do not accept this IP address. Ask for a replacement at the point of capture.",
  "attributes": [
    {
      "label": "Version",
      "value": "IPv6",
      "observed": true
    },
    {
      "label": "Scope",
      "value": "Special purpose",
      "observed": true
    }
  ],
  "signals": [
    {
      "label": "IANA registry",
      "value": "Documentation (RFC 3849). This address is not globally routable, so it cannot be a public client.",
      "tone": "bad",
      "source": "IANA special-purpose address registry",
      "provenance": "static_registry"
    }
  ],
  "capabilities": [
    {
      "id": "syntax",
      "state": "verified",
      "detail": "Valid IPv6"
    },
    {
      "id": "plan_validity",
      "state": "failed",
      "detail": "Documentation (RFC 3849)"
    },
    {
      "id": "reputation",
      "state": "unsupported",
      "detail": "Not applicable to a non-routable address"
    }
  ],
  "coverage_limitations": [],
  "analysis": {
    "checks": [
      "Address parsing",
      "IANA special-purpose screen"
    ],
    "ai": "disabled",
    "model": "claude-sonnet-5"
  },
  "usage": {
    "credits": 1,
    "credit_cost_table": {
      "phone": 1,
      "email": 1,
      "ip": 1,
      "domain": 2
    },
    "balance": 499,
    "idempotent_replay": false
  },
  "latency_ms": 1
}

Field reference

FieldTypeDescription
object / api_versionstring"lookup" and "1". Additive fields may be introduced; ignore unknown fields.
id / createdstringLookup ID and creation time in ISO 8601 format.
typestringphone, email, ip or domain. Automatically detected unless supplied.
countrystring | nullPhone country when determined, including from an international calling code. Otherwise null; email, IP and domain do not return geolocation.
normalizedstringDisplay form returned by the check. Parsed phones use international formatting with spaces, not guaranteed compact E.164. International domain names use Unicode display form. Inspect the E.164 attribute if your integration needs compact phone format.
verdictstringverified, risky, rejected or insufficient_evidence. Verified is not proof of identity, reachability or ownership. An evidence gap is not an invalid identifier.
risk_scoreinteger | null0 to 100, higher indicates more risk under the scoring rules. Not a probability of fraud. Null for insufficient_evidence.
evidence_coverageinteger0 to 100: how much of the expected evidence was obtained. Separate from risk.
headline / summary / recommendationstringHuman-readable assessment. Inspect individual capabilities and limitations before acting.
attributesarrayObjects with label, value (string) and observed (boolean). observed=false marks an inference.
signalsarraylabel, value, tone, source and provenance. Tones: good, warn, bad, neutral, unknown. Provenance: live_dns, live_rdns, static_registry, library, heuristic, ai_inference, unavailable.
capabilitiesarrayObjects with id, state and detail. IDs: syntax, plan_validity, infrastructure, reputation, carrier, reachability. Returned capabilities vary by type.
capabilities[].statestringverified: check established it; failed: check found a failure; inferred: indirect evidence; unsupported: not offered; unavailable: could not be checked. Missing support is not a negative finding.
coverage_limitationsarrayObjects with code and message explaining missing coverage or evidence.
analysis.checksstring[]Checks attempted for this identifier.
analysis.aistringdisabled, failed or ok. Disabled and failed use deterministic scoring. ok means the optional AI step completed, not that its inference is independently verified.
analysis.modelstring | nullConfigured model label when supplied. A label alone does not establish that the model ran.
usage.creditsintegerOriginal lookup cost, including on a replay. Phone/email/IP cost 1; domain costs 2. Completed rejected and inconclusive results are charged.
usage.credit_cost_tableobjectCurrent phone, email, ip and domain credit costs.
usage.balanceintegerAvailable account balance after the request.
usage.idempotent_replaybooleanTrue when the saved result was returned without a new charge.
latency_msnumberLookup execution duration in milliseconds; not an uptime or latency guarantee.

Idempotency

Successful responses include x-request-id, x-truelook-credits-charged, x-truelook-credit-balance and Server-Timing headers. Header names retain the existing API contract.

Send an Idempotency-Key header and a retry returns the original result without spending a second credit. Use a unique key for each logical lookup and reuse it only for retries of that same input, type and country. Do not use one signup ID for several different field checks.

  • Keys are scoped to the account and truncated to 200 characters. Keep your key within that length. The service does not reject a key reused with different input; it can return the earlier result instead.
  • While the stored result is available, a retry returns it with usage.idempotent_replay=true. usage.credits and x-truelook-credits-charged report the original cost, not an additional debit. Use usage.balance to inspect your remaining balance.
  • Without a key, the service deduplicates identical input/type/country requests within fixed ten-second time buckets. Requests on opposite sides of a bucket boundary may each be charged, even when less than ten seconds apart. Supply a key for reliable retries.
  • The result history has a retention window, so do not rely on indefinite response replay. Keep retry windows short. An interrupted request may be run again; abandoned reservations older than fifteen minutes are eligible for release by the reconciliation job, not guaranteed to clear at an exact wall-clock time.

Errors

Handled API errors carry a request_id, also returned in the x-request-id header. Quote it in support requests. A proxy or infrastructure failure may return a different response; check status and content type before assuming JSON. GET returns 405 method_not_allowed. OPTIONS returns 405 with Allow: POST and no response body; cross-origin browser calls are not supported.

Error shape
{
  "error": {
    "type": "insufficient_credits",
    "message": "Your credit balance cannot cover this lookup.",
    "request_id": "req_4f8a1c92b7e3d0a6f512",
    "balance": 0,
    "required": 2
  }
}
StatusTypeMeaning
400invalid_requestThe body was not valid JSON, or a field failed validation.
401authentication_requiredNo bearer token was supplied.
401invalid_api_keyThe key is unknown, revoked, or its account is suspended.
402insufficient_creditsThe balance cannot cover this lookup. The response includes balance and required.
422unrecognised_inputThe value could not be read as any of the four types. Send type or country to disambiguate.
429rate_limitedPlan rate limit exceeded. Honour the Retry-After header.
405method_not_allowedGET is not supported. Use POST. OPTIONS has an empty 405 response.
500internal_errorA handled execution failure. The service attempts to release the credit reservation. Contact support if the balance does not recover.

Rate limits and credits

PlanRequests / minCredit allowance
Free30500 once on signup
Starter12020,000 / month
Growth60085,000 / month
Professional1500300,000 / month

Credit cost per lookup: phone 1, email 1, IP 1, domain 2. Limits are enforced per account and, for API traffic, per key. A retry also counts toward the rate limit. Credit allowance is separate from requests per minute. Requests should be queued and retried with backoff after the Retry-After interval; no concurrency guarantee is published.

Coverage and limits

  • Phone: format and numbering-plan validation in the territories supported by the numbering-plan library. Carrier, porting and reachability are not offered, because no provider is contracted; the API reports those capabilities as unsupported rather than guessing.
  • Email: domain-level routing only. Orisift does not probe SMTP, so it never confirms that an individual mailbox exists or is monitored.
  • IP: IANA special-purpose screening for IPv4 and IPv6, plus reverse DNS. Network classification is a naming heuristic, not proxy or VPN detection, and geolocation is not returned at all.
  • Domain: delegation, address, mail and email-authentication policy. Registration data and domain age are not queried.
  • A resolver failure can leave a result with insufficient_evidence when required evidence is missing. Other independent checks may still establish a conclusion. It is never reported as proof that an identifier is invalid.
  • Orisift is not a consumer reporting agency. Its output is not a consumer report and must not be used to decide credit, employment, insurance or housing eligibility.
Full coverage matrix by country

What happens to what you send

  • Every API lookup saves a normalised identifier, fingerprint and result evidence in history. Normalisation is not anonymisation: an email, domain, phone number or IP address can remain identifiable, and the normalised value can equal the submitted value.
  • retain_input defaults to false. This omits the input property from newly saved results and removes other strings containing the submitted form, except strings equal to the normalised value. It does not prevent storage of the identifier. retain_input=true also preserves the submitted form. Using the API without opening history does not avoid this storage.
  • DNS checks send hostnames or IP addresses to public resolvers. When AI is allowed and available, evidence is sent to Anthropic. The submitted identifier is removed from the evidence before it is sent. Opt out in Settings for future dashboard and API lookups; anonymous previews never use AI.
  • Scheduled daily cleanup targets lookups older than 90 days; deletion depends on that job running. Account deletion removes active account records, keys, history and credit ledger. Contact privacy@orisift.com about processor or backup retention; deletion does not recall data already shared with a processor.

Get a key

500 credits once on signup. No card required.

Start for free