Google Apps Script 限制說明:配額規範、執行時間與觸發條件完整解析

Published on: | Last updated:

先說結論

OK,直接講重點。Google Apps Script (GAS) 很強大,而且免費,但它不是讓你無限使用的。最重要的觀念不是去背那些密密麻麻的配額 (Quota) 數字,而是從一開始寫 code 的時候,就要有「這個腳本總有一天會撞到牆」的心理準備。

所以,寫法上就要懂得繞路、懂得自我管理。這篇筆記就是要把這些「牆」在哪裡,還有怎麼「繞路」給整理出來。

為什麼官方文件看不懂?帳號類型是關鍵

很多人第一個卡關的點,就是去查 Google 的官方文件,然後覺得頭很痛。一下說這個可以用 100 次,一下又說可以用 1500 次。欸,到底是多少?

問題出在,你的「Google 帳號類型」會決定你的配額等級。這點超重要,但文件裡寫得不太直觀。簡單分成兩種:

  • 個人帳號 (Consumer): 就是你我都有的 `@gmail.com` 結尾的免費帳號。
  • Google Workspace 帳號: 以前叫 G Suite,就是公司或學校付費買的,用自己網域名稱結尾的帳號 (例如 `user@yourcompany.com`)。早期免費的 Workspace 帳號也算在這一類,但配額比較接近個人帳號。

它們的差別,直接看表格最清楚。我整理了幾個最常撞到的限制。

服務項目 個人帳號 (@gmail.com) Google Workspace 帳號 (付費版)
單次腳本執行時間 6 分鐘 / 次 一般是 6 分鐘,但某些 Workspace 版本 (例如 Enterprise) 可以申請延長到 30 分鐘。
每日觸發器總執行時間 90 分鐘 / 天 基本上是 1~6 小時 / 天,看你的 Workspace 版本等級。差很多!
MailApp.sendEmail 每日額度 一天只能寄給 100 個收件者。注意喔,是「收件者」,不是「封信」。一封信寄給 10 個人就用掉 10 個額度了。 一天 1,500 個收件者。量級完全不同,要做自動化報表或通知,這個就很關鍵。
UrlFetchApp.fetch 每日呼叫次數 20,000 次 / 天。聽起來很多,但如果你的腳本是高頻率去抓 API 資料,就要小心。 100,000 次 / 天。基本上不太會碰到。
建立文件 (Docs, Sheets, Slides) 250 個 / 天。 1,500 個 / 天。

你看,光是寄信跟觸發器總時間,付費帳號的額度就大超多。很多時候你寫的腳本沒問題,只是你的帳號等級不夠力而已。這跟美國那邊開發者社群討論的狀況很像,他們很早就習慣區分 Workspace 跟個人帳號的開發策略,但在台灣比較多個人開發者,所以更容易撞到免費帳號的牆。

腳本執行流程示意圖:撞到配額牆就該轉彎
腳本執行流程示意圖:撞到配額牆就該轉彎

最常見的幾個「撞牆」點

除了帳號等級,有幾個限制是所有人都會遇到的,算是 GAS 的天花板。

腳本執行時間:6 分鐘的詛咒

這大概是排名第一的殺手。任何一個 Apps Script 腳本,從開始執行到結束,一次最多就是 6 分鐘(對,大部分帳號都是)。只要超過 6 分鐘,Google 就會強制中止你的腳本,然後丟出一個 `Exceeded maximum execution time` 的錯誤。

什麼情況會超過 6 分鐘?

  • 處理好幾千列的 Google Sheet 資料。
  • 在一個迴圈裡,對每個項目都做很複雜的操作,例如呼叫外部 API、建立檔案等。
  • 網路不穩,導致 `UrlFetchApp` 等待回應的時間過長。

怎麼辦?唯一的解法就是「分批處理 (Batching)」。不要想著一次做完,要把大任務拆成好幾個小任務。這點後面案例會細講。

服務呼叫次數:API 不是讓你無限 call

就像上面表格看到的,`MailApp`、`UrlFetchApp` 這些跟外部溝通的服務,都有每日呼叫次數限制。你可能會想,「一天一萬次很多啊」,但如果你在一個 `for` 迴圈裡面寫了 `UrlFetchApp.fetch()`,然後這個迴圈要跑一千次,裡面又不小心寫錯多跑了幾次... 很快就用完了。

寫 Code 的時候,養成習慣用 `try...catch` 把這些 API 呼叫包起來,至少出錯時你可以在 Log 裡看到原因,而不是腳本莫名其妙停掉。


function fetchSomeData() {
  try {
    // 這裡放你的 UrlFetchApp 或 MailApp 等等的 code
    let response = UrlFetchApp.fetch("https://api.example.com/data");
    Logger.log(response.getContentText());
  } catch (e) {
    // 如果碰到配額限制或其他錯誤,e.message 通常會寫原因
    Logger.log("抓取資料失敗: " + e.message);
    // 你可以在這裡加上錯誤通知,例如寄信給自己
    // MailApp.sendEmail("your-email@gmail.com", "腳本錯誤", "錯誤訊息:" + e.message);
    // 但要注意,如果 MailApp 本身就超額,這段也會失敗
  }
}
真實世界:在執行紀錄中看到超時錯誤
真實世界:在執行紀錄中看到超時錯誤

案例:一個自動寄信腳本的進化史

講理論很空,我們直接用一個「從 Google Sheet 讀取名單並寄送客製化郵件」的腳本來當例子。

版本 1.0:天真爛漫版

