subscribe our youtube channel popup

Salesforce Spring ’26: Apex Cursors Explained for Large SOQL Processing

Salesforce developers have always had a tricky problem to solve. How do you process very large SOQL result sets without blowing past governor limits or building heavy Batch Apex jobs? Salesforce’s answer to this is Apex cursors and Apex pagination cursors. They let you work with large volumes of data in smaller, controlled pieces while staying inside a single-transaction boundary.

Let’s break this down clearly, without fluff.

What Are Apex Cursors?

An Apex cursor is a pointer to a SOQL query result, not the result itself.

Instead of pulling thousands or millions of records into memory at once, a cursor lets you fetch only a small slice of records at a time. You decide:

  • where to start
  • how many records to fetch
  • when to move forward or backward

This makes Apex cursors ideal for high-volume, high-resource processing where normal SOQL queries or collections would hit limits.

Apex cursors are:

  • Stateless
  • Designed for large result sets
  • Capable of handling up to 50 million rows per cursor
  • Often combined with chained Queueable Apex as an alternative to Batch Apex

Why Not Just Use Batch Apex?

Batch Apex works, but it has trade-offs:

  • Fixed batch size
  • Less flexibility in navigation
  • More overhead for some use cases

Apex cursors give you more control. You can decide how many records to process per transaction and when to continue, without being locked into batch execution rules.

For package developers and advanced orgs, this is a big deal.

How Apex Cursors Work

Here’s the flow at a high level:

  1. Execute a SOQL query using Database.getCursor() or Database.getCursorWithBinds()
  2. Salesforce returns a cursor, not records
  3. Call Cursor.fetch(position, count) to retrieve a slice of data
  4. Track the position yourself
  5. Continue fetching until all records are processed

Important detail: You are responsible for tracking the offset position. The cursor doesn’t remember it for you.

Simple Apex Cursor Example

What’s happening here:

  • Only 200 records are processed per execution
  • The job re-enqueues itself until all records are done
  • No massive memory load
  • No batch framework required

Governor Limits You Must Know

Even though cursors are powerful, they are not limit-free.

  • Each Cursor.fetch() call:
    • Counts against the SOQL query limit
    • Rows fetched count against the SOQL row limit
  • Use Cursor.getNumRecords() to know total rows available
  • Cursor expiration rules match API query cursors

Cursor-Specific Exceptions

Apex cursors introduce two new system exceptions:

  • System.FatalCursorException
    Non-recoverable error. The transaction fails.
  • System.TransientCursorException
    Temporary issue. The transaction can be retried safely.

This retry behavior makes cursors safer for long-running processes.

What Are Apex Pagination Cursors?

Pagination cursors solve a different problem. They are designed for UI-based pagination, such as:

  • Record list pages
  • Multi-page search results
  • Human-readable data browsing

Instead of processing massive data jobs, pagination cursors focus on consistent page sizes and user experience.

Key Differences: Cursor vs Pagination Cursor

Pagination cursors trade scale for consistency.

How Pagination Cursors Handle Deleted Records

This is where pagination cursors really shine.

If records are deleted after the cursor is created:

  • A standard cursor returns fewer rows
  • A pagination cursor skips deleted rows automatically and still returns a full page

This ensures users always see a consistent number of records per page.

Fetching Pages with Pagination Cursors

To fetch a page:

  1. Call fetchPage(startIndex, pageSize)
  2. Receive a CursorFetchResult object

From that object, you can:

  • getRecords() → retrieve records
  • getDeletedRows() → see how many were skipped
  • getNextIndex() → fetch the next page
  • isDone() → know when pagination is complete

The maximum page size is 2000 rows.

Pagination Cursor Limits and Behavior

  • Each pagination cursor can hold up to 100,000 rows
  • Higher daily cursor limit supports many users
  • Fetch calls still count toward SOQL limits
  • Same retry behavior using System.TransientCursorException

Monitoring Cursor Limits

Salesforce exposes detailed cursor limits through the Limits and OrgLimits classes.

You can track:

  • Cursor rows used
  • Fetch calls
  • Daily cursor counts
  • Pagination cursor usage
  • Shared daily row limits

This makes it easier to build safe, observable large-data logic.

When Should You Use Each?

Use Apex cursors when:

  • Processing millions of records
  • Running backend jobs
  • Replacing or simplifying Batch Apex
  • You need fine-grained control over execution

Use pagination cursors when:

  • Building UI pagination
  • Showing consistent page sizes
  • Supporting many users browsing records
  • Deleted records should not affect page count

Final Thoughts

Apex cursors give Salesforce developers a powerful middle ground.

You get:

  • Large-scale processing
  • Better control than Batch Apex
  • Safer retries
  • Cleaner memory usage

Pagination cursors complement this by solving UI pagination correctly, especially in dynamic datasets where records can change between page loads.

Used properly, cursors let you scale your Apex logic without fighting governor limits—and that’s a win every advanced Salesforce org needs.

FAQs

Can Apex cursors replace Batch Apex completely?
Not entirely. Batch Apex still makes sense for scheduled, chunked processing. Apex cursors are better when you want more control and flexibility using Queueable jobs.

Do Apex cursors store data in memory?
No. They store a pointer to the query result and fetch only the records you request.

What happens if a cursor fetch fails mid-processing?
If the error is transient, Salesforce throws System.TransientCursorException, and the transaction can be retried safely.

Are pagination cursors safe for large data exports?
No. They are optimized for UI pagination, not massive exports or background processing.

Do cursor fetch calls affect SOQL limits?
Yes. Both fetch calls and fetched rows count toward SOQL limits, so careful sizing still matters.

Leave a Reply

Your email address will not be published. Required fields are marked *