You have a spreadsheet of 500 Instagram handles from a discovery run, and every one of them is a dead end until you can reach a person. The bio says "collabs: link in bio", the link is a Linktree, the Linktree points to a website, and the address is on its contact page. Doing that by hand for 500 creators is a week of opening tabs.
/v1/intelligence/contact/complete does that walk in one call. Send a handle, get back the creator's own addresses in a fixed order, each with the page it was found on, the proof that ties it to the creator, and an MX check. This guide covers the two engines behind it, how the cache works, how to run a list without overspending, and how to send the email without breaking the law.
What the endpoint looks for, in order
| Priority | Source | Where the address was found |
|---|---|---|
| 0 | bio | Written in the profile bio |
| 1 | platform_business_email | The platform's business or contact email field |
| 2 and up | discovered | A page the creator links to or owns: link-in-bio page, website, contact or legal page |
emails[] comes back sorted by priority, so the first entry is the best one to use. An address the creator typed into their own bio always ranks above one the engine found on a website.
An address needs proof before it is published
A page can list many addresses: the creator's, their agency's, a web designer's, a support desk. The engine publishes an address only when there is positive proof that it belongs to the creator. The address sits on the creator's own profile, link hub, or website, or the mailbox domain or name matches the creator. Then an MX check drops domains with no mail route. When nothing passes, emails is an empty list. The engine does not guess an address on a domain it has not tied to the creator.
Fast or complete
| Engine | Path | What it reads | Time | Cost |
|---|---|---|---|---|
| Fast | /v1/intelligence/contact/fast | The profile, the bio, and a handful of pages the creator links to | about 3 s, 5 s cap | $0.02 |
| Complete (default) | /v1/intelligence/contact/complete | The fast stage, then, if it found nothing: the same handle on the other platforms, retried link hubs, contact and legal pages on the creator's domain, one web search | about 8 s on average, 120 s cap | $0.05 |
The path picks the engine: /v1/intelligence/contact/fast or /v1/intelligence/contact/complete. The bill and the usage log name the engine that ran. In our August 2026 test on 62 creators with a known address, fast found it for about 7 in 10 and complete for about 9 in 10. Neither engine calls a language model.
Look up one creator
q takes a username, an @handle, or a profile URL. platform is instagram (default), tiktok, or youtube. YouTube handles keep their case and also accept a UC... channel id.
curl "https://api.virev.ai/v1/intelligence/contact/complete?q=mrbeast" \
-H "Authorization: Bearer $VIREV_API_KEY"const res = await fetch(
"https://api.virev.ai/v1/intelligence/contact/complete?q=mrbeast",
{ headers: { Authorization: "Bearer kb_live_..." } }
);
const { data, meta } = await res.json();
console.log(data.emails);
// [{ email: "chucky@mrbeastbusiness.com", source: "discovered", priority: 2,
// source_url: "https://www.youtube.com/@mrbeast", surface: "own_profile",
// proof: 3, verification: { mx: "mx_present", ... } }]
console.log(data.tier, data.cached); // "complete" false
console.log(meta.cost); // "$0.0500"Everything sits under data. Each entry in emails[] has the address, its best source, every source that found it (sources), its priority, when it was first and last seen (first_seen_at, last_seen_at), the page it came from (source_url), the kind of page (surface, for example own_profile or own_site), the ownership proof score, and a verification object: mx (the mail-route check), smtp (the mailbox probe, complete engine only), and a summary status of deliverable, undeliverable, risky, or unverified. The SMTP result is recorded but never removes an address. The run itself reports tier (what you asked for), stage (fresh complete runs only), cached, cached_at, and elapsed_s. degraded lists anything that did not finish, such as a profile page that timed out.
The example above shows the two engines side by side. In a run on 23 September 2026, fast returned an empty list for @mrbeast on Instagram. Complete checked the same handle on YouTube and found the business address on the channel page.
POST form
curl -X POST "https://api.virev.ai/v1/intelligence/contact/fast" \
-H "Authorization: Bearer $VIREV_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "platform": "tiktok", "id": "khaby.lame" }'The body is strict
The body is { platform?, id, refresh? }. Unknown fields are rejected with a 400, and validation runs before auth and billing, so a malformed request is never charged. One request looks up one creator; send one request per handle.
Cache and refresh
Every address we find is stored against the creator's profile when that profile is in our catalog (persisted says whether it was stored), one entry per address. An address found by two sources keeps the better one, and the bio wins. With refresh=false (the default), a creator with at least one stored address comes back from the store with cached: true and cached_at, the last time a crawl saw one of those addresses. No page is fetched. A creator with no stored address runs the engine you called. Pass refresh=true to run the engine again, for example after a creator changes their link in bio; the answer is the merged list.
The price does not depend on the cache: a cached answer and a fresh run cost the same, and refresh=true costs the same as the default. A new run adds to the stored list and never deletes an address.
Pair it with the profile bundle
If you're building an outreach pipeline, /v1/intelligence/profile is the other half of the workflow. It returns the classification bundle (account_role, primary_niche, commercial_intent, brand_safety), which tells you whether to reach out. contact tells you where. Classify first, then look up contacts only for creators that fit.
import requests
HEADERS = {"Authorization": "Bearer kb_live_..."}
profile = requests.get(
"https://api.virev.ai/v1/intelligence/profile",
params={"q": "natgeo"},
headers=HEADERS,
).json()["data"]
if "travel" in (profile["primary_niche"] or "").lower():
contact = requests.get(
"https://api.virev.ai/v1/intelligence/contact/complete",
params={"q": "natgeo"},
headers=HEADERS,
).json()["data"]
print(contact["emails"]) # $0.05 spent only on a qualified leadEnrich a list without overspending
Real outreach starts from a list. One way to keep the bill down: run fast on every handle, then run complete only on the handles where fast found nothing. Respect the 100 req/min default rate limit (a 429 carries a Retry-After header). A non-200 is never charged, so a failed lookup costs nothing and can be retried.
import requests, time
BASE = "https://api.virev.ai/v1/intelligence/contact"
HANDLES = ["natgeo", "nasa", "bbcearth"] # from your discovery run
HEADERS = {"Authorization": "Bearer kb_live_..."}
def lookup(handle, engine):
while True:
res = requests.get(f"{BASE}/{engine}", params={"q": handle}, headers=HEADERS)
if res.status_code == 429:
time.sleep(int(res.headers.get("Retry-After", 5)))
continue # a non-200 is not billed
if res.status_code != 200:
return None # log and move on; you were not billed
return res.json()["data"]["emails"]
rows = []
for handle in HANDLES:
emails = lookup(handle, "fast") # $0.02
if emails == []:
emails = lookup(handle, "complete") # $0.05, only on misses
rows.append({"handle": handle, "emails": emails or []})
print(f"Looked up {len(rows)} creators")Cost anchors for planning a run: 1,000 creators is $50 on complete, or $20 on fast. Fast first, then complete on the misses, lands in between: if fast finds an address for 700 of the 1,000, the run costs $20 plus $15. Qualifying with /v1/intelligence/profile at $0.05 first shrinks the list before either. The starter offer, pay $1 and get a $5 balance, covers about 100 complete lookups or 250 fast ones.
Send the email without getting burned
Finding the address is the easy part. These are business contact points creators published so brands could reach them, but publishing an address is not consent to receive bulk mail, and the law treats it that way.
- CAN-SPAM (US): use accurate
From/Subjectheaders, include a real physical mailing address, and provide a working unsubscribe that you honor within 10 business days. - GDPR (EU recipients): you can rely on legitimate interest for B2B outreach, but you must offer a clear opt-out and stop on request. Keep a record of why you contacted them.
- Relevance over volume: use the
brand_fit_summaryandcontent_tagsyou already pulled to send something specific. A creator who gets a personal, on-niche pitch replies; one who gets a mail-merge blast blocks you. - Verify before a big send: the MX check tells you the domain takes mail, not that the mailbox exists. Run the list through your ESP's validation step to catch bounces before they hurt your sender reputation.
Discovery gives you the address. Etiquette gives you the reply. Treat every send as if the creator will screenshot it.
Turn handles into inboxes
Pay $1, get a $5 balance: about 100 complete contact lookups or 100 profile classifications. No subscription, no minimum.
Get your API key →