API reference / Company data & website pixels
AI guide ↗
Start here

Find the company behind a visit.

Look up an IP you already have, or install a website pixel and choose which visits to identify.

  • IP lookupSend an IPv4 or IPv6 address. Get saved company or network details, or request enrichment when needed.
  • Website pixelsCreate a tracker per customer website. Use our domain or connect your own.
  • Preview, then revealSee a session’s estimated country first. Request company or network details only for the sessions you choose.
  • Custom labelsAttach your customer ID, campaign or other labels. They travel with the session, reveal and webhook.
  • WebhooksReceive signed events for new sessions and completed reveals. Check deliveries and retry failures.
  • Usage & controlsCheck requests, outcomes, reveal units and limits. Read usage per tracker; enable, disable or delete pixels.
  • Partner dashboardSign in to see your account, API keys, pixels and usage, and manage webhooks.

Pixel flow: Install → Preview country → Choose a session → Reveal.
Each new reveal with company or network data uses one unit, including ISP and cached results. Previewing adds no reveal unit; retrying the same reveal adds no extra unit.

Use an API key from your server; never put it in the pixel. 202 means processing—follow Retry-After and poll. A company match is not guaranteed. OpenAPI · AI agent guide

Company lookup

Look up a company by IP address

Enter an IP address to find its company. Paste your key below to try it.

GET/v1/lookup
Try a request 1. Key → 2. Parameters → 3. Send

Send a real request to https://partner.rktad.com.

Your key stays in this tab. This page does not save it.
One public IP. No port or CIDR suffix.
Read-only: this request will not start enrichment. An unknown, unsubmitted IP returns 404.

Website pixels

See the country. Choose what to reveal.

Give each customer a tracker. Read visitor countries first, then request company or network data only for the sessions you choose.

  1. InstallCreate a tracker. Copy its script to the website.
  2. PreviewRead new sessions and their visitor countries.
  3. RevealChoose a session and request its details.
Create a pixel & try the flow →
What goes on the website?
<script async src="https://partner.rktad.com/tracking/a.js?tracker_id=YOUR_TRACKER_ID"></script>

Use the exact snippet returned by POST /v1/trackers. Add your own labels with data-custom; they appear in sessions, reveals and webhooks. Your API key stays on your server. Use your own domain →

GET /tracking/a.js loads the pixel. It sends visits to POST /tracking/collect automatically. It does not read forms or email fields.

Sessions, privacy and limits

This tracker collects IP and estimated country, public tracker/session tokens, visit times and any custom labels you explicitly supply. It does not read forms, emails or page contents. It uses first-party session storage for a per-tab visit token, renewed after 30 minutes without a tracked visit. Without storage, page reloads can create extra sessions. An IP or country change creates a separate session.

Follow your site’s consent requirements before loading the script. Sessions and receipt results are accessible for 30 days; billing counts remain. Country filtering happens in your app. A skipped session does not trigger new enrichment. The feed reports new sessions, not every page view.

Initial tracking limits: 200 capture requests/sec total, 100/sec per tracker, 10/sec per visitor IP. New reveal creation: 100/sec per partner, within the API key’s request limit. New enrichment shares the existing 900/minute intake and pending limits. These are admission limits, not a performance guarantee. Browser blocking, network failures and these limits can prevent collection.

Country is approximate and can reflect a VPN. IP Geolocation by DB-IP, September 2026 Lite data, CC BY 4.0. Applications displaying or using this country data must preserve the required attribution.

One new reveal with data = one billable unit. Includes ISP and cached data. Previewing, retrying the same reveal and polling add no extra unit. 202 means wait, then poll the receipt. Check reveal usage →

Send custom pixel parameters

Add your own labels with data-custom. They are saved from the first accepted visit in each session and returned as custom in session previews, reveal receipts and both webhook events. Later visits update last_seen_at, but do not replace these labels. Missing values and older sessions return {}.

<script async
  src="https://partner.rktad.com/tracking/a.js?tracker_id=YOUR_TRACKER_ID"
  data-custom='{"account_id":"acme","campaign":"autumn","plan":"pro"}'></script>
Dynamic JavaScript values
const pixel = document.createElement('script');
pixel.async = true;
pixel.src = 'https://partner.rktad.com/tracking/a.js?tracker_id=YOUR_TRACKER_ID';
pixel.dataset.custom = JSON.stringify({account_id: 'acme', campaign: 'autumn'});
document.head.appendChild(pixel);

Use a flat JSON object: up to 32 fields and 2,048 UTF-8 bytes total. Values may be strings (up to 512 characters), numbers, booleans or null. No arrays or nested objects. Field names must start with a letter and contain only letters, digits, underscores, dots or hyphens (up to 64 characters); constructor and prototype are reserved. Invalid data-custom stops that pixel execution and logs a browser warning. Invalid direct capture requests return 400 invalid_custom_parameters.

These labels are supplied by the website and can be changed by a visitor. Use them for context, never for access control, trusted account ownership or billing. Keep API keys, passwords and personal data out of the snippet. Use non-sensitive internal references. HTML-escape dynamic attribute values, or use the JavaScript example with dataset.custom.

Use your own tracker domain

Send us your hostname, such as track.yourcompany.com. Create a DNS-only CNAME to partner.rktad.com. Wait for us to activate HTTPS and assign it to your account, then use your returned snippet. Allow that hostname in your website’s script-src and connect-src policies.

Webhooks: events, signatures and delivery

Register your receiver before installing the pixel. Check the country in session.created, explicitly request a reveal, then receive reveal.completed.

curl https://partner.rktad.com/v1/webhooks \
  -H "Authorization: Bearer $PARTNER_API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{"name":"Production","url":"https://your-app.example/webhooks","events":["session.created","reveal.completed"]}'
Session event example
{
  "id": "evt_session_00000000-0000-4000-8000-000000000001",
  "type": "session.created",
  "schema_version": 1,
  "created_at": "2026-09-25T12:00:00.000Z",
  "data": {
    "session_id": "00000000-0000-4000-8000-000000000001",
    "tracker_id": "00000000-0000-4000-8000-000000000002",
    "customer_reference": "acme",
    "custom": {
      "account_id": "acme",
      "campaign": "autumn",
      "plan": "pro"
    },
    "visitor_country": "GB",
    "first_seen_at": "2026-09-25T12:00:00.000Z",
    "last_seen_at": "2026-09-25T12:00:00.000Z",
    "expires_at": "2026-10-25T12:00:00.000Z"
  }
}
Completed reveal example
{
  "id": "evt_reveal_00000000-0000-4000-8000-000000000003",
  "type": "reveal.completed",
  "schema_version": 1,
  "created_at": "2026-09-25T12:00:00.000Z",
  "data": {
    "reveal_id": "00000000-0000-4000-8000-000000000003",
    "session_id": "00000000-0000-4000-8000-000000000001",
    "tracker_id": "00000000-0000-4000-8000-000000000002",
    "custom": {
      "account_id": "acme",
      "campaign": "autumn",
      "plan": "pro"
    },
    "status": "complete",
    "billable": true,
    "billable_units": 1,
    "created_at": "2026-09-25T12:00:00.000Z",
    "completed_at": "2026-09-25T12:00:00.000Z",
    "expires_at": "2026-10-25T12:00:00.000Z",
    "result": {
      "status": "complete",
      "match_type": "company",
      "company_found": true,
      "enriched_at": "2026-09-18T12:00:00.000Z",
      "stale": false,
      "fresh_until": "2026-10-18T12:00:00.000Z",
      "refresh_status": "not_needed",
      "refresh_error": null,
      "network": {
        "connection_type": "Business",
        "audience_type": "Business",
        "audience_group": null,
        "detail_level": null,
        "is_isp": false
      },
      "company": {
        "name": "Example Company",
        "website": "example.com",
        "brand_name": null,
        "linkedin_url": null,
        "industry": "Software",
        "industry_subcategory": null,
        "employees": "120",
        "annual_revenue": null,
        "revenue_band": null,
        "naics_code": "541511",
        "sic_code": null,
        "city": "London",
        "region": null,
        "postal_code": null,
        "country_code": "GB",
        "country_name": "United Kingdom"
      }
    },
    "status_url": "/v1/reveals/00000000-0000-4000-8000-000000000003"
  }
}

Verify signatures against the exact raw body. Save the event durably before returning 2xx, and deduplicate by event ID. Delivery is at least once and may be out of order. Retry attempts do not add reveal units.

Verify every delivery

Headers: webhook-id, webhook-timestamp (Unix seconds), webhook-signature.
Signature: v1,BASE64_HMAC_SHA256. During secret rotation the header contains two space-separated signatures. Decode the base64 after whsec_ to get the signing key. Sign the exact string ID.TIMESTAMP.RAW_BODY. Do not parse and reserialize JSON before verification. Check a five-minute timestamp window and use a constant-time comparison. Treat malformed or duplicate headers as invalid. Keep your server clock synchronized.

import {createHmac, timingSafeEqual} from 'node:crypto';
function verifyWebhook(rawBody, headers, secret) {
  const id = headers['webhook-id'];
  const timestamp = headers['webhook-timestamp'];
  const signatures = headers['webhook-signature'];
  if (typeof id !== 'string' || !/^evt_[a-zA-Z0-9_-]{1,100}$/.test(id) ||
      typeof timestamp !== 'string' || !/^\d{10,11}$/.test(timestamp) ||
      Math.abs(Date.now() / 1000 - Number(timestamp)) > 300 ||
      typeof signatures !== 'string' || signatures.length > 512) return false;
  const expected = createHmac('sha256', Buffer.from(secret.slice(6), 'base64'))
    .update(id + '.' + timestamp + '.').update(rawBody).digest();
  return signatures.split(' ').some(part => {
    if (!/^v1,[A-Za-z0-9+/]{43}=$/.test(part)) return false;
    const actual = Buffer.from(part.slice(3), 'base64');
    return actual.length === expected.length && timingSafeEqual(actual, expected);
  });
}

Enforce a bounded receiver body limit (1MiB is sufficient for current event payloads). After verification, check event.id equals webhook-id. Atomically store the event and its ID in your own durable queue, then return 200 or204 promptly. Return2xx for an already stored duplicate too. Process business logic asynchronously. Never trust webhook payloads without signature validation.

Delivery guarantees and retries

At least once, not exactly once. Events may arrive more than once or out of order. Deduplicate by event.id, not session ID: a session can have multiple separately billed reveals. Retries and manual redelivery preserve event ID and payload; timestamp and signature are refreshed for each attempt. A timeout after your server stored the event can cause another delivery.

Public HTTPS port443 only. TLS certificates must be valid. Private/loopback/link-local/reserved addresses, this platform's origin, mixed public/private DNS answers and redirects are rejected. All destination IPs are checked on every attempt. No receiver response body is retained.

Any2xx acknowledges. Other HTTP statuses, DNS/TLS/connect errors and10-second timeouts retry with exponential delay (about15seconds initially, capped at6hours, with jitter). Retry-After is respected up to24hours. After12 attempts or7days per retry cycle, failed deliveries stay visible. Manual retry starts another cycle while the source payload remains available. Pending includes retrying, paused and in-flight deliveries. Failed, expired and cancelled are separate states.

