Outbound Callouts
An outbound callout is Salesforce reaching out to another system over HTTP — calling
a shipping API to get a rate quote, pushing a new Account to a billing system, or
checking inventory in a warehouse app. In Apex, this almost always means the Http,
HttpRequest, and HttpResponse classes.
The basic shape
HttpRequest request = new HttpRequest();
request.setEndpoint('callout:Shipping_API/rates');
request.setMethod('POST');
request.setHeader('Content-Type', 'application/json');
request.setBody(JSON.serialize(new Map<String, Object>{
'originZip' => '94105',
'destinationZip' => '10001',
'weightKg' => 2.5
}));
request.setTimeout(60000); // milliseconds; default is 10s, max is 120s
Http http = new Http();
HttpResponse response = http.send(request);
if (response.getStatusCode() == 200) {
Map<String, Object> result = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
// use result
} else {
// handle non-2xx: log, throw, or retry depending on the error
}
Notice the endpoint: callout:Shipping_API/rates. The callout: prefix points at a
Named Credential called Shipping_API rather than a hardcoded URL — Salesforce
resolves the real endpoint and injects authentication for you. More on this in
Authentication.
Callouts and transaction context
Apex callouts cannot run while there are pending, uncommitted DML changes in the same
transaction — and they are blocked entirely from a trigger's synchronous context. This
is why you'll often see a pattern like: a trigger fires → it enqueues a Queueable or
@future(callout=true) method → that asynchronous context makes the actual callout.
Handling failures
External systems fail, time out, and rate-limit you. A callout wrapper should distinguish between:
- Transient errors (timeouts, 503s, connection resets) — worth a bounded retry.
- Client errors (4xx) — usually a bug in the request; retrying won't help.
- Governor-limit errors — a sign the design needs to batch or throttle, not retry harder.
try {
HttpResponse response = http.send(request);
if (response.getStatusCode() >= 500) {
// transient — candidate for retry via a re-enqueued Queueable
} else if (response.getStatusCode() >= 400) {
// client error — log and stop, don't blindly retry
}
} catch (CalloutException e) {
// network-level failure (timeout, DNS, etc.) — also a retry candidate
}
Common mistakes
- Calling out directly from a trigger. This throws a runtime exception. Hand the call off to an asynchronous context instead.
- Hardcoding endpoint URLs and API keys in Apex. Use a Named Credential so secrets never live in code, debug logs, or version control.
- Ignoring the 100-callouts / 120-seconds-per-transaction limits. Large fan-out integrations need batching (Batch Apex, Queueable chaining) rather than one giant loop of callouts.
- Retrying forever in a tight loop. Always cap retry attempts, and prefer handing retries to an asynchronous job rather than looping synchronously.
- Forgetting
setTimeout(). The 10-second default is often too short for slower third-party APIs, leading to intermittent, hard-to-reproduce failures.
Learn more on Trailhead
Apex Integration Services covers Apex REST and SOAP callouts in more depth, with hands-on practice.
Quiz
Check your understanding
Question 1 of 5By default, how long does an Apex callout have before it times out?