Getting started

Generate your first profile

From zero to a verified business profile in under five minutes.

Prerequisites

You only need two things:

  1. A Lumen Sieve account — create one free.
  2. An API key — generate one from the API keys page in the dashboard.
Tip: Every key (lumen_live_sk_…) works the same from creation — there's no separate test/sandbox key. The Free plan's 50 lifetime lookups are the trial; keep using the same key after you upgrade.

1. Start a lookup

Send a POST request to /v1/profiles/lookup with a businessName and a countryHint (requires the profiles:write scope) — or, if you already know the business's website, a domain on its own (see the callout below). This is asynchronous — a cold full resolve runs the complete discovery → crawl → extraction → reconciliation pipeline and typically takes a few minutes, so the endpoint returns immediately with a businessId to poll rather than the finished profile.

Already have the website? Skip businessName and countryHint entirely and send { "domain": "acmecoffee.com", "mode": "full" } instead. The engine deep-crawls that domain, pulls the business's real name and every fingerprint (phone, email, address, socials) straight from the site, and derives the country from the domain itself — no locality hint needed even for federated countries. See the API reference for the full request shape.

cURL
bash
curl -X POST https://api.lumensieve.com/v1/profiles/lookup \
  -H "Authorization: Bearer lumen_live_sk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "businessName": "Blispa Barbershop & Spa",
    "countryHint": "KE",
    "mode": "full"
  }'
# => 202 Accepted
# { "businessId": "616de6d1-...", "pollUrl": "/v1/profiles/616de6d1-...", "status": "processing" }
TypeScript
typescript
const submit = await fetch('https://api.lumensieve.com/v1/profiles/lookup', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.LUMEN_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    businessName: 'Blispa Barbershop & Spa',
    countryHint: 'KE',
    mode: 'full',
  }),
});
const { businessId } = await submit.json(); // 202 Accepted

// Poll until the resolve finishes.
let profile;
while (true) {
  await new Promise((r) => setTimeout(r, 3000));
  const poll = await fetch(`https://api.lumensieve.com/v1/profiles/${businessId}`, {
    headers: { Authorization: `Bearer ${process.env.LUMEN_API_KEY}` },
  });
  if (poll.status === 202) continue; // still processing
  const body = await poll.json();
  if (!poll.ok) throw new Error(body.error?.message ?? 'lookup failed');
  profile = body.profile;
  break;
}
console.log(profile.business.trade_name.value);
Python
python
import os, time, requests

API_KEY = os.environ["LUMEN_API_KEY"]
BASE = "https://api.lumensieve.com"
headers = {"Authorization": f"Bearer {API_KEY}"}

submit = requests.post(
    f"{BASE}/v1/profiles/lookup",
    headers={**headers, "Content-Type": "application/json"},
    json={"businessName": "Blispa Barbershop & Spa", "countryHint": "KE", "mode": "full"},
)
submit.raise_for_status()  # 202 Accepted
business_id = submit.json()["businessId"]

# Poll until the resolve finishes.
while True:
    time.sleep(3)
    poll = requests.get(f"{BASE}/v1/profiles/{business_id}", headers=headers)
    if poll.status_code == 202:
        continue  # still processing
    poll.raise_for_status()
    profile = poll.json()["profile"]
    break

print(profile["business"]["trade_name"]["value"])

The mode field controls speed vs. depth:

ModeTypical latencyUse when
previewA few secondsYou want fast partial data (e.g. autocomplete, internal lookups)
fullAround 3 minutesYou need the complete profile (default)
Warning: US, IN, BR, CA, AU, DE, MX, and NG additionally require a localityHint (a real state, province, or city in that country) on every businessName-based lookup. These countries have real state/province-level divisions where the same business name legitimately repeats across many unrelated locations, so name + country alone isn't enough to identify the right one — omitting it returns MISSING_REQUIRED_FIELD. See the API reference for details.

Don't want to poll at all? See Lookup notifications to have Lumen Sieve call a URL of yours the moment a lookup finishes.

2. Read the response

Once GET /v1/profiles/{businessId} stops returning 202, a 200 carries the finished profile object — a nested, section-based document (see Data model for the full shape), not a flat record. Every field that has data is wrapped with its own confidence and source URLs:

Response (truncated, real example)
json
{
  "profile": {
    "business": {
      "trade_name": {
        "value": "Blispa Barbershop & Spa",
        "metadata": {
          "confidence": 0.995,
          "sources": ["https://blispabarbershop.co.ke/", "https://blispabarbershop.co.ke/about"],
          "last_verified": "2026-07-31T23:35:12Z"
        }
      }
    },
    "contact": {
      "phones": [{ "number": "+254746200969", "purpose": "main", "metadata": { "confidence": 0.995, "sources": ["..."] } }]
    },
    "metadata": {
      "confidence_score": 0.6084,
      "completeness_score": 0.6667,
      "sources_queried": 6,
      "sources_successful": 6
    }
  },
  "profileId": "ebbbe097-d475-4051-b358-8c638c85b32c"
}

3. Re-fetch a cached profile

Every profile is stored under the businessId from step 1. GET always returns exactly what's persisted, immediately, however old it is — it never re-resolves on its own. To get fresh data, call POST /v1/profiles/lookup again with the same businessName + countryHint: Lumen Sieve reuses the cached profile if it's still within the freshness window, or transparently runs a new resolve if it's gone stale. Pass force_refresh: true to always force a fresh resolve regardless of freshness.

cURL
bash
curl https://api.lumensieve.com/v1/profiles/616de6d1-... \
  -H "Authorization: Bearer lumen_live_sk_YOUR_KEY"

Next steps