List Drive Files Modified Since

“What landed in the Inbox folder since last run?” is the start of half the Drive automations we write. listDriveFilesModifiedSince(folderIdOrFolder, sinceDate, opts) returns plain objects (id, name, url, mimeType, lastUpdated, size) for files updated after sinceDate. You can filter by MIME type, and subfolders are optional (off by default). Results come back oldest first, so a checkpoint can move forward one file at a time.

When to use

  • Watching an intake folder and queueing new files for processing
  • Nightly “what changed?” reports on a client folder
  • Re-running a Drive job from a saved cursor after a timeout

How to use this snippet

/** * List files in a folder updated strictly after sinceDate. * * @param {string|GoogleAppsScript.Drive.Folder} folderIdOrFolder * @param {Date|string|number|null} sinceDate null = everything * @param {{mimeType?: string|string[], recursive?: boolean}=} opts * mimeType e.g. MimeType.PDF or ['application/pdf', 'image/png'] * recursive include subfolders (default false) * @return {Array<{id: string, name: string, url: string, mimeType: string, * lastUpdated: Date, size: number}>} oldest first */ function listDriveFilesModifiedSince(folderIdOrFolder, sinceDate, opts) { opts = opts || {}; var folder = typeof folderIdOrFolder === 'string' ? DriveApp.getFolderById(folderIdOrFolder) : folderIdOrFolder; var since = sinceDate ? new Date(sinceDate) : new Date(0); var mimes = opts.mimeType ? [].concat(opts.mimeType) : null; // Let Drive do the rough filtering (the query works to the second, in UTC). var q = "trashed = false and modifiedDate > '" + Utilities.formatDate(since, 'UTC', "yyyy-MM-dd'T'HH:mm:ss") + "'"; var out = []; (function walk(f) { var files = f.searchFiles(q); while (files.hasNext()) { var file = files.next(); var updated = file.getLastUpdated(); // Exact millisecond check, so the file your cursor points at isn't returned again if (updated.getTime() <= since.getTime()) continue; if (mimes && mimes.indexOf(file.getMimeType()) === -1) continue; out.push({ id: file.getId(), name: file.getName(), url: file.getUrl(), mimeType: file.getMimeType(), lastUpdated: updated, size: file.getSize() }); } if (opts.recursive) { var subs = f.getFolders(); while (subs.hasNext()) walk(subs.next()); } })(folder); // Oldest first: process in order, save each lastUpdated as your new cursor. out.sort(function (a, b) { return a.lastUpdated - b.lastUpdated; }); return out; }

Example

function reportNewPdfs() { var props = PropertiesService.getScriptProperties(); var cursor = props.getProperty('inboxCursor'); // ISO string or null var files = listDriveFilesModifiedSince(props.getProperty('inboxFolderId'), cursor, { mimeType: MimeType.PDF }); files.forEach(function (f) { console.log('%s %s %s', f.lastUpdated.toISOString(), f.name, f.url); // Advance after each file, so a timeout mid-loop doesn't lose progress props.setProperty('inboxCursor', f.lastUpdated.toISOString()); }); }

Tips:

  • “Modified” means updated. Renames, content edits and moves can bump lastUpdated, so a file you already handled may show up again. Track processed ids (a queue sheet works) if that matters.
  • Keep the cursor as an ISO string in Script Properties, or use readWriteCheckpoint for a { cursor, updatedAt } record.
  • Google Docs/Sheets report size as 0 because they don’t count as stored bytes. Don’t use size to spot empty files.
  • Recursive scans of deep trees get slow. Keep intake folders flat, or cap how many files you handle per run.

Tip: NitroGAS Co-Pilot can wrap listDriveFilesModifiedSince in a checkpointed time-driven job if you tell it which folder and what should happen to each new file.

Happy Coding!

NitroGAS Chrome Extension

Want this snippet handy inside the Apps Script editor? NitroGAS gives you free themes and snippets — plus optional Co-Pilot when you want a boost (1-week, 1-month, or yearly passes — no subscription). Happy Coding!

Get the Extension