Salesforce caps the number of active scheduled Apex jobs at 100 per org at any one time. Once you hit 100, calls to System.schedule(...) throw System.AsyncException: You've exceeded the maximum number of 100 scheduled Apex jobs. The cap counts both jobs scheduled programmatically and those scheduled through the Setup UI.
What counts toward the 100
| Counts? | Item |
|---|---|
| Yes | Schedulable classes scheduled via System.schedule |
| Yes | Schedulable classes scheduled via Setup → Apex Classes → Schedule Apex |
| Yes | Recurring scheduled jobs (each cron entry is one job) |
| Yes | Scheduled flows (in some orgs) |
| No | Batch jobs in the Apex Flex Queue (different limit: 100) |
| No | Queueable jobs (different limit: 50 enqueued per transaction) |
| No | @future invocations (different limit: 50 per transaction) |
Counting your current scheduled jobs
Integer scheduled = [
SELECT COUNT() FROM CronTrigger WHERE State = 'WAITING'
];
System.debug('Scheduled jobs: ' + scheduled);
The CronTrigger object exposes every scheduled job. State = 'WAITING' filters to jobs that haven’t fired yet (i.e., are taking up a slot for the next run).
The dispatcher pattern
If you legitimately need more than 100 distinct scheduled runs, don’t schedule each separately. Schedule one dispatcher and let it decide what to run:
public class HourlyDispatcher implements Schedulable {
public void execute(SchedulableContext ctx) {
Datetime now = Datetime.now();
Integer hour = now.hour();
Integer dayOfWeek = Integer.valueOf(now.format('u')); // 1=Mon..7=Sun
if (hour == 2) Database.executeBatch(new NightlyCleanupBatch());
if (hour == 6 && dayOfWeek == 1) Database.executeBatch(new WeeklyDigest());
if (hour == 0 && now.day() == 1) Database.executeBatch(new MonthlyArchive());
}
}
// Schedule once, every hour:
System.schedule('Hourly Dispatcher', '0 0 * * * ?', new HourlyDispatcher());
One scheduled job, many effective recurring tasks. Adding a new daily job is a code change instead of a Setup change — usually a net win for maintainability.
Adjacent limits to keep straight
| Limit | Value | Tracks |
|---|---|---|
| Active scheduled Apex jobs | 100 | System.schedule registrations |
| Simultaneous Schedulable executions | 5 | How many Schedulable execute() methods can run at once |
| Active + queued Batch jobs | 5 | Concurrent Batch execution |
| Apex Flex Queue size | 100 | Batch jobs in Holding |
| Future invocations per transaction | 50 | @future calls per request |
| Queueable jobs per transaction (sync) | 50 | System.enqueueJob per request |
| Queueable chain depth (single-chain) | Effectively unlimited in production, but only 1 chained Queueable at a time per parent |
What happens when you hit 100
try {
System.schedule('Job 101', '0 0 3 * * ?', new MyJob());
} catch (AsyncException e) {
// 'You've exceeded the maximum number of 100 scheduled Apex jobs.'
}
Either abort an older job (System.abortJob(id)) or refactor to a dispatcher.
Common interview follow-ups
- Where do I see the current count? — Setup → Scheduled Jobs, or
SELECT COUNT() FROM CronTrigger WHERE State = 'WAITING'. - Does this cap apply to Batch jobs? — No, that’s a separate limit (5 active + 100 in Flex Queue).
- Can I increase the limit? — Not via support. Use the dispatcher pattern.
Verified against: Apex Developer Guide — Apex Scheduler Limits, Execution Governors and Limits. Last reviewed 2026-05-17.