What is Apex Cursor?
Apex Cursors, introduced as a beta feature in Salesforce Summer ’24, offer a smarter way to handle large SOQL result sets. Instead of returning all records at once—as standard SOQL does—Apex Cursors retrieve data in smaller batches, making the process more efficient and controlled. This incremental data fetching helps reduce memory usage, boost performance, and allows developers to work with very large datasets without hitting governor limits or performance issues.
Benefits of Apex Cursors Over Batch Apex
1. Bidirectional Navigation
Apex Cursors allow developers to move forward and backward through query results, whereas Batch Apex only processes data in one forward direction.
2. Single-Transaction Processing
Cursor operations happen within a single transaction, ensuring consistent data state and reducing the complexity caused by Batch Apex’s multiple-transaction model.
3. Queueable Chaining Support
Cursors can be used inside Queueable Apex and can be chained across multiple jobs. Batch Apex cannot be chained this way.
4. More Predictable Execution
Because cursors work in a single transaction, execution is more predictable compared to Batch Apex, which may run batches at different times depending on system load.
5. Avoids Governor Limits Associated With Batch Apex
Batch Apex has strict limits such as max batch size, number of batches, and long-running transactions. Cursors bypass many of these constraints by fetching data in controlled chunks.
6. Simplified Logic
Cursors require less boilerplate code—no start, execute, or finish methods—making the overall logic easier to write and maintain.
7. Better for Sequential Data Processing
For cases where you need step-by-step or position-based processing, cursors offer finer control compared to Batch Apex’s segmented execution model.
Key Points to Understand About Apex Cursors
1. Apex Cursor Creation
An Apex Cursor is created when you run a SOQL query using Database.getCursor() or Database.getCursorWithBinds(). These methods return a cursor that points to the result set of the query.
2. Fetching Records with Apex Cursor
Use Cursor.fetch(position, count) to retrieve records.
position = starting row
count = number of rows to return
Apex Cursors can return up to 50 million rows, whether the operation is synchronous or asynchronous.
3. Understanding Limits
Apex Cursors follow the same expiration rules as API query cursors. Salesforce provides new limit methods to help monitor cursor usage:
Limits.getApexCursorRows() and Limits.getLimitApexCursorRows()
Limits.getFetchCallsOnApexCursor() and Limits.getLimitFetchCallsOnApexCursor()
These help you track how many rows and fetch calls you’ve used in the current transaction.
4. Exceptions to Know
Apex Cursors can throw two new system exceptions:
System.FatalCursorException – a non-recoverable error
System.TransientCursorException – a temporary issue; the transaction can be retried
Example: Using Apex Cursors in a Queueable Class
To understand how Apex Cursors work in practice, here’s a simple example that demonstrates how to retrieve records in chunks and process them using a Queueable Apex job.
public class QueryChunkingQueueable implements Queueable {
private Database.Cursor locator;
private Integer position;
public QueryChunkingQueueable() {
// Create the cursor using a SOQL query
locator = Database.getCursor(
'SELECT Id FROM Contact WHERE LastActivityDate = LAST_N_DAYS:400'
);
position = 0;
}
public void execute(QueueableContext ctx) {
// Fetch the next 200 records starting from the current position
List<Contact> scope = locator.fetch(position, 200);
// Move the cursor forward based on the number of records returned
position += scope.size();
// Perform your business logic here
// Example: process contacts, update records, etc.
// If more records remain, chain the next queueable job
if (position < locator.getNumRecords()) {
System.enqueueJob(this);
}
}
}
How This Example Works:
- A cursor is created using Database.getCursor(), pointing to the result set of the SOQL query.
- Each job fetches 200 records at a time using fetch(position, 200).
- The position moves forward based on how many records were returned.
- If more rows are available, the Queueable job is chained, and the next chunk is processed.
- This continues until all records are retrieved using the cursor.
This pattern is ideal for large datasets where you want fine-grained control, predictable execution, and the ability to process data in manageable chunks.
Conclusion
Apex Cursors provide a modern, efficient way to handle very large SOQL result sets by fetching data in small, controlled chunks. They reduce memory usage, avoid many Batch Apex limits, support bidirectional navigation, and run predictably within a single transaction. This makes them ideal for large, sequential data processing with simpler code and better performance.

