Scheduled Apex is a class that implements Schedulable and runs on a cron schedule. You register the class with a cron expression — “every day at 2 AM,” “every Sunday at midnight,” “on the 1st of the month” — and Salesforce invokes it at those times. It’s the Salesforce equivalent of a cron job.
The minimal shape
public class NightlyCleanup implements Schedulable {
public void execute(SchedulableContext ctx) {
// The work you want to run on schedule
Database.executeBatch(new StaleCaseCleanupBatch(), 200);
}
}
The interface has one method, execute(SchedulableContext). Salesforce calls it at every scheduled fire time.
Scheduling a class
Two options.
From Apex, using System.schedule
String cronExp = '0 0 2 * * ?'; // 2:00 AM every day
String jobName = 'NightlyCleanup';
Id jobId = System.schedule(jobName, cronExp, new NightlyCleanup());
System.schedule(name, cronExpression, instance) returns the JobId of the scheduled job. The job appears in Setup → Scheduled Jobs.
From the UI
Setup → Apex Classes → Schedule Apex lets admins pick a class and define the schedule with a form. No code required.
The Salesforce cron expression
Salesforce uses seconds-precision cron with seven fields:
Seconds Minutes Hours Day-of-Month Month Day-of-Week [Year]
| Position | Allowed values | Special chars |
|---|---|---|
| Seconds | 0–59 | , - * / |
| Minutes | 0–59 | , - * / |
| Hours | 0–23 | , - * / |
| Day-of-Month | 1–31 | , - * / ? L W |
| Month | 1–12 or JAN–DEC | , - * / |
| Day-of-Week | 1–7 or SUN–SAT | , - * ? / L # |
| Year (optional) | 1970–2099 | , - * / |
Common expressions:
0 0 2 * * ? Every day at 2:00 AM
0 30 4 * * ? Every day at 4:30 AM
0 0 12 ? * SUN Every Sunday at noon
0 0 0 1 * ? First day of every month at midnight
0 0 9 ? * MON-FRI Every weekday at 9 AM
Note: Day-of-Month and Day-of-Week can’t both have explicit values — one of them must be ?.
What you’d typically run
Pure Schedulable code is rare in real orgs. The pattern is Schedulable → Batch or Schedulable → Queueable:
public class NightlyCleanup implements Schedulable {
public void execute(SchedulableContext ctx) {
Database.executeBatch(new StaleCaseCleanupBatch(), 200);
}
}
Reasons to wrap a Batch in a Schedulable:
- Schedulable doesn’t get the higher async governor limits — Batch does.
- Schedulable can’t directly make callouts; Queueable / Batch can.
- You almost always want to process records, and Batch handles volume natively.
Limits to remember
| Limit | Value |
|---|---|
| Max scheduled jobs at one time | 100 |
| Max simultaneous Schedulable executions | 5 |
| Cron precision | 1 second |
| Earliest valid year | 1970 (per the cron implementation) |
Aborting a scheduled job
System.abortJob(jobId);
Or via Setup → Scheduled Jobs → Delete.
Common interview follow-ups
- Which interface does Scheduled Apex implement? —
Schedulable. One mandatory method:execute(SchedulableContext). - Can I make callouts from Schedulable? — Not directly. The Schedulable
executeruns without callout support; hand off to@future(callout=true)or a Queueable withDatabase.AllowsCallouts. - Can I edit the class while it’s scheduled? — No. Scheduled classes are locked from edits until you unschedule all references. Same for any class referenced from the schedulable.
Verified against: Apex Developer Guide — Scheduled Apex. Last reviewed 2026-05-17 for Spring ‘26.