Auto-import every CSV dropped in a folder
Append new vendor CSV exports into a master sheet automatically — drop the file, walk away.
Published Dec 10, 2025
Northwind’s vendors each send a weekly CSV export, and someone on the ops team opens every one, copies the rows, and pastes them into a master sheet. It is a half-hour of mind-numbing copy-paste that also invites mistakes — a missed file, a header pasted as data, a row dropped on the floor.
This script makes the folder do the work. Vendors drop their CSV into a watch
folder; every fifteen minutes the script picks up anything new, appends its rows
to the master sheet, and moves the file into a processed/ folder so it is
never imported twice. Drop the file, walk away — the master sheet catches up on
its own.
What you’ll need
- A Drive folder vendors drop CSV files into (the inbox).
- A second Drive folder for processed files, so imported CSVs are moved out of the way.
- A Google Sheet to act as the master — the script appends to its first tab.
- The three IDs — inbox folder, processed folder, and master sheet — which you paste into the config below.
The script
// Drive folder vendors drop new CSV files into.
const INBOX = '1abcCsvInboxId';
// Drive folder imported files are moved to, so they are not re-imported.
const PROCESSED = '1abcCsvProcessedId';
// The master spreadsheet rows are appended to.
const MASTER = '1abcMasterCsvSheetId';
/**
* Imports every CSV in the inbox folder into the master sheet, then
* moves each file to the processed folder. Safe to run on a schedule.
*/
function ingestCsvs() {
const folder = DriveApp.getFolderById(INBOX);
const processed = DriveApp.getFolderById(PROCESSED);
const sheet = SpreadsheetApp.openById(MASTER).getSheets()[0];
const files = folder.getFiles();
let imported = 0;
// 1. Step through every file currently in the inbox.
while (files.hasNext()) {
const file = files.next();
// 2. Skip anything that is not a CSV.
if (file.getMimeType() !== 'text/csv') continue;
// 3. Parse the file into a 2D array of rows.
const rows = Utilities.parseCsv(file.getBlob().getDataAsString());
// 4. A file with only a header (or empty) has nothing to import —
// move it out of the inbox anyway so it is not re-checked.
if (rows.length <= 1) {
file.moveTo(processed);
continue;
}
// 5. Drop the header row and append the data to the bottom of the sheet.
const data = rows.slice(1);
sheet
.getRange(sheet.getLastRow() + 1, 1, data.length, data[0].length)
.setValues(data);
// 6. Move the imported file out of the inbox so the next run skips it.
file.moveTo(processed);
imported++;
}
Logger.log('Imported ' + imported + ' CSV file(s).');
}
How it works
ingestCsvsopens the inbox folder, the processed folder, and the master sheet, then iterates over every file currently in the inbox.- It checks each file’s MIME type and skips anything that is not
text/csv, so stray files in the folder are left alone. - It reads the file as a string and runs
Utilities.parseCsv, which handles quoted fields and embedded commas, returning a clean 2D array of rows. - If the file has one row or fewer — just a header, or empty — there is no data
to import. The script still moves it to
processed/so it is not re-checked on every run. - Otherwise it drops the header row with
slice(1)and appends the data block to the master sheet in a singlesetValuescall, starting one row below the current last row. - It moves the file into the processed folder. Because the file leaves the inbox, the next scheduled run will not see it again — that is what stops double imports.
Example run
A vendor drops acme-week21.csv into the inbox:
date,sku,units
2026-05-18,WGT-100,40
2026-05-19,WGT-220,15
Within fifteen minutes the master sheet gains two rows at the bottom:
| date | sku | units |
|---|---|---|
| …existing rows… | ||
| 2026-05-18 | WGT-100 | 40 |
| 2026-05-19 | WGT-220 | 15 |
And acme-week21.csv is now in the processed/ folder. Nobody opened the file.
Trigger it
This runs unattended on a schedule:
- In the Apps Script editor, open Triggers (the clock icon).
- Click Add trigger.
- Choose function
ingestCsvs, event source Time-driven, type Minutes timer, interval Every 15 minutes. - Save and approve the authorisation prompt.
Watch out for
- The script assumes every CSV has the same columns in the same order as the master sheet, and that row 1 is always a header. A vendor who reorders columns or omits the header will quietly import misaligned data.
- It does not validate the data. Bad dates, blank cells or extra columns go straight into the master sheet — add checks if your vendors are unreliable.
- Files exported from a spreadsheet are sometimes reported as
application/vnd.ms-excelrather thantext/csv. If real CSVs are being skipped, loosen the MIME check or also match on a.csvfilename. - Each run is capped at six minutes. A backlog of many large files may not clear in one pass — the next run simply picks up the rest, which is fine.
moveTorequires edit access to both folders. If the script cannot move a file, the import may repeat on the next run, so make sure permissions are set.- Two runs overlapping is unlikely at a 15-minute interval, but if you shorten
it consider a
LockServicelock so the same file is never imported twice.
Related
Build an OKR tracker with progress rollups
Aggregate Northwind's key-result progress into objective-level scores automatically.
Updated Jan 14, 2026
Build a recurring-task generator
Spawn new Northwind task rows on a daily or weekly cadence from a Recurring sheet.
Updated Jan 10, 2026
Build a multi-sheet search-and-jump tool
Find a value across every tab of a workbook and click through to the cell that contains it.
Updated Jan 7, 2026
Auto-rebuild grouped summaries on edit
Refresh pivot-style rollups the instant data changes — no manual recompute.
Updated Jan 4, 2026
Build a data-quality scorecard
Grade any sheet on completeness, validity, and freshness — surface gaps as a single score.
Updated Dec 31, 2025