拿到需求,直接一個迴圈搞定。讀取每一列,然後 `MailApp.sendEmail`。

這在 50 人以下的名單運作得很好。但有一天,名單長到 200 人... 腳本就死了。免費帳號一天只能寄 100 個。GG。

版本 2.0:分批處理 + 狀態儲存

這才是專業的做法。核心精神是:**自己記住上次做到哪,然後讓腳本可以重複被觸發。**

我們會用到一個很重要的內建服務:`PropertiesService`。它就像是腳本的專屬小抽屜,可以讓你存一些簡單的文字資料,就算腳本執行結束,資料也還在。

作法變成:

  1. 用 `PropertiesService` 讀取一個我們自己設定的 `lastProcessedRow` 變數。如果第一次跑,就從第 2 列開始(跳過標題)。
  2. 一樣寫迴圈,但這次加上一個計數器,一次只處理 50 封信就好(確保不會超時,也給每日配額留點彈性)。
  3. 迴圈跑完後,把「最後處理到的那一列的行數」用 `PropertiesService` 存起來。
  4. 檢查工作是否全部做完。如果還沒,就動態建立一個 5 分鐘後執行的「觸發器」,讓它再跑一次同一個 function。
  5. 如果全部做完了,就把 `lastProcessedRow` 清掉,並且刪掉那個自動建立的觸發器。

這樣一來,不管名單有多長,你的腳本都能像螞蟻搬家一樣,一次搬一點,最終把任務完成。這就是設計「可恢復性 (resumable)」腳本的思路。

觸發器 (Triggers) 的隱形殺手

觸發器是 GAS 自動化的靈魂,但也充滿了陷阱。

  • 每日總時長限制: 個人帳號是 90 分鐘/天。這不是指單一觸發器,而是你帳號下「所有」觸發器執行的時間總和。如果你有一個每 5 分鐘跑一次、每次跑 2 分鐘的腳本,那你一天只能跑 45 次,之後的觸發器就會失敗。
  • `onEdit(e)` 的限制: 這是一個很方便的簡易觸發器,當試算表被編輯時會自動執行。但它能做的事情非常受限,例如它不能呼叫 `MailApp` 或任何需要授權的服務。如果你需要這些功能,必須手動建立「可安裝的 (installable)」`onEdit` 觸發器。
  • 觸發器會「默默死掉」: 有時候,特別是時間驅動的觸發器,可能會因為某些 Google 後台的原因執行失敗,而且你完全不會收到通知。所以,重要的自動化流程,最好在腳本的開頭和結尾加上日誌紀錄 (例如寫入另一個 Sheet),方便你追蹤它到底有沒有乖乖工作。官方文件有提到失敗會寄信通知,但實務上...嗯,別太指望它。
觸發器每日總時間就像一個會用完的沙漏
觸發器每日總時間就像一個會用完的沙漏

我該升級 Workspace 嗎?決策思路

這其實是個商業問題,不是技術問題。

問自己幾個問題:

  • 這個自動化腳本是公司營運的核心流程嗎?如果它停了,公司會不會少賺錢或多花人力成本?
  • 你花在「繞過」免費版限制的時間,換算成時薪,是不是已經超過升級 Workspace 的費用了?
  • 你需要更可靠的錯誤通知和更專業的技術支援嗎?

如果答案是「是」,那就別猶豫了。升級到 Google Workspace Starter 或 Standard 版本,光是 `MailApp` 1500 封/天和觸發器總時長變成 1 小時/天以上,就能解決 80% 的問題。

但如果這只是你的個人專案,或是偶爾跑一次的小工具,那學會跟免費版的限制共存,也是一種有趣的挑戰。

常見錯誤與修正

整理幾個在 Log 裡最常看到的錯誤訊息跟處理方向。

  • `"Service invoked too many times for one day: email."`
    原因: `MailApp` 或 `GmailApp` 的每日額度用完了。 就是上面說的 100/1500 限制。
    修正: 檢查你的迴圈,不要在一次執行中寄太多信。把寄送工作分散到一整天,或用我們案例中的分批處理法。
  • `"Exceeded maximum execution time"`
    原因: 腳本跑超過 6 分鐘被強制中止了。
    修正: 優化你的演算法,減少不必要的讀寫操作。最終極的解法還是分批處理 (`PropertiesService` + 觸發器)。
  • `"Authorization is required to perform that action."`
    原因: 腳本嘗試執行一個它沒有被授權的動作。最常發生在簡易觸發器 (simple triggers) 像 `onOpen(e)` 或 `onEdit(e)` 身上。
    修正: 從腳本編輯器手動執行一次那個 function,畫面會跳出授權請求,同意之後,可安裝的觸發器 (installable trigger) 就能正常運作了。
  • `"Exception: The starting row of the range is too small."`
    原因: 你用 `getSheetByName('...').getRange(0, 1)` 這種方式去選取儲存格。在 Apps Script 裡,行 (row) 跟欄 (column) 的編號都是從 `1` 開始,不是 `0`。
    修正: 這是新手常見錯誤,記得 `getRange(1, 1)` 才是 A1 儲存格。

說了這麼多,其實重點就是,寫 Apps Script 不能像寫單機程式那樣,要時時把「雲端」、「共享」、「限制」這幾個關鍵字放在心上。設計得好,它就是超強的免費自動化神器;沒考慮周全,它就會變成不定時炸彈。

你最常碰到的是哪一種限制呢?或是有沒有什麼繞過限制的獨門妙招?在下面分享一下吧!

Related to this topic:

Comments

撥打專線 LINE免費通話