API Reference

Lead Nexus API

A versioned REST API over the same 170M-contact database the workshop runs on. JSON in, JSON out. Authenticate with a bearer key, filter with the same vocabulary as the platform, and spend credits, at a 2x rate, only when a full record leaves the platform.

Base URL: https://hypertouch.ai/api/v1

The API is in early access. The endpoints below are enabled per account as they roll out; talk to sales for access.

Authentication

Every request is authenticated with a bearer key. Create keys from your account, then send the key in the Authorization header. For curl convenience, an X-Api-Key header is also accepted. Keys are shown once at creation, so store yours somewhere safe.

Authorization: Bearer ht_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

A missing or malformed key returns 401. A revoked or expired key returns 401 with a more specific code. Every authenticated response carries your remaining credit and rate-limit quota in the response headers: X-Credits-Remaining, X-RateLimit-Limit, and X-RateLimit-Remaining.

Errors

Errors always come back in the same envelope, with a stable machine-readable code and a human-readable message.

{ "error": { "code": "insufficient_credits", "message": "You have 12 credits; this request needs 100." } }
HTTPCodes
401invalid_key, key_revoked, key_expired
402insufficient_credits
403no_api_access, daily_cap_reached
404not_found
422validation_failed (with an error.fields object)
429rate_limited (with a Retry-After header)
500server_error

Unknown filter keys return 422 with the offending keys listed, so a typo fails loudly instead of silently returning the wrong rows.

Rate limits

Each key has a per-minute limit set by your plan. Limits are enforced per key, not per account, so you can isolate integrations by issuing a separate key for each. When you exceed the limit you get a 429 with a Retry-After header. The current ceiling and how much is left are on every response as X-RateLimit-Limit and X-RateLimit-Remaining.

Credits and delivered records

The API uses the same credit pool as the workshop, and API usage draws credits at a 2x rate. You spend credits only when a full record leaves the platform. A record you have already paid for is tracked against your account and is free on every future call, on any endpoint. We charge per unique record, not per call.

ActionCredits
Counts, facets, meta, me, usage0
Search with reveal false (contact fields masked)0
Search with reveal true, per new record returned2 each
Lead lookup (new record)2
Enrich match (new record)2
Enrich miss0
Export, per delivered record2
Re-delivery of a record you already paid for0

Pagination

Search returns one page at a time. Set page_size up to 100 and walk results with offset. The offset ceiling is 10,000; past that, switch to an export, which streams up to 25,000 records per job. Every search response echoes returned, offset, and page_size in its meta object.

The filter object

count, search, and exports all share one filter object. Pass arrays of values per key; a key with multiple values matches any of them. Mirror any key under an excludes object to negate it. This is the same engine the workshop filters run on.

KeyTypeBehavior
state, country, industry, company_size, revenue_range, seniority, department, persona, email_status, linkedin_industrystring[]Matches any of the supplied values.
timezonesstring[]Mapped to states, then matched by state.
naicsstring[] (codes or text)Text descriptions resolve to codes, then match the record NAICS codes.
company_domainsstring[]Matches the company domain.
keywordstring[]Tokenized match across the keyword scope columns.
keyword_mode"any" or "all"OR across keywords, or AND.
keyword_scopestring[]Which columns the keyword search covers. A subset of the searchable columns; defaults to the standard scope.
refinestring[]Boolean conditions: has_email, has_phone, has_cell_phone, has_direct_phone, has_linkedin, job_is_current.
any filterable insight fieldarray, {min,max}, or trueArray match, numeric range, or equals "Yes", depending on the field type.
excludes.*same keysThe same conditions, negated.

The only populated value for email_status is VALID, so refine with has_email is the idiomatic way to ask for records that have an email on file.

Endpoints

GET /me Free

Returns the authenticated user, the active plan and its API limits, the credit balance, and the calling key.

{
  "user": { "id": 42, "email": "dev@acme.com" },
  "plan": { "slug": "scale", "name": "Scale", "api_rate_per_min": 120, "api_daily_record_cap": 50000 },
  "credits": { "balance": 18211 },
  "key": { "name": "Production", "prefix": "ht_live_a1b2c3d4", "created_at": "...", "last_used_at": "..." }
}
POST /leads/count Free

