← Back to Platform & Technical
PLAT-013 Platform & Technical 15 min read For: Architects

Scheduled Apex vs Scheduled Flows: Tradeoffs at Scale

How Scheduled Apex and Scheduled Flows each work under the hood, where they share a queue and where they don't, which option wins for different use cases, and how to manage the scheduled job limits that bite enterprise orgs.

VS

Vishal Sharma

Salesforce Architecture Specialist · Updated May 2026

What You'll Learn

How Scheduled Apex and Scheduled Flows each work under the hood, where they share a queue and where they don't, which option wins for different use cases, and how to manage the scheduled job limits that bite enterprise orgs.

Two Mechanisms, One Shared Constraint

Salesforce offers two ways to schedule background work: Scheduled Apex (the Schedulable interface) and Scheduled Flows (the Scheduled trigger type). Both appear in the Scheduled Jobs list under Setup, and both compete for the same org-wide limit of 100 total scheduled jobs.

This shared limit is the most important fact for architects managing complex orgs. Salesforce AppExchange packages often consume scheduled jobs without visibility. An org with 15 installed packages can exhaust the 100-job limit before custom development has even started.

Insight The 100 scheduled job limit applies to all scheduled items combined: Scheduled Apex, Scheduled Flows, and managed package jobs. Audit your org's Scheduled Jobs before designing a new scheduled automation. Use SELECT Id, JobType, CronJobDetail.Name FROM CronTrigger to inventory existing jobs.

Scheduled Apex: Mechanics and Usage

A class implementing Schedulable can be registered for execution at specific times using a cron expression. The execute(SchedulableContext sc) method runs in its own transaction with full governor limits.

Scheduled Apex almost always delegates work to a Batch Apex class — the schedulable class itself processes zero records but calls Database.executeBatch() to spawn the batch job. The schedulable class is a trigger; the batch class is the worker.

// Schedulable class — triggers a batch job
public class DailyLeadScoreJob implements Schedulable {
  public void execute(SchedulableContext sc) {
    Database.executeBatch(new LeadScoreBatch(), 200);
  }
}

// Scheduling programmatically
String cron = '0 0 2 * * ?';  // 2 AM daily
System.schedule('Daily Lead Score', cron, new DailyLeadScoreJob());

// Scheduling via Setup > Apex Classes > Schedule Apex (no code needed)
// SOQL to check scheduled jobs
List<CronTrigger> jobs = [
  SELECT Id, CronJobDetail.Name, NextFireTime, State
  FROM CronTrigger
  WHERE CronJobDetail.JobType = '7'  // 7 = Schedulable
];
Key Point Scheduled Apex executes at approximately the scheduled time — not precisely. Salesforce processes the scheduled queue when infrastructure capacity allows. In heavily loaded orgs or during peak periods, a job scheduled for 2:00 AM may run at 2:05 AM or later. Do not design time-sensitive automations that require second-level precision.

Scheduled Flows: When They Make Sense

A Scheduled-Trigger Flow runs on a schedule against a batch of records matching specified criteria. Configure it from Flow Builder: choose "Schedule-Triggered Flow," set the frequency (once, daily, weekly), and the record filter criteria. Salesforce handles the scheduling and batching automatically.

Scheduled Flows shine for simple record-update automations that business users need to configure or modify without developer involvement. A nightly flow that updates Account tier based on last 90 days opportunity value is a good fit. No code, no deployment, visible to admins in Flow Builder.

The trade-offs versus Scheduled Apex:

  • Volume limit: Scheduled Flows have a practical upper limit of ~250,000 records per scheduled batch. For larger populations, Batch Apex handles them more reliably.
  • Error handling: Flow errors surface as fault paths or email notifications. Apex provides structured try/catch logging and custom error handling.
  • Callouts: Scheduled Flows cannot make external HTTP callouts. If your scheduled job needs to call an external API, Scheduled Apex is required.
  • Observability: Scheduled Apex job history is available in Setup > Apex Jobs with status, error messages, and duration. Scheduled Flow execution history is visible in Flow Interviews but less detailed.

Job Limit Management at Enterprise Scale

Enterprise orgs with 20+ managed packages routinely approach the 100-job ceiling. Management strategies:

  • Consolidate jobs: Combine multiple low-volume Scheduled Apex jobs into a single dispatcher class that runs multiple operations sequentially. One job slot, multiple operations.
  • Use Batch Apex self-chaining: Instead of scheduling a batch to run every 15 minutes, use the finish() method to re-schedule itself. One job slot that runs continuously.
  • Replace old jobs: Audit and retire scheduled jobs from inactive managed packages or obsolete automation before adding new ones.
  • Review package jobs: Contact AppExchange vendors about scheduled job consumption. Some vendors allow disabling package-internal scheduled jobs when not needed.
// Batch Apex self-chaining pattern (avoids scheduled job slot)
public class ContinuousLeadScoreBatch implements Database.Batchable<SObject>,
                                                  Database.Stateful {
  public Database.QueryLocator start(Database.BatchableContext bc) {
    return Database.getQueryLocator([SELECT Id FROM Lead WHERE IsConverted = false]);
  }

  public void execute(Database.BatchableContext bc, List<Lead> scope) {
    // process scope
  }

  public void finish(Database.BatchableContext bc) {
    // Re-queue itself to run again in 15 minutes
    System.schedule('LeadScore-' + System.now().getTime(),
      buildCron(15), new DailyLeadScoreJob());
  }
}
Warning Self-chaining batch jobs must clean up their own previous scheduled job entries or the scheduled job count grows unboundedly. Use System.abortJob(previousJobId) in the finish() method before scheduling the next run. Neglecting this creates ghost jobs that count against the 100-job limit.

Decision Framework: Apex or Flow?

Use Scheduled Flow when: the operation is simple record updates, the record population is under 250K, no external callouts are needed, and business users need to configure or modify the schedule.

Use Scheduled Apex when: the operation requires callouts, complex Apex logic, error handling with retry, volumes exceed 250K records, or the job must chain into Batch Apex for processing.

Key Takeaways

  • Both Scheduled Apex and Scheduled Flows share the 100-job org limit — managed packages consume this limit invisibly.
  • Scheduled Apex always delegates bulk work to Batch Apex — the Schedulable class is just a trigger.
  • Scheduled Flows cannot make external callouts — use Apex for any job that hits a remote API.
  • Job scheduling is not precise — Salesforce delivers jobs approximately at the scheduled time, not exactly.
  • Self-chaining batch jobs must clean up previous scheduled job entries to avoid exhausting the job limit.
  • Audit your org's job inventory via SOQL on CronTrigger before designing new scheduled automation.

Check Your Understanding

1. What is the org-wide limit for total scheduled jobs, shared across Scheduled Apex, Scheduled Flows, and packages?

A. 500 total scheduled jobs
B. 100 total scheduled jobs
C. 50 per job type (Apex and Flow separately)

2. A scheduled automation needs to call an external pricing API every night. Which tool must you use?

A. Scheduled Flow with a REST HTTP Action step
B. Scheduled Apex — Scheduled Flows cannot make external callouts
C. Either tool — both support outbound callouts when External Services is configured

3. When using a self-chaining Batch Apex pattern to avoid scheduled job slots, what must happen in the finish() method?

A. Call Database.executeBatch() to start the next batch immediately
B. Abort the previous scheduled job entry before scheduling the next run, to prevent unbounded job accumulation
C. Nothing — Salesforce automatically cleans up previous job entries

Discussion & Feedback