Skip to content

REST API

The Evident REST API lets you enrich IP addresses and stream visitor events from any HTTP client — a Next.js middleware, a Python script, a Segment webhook, or an existing server. Everything the dashboard does with CSV uploads and Amplitude syncs, you can do over the API.

Base URL: https://api.useevident.com

Authentication

Every request authenticates with an API key sent in the X-API-Key header.

X-API-Key: ipid_<64-hex-chars>

Generate keys in Settings → API Keys in the dashboard. The raw key is shown exactly once at creation — copy it into your secret store immediately. Evident stores only a SHA-256 hash and cannot recover the raw value later; if you lose it, generate a new key.

Never put the raw key in client-side code, environment variables prefixed NEXT_PUBLIC_* / REACT_APP_*, or a Git repo. Keys are per-tenant and grant full ingestion + lookup access; treat them like a database password.

Endpoints

The API is split into two families:

  • Lookup (/v1/lookup, /v1/bulk) — real-time IP enrichment. Fast, no persistence. Use when you need a company/industry for an IP but don’t want it counted as a visit.
  • Ingest (/v1/ingest, /v1/ingest/batch) — event ingestion. Enriches and persists to your Accounts dashboard. Use when the IP represents a real visitor.

POST /v1/lookup

Enrich a single IP address. Does not persist the result.

Request

curl -X POST https://api.useevident.com/v1/lookup \
  -H "X-API-Key: $EVIDENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"ip": "203.0.113.42"}'

Optional fields (all fields accepted as query params too):

Field Description
fields Array of enrichment fields to include (business_name, hostname, industry, …). Default: all.
redact Array of fields to strip from the response. Use geo to remove all location fields.
providers.allow / providers.deny Whitelist or blacklist enrichment providers.
deadline_ms Overall lookup budget (ms).
quality_min Return status: "insufficient_quality" if the result’s quality score is below this.

Response (200 OK)

{
  "ip": "203.0.113.42",
  "tenant_id": "clerk_abc123",
  "quality": 3,
  "status": "ok",
  "data": {
    "business_name": "Acme Corp",
    "org_name": "Acme Corporation Ltd",
    "hostname": "office.acmecorp.com",
    "etld_plus_one": "acmecorp.com",
    "industry": "Software",
    "description": "..."
  },
  "quota": {
    "daily_limit": 10000,
    "daily_used": 42,
    "daily_remaining": 9958,
    "reset_at": "2026-07-03T00:00:00+00:00"
  }
}

POST /v1/bulk

Enrich up to 512 IPs in a single call. Same options as /v1/lookup.

Request

curl -X POST 'https://api.useevident.com/v1/bulk?mode=sync' \
  -H "X-API-Key: $EVIDENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"ips": ["203.0.113.10", "203.0.113.11", "203.0.113.12"]}'

Modes:

  • mode=sync (default) — streams NDJSON, one enriched result per line. Right for scripts that want results back inline.
  • mode=async — accepts the batch and returns 202 Accepted with a job_id. Poll GET /v1/bulk/{job_id} to retrieve results. Right for larger batches or when you don’t want to hold an HTTP connection open.

Async response (202 Accepted)

{ "job_id": "9f4e...", "status": "queued" }

POST /v1/ingest

Persist up to 100 events synchronously. Each event is enriched inline and its identified company shows up on the Accounts dashboard within seconds.

Request

curl -X POST https://api.useevident.com/v1/ingest \
  -H "X-API-Key: $EVIDENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [
      {
        "ip": "203.0.113.42",
        "event_id": "evt_abc123",
        "event_type": "page_view",
        "timestamp": "2026-07-02T14:30:00Z",
        "properties": { "path": "/pricing", "referer": "https://twitter.com" }
      }
    ]
  }'

Event envelope (events[] — each element):

Field Required Description
ip Yes IPv4 or IPv6 address.
event_id No Idempotency key. Server generates a UUID if omitted. Re-sending the same (tenant_id, event_id) within 7 days is a no-op.
timestamp No RFC 3339 datetime. Defaults to receipt time.
event_type No Free-form label (e.g. page_view, signup_started).
account_id No Your identifier for the account/user. Otherwise inferred from the IP.
session_id No Integer session identifier.
platform No web / ios / android / etc.
properties No Arbitrary JSON. Preserved in the object-storage archive (see Raw payload archive).
user_properties No Arbitrary JSON. Preserved as above.

Additional keys not listed are also preserved — Evident does not reject unknown fields.

Response (200 OK)

{
  "stats": {
    "received": 1,
    "accepted": 1,
    "rejected": 0,
    "unique_ips": 1
  },
  "results": [
    {
      "event_id": "evt_abc123",
      "ip": "203.0.113.42",
      "status": "ok",
      "enrichment": {
        "business_name": "Acme Corp",
        "org_name": "Acme Corporation Ltd",
        "hostname": "office.acmecorp.com",
        "industry": "Software",
        "industry_category": "Technology",
        "classification": "corporate"
      },
      "quality": 3
    }
  ]
}

