Skip to main content

SF-0412 · Concept · Easy

What is a queueable apex?

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

Queueable Apex is a class that implements the Queueable interface and runs asynchronously when you submit it via System.enqueueJob(...). Salesforce introduced it in Winter ‘15 to address @future’s limitations — it accepts complex types, returns a JobId you can monitor, and can chain another job from inside its own execute method.

Anatomy

public class AccountSyncJob implements Queueable {
    private List<Account> accounts;

    public AccountSyncJob(List<Account> accs) {
        this.accounts = accs;
    }

    public void execute(QueueableContext ctx) {
        for (Account a : accounts) {
            a.Last_Synced__c = Datetime.now();
        }
        update accounts;
    }
}

Submit it:

Id jobId = System.enqueueJob(new AccountSyncJob(myAccounts));

System.enqueueJob returns the JobId synchronously. You can store it, query AsyncApexJob for status, or surface it to the user as a tracking reference.

Where Queueable beats @future

Capability@futureQueueable
Accept complex types (sObject, custom classes)NoYes
Return a JobIdNoYes
Chain another async jobNoYes — enqueueJob from inside execute
Track via Apex Jobs UILimitedFull
Make calloutsWith annotationWith Database.AllowsCallouts interface

In a 2026 codebase, Queueable is the default. @future exists for legacy code and a few one-line use cases.

Chaining

A Queueable can enqueue the next job from inside its own execute:

public class AccountSyncJob implements Queueable, Database.AllowsCallouts {
    private List<Id> idsToProcess;
    private Integer offset;

    public AccountSyncJob(List<Id> ids, Integer offset) {
        this.idsToProcess = ids;
        this.offset = offset;
    }

    public void execute(QueueableContext ctx) {
        Integer batchSize = 50;
        Integer end = Math.min(offset + batchSize, idsToProcess.size());
        List<Id> chunk = new List<Id>();
        for (Integer i = offset; i < end; i++) chunk.add(idsToProcess[i]);

        processChunk(chunk);

        if (end < idsToProcess.size()) {
            System.enqueueJob(new AccountSyncJob(idsToProcess, end));
        }
    }

    private void processChunk(List<Id> chunk) { /* callouts here */ }
}

Each job processes a slice and queues the next. The chain ends when there’s no more work.

Limits to keep in mind

LimitValue
Queueable jobs enqueued per synchronous transaction50
Queueable jobs enqueued from inside a Queueable execute1 (you can only chain one child at a time)
Depth of Queueable chainEffectively unlimited in production (sandboxes / Developer Edition cap at 5)
Max enqueued + active Queueable jobs (org-wide, with Finalizers)Counted toward the daily async limit (250,000)

The “one chained job at a time” rule is the most-asked interview detail. From inside a Queueable, you can call System.enqueueJob once. Calling it twice throws LimitException: Too many queueable jobs added to the queue: 2.

Add callouts

public class StripeSyncJob implements Queueable, Database.AllowsCallouts {
    public void execute(QueueableContext ctx) {
        HttpResponse resp = new Http().send(/* ... */);
    }
}

Database.AllowsCallouts is the Queueable equivalent of @future(callout=true).

Common interview follow-ups

  • Which interface does Queueable implement?Queueable. One method: execute(QueueableContext ctx).
  • Can Queueable accept sObjects? — Yes, that’s a major win over @future.
  • Can Queueable chain itself indefinitely? — In production, yes — there’s no hard depth cap (older docs said 5, but that limit applies only to Developer Edition / sandboxes).
  • What does System.enqueueJob return? — A Id (the JobId of the new AsyncApexJob), useful for tracking.

Verified against: Apex Developer Guide — Queueable Apex. Last reviewed 2026-05-17 for Spring ‘26.