Skip to main content

Webhooks

Instead of asking "is it ready yet?" again and again, give Icypeas a URL: it sends you the result the moment it's ready. It's the fastest and most scalable way to get results, and it doesn't count against your rate limits.

Two notifications​

WebhookSent whenContains
Search item processedA single search is done, or each row of a bulk search is done.The full result: emails, certainty, status, your externalId.
Bulk search doneA whole bulk search is done.Statistics (rows, emails found by certainty). Optionally every result.

Set them up​

For all your searches: in the app​

  1. Open the API page and make sure API access is on.
  2. Fill in the URLs under Icypeas can notify routes when a certain action is performed.
  3. Click Save notifications routes.

Notification URL fields on the API page

To turn a notification off, empty its field and save.

For one search: in the request​

Pass your URL with the search. It's used for that search only, on top of the ones set in the app.

EndpointField
/email-search, /email-verification, /domain-searchcustom.webhookUrl
/bulk-searchcustom.webhookUrlItem (each row) and custom.webhookUrlBulkDone (end)
{
"email": "pierre.landoin@icypeas.com",
"custom": {
"webhookUrl": "https://example.com/icypeas/result",
"externalId": "crm-1042"
}
}

Receive them​

  • Icypeas sends a POST request with a JSON body.
  • Answer with an HTTP 200 status.
  • The URL must be reachable from the internet. Webhook URLs from Zapier (Catch Hook), Make (Custom webhook) or n8n (Webhook node) work out of the box.

Every call has this shape:

{
"signature": "HMAC-SHA1 signature",
"timestamp": "2026-03-01T04:40:20Z",
"data": { "…": "the result or the statistics" }
}

Check that a call really comes from Icypeas (optional)​

Anyone who knows your webhook URL could call it. To be sure a call comes from Icypeas, recompute the signature with your API secret and compare:

  1. Take the path of your webhook URL, for example /icypeas/result for https://example.com/icypeas/result.
  2. Append the timestamp from the body.
  3. Lower-case the result.
  4. Compute its HMAC-SHA1 with your API secret, in hexadecimal.
  5. It must equal signature.
Node.js / Express
import crypto from "node:crypto";
import express from "express";

const app = express();
app.use(express.json({ limit: "50mb" }));

app.post("/icypeas/result", (req, res) => {
const { signature, timestamp, data } = req.body;
const payload = `${req.path}${timestamp}`.toLowerCase();
const expected = crypto
.createHmac("sha1", process.env.ICYPEAS_API_SECRET)
.update(payload)
.digest("hex");

if (expected !== signature) return res.sendStatus(401);

console.log(data.results?.emails, data.userData?.externalId);
res.sendStatus(200);
});

app.listen(3000);
Large bulk searches

If you use includeResultsInWebhook on a bulk search of more than 1,000 rows, make sure your server accepts large request bodies (see limit above).