Counts matching records for a filter object and, optionally, returns facet breakdowns. Up to three facet fields, each topped at 50 values. An unfiltered count is allowed.

Request

{
  "filters": { "seniority": ["C-Level","VP"], "industry": ["Computer Software"], "refine": ["has_email"] },
  "excludes": { "state": ["CA"] },
  "facets": ["state", "company_size"]
}

Response

{
  "total": 184223,
  "with_email": 96101,
  "with_phone": 121873,
  "facets": { "state": [ {"value":"TX","count":21044} ] }
}
GET /leads/{rbid} 2 credits if new

Looks up a single full record by its rbid. Returns 404 if there is no match. Costs two credits the first time you pull a given record, and is free thereafter. Add the query parameter reveal=false to return the record with contact fields masked for no credit.

GET /api/v1/leads/HT12345678?reveal=false
POST /leads/enrich 2 credits per match

Matches one record from exactly one identity block. Send an email, a LinkedIn URL, or a name with a company domain. A match returns the full record, always revealed, plus a match block. A miss returns 200 with match set to null and costs nothing.

Request, one of

{ "email": "jane.doe@acme.com" }

{ "linkedin_url": "https://www.linkedin.com/in/janedoe" }

{ "first_name": "Jane", "last_name": "Doe", "company_domain": "acme.com" }

Response

{ "match": { "confidence": "exact", "matched_on": "email" }, "record": { } }

Sending more than one identity block in a single request returns 422.

POST /exports 2 credits per delivered record

Queues an async bulk pull of up to 25,000 records, using the same engine as exports in the app. Credits are reserved up front against your lead_limit and unused credits are refunded when the job finishes. Exports appear in your in-app history with full re-download. Pass an idempotency_key to make retries safe.

Request

{
  "filters": { },
  "excludes": { },
  "columns": ["first_name","last_name","email","title","company_name"],
  "lead_limit": 25000,
  "max_per_company": 3,
  "exclude_delivered": true,
  "idempotency_key": "acme-batch-2026-06-10"
}

Response, on submit

202  { "export_id": 991, "status": "queued", "credits_reserved": 25000 }

Poll status

GET /api/v1/exports/991
{ "export_id": 991, "status": "processing", "progress": 62, "lead_count": null }

On completion the status response adds lead_count, credits_spent, credits_refunded, and a download_url. Fetch the file from GET /exports/991/download, authenticated with your key.

GET /usage Free

Returns your credit balance and a per-day rollup of requests, records, and credits spent. Defaults to the last 30 days; bound the window with from and to.

{
  "credits": { "balance": 18037 },
  "days": [ { "date": "2026-06-09", "requests": 412, "records": 9100, "credits_spent": 7211 } ],
  "delivered_total": 184223
}
GET /meta/fields GET /meta/options Free

/meta/fields returns the field catalog: each field name, label, data type, filter type, category, whether it is filterable and exportable, and a tier of contact or preview. /meta/options returns the value vocabulary for one field, for example /meta/options?field=industry. Use these to build your own filter UI.

Code samples

Enrich one record, in four stacks.

curl

curl https://hypertouch.ai/api/v1/leads/enrich \
  -H "Authorization: Bearer ht_live_..." \
  -H "Content-Type: application/json" \
  -d '{"email": "jane.doe@acme.com"}'

PHP (Guzzle)

$client = new GuzzleHttp\Client();
$res = $client->post('https://hypertouch.ai/api/v1/leads/enrich', [
    'headers' => ['Authorization' => 'Bearer ht_live_...'],
    'json'    => ['email' => 'jane.doe@acme.com'],
]);
$data = json_decode($res->getBody(), true);

Python (requests)

import requests

res = requests.post(
    "https://hypertouch.ai/api/v1/leads/enrich",
    headers={"Authorization": "Bearer ht_live_..."},
    json={"email": "jane.doe@acme.com"},
)
data = res.json()

Node (fetch)

const res = await fetch("https://hypertouch.ai/api/v1/leads/enrich", {
  method: "POST",
  headers: {
    "Authorization": "Bearer ht_live_...",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ email: "jane.doe@acme.com" }),
});
const data = await res.json();

Changelog

  • v1 launch. Count, search, lead lookup, enrich, exports, usage, and meta endpoints. Bearer key auth, shared credit pool at a 2x rate, per-key rate limits.
Get Started