Yes, this is a common question here on the Apigee community. I’d suggest looking through some prior discussions.
In your case, you could use JavaScript with the httpClient and the callback function, to do what you want.
<Javascript name='JS-Invoke-Calls' timeLimit='4200' >
<Properties>
<Property name='target'>https://my-endpoint.com/path</Property>
<Property name='items'>111,222,333</Property>
<Property name='qparam'>billingId</Property>
</Properties>
<ResourceURL>jsc://invoke-calls-in-parallel.js</ResourceURL>
</Javascript>
And then the JS looks like this:
var start = Date.now();
var responses = [];
function indexedOnComplete(index, numberOfCalls, response, error) {
var prefix = 'jscall.'+ index.toFixed(0);
context.setVariable(prefix +'.completed-at', String(Date.now() - start));
if (response) {
context.setVariable(prefix +'.response-status', response.status);
// collect the response content
responses.push(JSON.parse(response.content));
}
else {
context.setVariable(prefix +'.error', 'Whoops: ' + error);
responses.push(null);
}
// check if this is the final response. If so, set result variables in context.
if (numberOfCalls == responses.length) {
context.setVariable('jscall.all-done', String(Date.now() - start));
// all of the responses
context.setVariable('jscall.aggregated-result',
JSON.stringify(responses, null, 2));
}
}
// invoke one outbound http call, using the callback method
function invokeOne(item, index, cb) {
var url = properties.target + '?' + properties.qparam + '=' + item ;
var headers = { 'Item-Index' : index };
httpClient.send(new Request(url, 'GET', headers), cb);
}
// ====================================================================
var items = properties.items.split(new RegExp('\\s*,\\s*'));
var numberOfCalls = items.length;
context.setVariable('number-of-calls', numberOfCalls.toFixed(0));
items.forEach(function(currentItem, index) {
context.setVariable('jscall.' + index.toFixed(0) + '.item', currentItem);
var cb = function(response, error) {
return indexedOnComplete(index, numberOfCalls, response, error);
};
return invokeOne(currentItem, index, cb);
});