Payloads expire with their session or receipt, at most30days from session creation. A late reveal may have less time left. Expired payloads cannot be replayed; minimal delivery metadata remains90days. Operational cleanup is incremental. One endpoint may have up to32 concurrent deliveries and200 delivery starts/second; these are ceilings, not guaranteed throughput. Slow receivers reduce delivery speed. Queue capacity is2,500,000 pending deliveries per partner. A shared retained-payload storage limit can also temporarily stop new events; each event is limited to1MiB. When full, source capture/reveal completion is rejected with503 and is not acknowledged as successful; retry safely. A queued accepted reveal remains pending until completion can commit. Pause does not free queue slots.

POST /v1/webhooks/{id}/rotate-secret with {} returns a new signing_secret once. Both signatures are sent for24hours, then only the new one. A second rotation during this overlap returns409. On a lost rotation response, use the existing secret during the overlap, then rotate again after the overlap; inspect secret_rotation_until and do not assume the lost new secret can be recovered.

Errors and accounting

400 invalid_webhook / invalid_webhook_url / invalid_webhook_query: fix request.
401 unauthorized: check API key.
404 webhook_not_found / webhook_delivery_not_found / tracker_not_found: wrong ID or account.
409 webhook_disabled / webhook_rotation_pending: enable endpoint or wait for overlap.
410 webhook_event_expired: payload no longer available.
429 webhook_limit / webhook_management_limit / rate_limit / api_rate_limit: follow Retry-After. Management budget120/minute per partner per service, separate from lookup allowance.
503 webhook_queue_full / webhooks_unavailable / service_unavailable: retry with backoff; do not treat as delivered.

Creation, tests, delivery reads, configuration changes and redelivery do not count lookup requests or reveal units. Completing an already authorized reveal follows the existing per-reveal billing rule. Delivery success is not a condition for billing. No webhook automatically starts a new reveal.

Download the webhook guide (Markdown) →

API key & security

Get a key from your API administrator. Send it as Authorization: Bearer YOUR_API_KEY. Keep it on your server, not in a public page. A missing or invalid key returns 401.

Query parameters

ipstringrequired

Use one public IPv4 or IPv6 address, such as 8.8.8.8. Do not include a website URL or port.

Send exactly one ip query parameter. GET never starts enrichment.

New IP? Here’s what happens.

  1. POST the IP. Get a saved result now, or start a new lookup.
  2. 202 means wait. We’re looking it up. It does not mean “no company.”
  3. GET the result. Wait at least 15 seconds between checks. A new lookup may take longer.
See the flow diagram
From an IP address to a saved result Send an IP with POST. If a saved result exists, get 200 complete. If it is new, get 202 pending while we look it up and save it. Wait at least 15 seconds between GET checks. A completed lookup can be company, ISP or unclassified. HOW A LOOKUP WORKS 1 · SEND AN IP POST /v1/lookup 2 · CHECK OUR DATA Do we have a result? YES 200 · COMPLETE Your result is ready NEW IP 202 · PENDING We look it up and save it WHEN FINISHED 3 · CHECK AGAIN WITH GET Wait at least 15 seconds between checks. Finishing can take longer.
Download diagram ↗

Read the answer

200 · Done

company_found: true means we found a company. false means ISP or uncertain classification. Still check company: any details we have are included.

202 · Working

The answer is not ready. Check the returned status_url after Retry-After seconds.

404 on GET? Use POST to start the lookup. Errors are never a “no company” result.

See the result guide
What the four result states mean Company: 200, company_found true. ISP: 200, ISP flag set, company_found false; available company fields remain visible. Unclassified: 200, not enough company information, company_found false. Pending: 202, still being checked, company_found null. Pending and errors are not negative company matches. READ THE RESULT Company We found company information. 200 · company_found: true Internet provider (ISP) ISP flag set. Company fields still shown. 200 · company_found: false Unclassified Classification is uncertain. This does not prove it is not a business. 200 · company_found: false Pending / retrying We are still checking this IP. Check again later. The answer is unknown. 202 · company_found: null
Download guide ↗

Choose a response example in the tester. Examples are made up; test with a real public IP.

Errors & fixes

Check error.code below for the fix. Share request_id with support.

{
  "error": {
    "code": "unauthorized",
    "message": "The key is missing, malformed, invalid, expired or revoked."
  },
  "request_id": "00000000-0000-4000-8000-000000000001"
}

58 known application codes

