Quickstart: Enrich Emails with Intent

What this does

Send an email address (or a LinkedIn URL), get back the intent topics that person is actively researching, with scores. This is the fastest path to a working Delivr integration: one endpoint, one POST, useful data in the response.

email in  ->  POST /api/v1/intent/lookup  ->  topics + scores out

Typical uses: scoring leads in a CRM, an enrichment column in Clay or a spreadsheet workflow, prioritizing outreach by what a prospect is researching this week.

Before you start

You need an organization API key + secret pair. Create one in the dashboard under Settings > API keys; the Authentication guide walks through it. Every request below sends both headers:

X-Api-Key: dlvr_...
X-Api-Secret: ...

1. Look up one person

curl -X POST "https://api.delivr.ai/api/v1/intent/lookup" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "X-Api-Secret: YOUR_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"id": "row-42", "email": "[email protected]"}'

Response (200 OK)

{
  "response": {
    "id": "row-42",
    "matched": true,
    "matched_on": "email",
    "topics": [
      {
        "topic_id": "4eyes_103456",
        "topic_name": "CRM Software",
        "score": "high",
        "perc_score": 87
      }
    ]
  },
  "as_of": "2026-06-19"
}

How to read it:

  • id is your own opaque row identifier, echoed back. Use it to join results to your source data.
  • matched: false with an empty topics array means the person is not in the current intent data. That is a normal outcome, never an error.
  • score is the intent tier (high or medium); perc_score is a 0-99 percentile strength within the topic.
  • as_of is the date of the intent dataset serving your request. Data refreshes daily.
  • You can send linkedin_url instead of email. A LinkedIn URL identifies the person rather than one address, so it resolves across every address we hold for them and can match where a single email would not.

2. Look up a list (up to 10,000 per call)

POST /api/v1/intent/batch takes up to 10,000 items per request, mixing email and linkedin_url freely. Results come back in input order, one per item.

curl -X POST "https://api.delivr.ai/api/v1/intent/batch" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "X-Api-Secret: YOUR_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {"id": "r1", "email": "[email protected]"},
      {"id": "r2", "linkedin_url": "https://www.linkedin.com/in/jane-doe-87745243"},
      {"id": "r3", "email": "[email protected]"}
    ]
  }'

The response carries matched_count plus a results array shaped like the single lookup. Unmatched items simply come back matched: false.

3. Preview cost before you run (dry_run)

Every intent endpoint accepts "dry_run": true. The API resolves your request, tells you exactly how many records a live run would bill, and charges nothing:

curl -X POST "https://api.delivr.ai/api/v1/intent/batch" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "X-Api-Secret: YOUR_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"items": [...], "dry_run": true}'
{
  "dry_run": true,
  "meter": "intent_signal_scored",
  "records_to_charge": 2
}

You are billed per matched record only; unmatched lookups cost nothing. Details in Rate Limits and Metering.

Complete example: enrich a CSV (Python)

import csv
import requests

API = "https://api.delivr.ai/api/v1/intent/batch"
HEADERS = {
    "X-Api-Key": "YOUR_API_KEY",
    "X-Api-Secret": "YOUR_API_SECRET",
}
BATCH_SIZE = 10_000  # server-side maximum per call

def batches(rows, size):
    for i in range(0, len(rows), size):
        yield rows[i : i + size]

with open("leads.csv") as f:
    rows = [{"id": str(i), "email": r["email"]} for i, r in enumerate(csv.DictReader(f))]

enriched = {}
for chunk in batches(rows, BATCH_SIZE):
    resp = requests.post(API, headers=HEADERS, json={"items": chunk}, timeout=60)
    resp.raise_for_status()
    for result in resp.json()["response"]["results"]:
        if result["matched"]:
            top = result["topics"][0] if result["topics"] else None
            enriched[result["id"]] = {
                "top_topic": top["topic_name"] if top else "",
                "score": top["score"] if top else "",
                "topic_count": len(result["topics"]),
            }

print(f"matched {len(enriched)} of {len(rows)}")

Complete example: enrich one row (JavaScript)

const resp = await fetch('https://api.delivr.ai/api/v1/intent/lookup', {
  method: 'POST',
  headers: {
    'X-Api-Key': process.env.DELIVR_API_KEY,
    'X-Api-Secret': process.env.DELIVR_API_SECRET,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ id: 'lead-1', email: '[email protected]' }),
});
if (!resp.ok) throw new Error(`Delivr API ${resp.status}`);

const { response } = await resp.json();
if (response.matched) {
  const researching = response.topics.map((t) => t.topic_name).join(', ');
  console.log(`Researching: ${researching}`);
}

Errors you might see

StatusMeaningFix
400Malformed request (missing email/linkedin_url, batch over 10,000 items)Check the request body against the examples above
401Missing or invalid key/secret pairRe-copy both halves from the dashboard; see Authentication
429Over your request rate limit (default 10 requests/sec per organization)Back off and retry; see Rate Limits and Metering

An unmatched identifier is not an error: it returns 200 with matched: false.

Where to go next

  • Company-level intent: POST /api/v1/intent/company scores a company domain the same way. Same auth, same shape.
  • Discovery (topic to people/companies): given a topic, list the people or companies showing intent with POST /api/v1/intent/people and /api/v1/intent/companies. Free capped previews exist at /preview variants so you can see real records before importing.
  • Which API should I use? if your use case is not a per-row lookup.
  • Full API reference for every field on every endpoint.

Did this page help you?