Apex Cursors Explained for Large SOQL Processing

Introduction

Ever hit a SOQL limit trying to process millions of records in Salesforce Apex? You’re not alone.

Processing large datasets in Apex used to mean complex Batch Apex jobs or work-arounds with OFFSET pagination — often slow, clunky, and full of governor limit hurdles.

But now, Apex Cursors are here to change the game.

In this article, you’ll learn:

  • What Apex cursors are and how they work
  • Why they matter for large SOQL processing
  • Real limits you must watch out for
  • How they compare to Batch Apex
  • Simple code examples, best practices, and FAQs
Understanding Apex cursors salesforce in depth

Table of Contents


What Are Apex Cursors?

Apex Cursors are pointers to a SOQL query result, not the full result itself. Instead of retrieving all rows at once, a cursor lets you fetch smaller chunks of records on demand — just like scrolling through pages in a long book without loading the entire book at once.

This makes Apex cursors ideal for processing large datasets efficiently with controlled memory and less risk of running into platform limits.

👉 Imagine trying to load a million rows into memory all at once — dangerous for Apex! A cursor avoids that by fetching only what you need.


Why Apex Cursors Matter in Large SOQL Queries

Traditionally, developers used Batch Apex or SOQL with OFFSET to handle big data. But:

  • OFFSET stops working effectively past 2,000 rows
  • Batch Apex introduces overhead and rigid structures
  • Memory and governor limits still bite you

Apex Cursors solve these because they:

✔ Keep memory usage low
✔ Let you control fetch size
✔ Work well with Queueable Apex
✔ Can process up to 50 million rows per cursor

salesforce

Read this Salesforce Documentation on apex cursor here.


How Apex Cursors Work — Simple Flow + Example

Typical Cursor Flow

  1. Create a cursor using Database.getCursor()
  2. Fetch chunks of data using cursor.fetch(position, count)
  3. Track position yourself (cursor doesn’t track it internally)
  4. Repeat until done

Real-life analogy: Think of a movie buffer streaming only 10 seconds at a time instead of downloading the whole movie first.


Sample Apex Cursor Code

Database.Cursor cursor = Database.getCursor(
    'SELECT Id, Name FROM Account ORDER BY CreatedDate'
);

Integer position = 0;
Integer batchSize = 500;

while(true) {
    // Fetch next batch of records
    List<SObject> records = cursor.fetch(position, batchSize);

    if (records.isEmpty()) {
        break;
    }

    // Process the records
    for (SObject r : records) {
        // your logic here
    }

    position += batchSize;
}

Note: You must manage the position (index) yourself.

Key Limits You Must Know (aka apex cursors limit)

Salesforce imposes limits to protect platform performance. These are important if you work with large SOQL operations.

Limit TypeValue
Max rows per cursor50 million records
Max fetch calls per transaction10 fetch calls
Max cursors per day10,000
Total rows fetched per day100 million
Fetched rows count toward SOQL limitsYes (counts like regular SOQL)

Quick take: You can handle huge result sets — but you still must design your logic to stay within these limits. For example, each .fetch() counts toward your SOQL query budget.


Apex Cursors vs Batch Apex — Quick Comparison

FeatureBatch ApexApex Cursors
StructureFixed (Start → Execute → Finish)Flexible
ScalabilityGoodExcellent (up to 50M rows)
Memory UseHigherLower
Position TrackingManaged by frameworkYou manage it
Use CaseStandard bulk async jobsHigh-control large processing

When to choose which?

  • Use Batch Apex for scheduled, high-volume jobs with simple needs.
  • Use Apex Cursors when you need fine-grained, controlled chunking, or advanced navigation through results.

Best Practices for Large SOQL Processing

✔ Always use cursors with Queueable Apex or async logic for extended processing.
✔ Avoid large .fetch() sizes — keep them manageable (500–2,000 records).
✔ Use Indexes and selective filters in your SOQL to avoid performance bottlenecks.
✔ Track your cursor positions persistently if needed across transactions.
✔ Handle exceptions like System.TransientCursorException and retry safely.


FAQs

1. What is the difference between Apex Cursors and Pagination Cursors?

Apex Cursors are for server-side data processing, while Pagination Cursors are optimized for UI pagination with consistent page sizes even when records change.


2. Do Apex Cursors load all records into memory?

No — they fetch only the requested chunk. The cursor itself maintains a pointer and does not store the entire dataset in heap.


3. Can Apex Cursors replace Batch Apex entirely?

Not always. Batch Apex is still great for scheduled bulk operations. Cursors shine when you want custom control over data navigation and chunk sizes.


4. Do cursor fetch calls count toward SOQL limits?

Yes — both fetch calls and rows returned count toward your SOQL governor limits.


5. What happens if a fetch fails?

Salesforce can throw exceptions like System.TransientCursorException — indicating that the failure is retryable. Handle these with safe retry logic.


Conclusion

Apex cursors are a powerful new way to process large SOQL results in Salesforce — giving developers the ability to chunk query results efficiently without overwhelming memory or hitting hard limits. Used with Queueable Apex or in custom large-data flows, cursors can dramatically simplify and scale your logic.