HTTP / codeWhat happenedWhat to do
400invalid_ipThe IP is missing, malformed, private or reserved.Send one public IPv4 or IPv6 address, without a port, CIDR suffix or zone ID.
400invalid_queryThe query parameters do not match the endpoint.GET lookup needs exactly one ip parameter. POST lookup takes JSON, with no query string.
400invalid_jsonThe request body is not valid JSON.Send valid JSON such as {"ip":"8.8.8.8"}.
400invalid_bodyThe JSON body has the wrong shape or extra fields.Send an object containing only ip. A missing or invalid ip returns invalid_ip.
400invalid_rangeThe usage dates or query parameters are invalid.Use real UTC dates, from < to, at most 31 days, with no duplicate or extra parameters.
401unauthorizedThe key is missing, malformed, invalid, expired or revoked.Send exactly one Authorization: Bearer header with an active key. Contact your API administrator for a replacement.
404not_requestedGET found no saved result and no API enrichment submission.POST the same IP to request enrichment, then poll with GET. This is not a confirmed no-company result.
404not_foundThe endpoint does not exist.Check the path. Use /v1/lookup or /v1/usage.
405method_not_allowedThe lookup endpoint does not accept this HTTP method.Use GET or POST; the Allow response header lists the accepted methods.
413body_too_largeThe request body exceeds 1,024 bytes.Submit one IP per request in a small JSON object.
414uri_too_longThe request URL exceeds 512 characters.Shorten the path and query. Never put your API key in the URL.
415unsupported_media_typeThe POST Content-Type is not application/json.Set Content-Type: application/json.
415unsupported_encodingThe request has a Content-Encoding header.Send uncompressed JSON without Content-Encoding.
429rate_limitThis key has exceeded its request rate.Wait at least Retry-After seconds, then reduce concurrency. View your key limits with GET /v1/usage.
429api_rate_limitThe API has reached its shared request capacity.Honor Retry-After, add jitter and retry with lower concurrency.
429lookup_capacityToo many distinct IP lookups are in flight.Honor Retry-After and retry with fewer simultaneous lookups.
429pending_limitYour partner has reached its limit for unfinished enrichments.Wait for existing lookups to finish before submitting new IPs. View enrichment and limits in GET /v1/usage. Repeating an accepted IP is allowed.
429enrichment_limitThis key has used its daily allowance for new enrichment reservations.Wait until the next UTC day or ask for a higher allowance. Existing saved lookups remain available; retrying every 15 seconds will not reset the allowance.
429enrichment_capacityThe shared enrichment intake or outstanding-work limit is full.Honor Retry-After and use backoff. Repeated submissions do not speed up the lookup.
503api_busyThe API is temporarily busy.Honor Retry-After (normally 5 seconds for this code) and retry with backoff.
503backend_unavailableThe lookup or enrichment request could not be completed.Retry with backoff. A failed POST response does not prove submission failed; repeating the same POST is safe.
503backend_timeoutThe request took too long to complete.Retry with backoff. Do not interpret a timeout as a no-company result.
503submission_unconfirmedAn enrichment reservation exists, but acceptance was not confirmed.Repeat POST for the same IP to confirm submission, then poll with GET.
503lookup_reconciliation_requiredWork is marked complete, but the saved lookup is unavailable.Retry later; if persistent, share request_id with your API administrator.
503usage_unavailableUsage reporting is temporarily unavailable.Retry with backoff. A POST may already have been submitted; repeating the same IP is safe.
503service_unavailableAn unexpected temporary service failure occurred.Honor Retry-After and retry with backoff; share request_id if the error persists.
400invalid_origininvalid originCheck the request fields and formats.
400invalid_trackerinvalid trackerCheck the request fields and formats.
400invalid_captureinvalid captureCheck the request fields and formats.
400invalid_custom_parametersinvalid custom parametersCheck the request fields and formats.
400invalid_cursorinvalid cursorCheck the request fields and formats.
400invalid_idempotency_keyinvalid idempotency keyCheck the request fields and formats.
403untrusted_collectoruntrusted collectorCheck the tracker origin and enabled state.
403tracker_origin_deniedtracker origin deniedCheck the tracker origin and enabled state.
404tracker_not_foundtracker not foundCheck the ID and partner account.
404session_not_foundsession not foundCheck the ID and partner account.
404reveal_not_foundreveal not foundCheck the ID and partner account.
409idempotency_conflictidempotency conflictResolve the state conflict; do not create a new reveal just to retry.
409tracker_disabledtracker disabledResolve the state conflict; do not create a new reveal just to retry.
410tracker_deletedtracker deletedThis resource or payload is no longer available.
410session_expiredsession expiredThis resource or payload is no longer available.
410reveal_expiredreveal expiredThis resource or payload is no longer available.
429tracking_capacitytracking capacityFollow Retry-After and your account limits.
429tracker_limittracker limitFollow Retry-After and your account limits.
503tracking_unavailabletracking unavailableRetry with backoff. Keep the same Idempotency-Key for reveal retries.
503reveal_unconfirmedreveal unconfirmedRetry with backoff. Keep the same Idempotency-Key for reveal retries.
400invalid_webhookinvalid webhookCheck the request fields and formats.
400invalid_webhook_urlinvalid webhook urlCheck the request fields and formats.
400invalid_webhook_queryinvalid webhook queryCheck the request fields and formats.
404webhook_not_foundwebhook not foundCheck the ID and partner account.
404webhook_delivery_not_foundwebhook delivery not foundCheck the ID and partner account.
409webhook_disabledwebhook disabledResolve the state conflict; do not create a new reveal just to retry.
409webhook_rotation_pendingwebhook rotation pendingResolve the state conflict; do not create a new reveal just to retry.
410webhook_event_expiredwebhook event expiredThis resource or payload is no longer available.
429webhook_limitwebhook limitFollow Retry-After and your account limits.
429webhook_management_limitwebhook management limitFollow Retry-After and your account limits.
503webhook_queue_fullwebhook queue fullRetry with backoff. Keep the same Idempotency-Key for reveal retries.
503webhooks_unavailablewebhooks unavailableRetry with backoff. Keep the same Idempotency-Key for reveal retries.

Connection errors

A connection failure may return no JSON or request ID. Handle unexpected HTTP codes and non-JSON responses. Record the status and request ID when available, and contact support if retries fail.

If a POST times out or returns 503, the IP may already have been submitted. Repeat the same POST to confirm. Never turn a transport failure, 404 or 202 into “no company.”

Optional company clues

Some keys include company_signals: possible companies linked to recent activity on that IP. A clue is not a confirmed employer. submitted means a submit event was seen; field_only means an email was typed. Check flags before using a clue. The main company result stays separate.

No email addresses or visited pages are returned. unavailable means clues could not be checked; it does not mean there are none.

All response fields

Null means a usable value is unavailable. Do not infer a value from a missing field. Large employees and annual_revenue integers may be decimal strings to preserve precision.

