Salesforce REST Callouts
Salesforce rarely works alone. A company may need current exchange rates from a finance service, shipping status from a courier, or a tax calculation from a billing system. A callout is the Apex mechanism that sends a request from Salesforce to an outside web service and reads the answer. REST callouts use simple web addresses and JSON data, which makes them the most common integration style today.
The Request and Response Cycle
+-----------+ HTTP Request +------------------+
| Salesforce| ----------------> | External Service |
| Apex Code | | (Weather API) |
| | <---------------- | |
+-----------+ HTTP Response +------------------+
(JSON data)
Apex sends a request with a method, an address, and optional data. The external service answers with a status code and a body.
The Three Apex Classes
| Class | Role |
|---|---|
| Http | Sends the request |
| HttpRequest | Holds the address, method, headers, and body |
| HttpResponse | Holds the status code and the returned body |
HTTP Methods in Plain Words
- GET reads information.
- POST creates something new.
- PUT replaces something that exists.
- PATCH changes part of something.
- DELETE removes something.
A Complete GET Callout
public class WeatherService {
public static String getTemperature(String city) {
HttpRequest req = new HttpRequest();
req.setEndpoint('callout:Weather_API/current?city=' + EncodingUtil.urlEncode(city, 'UTF-8'));
req.setMethod('GET');
req.setHeader('Accept', 'application/json');
req.setTimeout(20000);
Http http = new Http();
HttpResponse res = http.send(req);
if (res.getStatusCode() == 200) {
Map<String, Object> data =
(Map<String, Object>) JSON.deserializeUntyped(res.getBody());
return String.valueOf(data.get('temperature'));
}
return 'Error: ' + res.getStatusCode();
}
}
The endpoint begins with callout: followed by a Named Credential name. Salesforce replaces that prefix with the saved address and login details.
A POST Callout With a JSON Body
HttpRequest req = new HttpRequest();
req.setEndpoint('callout:Order_System/orders');
req.setMethod('POST');
req.setHeader('Content-Type', 'application/json');
req.setBody(JSON.serialize(new Map<String, Object>{
'customer' => 'Sunrise Traders',
'amount' => 2500
}));
HttpResponse res = new Http().send(req);
Reading Status Codes
| Code | Meaning | Action |
|---|---|---|
| 200 | Request worked | Read the body |
| 201 | Record created | Store the returned Id |
| 400 | Bad request data | Check the body format |
| 401 | Login failed | Check credentials |
| 404 | Address not found | Check the endpoint |
| 500 | Server problem | Retry later or alert support |
Parsing JSON
JSON.deserializeUntyped converts text into Maps and Lists, which suits quick jobs. A typed approach defines an Apex class that mirrors the JSON shape. JSON.deserialize fills the class fields automatically, and the code becomes easier to read and test.
public class Rate { public String currency_code; public Decimal value; }
Rate r = (Rate) JSON.deserialize(res.getBody(), Rate.class);
Rules and Limits
- One transaction allows 100 callouts.
- The default timeout is 10 seconds, and the maximum is 120 seconds.
- Salesforce blocks a callout after uncommitted DML in the same transaction. Run the callout first or move it to an asynchronous method.
- Triggers cannot call out directly. A trigger hands the work to a Queueable or future method marked for callouts.
- Each endpoint must appear in a Named Credential or Remote Site Setting.
Testing Callouts
Test classes cannot reach the internet. A mock class implementing HttpCalloutMock returns a fixed response.
@isTest
global class WeatherMock implements HttpCalloutMock {
global HTTPResponse respond(HTTPRequest req) {
HttpResponse res = new HttpResponse();
res.setStatusCode(200);
res.setBody('{"temperature": "31"}');
return res;
}
}
The test calls Test.setMock(HttpCalloutMock.class, new WeatherMock()) before running the method.
Calling Out From User Actions
Many integrations start from a button click or a record save. A Lightning component can call an Apex method marked with @AuraEnabled, and that method makes the callout while the user waits. Keep the call short, show a loading spinner, and display a friendly message when the service is slow or offline. Long-running calls belong in a Queueable job that updates the record when the answer arrives.
Handling Failure Gracefully
External services fail for many reasons: network drops, maintenance windows, and expired keys. Wrap every callout in try and catch, because Salesforce throws a CalloutException when the connection fails. The catch block logs the problem and returns a safe default. Retry logic belongs in a Queueable job that re-enqueues itself a few times with a counter, so a short outage never blocks users.
Respect the rate limits of the external provider. A trigger that fires a callout for each record in a large data load can overwhelm the partner service. Send records in groups through one request when the service allows bulk calls.
Best Practices
- Store addresses and secrets in Named Credentials, never in code.
- Handle every status code, not only 200.
- Set a sensible timeout.
- Log failed calls so support teams can investigate.
- Keep response parsing in a small helper class.
