appscript.dev
Automation Intermediate Sheets Drive

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

  1. ingestCsvs opens the inbox folder, the processed folder, and the master sheet, then iterates over every file currently in the inbox.
  2. It checks each file’s MIME type and skips anything that is not text/csv, so stray files in the folder are left alone.
  3. 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.
  4. 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.
  5. Otherwise it drops the header row with slice(1) and appends the data block to the master sheet in a single setValues call, starting one row below the current last row.
  6. 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:

dateskuunits
…existing rows…
2026-05-18WGT-10040
2026-05-19WGT-22015

And acme-week21.csv is now in the processed/ folder. Nobody opened the file.

Trigger it

This runs unattended on a schedule:

  1. In the Apps Script editor, open Triggers (the clock icon).
  2. Click Add trigger.
  3. Choose function ingestCsvs, event source Time-driven, type Minutes timer, interval Every 15 minutes.
  4. 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-excel rather than text/csv. If real CSVs are being skipped, loosen the MIME check or also match on a .csv filename.
  • 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.
  • moveTo requires 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 LockService lock so the same file is never imported twice.

Related