Apps Script UrlFetch 20,000 calls a day: budget Sume polling
Consumer Apps Script allows 20,000 UrlFetch calls a day and 90 minutes of trigger time. One Sume bulk run per sheet plus a slow poll fits well inside both.

Google Apps Script allows 20,000 UrlFetch calls a day on a consumer account and 100,000 on Google Workspace, with 6 minutes per execution. A sheet that calls Sume once per row and polls each job every minute can spend that budget in a day. Send the rows as one Sume bulk run and poll the queue rarely, and the same sheet uses a few hundred calls.
What are the Apps Script limits that matter here?
Google's quotas page lists the numbers below. The two that bite a video sheet are the daily UrlFetch count and the total trigger runtime, because time-driven triggers are how a script polls.
| Quota | Consumer | Google Workspace |
|---|---|---|
| URL Fetch calls per day | 20,000 | 100,000 |
| Script runtime per execution | 6 min | 6 min |
| Triggers total runtime per day | 90 min | 6 hr |
| URL Fetch response size | 50 MB per call | 50 MB per call |
| URL Fetch header size | 8 KB per call | 8 KB per call |
| Simultaneous executions | 30 per user | 1,000 per script |
How many calls does a naive sheet use?
Take 200 rows. A per-row submit is 200 fetches. Polling each job once a minute for a 10-minute render adds about 2,000 more, so one sheet run costs about 2,200, and ten runs a day would exhaust a consumer account.
Sume's bulk run takes 1 to 100 items per request. Two requests cover 200 rows. Queue progress is one GET /v1/format-run-queues/{id}, which returns counts for the whole queue, so polling cost no longer scales with rows.
What does the cheap version look like?
Submit in chunks of 100 with a deterministic Idempotency-Key so a retried execution cannot enqueue the same sheet twice. Store the returned queue id in script properties, then let a time-driven trigger read the queue every few minutes.
A 5-minute trigger is 288 polls a day. If each poll reads one queue, that is 288 of your 20,000 fetches, and each execution lasts a second or two, far under 90 minutes of trigger runtime.
function submitChunk(rows, batchId) {
const res = UrlFetchApp.fetch(
'https://api.sume.com/v1/formats/acme/promo/bulk-runs', {
method: 'post',
contentType: 'application/json',
muteHttpExceptions: true,
headers: {
Authorization: 'Bearer ' + PropertiesService.getScriptProperties().getProperty('SUME_API_KEY'),
'Idempotency-Key': 'sheet-' + batchId,
},
payload: JSON.stringify({
concurrency: 3,
items: rows.slice(0, 100).map(function (r) {
return { instruction: r[0], generation_spend_cap_usd: 5 };
}),
}),
});
if (res.getResponseCode() !== 202 && res.getResponseCode() !== 200) {
throw new Error('sume ' + res.getResponseCode() + ' ' + res.getContentText());
}
return JSON.parse(res.getContentText()).data.id;
}Why poll instead of receiving Sume's webhook?
Sume signs every webhook with a sume-v1 HMAC carried in request headers, and a receiver must check it. This post does not rely on a script web app reading those headers; it polls the queue from a trigger instead, which needs no public endpoint. If you want push delivery, put a small relay in front, as in the Apps Script signature post.
Polling is a supported path. The jobs page says to keep status polling as the backup for any webhook, and to back off rather than resubmit a paid request because a local process timed out.
What does Sume not do?
Sume has no Sheets connector in these docs, and the queue has no callback of its own. The sheet owns row mapping. Use the queue items[] order, which comes back with run_id per item, to write results to the right rows, and branch on counts.failed rather than reading completed as all succeeded.
Sources
Related posts
More in Integrations
- Apps Script UrlFetch 20,000/day and 6 min: size a Sume sheet run
Apps Script allows 20,000 UrlFetch calls a day and 6 minutes per run on a consumer account. How to size a Sume sheet batch inside both limits.
- Make an avatar video from an MCP agent: tools, dry run, spend cap
The hosted MCP server of Sume exposes avatars_list, avatar-videos_create and jobs_wait. How dry_run and max_spend_usd gate a paid call, and the scope you need.
- Poll a Sume job from a CI step with curl and jq: exit codes
A 23-line bash script that polls /v1/jobs/{id}/status and exits 0, 1, 2 or 3, so a CI step can tell done, failed, still running and a bad request apart.
- Bun.serve webhook receiver for Sume: raw body, verify, dedupe
A 24-line Bun.serve receiver for Sume job webhooks: read the raw body first, verify sume-v1, dedupe on job_id, answer 204 before the work. Tested on Bun 1.4.
Written by Sume