Workaround for the 10 web service callout limit in apex?

(community wiki)

@future (valid for triggers and visualforce)

From trigger or VF context you can have up to 10 calls to @future & each of them has a separate context that allows 10 callouts. So you can get up to 100 callouts from a trigger (that itself you're guaranteed will not contain more than 200 records if it's saved with API > 20.0, capped at 100 otherwise) or 10+100 in VF. Not bad. Before you decide to abuse it - consider the fact that there's a 24 hour rolling limit on @future invocations and that multiple callouts to same endpoint might also throw an error. From governor limits page:

A callout request is limited to a maximum of 20 simultaneous requests to URLs with the same host. The host is defined by the unique subdomain for the URL, for example, www.mysite.com and extra.mysite.com are two different hosts. This limit is calculated across all organizations that access the same host. If this limit is exceeded, a CalloutException will be thrown.

Batch Jobs

Batch jobs let you process many records (up to 50 M) with whatever granularity you wish (if you don't specify it, granularity will be 200). Essentially you decide which records you want to work on, you get a fresh context & governor limits for each chunk of data you're processing and then at the end of the batch job you can send an email, kick-off another batch etc. They're great for background processing tasks.

In batch jobs you can specify an optional scope parameter. If you really need this, the batch job may even be instructed to process 1 record at a time (and again - each execute() will let you make 10 callouts). That is - if you'll make sure that the batch class implements Database.AllowsCallouts ;) Read more about Database.executeBatch if you want to take this path.

Essentially:

public class MyBatchableClass implements Database.Batchable<sObject>, Database.AllowsCallouts{
// definitions of start(), execute() and finish() as in any batch
}

// and then in code that fires it (scheduled class? something that happens 
// after user clicks a button? you use 
Database.executeBatch(new MyBatchableClass(), 1);

// instead of 
// Database.executeBatch(new MyBatchableClass());

Javascript-related solutions

You can jump back and forth in the context. Build a Visualforce page that would process N records at a time, return to the browser, issue a next call that processes another N...

You know your data best - N can be a fixed number or maybe you'll just want to compare output of Limits methods:

if(Limits.getCallouts() == Limits.getLimitCallouts()){
    return 'I\'m not done yet';
} 

This is best suited in environment where user expects some kind of progress report like "X out of Y records processed". If you're after a fully automated background solution that can't be interrupted by user closing the tab for example - go for one of previous two.

"The call" can be Javascript that hits Apex code in form of webservice call (in ajax toolkit - for example a button on list view that processes selected records), @RemoteAction , actionFunction etc.


The existing answers are all pretty good. I've had to work with all of these situations before. Ultimately if you need more than even those workarounds will allow you'll have to look at redoing the web service so you don't have to make as many callouts. Options for that? How about:

If you have control over the webservice you could alter it to be able to take a batch set of data and process them all individually on the server.

If you don't have control over the webservice you could look at writing an intermediary service that took the batch of data, and then individually called the real webservice individually.

That last one in particular would be a last resort sort of attempt as it's really messy but sometimes that's what it takes to get SFDC to work the way you need it to.


Setup an APEX Batch Job: Apex Developer Guide for using batch apex

Than use an object iterator to keep track of how many total callouts you have to make through a simple association.

If you have 50 callouts to make, than you might need 5 records, each of which can represent a total of 10 possible callouts.

Hopefully whatever service you need to call out to get results from allows you to index results so you don't always have to start at the begging.

There will be some delay because Apex Batch jobs run when system resources are available, and there can be a delay.

Another way might be to use a page reference that points back to the same page, and increments an iterator until no more results are needed:

    public PageReference continue(){
        PageReference pageRef = ApexPages.currentPage();
        sObject result = [SELECT IndexId__c FROM sObject WHERE Id ...];
        if(result < 1000){
           methodThatMakesCalloutsAndIncrementIndex();
          pageRef.setRedirect(true);
       }
       else{
          pageRef = Page.FinishedPage;
       }
    }