Setup, once
- Create a free API key at /app.
- In your sheet: Extensions → Apps Script, paste the script below, save.
- In the script editor: Project Settings → Script Properties, add
DEDUPE_KEYwith your key. - Reload the sheet. A Dedupe.dev menu appears.
The script
const FUZZY = false; // true for fuzzy name matching
const THRESHOLD = 0.88; // used only when FUZZY is true
const KEY_COLUMNS = []; // zero-based, e.g. [0] for column A; [] = whole row
function onOpen() {
SpreadsheetApp.getUi().createMenu("Dedupe.dev")
.addItem("Remove duplicate rows", "dedupeActiveSheet")
.addToUi();
}
function dedupeActiveSheet() {
const sheet = SpreadsheetApp.getActiveSheet();
const rows = sheet.getDataRange().getDisplayValues();
const csv = rows.map(r => r.map(cell => {
const s = String(cell);
return /[",\n\r]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s;
}).join(",")).join("\n");
const key = PropertiesService.getScriptProperties().getProperty("DEDUPE_KEY");
const res = UrlFetchApp.fetch("https://dedupe.dev/api/dedupe", {
method: "post",
contentType: "application/json",
headers: { Authorization: "Bearer " + key },
payload: JSON.stringify({
mode: "rows",
data: csv,
options: { keyColumns: KEY_COLUMNS, hasHeader: true,
fuzzy: FUZZY, threshold: FUZZY ? THRESHOLD : undefined },
}),
muteHttpExceptions: true,
});
const body = JSON.parse(res.getContentText());
if (res.getResponseCode() !== 200) throw new Error(body.error);
const cleaned = Utilities.parseCsv(body.result);
sheet.clearContents();
sheet.getRange(1, 1, cleaned.length, cleaned[0].length).setValues(cleaned);
SpreadsheetApp.getActiveSpreadsheet().toast(
body.kept + " rows kept, " + body.removed + " duplicates removed", "Dedupe.dev");
}
What to know before running it
The script replaces the sheet's contents with the deduplicated rows, so run it on a copy the
first time. Rows travel to the API over HTTPS and are processed in memory, never stored; if the
sheet must not leave your machine at all, use the browser tool on a CSV download
instead, which is free and uploads nothing. Each run is metered like any API call (one unit per
row), and appears in your dashboard with its row counts. To run it nightly with
no clicks, add a time-driven trigger in the script editor pointing at
dedupeActiveSheet.
FAQ
Frequently asked questions
Does this upload my whole spreadsheet?
It sends the active sheet's rows to the dedupe API over HTTPS, which is the point of the script: this is the API path, not the local browser tool. Nothing sent is stored; the endpoint deduplicates in memory and answers. For the zero-upload version, download the sheet as CSV and use the in-browser tool instead.
Where does the API key live?
In Script Properties (Project Settings → Script Properties), not in the code. Anyone who can open the script editor can read properties, so on a shared sheet treat the key as shared with your collaborators, and rotate it from /app/keys if that changes.
Can it do fuzzy matching?
Yes: set FUZZY to true and pick a threshold in the script. 0.85 to 0.92 works for names. The run replaces the sheet contents with the deduplicated rows, so check the removed count in the toast before saving anything downstream.
Why not a marketplace add-on?
An add-on needs OAuth scopes reviewed by Google and gives us standing access to your spreadsheets. Thirty lines of script you paste yourself do the same job with access you can read line by line.
Last updated