Keep your CRM's company records current
Refresh every account's size, industry and headquarters on a schedule. Pay the cache price for recent data, and fetch fresh only when you need to.
How it works#
- Once a week, take the accounts in your CRM that have a website.
- Look each one up by domain, and say how old an answer may be, for example 30 days. When our copy is younger than that, it comes from our cache at the cache price.
- Every answer carries a fingerprint of the company's data. Compare it with the one you stored last time, and write to your CRM only when it changed.
- Store the company's numeric id as its key. It stays the same when a company renames, and a website, an old name and a LinkedIn company URL all lead back to it.
curl https://api.happyfetch.net/v1/domain --max-time 120 \
-H "Authorization: Bearer $HAPPYFETCH_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"domain":"stripe.com"}'const res = await fetch("https://api.happyfetch.net/v1/domain", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.HAPPYFETCH_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(), // send the same key when you retry this call
},
body: JSON.stringify({
"domain": "stripe.com"
}),
signal: AbortSignal.timeout(120_000),
});
const { data, meta, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
console.log(meta.request_id, meta.cache.status, data);import os, uuid, requests
res = requests.post(
"https://api.happyfetch.net/v1/domain",
headers={
"Authorization": f"Bearer {os.environ['HAPPYFETCH_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()), # send the same key when you retry this call
},
json={
"domain": "stripe.com"
},
timeout=120,
)
body = res.json()
if not res.ok:
raise RuntimeError(f"{body['error']['code']}: {body['error']['message']}")
print(body["meta"]["request_id"], body["meta"]["cache"]["status"])The weekly job#
import os
import time
import uuid
import requests
API = "https://api.happyfetch.net"
HEADERS = {"Authorization": f"Bearer {os.environ['HAPPYFETCH_API_KEY']}"}
THIRTY_DAYS = 30 * 24 * 3600
def refresh(domain: str, last_hash: str | None) -> dict | None:
"""Returns the company when it changed since last_hash, else None."""
key = str(uuid.uuid4()) # the same key on every retry, so a retry never pays twice
for attempt in range(5):
try:
res = requests.post(
f"{API}/v1/domain",
headers={**HEADERS, "Idempotency-Key": key},
json={"domain": domain, "max_age": THIRTY_DAYS},
timeout=120,
)
body = res.json()
except (requests.RequestException, ValueError):
time.sleep(2**attempt) # a timeout or a dropped connection: retry with the same key
continue
if res.ok:
meta = body["meta"]
if meta["content_hash"] == last_hash:
return None # nothing changed
key = meta["canonical"]["id"] or meta["canonical"]["slug"] # the id never changes
return {"key": key, "hash": meta["content_hash"], **body["data"]}
error = body["error"]
if not error["retryable"]:
return None # not_found, invalid_domain, insufficient_credits: see error["code"]
time.sleep(error.get("retry_after_seconds") or 2**attempt)
return NoneEvery error says whether retrying helps, and how long to wait when you're over your rate limit. The job above follows both.
What it costs#
| Lookups a month | Smallest pack (Starter) | Biggest pack (Business) |
|---|---|---|
| 1,000 lookups by website domain | $2.00 | $1.00 |
| 10,000 lookups by website domain | $20.00 | $10.00 |
| 100,000 lookups by website domain | $200.00 | $100.00 |
Every lookup fetched fresh, at 4 credits each. Lookups our cache answers cost half. A confirmed not-found costs the same, and nothing from our cache. Starter: $10 for 20,000 credits. Business: $500 for 2,000,000 credits. Credits never expire.
Accounts we already hold, recent enough for you, come from our cache at half the price: $0.50 per 1,000 domain lookups on our biggest pack. If your CRM stores each company's LinkedIn URL, look up by URL instead: $0.50 per 1,000, and $0.25 from our cache.
Check before you pay, for free#
The cache check tells you how old our copy of a company is, and what a lookup would cost right now. It's free, so a job can ask first and decide per account.
Good to know#
- Fresh on demand. Ask for a fresh fetch for the accounts that matter most this week, like the ones in an open deal. Everything else can come from the cache.
- Any age you like. There's no limit on how old an answer may be. Accept an older copy and anything we hold comes back at the cache price; only what we've never fetched is fetched fresh.
Try it on your own companies
200 credits free when you verify your email. No card needed, no subscription.