Batch Apex in Salesforce: Syntax, Examples and Limits (2026)

Header image: Batch Apex, three job lanes showing one start call, five execute chunks of 200 records and one finish call.

Hey friends,

Batch Apex is an Apex class that implements the Database.Batchable interface so Salesforce can process a large record set in chunks: start collects the records, execute runs once per chunk (200 records by default) in its own transaction, and finish runs once at the end.

Every chunk gets fresh governor limits, which is why batch is the go-to tool for nightly cleanups, archiving and mass updates. All the facts here come from the Apex Developer Guide pages Batch Apex and Use Batch Apex. It's also core material for the Platform Developer II exam, under Process Automation, Logic, and Integration and Performance.

What is the Batch Apex syntax?

A batch class implements three methods. Here's a complete one that closes stale open cases. Notice the class is public, because every Batchable method must be public or global.

CloseStaleCases.cls
1public with sharing class CloseStaleCases implements Database.Batchable<sObject> {
2    public Database.QueryLocator start(Database.BatchableContext bc) {
3        return Database.getQueryLocator(
4            'SELECT Id, Status FROM Case WHERE IsClosed = false AND LastModifiedDate < LAST_N_DAYS:90');
5    }
6    public void execute(Database.BatchableContext bc, List<Case> scope) {
7        for (Case c : scope) { c.Status = 'Closed'; }
8        update scope;
9    }
10    public void finish(Database.BatchableContext bc) {
11        AsyncApexJob job = [SELECT Status, NumberOfErrors, TotalJobItems
12                            FROM AsyncApexJob WHERE Id = :bc.getJobId()];
13        System.debug('Done: ' + job.TotalJobItems + ' batches, ' + job.NumberOfErrors + ' errors');
14    }
15}
MethodRunsWhat goes in it
startOnce, at the beginningReturn a Database.QueryLocator or an Iterable with the records to process
executeOnce per chunkThe real work on that chunk of records, in its own transaction
finishOnce, at the endPost-processing: a summary email, logging, or starting the next job

How do you run a batch job?

Call Database.executeBatch. It returns the ID of the AsyncApexJob record, which you can query for progress or pass to System.abortJob. Run it from Anonymous Apex in the Developer Console or VS Code to try it out:

Anonymous.apex
1Id jobId = Database.executeBatch(new CloseStaleCases());       // 200 per chunk
2Id smaller = Database.executeBatch(new CloseStaleCases(), 50); // 50 per chunk

The second argument is the scope (batch size). Lower it when each record needs a lot of work, like callouts or complex DML. With a QueryLocator the max is 2,000. With an Iterable there's no upper limit, but other limits will catch you. Salesforce says the optimal scope size is a factor of 2,000, like 100, 200 or 400.

To watch or stop a job, go to Setup, type Apex Jobs in Quick Find, and open Apex Jobs.

Is each batch its own transaction?

Yes. A job with 1,000 records and the default scope runs as five transactions of 200. Governor limits reset for each one, and if the second chunk fails, the first chunk's updates stay committed. Nothing rolls back across chunks.

That's the most tested idea in this topic. If a scenario needs all-or-nothing across the whole data set, batch alone doesn't give you that. Track failures (a stateful counter or an error log object) and handle them in finish.

Also: the order batches run in isn't guaranteed. Don't write logic that assumes chunk 1 finishes before chunk 2.

What are the Batch Apex limits?

Batch Apex limits table: default batch size 200, max QueryLocator scope 2,000, 50 million records per QueryLocator, 5 jobs queued or active, 100 jobs in the flex queue, 100 callouts per method.

The handful of batch numbers worth memorizing.

  • Up to 5 batch jobs can be queued or active at once. Extra submissions sit in the Apex flex queue with status Holding, up to 100 jobs. You can reorder Holding jobs in Setup (Apex Flex Queue) or with System.FlexQueue methods.
  • Only one batch job's start method runs at a time in an org.
  • A QueryLocator can return up to 50 million records. Past that, the job fails immediately.
  • Async method executions per 24 hours: 250,000 or 200 times your user licenses, whichever is greater, shared by batch, Queueable, scheduled Apex and future methods. Start, execute and finish each count.
  • You can't call a future method from a batch class. The limits page lists future calls as 0 in batch context.
  • Each of start, execute and finish can make up to 100 callouts, but only if the class implements Database.AllowsCallouts.

