Skip to main content

SF-0369 · Concept · Medium

Explain about Iterable in batch apex?

✓ Verified by Vikas Singhal · Last reviewed 5/17/2026 · Updated for Spring '26

Iterable<sObject> is the alternative return type for a batch start method. Use it when your dataset can’t be expressed as a single SOQL query — for example, a list merged from multiple queries, records computed from an external API, or a custom in-memory collection.

The trade-off: capped at 50,000 records (standard SOQL governor applies), versus 50 million for QueryLocator.

The pattern

public class CustomSourceBatch implements Database.Batchable<sObject> {

    public Iterable<sObject> start(Database.BatchableContext ctx) {
        // Build the list however you want
        List<Account> activeAccounts = [SELECT Id FROM Account WHERE Active__c = true];
        List<Account> recentAccounts = [SELECT Id FROM Account WHERE CreatedDate = LAST_N_DAYS:7];

        // Merge + dedupe
        Map<Id, Account> merged = new Map<Id, Account>();
        for (Account a : activeAccounts) merged.put(a.Id, a);
        for (Account a : recentAccounts) merged.put(a.Id, a);

        return merged.values();
    }

    public void execute(Database.BatchableContext ctx, List<Account> scope) {
        // ... process the chunk
    }

    public void finish(Database.BatchableContext ctx) {}
}

merged.values() returns a List<Account>, which satisfies Iterable<sObject> (every Apex list implements Iterable).

A truly custom iterator

For very custom data sources, you can build your own iterator:

public class CountdownIterator implements Iterator<sObject>, Iterable<sObject> {
    private Integer cur;
    public CountdownIterator(Integer start) { this.cur = start; }
    public Boolean hasNext() { return cur > 0; }
    public sObject next() {
        Account a = new Account(Name = 'Account ' + cur);
        cur--;
        return a;
    }
    public Iterator<sObject> iterator() { return this; }
}

public class CountdownBatch implements Database.Batchable<sObject> {
    public Iterable<sObject> start(Database.BatchableContext ctx) {
        return new CountdownIterator(100);
    }
    // ...
}

This is rare in practice — most teams use the simpler “build a list, return it” pattern.

QueryLocator vs Iterable side-by-side

Database.QueryLocatorIterable<sObject>
Max records50,000,00050,000
SourceSingle SOQL queryAnything
Memory profileStreamed, low memoryAll loaded upfront
Composite sources (multi-query)NoYes
Required when SOQL alone isn’t enoughYes
Performance for huge datasetsExcellentPoor (hits memory)

When Iterable is the right choice

  • Records merged from multiple queries that you want to dedupe before batching.
  • External API results — fetch the records via callout (in a prep step), then iterate.
  • Calculated records — e.g., generate placeholder records on the fly.
  • Records filtered with logic SOQL can’t express — though usually you can find a SOQL form.

When QueryLocator wins

  • Anything over 50,000 records.
  • Single SOQL query is enough.
  • You want the platform to stream records efficiently.

Common interview follow-ups

  • Can I return Iterable<MyCustomType>? — Yes, if you declared the class implements Database.Batchable<MyCustomType>. Most code uses sObject though.
  • Does Iterable bypass the 50K SOQL limit? — No. That’s the whole reason QueryLocator exists.
  • What’s the practical max for Iterable? — Whatever fits in heap. The 50K SOQL ceiling is the hard wall, but you’ll likely hit heap (6 MB sync / 12 MB async) first on large datasets.

Verified against: Apex Developer Guide — Using Batch Apex. Last reviewed 2026-05-17.