Error3 fields
FieldTypeMeaning
errorrequiredobjectMachine-readable code and a human-readable message.
request_idrequiredstringInclude this identifier when reporting a failed request.
company_signalsCompanySignalsOptional company clues for enabled keys. A clue is not a confirmed employer. Check its flags; your main lookup result stays separate.
Company16 fields
FieldTypeMeaning
namerequiredstring | nullCompany field; null when unavailable.
websiterequiredstring | nullCompany field; null when unavailable.
brand_namerequiredstring | nullMarketing or alternate company name; not necessarily a registered brand.
linkedin_urlrequiredstring | nullCompany field; null when unavailable.
industryrequiredstring | nullCompany field; null when unavailable.
industry_subcategoryrequiredstring | nullCompany field; null when unavailable.
revenue_bandrequiredstring | nullCompany field; null when unavailable.
naics_coderequiredstring | nullCompany field; null when unavailable.
sic_coderequiredstring | nullCompany field; null when unavailable.
cityrequiredstring | nullCompany field; null when unavailable.
regionrequiredstring | nullCompany field; null when unavailable.
postal_coderequiredstring | nullCompany field; null when unavailable.
country_coderequiredstring | nullCompany field; null when unavailable.
country_namerequiredstring | nullCompany field; null when unavailable.
employeesrequiredstring | integer | nullReported employee count or range. Preserve strings; ranges are not exact headcounts and large counts may be decimal strings.
annual_revenuerequiredstring | integer | nullAnnual sales value; currency and reporting period are unspecified. May be a decimal string to preserve precision.
Lookup14 fields
FieldTypeMeaning
iprequiredstringThe IP spelling you submitted.
matched_iprequiredstringExact spelling of the IP address that matched
statusrequired"complete"complete is a finished lookup; pending and retrying are unfinished.
match_typerequired"company" | "isp" | "unclassified"company: company information found; isp: internet service provider; unclassified: insufficient evidence to classify. Unclassified is not proof that the IP is not a business.
company_foundrequiredbooleanTrue only when match_type is company. False for complete ISP/unclassified results. Pending responses instead use null.
enriched_atrequiredstring | nullUTC timestamp of the saved enrichment, not the current API request time.
companyrequiredCompany | nullAll available company fields, including ISP/unclassified records; null only when every public company field is unavailable. Presence does not override network.is_isp or establish a non-ISP business match.
networkrequiredobjectInformation about the IP connection. Fields can be null.
request_idrequiredstringInclude this identifier when reporting a failed request.
stalerequiredbooleanWhether this saved result is due for refresh. Expiry does not remove available fields.
fresh_untilrequiredstring | nullExpiry of this saved result. Use this timestamp rather than assuming a fixed lifetime. Most company results last 30 days; some last 7 days. Null if no usable timestamp.
refresh_statusrequired"not_needed" | "not_requested" | "pending" | "deferred"not_needed: no additional work needed; not_requested: this GET started no work (another request may have done so); pending: unfinished enrichment has been accepted; deferred: POST could not confirm or admit work, so retry POST after Retry-After. A saved ISP/unclassified result may be fresh while additional company enrichment is pending.
refresh_errorrequirednull | "pending_limit" | "enrichment_limit" | "enrichment_capacity" | "submission_unconfirmed" | "lookup_failed"Reason for deferred refresh. Existing data is still returned with HTTP 200. Apply daily/pending limits or temporary-error retry guidance. Unknown acknowledgement: repeat the same POST; do not assume no job exists.
company_signalsCompanySignalsOptional company clues for enabled keys. A clue is not a confirmed employer. Check its flags; your main lookup result stays separate.
Pending13 fields
FieldTypeMeaning
iprequiredstringThe IP spelling you submitted.
lookup_iprequiredstringCanonical form used for the enrichment request.
statusrequired"pending" | "retrying"complete is a finished lookup; pending and retrying are unfinished.
company_foundrequirednulltrue only for a company match; false for completed ISP/unclassified results; null while pending.
companyrequirednullAll available company fields, even when ISP or unclassified. Null only when no fields are available.
submitted_atrequiredstringUTC time of the original API reservation.
attemptsrequiredintegerNumber of lookup attempts; 0 while awaiting the first attempt.
next_retry_atrequiredstring | nullNext scheduled retry in UTC, or null. Not a completion estimate.
last_errorrequirednull | "lookup_failed"lookup_failed means an unsuccessful lookup attempt, not a negative company match.
status_urlrequiredstringRelative same-origin URL for getLookup. Send the same Bearer authentication; do not follow arbitrary external URLs.
poll_after_secondsrequiredintegerSuggested minimum delay before the next GET poll; also provided by Retry-After.
request_idrequiredstringInclude this identifier when reporting a failed request.
company_signalsCompanySignalsOptional company clues for enabled keys. A clue is not a confirmed employer. Check its flags; your main lookup result stays separate.
Usage12 fields
FieldTypeMeaning
key_idrequiredstringPublic key identifier; not the secret key.
namerequiredstringName assigned to this API key.
fromrequiredstringInclusive start in UTC.
torequiredstringExclusive end in UTC.
timezonerequired"UTC"Always UTC.
limitsrequiredobjectRequest-rate limits and optional daily enrichment budget. null means no daily cap; 0 means saved IPs only.
totalsrequiredobjectRequests, cache hits and average server processing time in milliseconds.
outcomesrequiredobjectCounts by company, isp, unclassified, enqueued, pending, retrying, invalid_request, rate_limited or error.
hourlyrequiredarrayPer-hour, per-outcome counters in UTC.
accountingrequiredstringWhat the counters include and exclude.
enrichmentrequiredEnrichmentStatusCurrent enrichment status for the partner that owns this key; no IP list or other partners are exposed.
revealsRevealUsage
EnrichmentStatus6 fields
FieldTypeMeaning
pendingrequiredintegerUnfinished unique enrichments started by this partner, shared across its keys. Current snapshot, independent of usage date range.
submission_unconfirmedrequiredintegerPending requests whose acceptance is not confirmed. Repeat the original POST to confirm.
retryingrequiredintegerPending IPs with at least one observed unsuccessful attempt. Updates periodically.
oldest_pending_atrequiredstring | nullSubmission time of the oldest unfinished request, or null.
oldest_checked_atrequiredstring | nullOldest status-check time among pending requests; null if none or any are not checked yet. Status updates can lag.
completed_observed_last_minuterequiredintegerRequests observed to finish during the last minute; not a guaranteed completion rate.
CompanySignals4 fields
FieldTypeMeaning
statusrequired"available" | "unavailable"unavailable means the clues could not be checked. An empty available list means no eligible recent clues were found.
window_daysrequired30
truncatedrequiredbooleanMore evidence may exist than the bounded response includes.
candidatesrequiredarray
CompanyCandidate11 fields
FieldTypeMeaning
domainrequiredstringObserved email domain, with common personal and reserved test domains excluded. Unlisted personal/disposable domains can still appear.
companyrequiredCompany | nullProfile matched by exact website hostname, ignoring www. Null if unmatched or multiple profiles share the domain. Subdomains are not guessed.
verifiedrequiredfalse
evidencerequired"submitted" | "field_only"submitted records a browser submit event, not confirmed form acceptance. field_only means an email field was observed.
first_seen_atrequiredstringFirst qualifying observation within the rolling 30-day window.
last_seen_atrequiredstring
observationsrequiredintegerDistinct captured events. Retries/replay are deduplicated; blur and submit can be separate events.
submitted_observationsrequiredinteger
observed_visitsrequiredintegerDistinct known page-run identifiers; 0 means none were recorded. Not a person or session count.
sitesrequiredintegerNumber of distinct website/installation combinations; their identities are private.
flagsrequiredarrayReasons to treat a candidate cautiously. An empty list is not verification. Bot/connection flags describe the current IP classification, not necessarily its classification at observation time.
CustomParameters
{
  "type": "object",
  "maxProperties": 32,
  "propertyNames": {
    "pattern": "^[A-Za-z][A-Za-z0-9_.-]{0,63}$",
    "not": {
      "enum": [
        "constructor",
        "prototype"
      ]
    }
  },
  "additionalProperties": {
    "anyOf": [
      {
        "type": "string",
        "maxLength": 512
      },
      {
        "type": "number"
      },
      {
        "type": "boolean"
      },
      {
        "type": "null"
      }
    ]
  },
  "description": "Installer-supplied, untrusted labels. Flat JSON object, at most 2048 UTF-8 bytes after JSON serialization. First accepted visit freezes custom for that session. Missing/legacy custom is {}. Never use these values for authorization or billing.",
  "example": {
    "account_id": "acme",
    "campaign": "autumn",
    "plan": "pro"
  }
}
TrackerCreate3 fields
FieldTypeMeaning
namerequiredstringName assigned to this API key.
originsrequiredarray
customer_referencestring | null
Tracker8 fields
FieldTypeMeaning
idrequiredstring
namerequiredstringName assigned to this API key.
originsrequiredarray
customer_referencerequiredstring | null
created_atrequiredstring
disabled_atrequiredstring | null
snippetrequiredstring
request_idstringInclude this identifier when reporting a failed request.
TrackerDeleted4 fields
FieldTypeMeaning
idrequiredstring
deletedrequiredtrue
deleted_atrequiredstring
request_idrequiredstringInclude this identifier when reporting a failed request.
TrackerList2 fields
FieldTypeMeaning
trackersrequiredarray
request_idrequiredstringInclude this identifier when reporting a failed request.
TrackerUsage11 fields
FieldTypeMeaning
tracker_idrequiredstring
fromrequiredstringInclusive start in UTC.
torequiredstringExclusive end in UTC.
timezonerequired"UTC"Always UTC.
totalsrequiredobjectRequests, cache hits and average server processing time in milliseconds.
hourlyrequiredarrayPer-hour, per-outcome counters in UTC.
pending_revealsrequiredinteger
metrics_started_atrequiredstring
partial_historyrequiredbooleanTrue if this tracker existed before full counters started and the range includes that earlier time. Earlier sessions and reveals include retained records only; zero may represent missing history.
accountingrequiredstringWhat the counters include and exclude.
request_idstringInclude this identifier when reporting a failed request.
SessionPreview9 fields
FieldTypeMeaning
session_idrequiredstring
tracker_idrequiredstring
customer_referencerequiredstring | null
customCustomParameters
visitor_countryrequiredstring | nullEstimated visitor IP country, not company headquarters. null means unknown. VPNs can affect location.
first_seen_atrequiredstring
last_seen_atrequiredstring
expires_atrequiredstring
request_idstringInclude this identifier when reporting a failed request.
SessionFeed4 fields
FieldTypeMeaning
sessionsrequiredarray
next_cursorrequiredstring
has_morerequiredboolean
request_idrequiredstringInclude this identifier when reporting a failed request.
RevealResult10 fields
FieldTypeMeaning
statusrequired"complete"complete is a finished lookup; pending and retrying are unfinished.
match_typerequired"company" | "isp" | "unclassified"company: company information found; isp: internet service provider; unclassified: insufficient evidence to classify. Unclassified is not proof that the IP is not a business.
company_foundrequiredbooleanTrue only when match_type is company. False for complete ISP/unclassified results. Pending responses instead use null.
enriched_atrequiredstring | nullUTC timestamp of the saved enrichment, not the current API request time.
companyrequiredCompany | nullAll available company fields, including ISP/unclassified records; null only when every public company field is unavailable. Presence does not override network.is_isp or establish a non-ISP business match.
networkrequiredobjectInformation about the IP connection. Fields can be null.
stalerequiredbooleanWhether this saved result is due for refresh. Expiry does not remove available fields.
fresh_untilrequiredstring | nullExpiry of this saved result. Use this timestamp rather than assuming a fixed lifetime. Most company results last 30 days; some last 7 days. Null if no usable timestamp.
refresh_statusrequired"not_needed" | "not_requested" | "pending" | "deferred"not_needed: no additional work needed; not_requested: this GET started no work (another request may have done so); pending: unfinished enrichment has been accepted; deferred: POST could not confirm or admit work, so retry POST after Retry-After. A saved ISP/unclassified result may be fresh while additional company enrichment is pending.
refresh_errorrequirednull | "pending_limit" | "enrichment_limit" | "enrichment_capacity" | "submission_unconfirmed" | "lookup_failed"Reason for deferred refresh. Existing data is still returned with HTTP 200. Apply daily/pending limits or temporary-error retry guidance. Unknown acknowledgement: repeat the same POST; do not assume no job exists.
Reveal13 fields
FieldTypeMeaning
reveal_idrequiredstring
session_idrequiredstring
tracker_idrequiredstring
customCustomParameters
statusrequired"pending" | "complete"complete is a finished lookup; pending and retrying are unfinished.
billablerequiredbooleanTrue when this reveal has one recorded billable unit. Re-reading this receipt does not charge again.
billable_unitsrequired0 | 1
created_atrequiredstring
completed_atrequiredstring | null
expires_atrequiredstring
resultrequiredRevealResult | null
status_urlrequiredstringRelative GET URL for checking this IP again.
request_idrequiredstringInclude this identifier when reporting a failed request.
RevealUsage4 fields
FieldTypeMeaning
billable_revealsrequiredinteger
completed_revealsrequiredinteger
hourlyrequiredarrayPer-hour, per-outcome counters in UTC.
accountingrequiredstringWhat the counters include and exclude.
WebhookCreate4 fields
FieldTypeMeaning
namerequiredstringName assigned to this API key.
urlrequiredstringPublic HTTPS on port443; no credentials, fragment, private addresses or redirects. Immutable; create a new endpoint to change it.
eventsrequiredarray
tracker_idstring | nullOmit or null for all trackers owned by this partner.
WebhookUpdate3 fields
FieldTypeMeaning
namestringName assigned to this API key.
eventsarray
enabledboolean
Webhook12 fields
FieldTypeMeaning
idrequiredstring
namerequiredstringName assigned to this API key.
urlrequiredstring
eventsrequiredarray
tracker_idrequiredstring | null
enabledrequiredboolean
created_atrequiredstring
updated_atrequiredstring
deleted_atrequiredstring | null
secret_rotation_untilrequiredstring | null
deliveriesrequiredobject
request_idstringInclude this identifier when reporting a failed request.
WebhookSecret13 fields
FieldTypeMeaning
idrequiredstring
namerequiredstringName assigned to this API key.
urlrequiredstring
eventsrequiredarray
tracker_idrequiredstring | null
enabledrequiredboolean
created_atrequiredstring
updated_atrequiredstring
deleted_atrequiredstring | null
secret_rotation_untilrequiredstring | null
deliveriesrequiredobject
request_idstringInclude this identifier when reporting a failed request.
signing_secretrequiredstringShown once on creation/rotation. Verify HMAC SHA-256 signatures server-side. Never send this secret to a browser installation snippet.
WebhookList4 fields
FieldTypeMeaning
webhooksrequiredarray
limitsrequiredobjectRequest-rate limits and optional daily enrichment budget. null means no daily cap; 0 means saved IPs only.
workerrequiredobject
request_idstringInclude this identifier when reporting a failed request.
WebhookDeleted4 fields
FieldTypeMeaning
idrequiredstring
deletedrequiredtrue
deleted_atrequiredstring
request_idstringInclude this identifier when reporting a failed request.
WebhookTest4 fields
FieldTypeMeaning
delivery_idrequiredstring
event_idrequiredstring
statusrequired"pending"complete is a finished lookup; pending and retrying are unfinished.
request_idstringInclude this identifier when reporting a failed request.
WebhookRevealReceipt12 fields
FieldTypeMeaning
reveal_idrequiredstring
session_idrequiredstring
tracker_idrequiredstring
customCustomParameters
statusrequired"pending" | "complete"complete is a finished lookup; pending and retrying are unfinished.
billablerequiredbooleanTrue when this reveal has one recorded billable unit. Re-reading this receipt does not charge again.
billable_unitsrequired0 | 1
created_atrequiredstring
completed_atrequiredstring | null
expires_atrequiredstring
resultrequiredRevealResult | null
status_urlrequiredstringRelative GET URL for checking this IP again.
WebhookEvent
{
  "oneOf": [
    {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "id": {
          "type": "string"
        },
        "type": {
          "const": "session.created"
        },
        "schema_version": {
          "const": 1
        },
        "created_at": {
          "type": "string",
          "format": "date-time"
        },
        "data": {
          "$ref": "#/components/schemas/SessionPreview"
        }
      },
      "required": [
        "id",
        "type",
        "schema_version",
        "created_at",
        "data"
      ]
    },
    {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "id": {
          "type": "string"
        },
        "type": {
          "const": "reveal.completed"
        },
        "schema_version": {
          "const": 1
        },
        "created_at": {
          "type": "string",
          "format": "date-time"
        },
        "data": {
          "$ref": "#/components/schemas/WebhookRevealReceipt"
        }
      },
      "required": [
        "id",
        "type",
        "schema_version",
        "created_at",
        "data"
      ]
    },
    {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "id": {
          "type": "string"
        },
        "type": {
          "const": "webhook.test"
        },
        "schema_version": {
          "const": 1
        },
        "created_at": {
          "type": "string",
          "format": "date-time"
        },
        "data": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "message": {
              "type": "string"
            }
          },
          "required": [
            "message"
          ]
        }
      },
      "required": [
        "id",
        "type",
        "schema_version",
        "created_at",
        "data"
      ]
    }
  ],
  "examples": [
    {
      "id": "evt_session_00000000-0000-4000-8000-000000000001",
      "type": "session.created",
      "schema_version": 1,
      "created_at": "2026-09-25T12:00:00.000Z",
      "data": {
        "session_id": "00000000-0000-4000-8000-000000000001",
        "tracker_id": "00000000-0000-4000-8000-000000000002",
        "customer_reference": "acme",
        "custom": {
          "account_id": "acme",
          "campaign": "autumn",
          "plan": "pro"
        },
        "visitor_country": "GB",
        "first_seen_at": "2026-09-25T12:00:00.000Z",
        "last_seen_at": "2026-09-25T12:00:00.000Z",
        "expires_at": "2026-10-25T12:00:00.000Z"
      }
    },
    {
      "id": "evt_reveal_00000000-0000-4000-8000-000000000003",
      "type": "reveal.completed",
      "schema_version": 1,
      "created_at": "2026-09-25T12:00:00.000Z",
      "data": {
        "reveal_id": "00000000-0000-4000-8000-000000000003",
        "session_id": "00000000-0000-4000-8000-000000000001",
        "tracker_id": "00000000-0000-4000-8000-000000000002",
        "custom": {
          "account_id": "acme",
          "campaign": "autumn",
          "plan": "pro"
        },
        "status": "complete",
        "billable": true,
        "billable_units": 1,
        "created_at": "2026-09-25T12:00:00.000Z",
        "completed_at": "2026-09-25T12:00:00.000Z",
        "expires_at": "2026-10-25T12:00:00.000Z",
        "result": {
          "status": "complete",
          "match_type": "company",
          "company_found": true,
          "enriched_at": "2026-09-18T12:00:00.000Z",
          "stale": false,
          "fresh_until": "2026-10-18T12:00:00.000Z",
          "refresh_status": "not_needed",
          "refresh_error": null,
          "network": {
            "connection_type": "Business",
            "audience_type": "Business",
            "audience_group": null,
            "detail_level": null,
            "is_isp": false
          },
          "company": {
            "name": "Example Company",
            "website": "example.com",
            "brand_name": null,
            "linkedin_url": null,
            "industry": "Software",
            "industry_subcategory": null,
            "employees": "120",
            "annual_revenue": null,
            "revenue_band": null,
            "naics_code": "541511",
            "sic_code": null,
            "city": "London",
            "region": null,
            "postal_code": null,
            "country_code": "GB",
            "country_name": "United Kingdom"
          }
        },
        "status_url": "/v1/reveals/00000000-0000-4000-8000-000000000003"
      }
    }
  ]
}
WebhookDelivery11 fields
FieldTypeMeaning
idrequiredstring
event_idrequiredstring
typerequired"session.created" | "reveal.completed" | "webhook.test"
staterequired"pending" | "delivered" | "failed" | "cancelled" | "expired"
attemptsrequiredintegerNumber of lookup attempts; 0 while awaiting the first attempt.
created_atrequiredstring
last_attempt_atrequiredstring | null
delivered_atrequiredstring | null
next_attempt_atrequiredstring | null
http_statusrequiredinteger | null
errorrequiredstring | nullMachine-readable code and a human-readable message.
WebhookDeliveryDetail13 fields
FieldTypeMeaning
idrequiredstring
event_idrequiredstring
typerequired"session.created" | "reveal.completed" | "webhook.test"
staterequired"pending" | "delivered" | "failed" | "cancelled" | "expired"
attemptsrequiredintegerNumber of lookup attempts; 0 while awaiting the first attempt.
created_atrequiredstring
last_attempt_atrequiredstring | null
delivered_atrequiredstring | null
next_attempt_atrequiredstring | null
http_statusrequiredinteger | null
errorrequiredstring | nullMachine-readable code and a human-readable message.
eventrequiredWebhookEvent | null
request_idstringInclude this identifier when reporting a failed request.
WebhookDeliveryList4 fields
FieldTypeMeaning
deliveriesrequiredarray
has_morerequiredboolean
next_cursorrequiredstring
request_idstringInclude this identifier when reporting a failed request.
Network5 fields
FieldTypeMeaning
connection_typerequiredstring | nullType of internet connection; nullable.
audience_typerequiredstring | nullAudience category; nullable.
audience_grouprequiredstring | nullAudience segment; nullable.
detail_levelrequiredstring | nullAvailable detail level; nullable.
is_isprequiredboolean | nullTrue: internet service provider. False: non-ISP. Null: unspecified. False alone does not establish a company match; use company_found and match_type.

