Google Sheets
Turn a Google Sheet into an email finder. You paste a script once (2 minutes), then an Icypeas menu appears in your sheet to find or verify emails for every row.
Step 1: prepare your sheet​
Create a sheet with exactly these columns, starting in cell A1:
- To find emails
- To verify emails
| A | B | C | D | E | F | G | |
|---|---|---|---|---|---|---|---|
| 1 | First name | Last name | Company or website | Certainty | Status | Search id | |
| 2 | Pierre | Landoin | icypeas.com |
Fill columns A to C. Leave D to G empty: the script fills them.
| A | B | C | D | |
|---|---|---|---|---|
| 1 | Certainty | Status | Search id | |
| 2 | pierre.landoin@icypeas.com |
Fill column A. Leave B to D empty: the script fills them.
Step 2: paste the script​
- In your sheet, click Extensions › Apps Script. A new tab opens.
- Delete everything in the editor, and paste the script below (use the copy button in its top-right corner).
- Click the 💾 Save icon, then close the tab and reload your sheet.
// Icypeas for Google Sheets: https://api-doc.icypeas.com/no-code/google-sheets
const ICYPEAS_API = "https://app.icypeas.com/api";
const ROWS_PER_RUN = 40; // keeps each run under Google's 6-minute limit
const FINAL_STATUSES = ["DEBITED", "FOUND", "NOT_FOUND", "DEBITED_NOT_FOUND", "BAD_INPUT", "INSUFFICIENT_FUNDS", "ABORTED"];
function onOpen() {
SpreadsheetApp.getUi()
.createMenu("Icypeas")
.addItem("Find emails (columns A to C)", "findEmails")
.addItem("Verify emails (column A)", "verifyEmails")
.addSeparator()
.addItem("Set my API key", "setApiKey")
.addToUi();
}
function setApiKey() {
const ui = SpreadsheetApp.getUi();
const answer = ui.prompt("Icypeas API key", "Paste your API key (app.icypeas.com > API):", ui.ButtonSet.OK_CANCEL);
if (answer.getSelectedButton() === ui.Button.OK) {
PropertiesService.getUserProperties().setProperty("ICYPEAS_API_KEY", answer.getResponseText().trim());
ui.alert("Saved. You can now use the Icypeas menu.");
}
}
function findEmails() {
run_("email-search");
}
function verifyEmails() {
run_("email-verification");
}
function run_(task) {
const isSearch = task === "email-search";
const width = isSearch ? 7 : 4;
const resultCol = isSearch ? 3 : 1; // Email (D) or Certainty (B)
const statusCol = width - 2;
const idCol = width - 1;
const sheet = SpreadsheetApp.getActiveSheet();
const lastRow = sheet.getLastRow();
if (lastRow < 2) return toast_("Add some rows below the header first.");
const rows = sheet.getRange(2, 1, lastRow - 1, width).getValues();
// 1. Start a search for every row that has no result yet.
const pending = [];
for (let i = 0; i < rows.length && pending.length < ROWS_PER_RUN; i++) {
const r = rows[i];
if (r[resultCol] !== "" || FINAL_STATUSES.indexOf(r[statusCol]) !== -1) continue;
let id = r[idCol];
if (!id) {
const body = isSearch ? { firstname: r[0], lastname: r[1], domainOrCompany: r[2] } : { email: r[0] };
if (isSearch ? !r[2] || (!r[0] && !r[1]) : !r[0]) continue;
const res = call_("/" + task, body);
if (!res.success) {
const why = (res.validationErrors || []).map((e) => e.field + ": " + e.type).join(", ");
sheet.getRange(i + 2, statusCol + 1).setValue("INPUT ERROR " + why);
continue;
}
id = res.item._id;
sheet.getRange(i + 2, statusCol + 1, 1, 2).setValues([["IN_PROGRESS", id]]);
Utilities.sleep(150);
}
pending.push({ row: i + 2, id: id });
}
if (!pending.length) return toast_("Nothing to do: every row already has a result.");
toast_("Searching " + pending.length + " rows, please wait…");
Utilities.sleep(10000);
// 2. Read the results (max 30 reads per minute).
let done = 0;
pending.forEach(function (p) {
const res = call_("/bulk-single-searchs/read", { id: p.id });
Utilities.sleep(2100);
const item = res.items && res.items[0];
if (!item) return;
sheet.getRange(p.row, statusCol + 1).setValue(item.status);
const email = item.results && item.results.emails && item.results.emails[0];
if (FINAL_STATUSES.indexOf(item.status) !== -1) done++;
if (!email) return;
if (isSearch && email.email) sheet.getRange(p.row, 4, 1, 2).setValues([[email.email, email.certainty]]);
if (!isSearch) sheet.getRange(p.row, 2).setValue(email.certainty);
});
toast_(done + " of " + pending.length + " rows done. Run it again to continue with the rest.");
}
function call_(path, body, attempt) {
const key = PropertiesService.getUserProperties().getProperty("ICYPEAS_API_KEY");
if (!key) throw new Error("Set your API key first: menu Icypeas > Set my API key.");
const res = UrlFetchApp.fetch(ICYPEAS_API + path, {
method: "post",
contentType: "application/json",
headers: { Authorization: key },
payload: JSON.stringify(body),
muteHttpExceptions: true,
});
const code = res.getResponseCode();
if (code === 401) throw new Error("Icypeas refused the API key. Check it with menu Icypeas > Set my API key.");
if (code === 429 && (attempt || 0) < 3) {
Utilities.sleep(10000);
return call_(path, body, (attempt || 0) + 1);
}
return JSON.parse(res.getContentText());
}
function toast_(message) {
SpreadsheetApp.getActive().toast(message, "Icypeas", 8);
}
Step 3: add your API key​
- In your sheet, open the new Icypeas menu (next to Help) › Set my API key.
- The first time, Google asks you to authorize the script: click Continue, choose your account, then Allow. If you see "Google hasn't verified this app", click Advanced › Go to Untitled project: it's your own script, it only talks to Icypeas.
- Paste your API key and click OK.
Your key is stored in your Google account only, not in the sheet: people you share the sheet with won't see it.
Step 4: run it​
Click Icypeas › Find emails (or Verify emails). After about a minute, the results appear:
- Email: the email found.
- Certainty: how reliable it is (
ultra_sure,very_sure,probable,undeliverable…). See Email certainty. - Status: where the search is. See Result statuses.
Each run handles 40 rows. Run it again to continue: rows that already have a result are skipped, and searches still in progress are picked up without being charged twice.
For thousands of rows, ask a developer (or an AI assistant, see AI agents) to switch the script to the bulk search endpoint, which takes 5,000 rows per call.
Troubleshooting​
| You see | Fix |
|---|---|
| No Icypeas menu | Reload the sheet. Check the script was saved in Extensions › Apps Script. |
| Icypeas refused the API key | Re-copy your key from the API page and use Set my API key again. Check that API access is activated. |
INPUT ERROR … in Status | A required cell is empty or invalid on that row. For a search you need a company and at least a first or last name. |
INSUFFICIENT_FUNDS | You're out of credits. Get more. |
Status stays IN_PROGRESS | Run the menu again in a minute. |