For the full table, see Execution Governors and Limits.

Cert Lab by Nick Frates

Let the Platform Developer II Cert Lab test you to success

Scenario questions on Batch Apex, Queueable, triggers and large data volumes, with an explanation for every option.

Start the Platform Developer II Cert Lab for $9.99 →

Not ready to buy? Try a free lesson and see how fast you learn.

What do the batch job statuses mean?

StatusMeaning
HoldingSubmitted and waiting in the flex queue
QueuedWaiting to run
Preparingstart is running
Processingexecute chunks are running
CompletedFinished, with or without failures
AbortedStopped by a user
FailedSystem failure

How do you test Batch Apex?

Wrap Database.executeBatch in Test.startTest() and Test.stopTest(). When stopTest runs, the async job runs synchronously, so your asserts after it see the results. A test can only run one execute, so insert no more records than your scope (200 by default).

CloseStaleCasesTest.cls
1@IsTest
2private class CloseStaleCasesTest {
3    @IsTest static void closesOldCases() {
4        insert new Case(Subject = 'Old', Status = 'New');
5        Test.startTest();
6        Database.executeBatch(new CloseStaleCases());
7        Test.stopTest();
8        // assert on the results here
9    }
10}

One catch in this example: a case inserted in the test has today's LastModifiedDate, so the 90-day filter skips it. In real tests, pass the query or cutoff date into the constructor so the test can control it. My Apex test class guide covers test data patterns.

How do you chain batch jobs?

Call Database.executeBatch (or System.scheduleBatch) from the finish method. The next job starts after the current one finishes, which gives you strict sequential order, like archive first, then delete. Salesforce suggests chaining over fixed-interval schedules when processing times vary, and Queueable Apex when you don't need batch-style chunking.

Related reads: Database.Stateful to keep totals across chunks, QueryLocator vs Iterable for the start method, and how to schedule Batch Apex.

What Batch Apex mistakes should you avoid?

  • Starting batch jobs from a trigger without a guard. A data load can fire it many times and blow through the 5-active and 100-Holding limits.
  • Relationship subqueries in the QueryLocator. Salesforce says they force a slower, non-chunking implementation. Query child records inside execute instead.
  • Expecting static variables to survive between chunks. They reset. Only instance variables survive, and only with Database.Stateful.
  • Callouts without Database.AllowsCallouts. Add the interface to the class definition.
  • Assuming a failed chunk rolls back the job. Earlier chunks stay committed.
  • Non-idempotent callouts. Salesforce notes a chunk can be rolled back and rerun after maintenance downtime, so callouts must be safe to retry.

Quick backstory. I loved Focus on Force and used it on my way to 17 Salesforce certifications. At first I read the study guide cover to cover, then took the tests. What I eventually noticed: I learned way faster when I skipped ahead and took practice tests rapid-fire, reading the explanation for every miss. So I built Cert Lab around fast testing, with up-to-date doc links in every explanation. It's designed to test you to success.

FAQ

What is the default batch size in Batch Apex?

200 records per execute call. Pass a second argument to Database.executeBatch to change it, up to 2,000 with a QueryLocator.

How many batch jobs can run at once?

Five can be queued or active. Up to 100 more can wait in the Apex flex queue with Holding status.

Batch Apex or Queueable Apex?

Batch when you need to chunk a big record set with fresh limits per chunk. Queueable for a single async job, chaining and complex types. My asynchronous Apex guide compares all four async tools.

Can Batch Apex make callouts?

Yes, if the class implements Database.AllowsCallouts. Each of start, execute and finish can make up to 100 callouts.

Where should you go next?

Nick Frates, a senior Salesforce developer with 17 certifications, built the Platform Developer II Cert Lab at nickfrates.com: rapid-fire practice questions for every exam section, each answer explained.

Cert Lab by Nick Frates

Let the Platform Developer II Cert Lab test you to success

Lessons, flashcards, a quiz for each of the five exam sections and full practice exams, every answer explained.

Start the Platform Developer II Cert Lab for $9.99 →

Not ready to buy? Try a free lesson and see how fast you learn.

Hope this helps!

Best,

Nick

Keep reading

See all posts →

Salesforce Saturdays

Join 1,033 Salesforce pros getting one 10-minute tip on Salesforce tech or career growth every Saturday.