The downloadable OpenAPI file includes schemas, examples, authentication, response headers and the complete application-error catalog under x-error-codes and x-tracking-error-codes.

Usage limits & retries
  • Use GET /v1/usage to read the limits assigned to your key. Request-rate limits apply to authenticated API calls; lookup responses and successful tracker/session/reveal calls count as requests. Billable reveal units are listed separately under reveals.
  • Keys belonging to the same partner share a limit for unfinished enrichments. Check enrichment and limits.pending_enrichments in your usage response. A 429 pending_limit means wait for existing work to finish; saved lookups still work.
  • The daily budget is optional: limits.new_enrichments_per_day: null means no daily cap; 0 means saved IPs only. When set, each new reservation uses that budget, even if submission needs a retry. It resets at 00:00 UTC. Reads use no new reservation. A refresh uses a reservation just like a new lookup.
  • For 202 responses, wait at least Retry-After seconds (currently 15) between manual or application polls. No enrichment completion deadline is promised.
  • For temporary 429/503 failures, honor Retry-After and use exponential backoff with jitter. enrichment_limit requires the next UTC day or a limit increase.
  • Use fresh_until to see when saved details expire. POST can return those details while looking for newer or more complete information. refresh_status: pending means accepted; deferred means check refresh_error and retry POST later. GET only reads. Poll the same IP after Retry-After until stale: false and refresh_status: not_needed. Completed results can be cached for up to 60 seconds. New lookups may take longer; no completion time is guaranteed.
  • The API supports public IPv4, native IPv6 and IPv4-mapped IPv6. Equivalent address spellings may match the same result.