Governor Limits and Scope in Batch Apex

Governor limits and scope in Batch Apex control how Salesforce divides large data processing work into smaller transactions. Apex runs in a multitenant environment, so the Apex runtime engine enforces governor limits to prevent one org or process from consuming shared resources. When Apex code exceeds a hard governor limit, Salesforce throws a runtime limit exception that cannot be handled by a normal try-catch block.

Batch Apex is used when a job must process more records than a single synchronous Apex transaction can safely handle. Salesforce calls the start() method once, splits the returned records into scopes, calls execute() once for each scope, and finally calls finish() after all scopes are processed.

How Batch Apex Scope Splits Records into Transactions

In Batch Apex, scope means the number of records passed to one execution of the execute() method. Each execution is a separate transaction, so many governor limits reset for every scope. This is the main reason Batch Apex is useful for large data volumes.

  • If no scope size is specified when calling Database.executeBatch, Salesforce uses the default batch size.
  • When the batch uses Database.QueryLocator, the scope size can be set up to 2,000 records.
  • A smaller scope is usually better when each record requires heavy processing, callouts, complex DML, or large heap usage.
  • A larger scope can reduce the number of execute() calls, but it also increases the work done in each transaction.

Example: If a Batch Apex job processes 1,000 records with a scope size of 200, Salesforce divides the work into 5 scopes. The execute() method runs 5 times. Each execute() run gets its own transaction-level governor limits.

Important Governor Limits for Batch Apex Jobs

The following limits are the ones developers most often need while designing Batch Apex jobs. Always verify limits against the Salesforce documentation for the API version and org edition you are working with, because some limits can vary by license, edition, or Salesforce updates.

Batch Apex limitWhat it means in practice
One batch start() method can run at a time in an orgThe start phase of Batch Apex jobs is controlled separately from the execute phase.
Up to 5 queued or active Batch Apex jobsIf more jobs are submitted, they can be placed in the Apex flex queue when available.
Up to 100 Batch Apex jobs in holding status in the Apex flex queueThe flex queue holds jobs before they move into the normal queued or active batch job limit.
Asynchronous Apex executions per 24-hour periodThe daily org limit is commonly 250,000 or the number of applicable user licenses multiplied by 200, whichever is greater. This limit is shared by async Apex types such as Batch Apex, Queueable Apex, Scheduled Apex, and future methods.
Up to 50 million records from Database.QueryLocatorThis is why QueryLocator is commonly used for very large Batch Apex jobs.
Maximum 100 callouts per Apex transactionFor Batch Apex, each start(), each execute() scope, and finish() are separate transactions. The batch class must implement Database.AllowsCallouts to make callouts.

Batch Apex Callout Limit Example with Scope Size

Suppose a batch processes 1,000 records with a scope size of 200. Salesforce creates 5 execute transactions. If the batch class implements Database.AllowsCallouts, each execute transaction can make up to the transaction callout limit. However, you should not design the job to depend on using the full limit for every scope; leave room for retries, authentication callouts, logging, and future maintenance changes.

  • Total records: 1,000
  • Scope size: 200
  • Number of execute transactions: 1,000 ÷ 200 = 5
  • If one callout is made for each record, each execute transaction makes 200 callouts, which exceeds the transaction callout limit.
  • To handle one callout per record safely, choose a smaller scope such as 100 or less, depending on the rest of the transaction logic.

This is why the best scope size depends on what the batch does. A simple update-only batch may use a larger scope, while a callout-heavy batch usually needs a smaller scope.

Batch Apex Scope Syntax with Database.executeBatch

The second argument in Database.executeBatch sets the scope size. In this example, Salesforce passes up to 100 Account records to each execute() call.

</>
Copy
Id jobId = Database.executeBatch(new AccountBatchJob(), 100);

A typical Batch Apex class has three methods: start(), execute(), and finish(). The scope list received by execute() contains only the records assigned to that transaction.

