Synchronizing high-volume data between Salesforce and Google Workspace is a common architectural requirement. Instead of making individual HTTP callouts for every single row—which quickly burns through Salesforce governor limits—you can leverage Google Sheets batch APIs to read and write large sets of spreadsheet data in a single roundtrip.
1. Configuring Google Cloud Console
Before writing Apex callouts, configure your Google Cloud project to allow authorized OAuth 2.0 communication:
- Step 1: Log in to the Google Cloud Console and create a dedicated project.
- Step 2: Under APIs & Services > Library, search for and enable the Google Sheets API and Google Drive API.
- Step 3: Navigate to Credentials > Create Credentials > OAuth client ID.
- Step 4: Select Web application, and register your Salesforce callback URL under Authorized redirect URIs (e.g.,
https://yourMyDomain.my.salesforce.com/services/authcallback/GoogleSheets). - Step 5: Copy your generated
Client IDandClient Secret.
2. Modern Authentication: Named Credentials vs. Custom OAuth
While you can write custom OAuth token exchange logic in Apex, Salesforce's native integration tooling eliminates boilerplate authentication code.
client_secret or manage refresh token rotations manually inside an Apex class. Hardcoded secrets create security vulnerabilities and risk breaking if tokens expire mid-transaction. Always store authentication parameters inside Named Credentials with an Auth. Provider.
3. Implementing the Batch Callout in Apex
Google Sheets provides dedicated batch endpoints (such as batchUpdate and values:batchUpdate) that accept clean JSON payloads representing multiple cell operations.
Apex Service: Executing Google Sheets Batch Updates
public with sharing class GoogleSheetsBatchService {
// Named Credential endpoint (callout:Google_Sheets_NC maps to https://sheets.googleapis.com)
private static final String ENDPOINT = 'callout:Google_Sheets_NC/v4/spreadsheets/';
/**
* Updates multiple cell ranges in a Google Sheet in a single request
* @param spreadsheetId Target Google Sheet file ID
* @param sheetTabName Name of sheet tab (e.g., Sheet1)
*/
public static HttpResponse sendBatchValueUpdate(String spreadsheetId, String sheetTabName) {
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint(ENDPOINT + EncodingUtil.urlEncode(spreadsheetId, 'UTF-8') + '/values:batchUpdate');
request.setMethod('POST');
request.setHeader('Content-Type', 'application/json');
// Construct Google batch payload
Map<String, Object> payload = new Map<String, Object>();
payload.put('valueInputOption', 'USER_ENTERED');
List<Map<String, Object>> dataList = new List<Map<String, Object>>();
// Range 1: Header / Status Column
Map<String, Object> updateBlock = new Map<String, Object>();
updateBlock.put('range', sheetTabName + '!A1:C1');
updateBlock.put('values', new List<List<String>>{
new List<String>{'Account Name', 'Industry', 'Sync Status'}
});
dataList.add(updateBlock);
// Range 2: Record Data
Map<String, Object> dataBlock = new Map<String, Object>();
dataBlock.put('range', sheetTabName + '!A2:C3');
dataBlock.put('values', new List<List<String>>{
new List<String>{'Acme Corp', 'Technology', 'Processed'},
new List<String>{'Global Media', 'Entertainment', 'Processed'}
});
dataList.add(dataBlock);
payload.put('data', dataList);
request.setBody(JSON.serialize(payload));
HttpResponse response = http.send(request);
if (response.getStatusCode() == 200) {
System.debug('Batch sync successful: ' + response.getBody());
} else {
System.debug(LoggingLevel.ERROR, 'Sync error: ' + response.getStatusCode() + ' - ' + response.getBody());
}
return response;
}
}
4. Triggering the Batch Callout from Apex
To execute the sync from an anonymous execution block, Flow action, or Queueable worker, invoke your service method:
// Execute batch update using your target Spreadsheet ID String targetSpreadsheetId = '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms'; GoogleSheetsBatchService.sendBatchValueUpdate(targetSpreadsheetId, 'Sheet1');
5. Architecture & Governor Limit Best Practices
- Salesforce Callout Limits: Up to 100 HTTP callouts per synchronous transaction; total request timeout maximum of 120 seconds.
- Google Sheets Quota: Standard limit is 300 read/write requests per minute per project (batching prevents hitting per-minute limits).
- Asynchronous Execution: Wrap large sync jobs in Queueable Apex or Batch Apex to keep UI transactions responsive.
values:batchUpdate with native Salesforce Named Credentials to ensure secure, token-managed data transfers without exceeding governor limits.
Summary
Batching your API payloads transforms sluggish point-to-point data pushes into an enterprise-ready pipeline. By replacing custom token management with Salesforce Named Credentials and grouping updates into JSON batch requests, your Apex integrations stay secure, maintainable, and well within platform limits.