results[] has one entry per input event, in order. status values:

  • ok — event persisted, enrichment resolved. Renders on the Accounts page.
  • filtered — IP identified as infrastructure noise (cloud provider, CDN, ISP, hosting). Not persisted to Accounts, but returned here so you can inline-gate UX (e.g. drop a chat-widget prompt for suspected bots).
  • invalid_ip — the ip field failed IPv4/IPv6 parsing.

Limits: 100 events per request, 1 MB request body. Larger batches must use /v1/ingest/batch.

POST /v1/ingest/batch

Ingest a large NDJSON batch asynchronously. Right for backfills and any workload above the sync endpoint’s cap.

Request (send NDJSON — one event per line — as the request body)

curl -X POST https://api.useevident.com/v1/ingest/batch \
  -H "X-API-Key: $EVIDENT_API_KEY" \
  -H "Content-Type: application/x-ndjson" \
  --data-binary @events-30d.ndjson

Response (202 Accepted)

{
  "job_id": "9f4e...",
  "status": "queued",
  "received": 42315
}

Limits: 1,000,000 events per request, 100 MB body. The endpoint returns as soon as the job is queued; poll the status endpoint for progress.

GET /v1/ingest/batch/{job_id}

Poll the status of a queued batch ingestion job.

Request

curl https://api.useevident.com/v1/ingest/batch/9f4e... \
  -H "X-API-Key: $EVIDENT_API_KEY"

Response (200 OK)

{
  "job_id": "9f4e...",
  "status": "running",
  "progress_pct": 42,
  "progress_current": 17832,
  "progress_total": 42315,
  "progress_message": "Enriching batch...",
  "result_summary": null
}

status progresses queuedrunningcompleted (or failed). When completed, result_summary includes final counts (processed, valid_ips, identified_ips).

When to use which endpoint

You want to… Use
Look up a single IP without persisting POST /v1/lookup
Look up many IPs inline for a script POST /v1/bulk?mode=sync
Look up many IPs and pick up results later POST /v1/bulk?mode=async + GET /v1/bulk/{job_id}
Track live visitors on your site or app POST /v1/ingest from your web middleware
Backfill historical visitors from access logs POST /v1/ingest/batch
Check on a queued batch GET /v1/ingest/batch/{job_id}

Rate limits

Rate limits are enforced per tenant via a token-bucket algorithm.

Plan Steady-state Burst
Starter 5 req/sec 20
Growth 15 req/sec 60
Professional 25 req/sec 100
Custom 100 req/sec 500

When you exceed the limit, the API returns 429 Too Many Requests. Back off and retry.

Quotas

Your tracked accounts quota (visible in Settings → Quotas) counts unique identified companies per month. Duplicate visits from the same IP on the same day are deduplicated automatically — a customer who hits your site 500 times counts as one billable IP.

Cloud provider / CDN / ISP / hosting IPs are classified and excluded from your quota automatically. You’ll see them in the results[] array with status: "filtered", but they don’t count against your allowance.

Error responses

Errors follow FastAPI conventions — a detail string in the response body, with the HTTP status code carrying the semantic.

Code Meaning Common causes
400 Bad Request Malformed request Empty events array, whitespace-only batch body
401 Unauthorized Invalid or missing API key Missing X-API-Key, key was revoked, key doesn’t exist
413 Payload Too Large Body or event count over the endpoint’s cap Over 100 events on sync, over 1 MB body on sync, over 100 MB batch body
422 Unprocessable Entity Schema mismatch Missing required field (e.g. ip), wrong data type
429 Too Many Requests Rate limit exceeded Traffic burst above the plan’s burst cap; queue depth exceeded on batch
500 Internal Server Error Server error Unexpected pipeline failure. Retry with exponential backoff; if persistent, contact support

Idempotency

The ingest endpoints deduplicate by (tenant_id, event_id) for 7 days. If you re-send the same event within that window — say, from a retried Next.js middleware invocation — the second call is a no-op and the response still returns status: "ok" for the event.

If you rely on server-generated event IDs (omit event_id), every call is treated as a distinct event.

Raw payload archive

If your plan or configuration enables raw event archival, incoming /v1/ingest and /v1/ingest/batch payloads are archived to object storage as NDJSON, partitioned by tenant, day, source type, and run ID. This preserves properties, user_properties, and any other fields Evident’s pipeline doesn’t natively query, so you can replay historical batches or audit exact payloads later.

Contact support@useevident.com if you need archive access for your workspace.

SDKs

Official SDKs are on the roadmap — Python and Node clients that wrap this API with typed models, retries, and idempotency helpers. In the meantime, the raw endpoints are stable and safe to build against.