Orientation
The six categories
Category 01
Per-Transaction Apex Limits
The headline governor limits. Counted per transaction and reset to zero for the next one. Asynchronous contexts (Batch, Future, Queueable, Scheduled) get the roomier budgets where the two columns differ.
| Limit | Synchronous | Asynchronous |
|---|---|---|
| SOQL queries issued 1 | 100 | 200 |
| Records retrieved by a single SOQL query | 50,000 | same |
Records retrieved by Database.getQueryLocator | 10,000 | same |
| SOSL queries issued | 20 | same |
| Records retrieved by a single SOSL query | 2,000 | same |
| DML statements issued | 150 | same |
Records processed by DML, Approval.process, or emptyRecycleBin | 10,000 | same |
| Stack depth for recursive trigger fire (insert/update/delete) | 16 | same |
| Callouts (HTTP requests or web service calls) | 100 | same |
| Cumulative timeout for all callouts | 120 s | same |
@future methods per Apex invocation | 50 | same |
Jobs added to the queue with System.enqueueJob | 50 | 1 |
sendEmail methods | 10 | same |
| Heap size | 6 MB | 12 MB |
| CPU time on Salesforce servers | 10,000 ms | 60,000 ms |
| Maximum execution time per transaction | 10 min | same |
| Push-notification method calls per transaction | 10 | same |
| Push notifications per method call | 2,000 | same |
1 Each parent-child relationship subquery counts as an extra query; subqueries have a limit of 3× the top-level number (300). SOQL on custom metadata types doesn't count toward the limit. CPU time excludes time spent waiting on the database and callouts.
Category 02
Per-Transaction Certified Managed Package Limits
Code from a certified managed package (built by a Salesforce ISV partner that passed security review) gets its own separate allowance for most per-transaction limits — so a transaction spanning your code plus a package can exceed a single org limit.
| Behaviour | What it means |
|---|---|
| Separate pools | The package gets its own 100 SOQL, 150 DML, etc., in addition to your org's native limits. |
| Shared (counted once) | Heap size, CPU time, max transaction time, and a few others are shared across the whole transaction regardless of packages. |
| Namespaces | No limit on how many certified namespaces are invoked in one transaction. |
| Non-certified packages | Uncertified or non-ISV package code has no separate limits — its usage counts against your org's totals. |
Category 03
Lightning Platform Apex Limits
Not tied to a single transaction. The platform enforces these org-wide, frequently over a rolling 24-hour window.
| Limit | Value |
|---|---|
| Async Apex executions per 24 h (Batch + Future + Queueable + Scheduled) | 250,000 or licenses × 200 whichever is greater |
| Concurrent synchronous long-running transactions (> 5 s) per org | 10 |
| Apex classes scheduled concurrently | 100 |
| Batch Apex jobs in the flex queue (Holding status) | 100 |
| Batch Apex jobs Queued or Active concurrently | 5 |
Batch Apex start method concurrent executions | 1 |
| Query cursors open concurrently per user | 50 |
Category 04
Static Apex Limits
Fixed values applied across all transactions.
| Limit | Value |
|---|---|
| Default callout timeout (HTTP / web service) | 10 s |
| Maximum callout request or response size | 6 MB sync / 12 MB async |
| Maximum SOQL query run time before Salesforce cancels it | 120 s |
| Class & trigger code units in one Apex deployment | 7,500 |
| Apex trigger batch size | 200 |
| For-loop list batch size | 200 |
Records returned for a Batch Apex Database.QueryLocator | 50 million |
Category 05
Size-Specific Apex Limits
Caps on the size of the code itself.
| Limit | Value | Type |
|---|---|---|
| Total Apex code across the whole org | 6 MB | soft |
| Characters in a single class | 1,000,000 | hard |
| Characters in a single trigger | 1,000,000 | hard |
| Compiled size of a single method | 65,535 bytecode | hard |
The 6 MB org-wide code limit excludes test classes and managed-package code, and can be raised by Salesforce Support.
Category 06
Miscellaneous Apex Limits
| Limit | Value |
|---|---|
SOQL OFFSET clause maximum rows | 2,000 |
| Aggregate / relationship subqueries per SOQL query | 300 |
Records per Database.getQueryLocator (non-batch) | 10,000 |
| Items in a List / Set / Map collection | limited only by heap |
Category 06 · email & push
Email & Push Notification Limits
| Limit | Value |
|---|---|
Messaging.sendEmail invocations per transaction | 10 |
| Single emails to external addresses via Apex, per day | 5,000 |
| Push-notification method calls per transaction | 10 |
| Notifications per push-notification method call | 2,000 |
Mass-email recipient limits are edition-dependent and counted separately from the Apex single-email allocation.
Adjacent allocations
Other platform limits people lump in
These aren't Apex per-transaction governor limits, but they trip people up the same way, so they're worth listing alongside.
| Limit | Value |
|---|---|
| Web-to-Lead | 500 / day |
| Web-to-Case | 5,000 / day |
| Email-to-Case | 2,500 / day |
| Paused Flow interviews per org | 50,000 |
| Sharing rules per object | 300 |
| Role hierarchy | 500 default · ~10,000 max |
At runtime
Read any limit with the Limits class
Every meter has a paired "used" and "ceiling" method. Log them or back off before you trip.
| Meter | Used so far | The cap |
|---|---|---|
| SOQL queries | getQueries() | getLimitQueries() |
| SOQL query rows | getQueryRows() | getLimitQueryRows() |
| DML statements | getDmlStatements() | getLimitDmlStatements() |
| DML rows | getDmlRows() | getLimitDmlRows() |
| SOSL queries | getSoslQueries() | getLimitSoslQueries() |
| CPU time | getCpuTime() | getLimitCpuTime() |
| Heap size | getHeapSize() | getLimitHeapSize() |
| Callouts | getCallouts() | getLimitCallouts() |
| @future calls | getFutureCalls() | getLimitFutureCalls() |
| Queueable jobs | getQueueableJobs() | getLimitQueueableJobs() |
| Email invocations | getEmailInvocations() | getLimitEmailInvocations() |
| Aggregate / subqueries | getAggregateQueries() | getLimitAggregateQueries() |
// Live usage vs ceiling for the current transaction
System.debug('SOQL: ' + Limits.getQueries() + ' / ' + Limits.getLimitQueries());
System.debug('DML: ' + Limits.getDmlStatements() + ' / ' + Limits.getLimitDmlStatements());
System.debug('CPU: ' + Limits.getCpuTime() + ' / ' + Limits.getLimitCpuTime());When you trip one
The exception message → which limit
A LimitException can't be caught to keep running past the cap — the whole transaction rolls back. Here's how to read the message.
| Runtime message | The limit it hit |
|---|---|
| Too many SOQL queries: 101 | 100 SOQL queries (sync) |
| Too many query rows: 50001 | 50,000 records from SOQL |
| Too many DML statements: 151 | 150 DML statements |
| Too many DML rows: 10001 | 10,000 records via DML |
| Apex CPU time limit exceeded | 10 s sync / 60 s async CPU |
| Apex heap size too large | 6 MB sync / 12 MB async heap |
| Too many SOSL queries: 21 | 20 SOSL queries |
| Too many callouts: 101 | 100 callouts |
| Too many future calls: 51 | 50 @future invocations |
| Maximum stack depth reached: 17 | 16 recursive trigger levels |