</>
Copy
global class AccountBatchJob implements Database.Batchable<SObject> {

    global Database.QueryLocator start(Database.BatchableContext bc) {
        return Database.getQueryLocator(
            'SELECT Id, Name FROM Account WHERE Name != null'
        );
    }

    global void execute(Database.BatchableContext bc, List<Account> scope) {
        for (Account acc : scope) {
            acc.Description = 'Processed by Batch Apex';
        }
        update scope;
    }

    global void finish(Database.BatchableContext bc) {
        System.debug('Batch Apex job completed.');
    }
}

Batch Apex Limitations Developers Should Remember

  1. Methods declared as future are not allowed inside classes that implement the Database.Batchable interface.
  2. Future methods should not be called from a Batch Apex class. Use Queueable Apex or another suitable asynchronous design only when the chaining pattern is supported and controlled.
  3. For sharing recalculation, Salesforce recommends that the execute() method delete and then recreate Apex managed sharing for the records in the batch.
  4. For each 10,000 AsyncApexJob records, Salesforce creates one additional AsyncApexJob record of type BatchApexWorker for internal use.
  5. A Batch Apex job should avoid making one DML statement or one SOQL query inside a loop. Bulkify the logic inside each scope.

Choosing the Right Scope Size for Batch Apex Governor Limits

Choosing the scope size is a design decision. Start with the amount of work done per record, not only the total number of records. The safest scope is the largest number of records that can be processed without getting close to SOQL, DML, heap, CPU time, callout, and row limits in one transaction.

Batch Apex scenarioScope guidance
Simple field updates with bulk DMLA larger scope can work if CPU time and DML rows remain safe.
One HTTP callout per recordUse a scope size below the per-transaction callout limit. Leave margin for authentication and error handling callouts.
Complex calculations or heavy automationUse a smaller scope to reduce CPU time and heap pressure.
Parent-child processing with many related recordsTest with realistic data volumes because related queries and DML rows can grow quickly.
Jobs that run during busy business hoursUse conservative scope sizes and monitor AsyncApexJob status to avoid queue pressure.

Best Practices to Avoid Batch Apex Governor Limit Errors

  • Bulkify execute logic: collect records in lists and maps, then run SOQL and DML outside loops.
  • Use selective queries: filter records in start() so the batch does not scan unnecessary data.
  • Keep scope size realistic: reduce scope when callouts, automation, formulas, flows, triggers, or related-object queries add transaction cost.
  • Monitor async usage: check Apex Jobs, flex queue status, and daily asynchronous execution consumption for large recurring jobs.
  • Handle partial failures: use controlled error logging so one bad record does not hide the status of the whole batch.
  • Test with large data: small test data may not reveal CPU, heap, row, or callout problems that appear in production volumes.

Official Salesforce References for Batch Apex Limits

Use the official Salesforce documentation when confirming exact limits for implementation work:

FAQ on Governor Limits and Scope in Batch Apex

What is scope in Batch Apex?

Scope in Batch Apex is the group of records passed to one execution of the execute() method. If a job processes 1,000 records with a scope size of 200, Salesforce runs the execute() method 5 times.

Do governor limits reset for every Batch Apex execute method?

Yes. Each execute() call runs as a separate transaction, so transaction-level governor limits reset for every scope. The start() and finish() methods also run in their own transactions.

What is the maximum scope size for Batch Apex?

When a Batch Apex job uses Database.QueryLocator, the scope size passed to Database.executeBatch can be up to 2,000 records. A smaller scope is often better when the logic is callout-heavy or CPU-heavy.

How many records can Database.QueryLocator return in Batch Apex?

Database.QueryLocator can return up to 50 million records for a Batch Apex job. The records are then split into smaller scopes for processing.

Can Batch Apex make callouts?

Yes. A Batch Apex class can make callouts when it implements Database.AllowsCallouts. The normal per-transaction callout limit still applies to each start(), execute(), and finish() transaction.

QA Checklist for This Batch Apex Limits Tutorial

  • Confirm that Batch Apex scope is explained as records per execute() transaction.
  • Verify that the callout example does not assume unlimited callouts across a scope.
  • Check that the limits table separates org-level async limits from transaction-level governor limits.
  • Ensure all Apex examples use bulk DML outside loops.
  • Review Salesforce official documentation before relying on numeric limits in a production design.