Callouts are how Salesforce talks to the rest of the world. REST callouts in Apex are straightforward once you understand the three things that trip people up: Remote Site Settings, Named Credentials, and test mocking. This guide covers all of them.

Prerequisites

Before any callout works, the target endpoint must be whitelisted. Go to Setup → Remote Site Settings and add the base URL. Without this, Salesforce throws a System.CalloutException regardless of your code.

Better: use Named Credentials instead of Remote Site Settings. More on that below.

The Basic GET Callout

HttpRequest req = new HttpRequest();
req.setEndpoint('https://api.example.com/data');
req.setMethod('GET');
req.setHeader('Authorization', 'Bearer ' + token);
req.setTimeout(10000); // 10 seconds max

Http http = new Http();
HttpResponse res = http.send(req);

if (res.getStatusCode() == 200) {
    Map<String, Object> body =
        (Map<String, Object>) JSON.deserializeUntyped(res.getBody());
    // process body
} else {
    throw new CalloutException('API error: ' + res.getStatus());
}

POST with a JSON Body

HttpRequest req = new HttpRequest();
req.setEndpoint('https://api.example.com/orders');
req.setMethod('POST');
req.setHeader('Content-Type', 'application/json');
req.setBody(JSON.serialize(new Map<String, Object>{
    'orderId' => '12345',
    'amount'  => 99.99
}));

HttpResponse res = new Http().send(req);

Use Named Credentials

Hard-coding tokens in Apex is a security risk and a maintenance nightmare. Named Credentials store credentials securely and let you reference them by name:

req.setEndpoint('callout:My_Named_Credential/data');

Salesforce substitutes the base URL and auth headers automatically. The credential is managed in Setup — developers never touch the actual secret. See the Named Credentials guide for full setup steps.

Callouts in Triggers

You cannot make a synchronous callout from a trigger. Use @future(callout=true) or a Queueable with Database.AllowsCallouts:

public class OrderSyncQueueable implements Queueable, Database.AllowsCallouts {
    private List<Id> orderIds;
    public OrderSyncQueueable(List<Id> ids) { this.orderIds = ids; }

    public void execute(QueueableContext ctx) {
        // make callout here
    }
}

Mocking Callouts in Tests

Callouts do not execute in test context — you must mock them. Implement HttpCalloutMock:

@isTest
global class MockHttpResponse implements HttpCalloutMock {
    global HTTPResponse respond(HTTPRequest req) {
        HttpResponse res = new HttpResponse();
        res.setStatusCode(200);
        res.setBody('{"status":"ok"}');
        return res;
    }
}

@isTest
static void testCallout() {
    Test.setMock(HttpCalloutMock.class, new MockHttpResponse());
    // call your class
}

Error Handling Pattern

Never let a callout fail silently. At minimum: log the status code and body to a custom object or platform event, and rethrow or handle gracefully based on whether the failure is transient (5xx) or permanent (4xx).

SK

Sumit Kumar Singh

Independent Salesforce Consultant

I've built integrations between Salesforce and dozens of external systems. REST callouts are the backbone of most of them.

About the Author