Firebase scheduled functions: call a paid API once a day

Declare a Firebase scheduled function with onSchedule, a cron and a timeZone, keep the API key in secrets, and key each paid call to scheduleTime.

5 min readSume
All posts

Firebase scheduled functions run on a timer: declare the function with onSchedule from firebase-functions/v2/scheduler, give it a schedule in Unix crontab or App Engine syntax, such as "every day 00:00"; when you deploy, Firebase creates a Cloud Scheduler job that invokes it at those times. Firebase also warns that, depending on how you design your scheduling logic, a function may be triggered multiple times, with the next instance running while the previous one is still executing, so a scheduled function that calls a paid API should key each call to its scheduled time.

Firebase facts come from its Schedule functions page and the reference pages under Sources; Sume facts come from Create a run, Runs and results and Webhooks. All were read on 2026-09-28. Sume has no Firebase integration: the function makes one plain HTTPS call. The same job on Supabase is Supabase cron Edge Function: start an AI video on a schedule.

How do I write a scheduled function that calls an API?

Pass an options object instead of a bare schedule, to set timeZone, the timezone the schedule executes in, and secrets. Use the unix-cron format: for those jobs, event.scheduleTime is the job's schedule time in RFC 3339 UTC, and its date makes the Idempotency-Key. On a manual trigger the field holds the execution time instead, so a hand-run on the same UTC date still sends the same key. This function starts one Sume Format run at 06:00 New York time:

const { onSchedule } = require("firebase-functions/v2/scheduler");
const { logger } = require("firebase-functions");

exports.dailyRecap = onSchedule(
  { schedule: "0 6 * * *", timeZone: "America/New_York", secrets: ["SUME_API_KEY"] },
  async (event) => {
    const day = event.scheduleTime.slice(0, 10); // the slot's UTC date
    const res = await fetch("https://api.sume.com/v1/formats/acme/daily-recap/runs", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.SUME_API_KEY}`,
        "Content-Type": "application/json",
        "Idempotency-Key": `daily-recap-${day}`,
      },
      body: JSON.stringify({
        input: { day },
        communication: { webhook_url: "https://example.com/hooks/sume" },
      }),
    });
    const { data, error } = await res.json();
    if (!res.ok) throw new Error(`${res.status} ${error.code}`);
    logger.log("run", data.id, data.idempotency_hit ? "replay" : "new");
  },
);

Why does a scheduled function run twice?

Firebase's page warns that, depending on your scheduling logic, the next instance can start while the previous one is still executing. Its sample also points to the Cloud Scheduler console for running the task by hand, and retryCount sets the number of retry attempts for a failed run. Every repeat on the same UTC date sends the same key and body, so Sume answers it with 200, the original receipt and idempotency_hit: true: no second run, no second charge.

Keep input and webhook_url fixed for the slot, because the same key with a different body is 409 idempotency_conflict and nothing runs. Idempotency keys for AI video APIs covers the other replay cases, and a run that ended failed needs a new key, as Sume Format run failed explains.

Where does the API key go?

In Cloud Secret Manager, through the Firebase CLI. firebase functions:secrets:set SUME_API_KEY asks for the value; bind it with secrets: ["SUME_API_KEY"] and deploy. Only functions that list a secret in their secrets option get it as an environment variable, and a new value needs a redeploy of every function that references it. Firebase says .env files are not a secure way to store API keys, and Sume's docs keep keys out of frontend JavaScript and mobile apps, so never call the API from your Firebase client app.

How long can a scheduled function run?

At most 30 minutes, while a run can take up to 90. Return right after the create: stopping the wait never stops the run or its spend, and Sume delivers the result when the run ends.

From Firebase's GlobalOptions reference and Sume's Runs and results, read 2026-09-28.
LimitValueDocumented by
timeoutSeconds left unset60 secondsFirebase
Maximum for scheduled functions1,800 seconds (30 minutes)Firebase
Long-form video15 to 30 minutes of workSume
Run deadlineAt most 90 minutes after creation, then finalized as failedSume

How do I get the finished video back?

With an HTTP function. Put its URL in communication.webhook_url, and Sume POSTs one signed format.run.terminal receipt there when the run completes or fails. Keep the workspace signing secret as a second Firebase secret and list it in that function's secrets too: a function that doesn't list a secret gets an undefined value, so refuse to verify when it is empty.

Verify the HMAC-SHA256 signature over <timestamp>.<raw_body> against the raw bytes before parsing, record the event, and answer any 2xx within 10 seconds. Signed webhooks for Sume video runs covers the rest of the check: the sume-v1= entries a secret rotation adds, the timestamp window, and dedupe.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume