API Reference

REST. Bearer auth. JSON in, JSON out.

Authentication

Every request needs an API key. Get one from the dashboard. Two modes supported:

Bearer (simple)

Authorization: Bearer ic_pk_XXXX.ic_sk_YYYY

HMAC signed (icypeas-style)

X-API-Key: ic_pk_XXXX
X-Timestamp: 1735689600
X-Signature: sha256=<hex>

# signature = HMAC_SHA256(hmac_secret,
#   timestamp + "
" + method + "
" + path + "
" + rawBody)
# timestamp must be within 300s of server clock.

The HMAC secret is minted alongside your API key and shown once. Bearer mode is fine for most use — HMAC is for high-security integrations where you don't want the bearer token to leak in logs.

POST /v1/verify

Verify one email. 1 credit. Optional webhook URL fires per-item (fire-and-forget) with the same HMAC signing as bulk callbacks.

{
  "email": "jane@stripe.com",
  "webhook": "https://your-app/hook"  // optional
}

Response:

{
  "email": "jane@stripe.com",
  "status": "deliverable",
  "reason": "accepted",
  "mxHost": "aspmx.l.google.com",
  "mxProvider": "google_workspace",
  "smtpCode": 250,
  "_meta": { "credits_charged": 1, "credits_remaining": 99, "plan": "free" }
}

POST /v1/find

Verify a professional email from name + domain. 3 credits.

{
  "first": "Jane",
  "last": "Doe",
  "domain": "stripe.com"
}

Response (verified path):

{
  "verified_email": "jane.doe@stripe.com",     // ← safe to send
  "pattern_guess":  null,
  "found":          "jane.doe@stripe.com",     // legacy; same as verified_email
  "status":         "found",
  "confidence":     0.98,
  "mxProvider":     "google_workspace"
}

Response (unverified / catch-all path):

{
  "verified_email": null,                      // ← DO NOT ship as verified
  "pattern_guess":  "jane.doe@catchall.com",   // best guess, use at your own risk
  "found":          null,                      // legacy — not populated for guesses
  "status":         "catch_all",
  "confidence":     0.45,
  "note":           "domain accepts all mail — returning top-ranked guess, unverified"
}
Integration rule: only verified_email is safe to send. pattern_guess is our best deduction — helpful for research, but shipping it as "verified" will damage your sending-domain reputation via bounces. The HubSpot integration only marks contacts icy_verified_at when verified_email is populated.

POST /v1/bulk

Async batch. 1 credit / verify, 3 / find. Up to 10,000 items per job.

{
  "action": "find",
  "items": [
    { "first": "Jane", "last": "Doe", "domain": "stripe.com" }
  ],
  "webhook": "https://your-app/webhook/Coalesce"
}

Returns { "jobId": "job_..." , "status": "processing" }. Poll GET /v1/bulk/<id>.

Status codes

deliverable RCPT accepted, not a catch-all.
catch_all Accepts all; best-guess returned.
protected Proofpoint / O365 blocks enumeration; confidence-scored guess.
risky Accepted but is a role account.
undeliverable MX rejected, syntax invalid, disposable, or no MX.
unknown Transient; retry with backoff.

Rate limits

Daily credit cap by plan. _meta.credits_remaining tracks usage. 429 with retry_at when exhausted.

Errors

401  missing / invalid / revoked API key
429  rate_limit_exceeded  (retry after midnight UTC)
502  worker_unavailable   (SMTP node offline; retry)
400  malformed request
Coalesce · home