The Archive SDK Data Query enables programmatic access to archived Salesforce data in the Archive managed package. These data queries automate business processes, support third-party system integrations, create custom views and reports, and can cross-reference archived data with active records.
While tools like the Archive Widget and Archive Search provide user-friendly options for manual data retrieval, the SDK offers advanced capabilities for direct data access via code. This article covers the usage of the Archive SDK, including query parameters, filters, pagination, and best practices.
Key Features
- Global Search: Search archived records using specified criteria.
- Fuzzy Search: Use partial matching with an asterisk (*).
- Pagination: Retrieve large datasets in batches (up to 1,000 records per page).
- Filters: Apply up to six filters to refine your results.
- Deleted Object Support: Query archived records even if the object has been deleted from your production org.
Excluded Objects
These Salesforce objects are filtered out and not searchable via the SDK.
FeedHistoryRelationShare
To maintain optimal performance, and make sure that only relevant archived data is returned, these objects are excluded from searches.
To access archived files, use the Archive Widget or Archive Search.
The Archive SDK enforces these request limits.
| Interval | Request Limit |
|---|---|
| Per Minute | 1,000 |
| Per Hour | 30,000 |
| Per Day | 100,000 |
If you exceed this limit, you receive HTTP status code 429.
Archive SDK Data Query Methods
To define how you query archived data, set the method provided by the Archive SDK.
- Accessing Archive Data: The methods within
ArchiverAccessorare designed to interact with archived records. Then build queries and retrieve data stored in the Archive managed package. - Defining Query Operations: These signatures tell you what kinds of queries you can perform using the SDK, such as global searches and filtered queries.
- Guiding Usage: By examining method signatures, developers can understand the inputs and outputs required for their implementations.
performArchiverGlobalSearch Method
Performs a global search on archived data using specified filters and parameters.
Query Parameters Overview
The Data Query SDK uses several parameters to refine your search. Some parameters are required, while others are optional. Understanding how to configure each parameter helps you get the most accurate and relevant results.
- Object Name (Mandatory)
Specifies the type of Salesforce object to query (e.g.,
Account, Contact, Case).Example:
string sObjectName = 'Account'This example specifies that the query targets Account records.
Note Each query targets a single object. Use separate queries to retrieve data from multiple objects. - Search Filters (Mandatory)
Filters narrow down the search results based on field conditions.
- Field Name: The field you want to filter by (e.g., "Name," "CreatedDate").
- Value or List of Values: The specific value (or values) you want to
match.
Single Value Filter Example
OB_Archiver.SearchFilter filter = new OB_Archiver.SearchFilter('Name', 'test');This query returns records where the Name field matches 'test.'
Multiple Value Filter Example:OB_Archiver.SearchFilter filter = new OB_Archiver.SearchFilter('Status', new List<String>{'Open', 'Closed'});This query returns records where the Status field matches "Open" or "Closed."
Handling Empty/Null Values:To search for records where a field is empty or null, add an empty string ("") to the List of Values.
Example:
List<SearchFilter> filters = new List<SearchFilter>(); filters.add(new SearchFilter('Subject', ''));This filter returns Case records where the Subject field is either empty or null.
Limits:
- Minimum Filters: 1 per query.
- Maximum Filters: 6 per query (combined with AND logic).
- Logic: Filters are combined using AND logic by default. OR logic is not currently supported.
- Maximum Filter Value List Size: A filter's value list can't exceed 1,000 items.
- Date Filters (Optional)
If you want to search based on dates, use the DateRange parameter and the mandatory filters. This is useful for filtering records by fields such as CreatedDate, ModifiedDate, or Archive Date.
Example: Date Filter for Specific Date Query
Date todayDate = Date.today(); Date jan1st2024 = Date.newInstance(2024, 1, 1); DateRange dateRange = new DateRange('archive_date', jan1st2024, todayDate);This query returns records archived between January 1, 2024, and today.
Example: Date Filter for Multiple Date Range
List<SF_Archive.SearchFilter> filters = new List<SF_Archive.SearchFilter>(); SF_Archive.SearchFilter filter = new SF_Archive.SearchFilter('Name', 'test'); filters.add(filter); string sObjectName = 'Account'; Date myDate = Date.newInstance(2024, 1, 11); // January 11, 2024 Date myDate2 = Date.newInstance(2023, 12, 25); // December 25, 2023 SF_Archive.dateRange range = new SF_Archive.dateRange('CreatedDate', myDate, myDate); SF_Archive.dateRange range2 = new SF_Archive.dateRange('CreatedDate', myDate2, myDate2); list<SF_Archive.dateRange> ranges = new list<SF_Archive.dateRange> {range, range2}; SF_Archive.ArchiverAccessorResponse res = SF_Archive.ArchiverAccessor.performArchiverGlobalSearch(sObjectName, filters, ranges);This query returns records archived between December 12, 2023 to January 11, 2024.
To search based on the Archive Date add date field: archive_date
Date Format: Use the Apex date format: MM/DD/YYYY.
- Returned Fields (Optional)
Specify fields to include in the results. If not specified, all fields are returned.
Example: Returning Specific Fields
List<string> fields = new List<string>{'Name', 'CreatedDate'};This query returns only the Name and CreatedDate fields for each record.
- Page Size (Optional)
Sets the number of records per query. The default is 25, and the maximum is 1,000.
Example: Custom Page Size
OB_Archiver.archiverAccessorResponse res = OB_Archiver.archiverAccessor.performArchiverGlobalSearch('Account', filters, 100, fields);This query returns 100 records per page.
Note Exceeding 1,000 records triggers a runtime error. - Sorting Direction (Optional)
Sort results by a field, such as
CreatedDate.- Ascending (
'asc'): Oldest to newest. - Descending (
'desc'): Newest to oldest. - Example: Sorting Query
SortingDirection sortingDirection = new SortingDirection('desc');This query returns records sorted in descending order based on their CreatedDate
Return Type:
List<ArchiveResult> - Specifies that the method returns a list of
ArchiveResultobjects representing the search results. - Modifiers:
public: The method is accessible outside the class.static: The method can be called on the class itself without needing an instance of the class.
- Ascending (
getNextPage Method
Retrieves the next page of results using the scrollId from the previous response. Use this method to paginate through large result sets that exceed the page size limit.
ScrollId (Mandatory)
Use pagination to retrieve large datasets by fetching results in pages of up to 1,000 records.
To set up the pagination workflow, perform the initial query, use the scrollId
from the response to fetch subsequent pages, and continue until
scrollId = '-1'..
Example: Fetching Results with Pagination
// Perform initial search query
OB_Archiver.archiverAccessorResponse res = OB_Archiver.archiverAccessor.performArchiverGlobalSearch('Account',
filters, 100, fields);
// Get the search response
OB_Archiver.archiverAccessorSearchResponse searchResponse = res.getSearchResponseMap();
// Process first page of results
system.debug('Total result count: ' + searchResponse.totalResultCount);
system.debug('Records: ' + searchResponse.records);
// Fetch next page using the scrollId
if (searchResponse.scrollId != '-1') {
OB_Archiver.archiverAccessorResponse nextPageRes = OB_Archiver.archiverAccessor.getNextPage(searchResponse.scrollId);
OB_Archiver.archiverAccessorSearchResponse nextPageResponse = nextPageRes.getSearchResponseMap();
system.debug('Next Page Records: ' + nextPageResponse.records);
}
Access the Methods
To find the method signatures, go to Setup, then
Apex Classes. Open the ArchiverAccessor
class. Locate all methods listed under the
performArchiverGlobalSearch namespace.
Sample Query
This example demonstrates searching for Account records with these
conditions.
Fields:Name, CreatedDate.Limit: 100 records per page.Name: Contains "test".Sorting: Descending byCreatedDate.
List<OB_Archiver.SearchFilter> filters = new List<OB_Archiver.SearchFilter>();
OB_Archiver.SearchFilter filter = new OB_Archiver.SearchFilter('Name', 'test');
filters.add(filter);
List<string> fields = new List<string>{'Name', 'CreatedDate'};
SortingDirection sortingDirection = new SortingDirection('desc');
OB_Archiver.archiverAccessorResponse res = OB_Archiver.archiverAccessor.performArchiverGlobalSearch('Account',
filters, 100, fields, sortingDirection);
Troubleshooting Common Errors
| Error Code | Description | Resolution |
|---|---|---|
INTERNAL_ERROR |
An unexpected failure occurred. | Contact support with the error details. |
RESOURCE_LIMIT_ERROR |
Exceeded pagination or query size limits. | Reduce the page size or refine search filters. |
VALIDATION_ERROR |
Missing or invalid search criteria. | Make sure that all filters and fields are valid. |
Additional Tips
- API Limits: Monitor your API usage to avoid hitting limits.
- Casting Response Structure: To cast the response to an sObject, serialize it into JSON and then deserialize it back into an sObject.
- Field Accessibility: Make sure that fields are enabled and accessible.
- Request Structure: Double-check all query parameters.
For unresolved issues, contact Archive SDK Support with detailed logs.
For testing the SDK, see Archive SDK Testing.
- Archive SDK Data Query—Sample Use Case 1
In this code example, a filter, date range, and create an archive search request are validated through the status code in the Archive managed package. - Archive SDK Data Query—Sample Use Case 2
Example use case that shows how to build and customize a widget that displays High Priority Accounts in the Archive managed package. - Archive SDK Data Query—Sample Use Case 3
Example use case that shows how Salesforce's queueable mechanism handles tasks without exceeding governor limits, such as CPU limits or Timeout callouts in the Archive managed package.
Archive SDK Data Query—Sample Use Case 1
In this code example, a filter, date range, and create an archive search request are validated through the status code in the Archive managed package.
A getNextPage call following (scrollId != -1) is
included because the size of the results is limited in the
performArchiverGlobalSearch call.
string objectName = 'Account';
list<OB_Archiver.SearchFilter>request = new list<OB_Archiver.SearchFilter>();
OB_Archiver.SearchFilter filter1 = new OB_Archiver.SearchFilter('Name', 'test');
request.add(filter1);
OB_Archiver.DateRange range = new OB_Archiver.dateRange('CreatedDate',
system.today()-150,system.today());
OB_Archiver.ArchiverAccessorResponse res =
OB_Archiver.ArchiverAccessor.performArchiverGlobalSearch(objectName, request, range);
if(res.getStatusCode() == 200){
system.debug('code: ' + res.getStatuscode());
OB_Archiver.ArchiverAccessorSearchResponse searchResponse = res.getSearchResponseMap();
system.debug('count: '+ searchResponse.totalResultCount);
system.debug('scrollid: '+ searchResponse.scrollId);
system.debug('list size: '+ searchResponse.records.size());
string scrollId = searchResponse.scrollId;
integer page = 1;
while(scrollId != '-1'){
page++;
OB_Archiver.ArchiverAccessorResponse secondPage =
OB_Archiver.ArchiverAccessor.getNextPage(searchResponse.scrollId);
if(secondPage.getStatusCode() == 200){
OB_Archiver.ArchiverAccessorSearchResponse searchResponse2 =
secondPage.getSearchResponseMap();
scrollId = searchResponse2.scrollId;
system.debug('count: '+ searchResponse2.totalResultCount);
system.debug('scrollid: '+ searchResponse2.scrollId);
system.debug('list size: '+ searchResponse2.records.size());
system.debug('page number: '+ page);
} else{
system.debug('response is != 200: '+ secondPage.getStatusCode());
system.debug('body: '+ secondPage.getBody());
break;
} }}else{
system.debug('status code erorr|: '+ res.getStatusCode());
system.debug('status code message|: '+ res.getErrorMessage()); }
Archive SDK Data Query—Sample Use Case 2
Example use case that shows how to build and customize a widget that displays High Priority Accounts in the Archive managed package.
Use Case
Your business requires that the Tier 3 Support Team have access to view archived Cases with a high-priority status.
This example shows how to:
- Create a customized Lightning Web Component (LWC).
- Pull archived data using the data query SDK.
- Apply the filter (High Priority).
Note
Other filters can be customized by referring to the code here.
This example is limited to 25 records. The SDK allows you to set the results per page at 1,000, the default is 25, with the option of pagination through the results.
Widget Displaying High Priority Cases
Sample SdkUseCaseWidget.js
In this code example, the parameter is AccountRecordId.
import { LightningElement, api, track } from 'lwc';
import getCases from '@salesforce/apex/ArchiveSdk.getCases';
export default class SampleSdkUseCaseWidget extends LightningElement {
@api recordId;
columns = [
{ label: 'Case Number', fieldName: 'caseNumber', type: 'text', cellAttributes: {alignment: 'center'}},
{ label: 'Subject', fieldName: 'subject', type: 'text' , cellAttributes: {alignment: 'center'}, },
{ label: 'Priority', fieldName: 'priority', type: 'text', cellAttributes: {alignment: 'center'}, },
{ label: 'Status', fieldName: 'status', type: 'text', cellAttributes: {alignment: 'center'}, }
];
@track records;
@track data
@track _loading;
async connectedCallback() {
this._loading = true;
try {
const result = await getCases({parentId: this.recordId})
this.records = result;
this._loading = false;
} catch (error) {
this._loading = false;
}}
}
Sample SdkUseCaseWidget.html
The Apex controller that the LWC uses calls the Data Query SDK.
OB_Archiver.archiverAccessor.performArchiverGlobalSearch(objectName, request, fields, range);
This is the call for the Archive SDK. It includes these parameters.
-
ObjectName—Depicts the Search
-
Filters—Used to retrieve the data.
-
Fields—The projected fields to return from data.
-
Range—The date range used to query the data.
<template>
<template if:true={_loading}>
<lightning-spinner></lightning-spinner>
</template>
<lightning-datatable
key-field="caseNumber"
data={records}
columns={columns}
hide-checkbox-column>
</lightning-datatable>
</template>
ArchiveSdk.cls
This code block is the Apex controller.
public with sharing class ArchiveSDK {
@AuraEnabled
public static List<CaseTableWrapperExample.CaseRecord> getCases(String parentId) {
// Create an empty list to hold maps with String keys and Object values
List<Map<String, Object>> totalRecords = new List<Map<String, Object>>();
// Create a list to store search filter objects
List<SF_Archive.SearchFilter> request = new List<SF_Archive.SearchFilter>();
// Create a search filter for the 'AccountId' field with a given 'parentId' value
SF_Archive.SearchFilter filter1 = new SF_Archive.SearchFilter('AccountId', parentId);
// Create a search filter for the 'Priority' field with a given 'High' value
SF_Archive.SearchFilter filter2 = new SF_Archive.SearchFilter('Priority', 'High');
// Add the filters to the request list
request.add(filter1);
request.add(filter2);
// Define the object name as 'Case'
String sObjectName = 'Case';
// Create a list of Strings representing field names to be retrieved from Archive DB
List<String> fields = new List<String>{'CaseNumber', 'Subject', 'Priority', 'Status'};
// Create the DateRange based on the created date
SF_Archive.DateRange range = new SF_Archive.DateRange('CreatedDate', system.today() - 150, system.today());
// Perform an initial global search and store the response
SF_Archive.ArchiverAccessorResponse res = SF_Archive.ArchiverAccessor.performArchiverGlobalSearch(sObjectName, request, range);
// Check if the HTTP response status code is 200 (success)
if (res.getStatusCode() == 200) {
system.debug('code: ' + res.getStatuscode());
// Retrieve the initial search response map
SF_Archive.ArchiverAccessorSearchResponse searchResponse = res.getSearchResponseMap();
system.debug('count: ' + searchResponse.totalResultCount);
system.debug('scrollid: ' + searchResponse.scrollId);
system.debug('list size: ' + searchResponse.records.size());
// Store the scroll ID for pagination
String scrollId = searchResponse.scrollId;
// Initialize the page number
Integer page = 1;
// Add the records from the initial search to the totalRecords list
totalRecords.addAll(searchResponse.records);
// Continue to fetch additional pages while the scroll ID is not '-1'
while (scrollId != '-1') {
page++;
// Get the next page of search results using the UPDATED scrollId
SF_Archive.ArchiverAccessorResponse nextPageResponse = SF_Archive.ArchiverAccessor.getNextPage(scrollId);
// Check if the HTTP response status code is 200 (success)
if (nextPageResponse.getStatusCode() == 200) {
// Retrieve the search response for the current page
SF_Archive.ArchiverAccessorSearchResponse searchResponse2 = nextPageResponse.getSearchResponseMap();
// CRITICAL FIX: Update the scrollId with the value from the new response
scrollId = searchResponse2.scrollId;
system.debug('count: ' + searchResponse2.totalResultCount);
system.debug('scrollid: ' + searchResponse2.scrollId);
system.debug('list size: ' + searchResponse2.records.size());
system.debug('page number: ' + page);
// Add the records from the current page to the totalRecords list
totalRecords.addAll(searchResponse2.records);
} else {
// Output an error message if the HTTP response status code is not 200
system.debug('response is != 200: ' + nextPageResponse.getStatusCode());
system.debug('body: ' + nextPageResponse.getBody());
// Break out of the loop if there is an error
break;
}
} // End of while loop
} else {
// Handle error from initial search
system.debug('status code error: ' + res.getStatusCode());
system.debug('status code message: ' + res.getErrorMessage());
}
// Return the list of total records
List<CaseTableWrapperExample.CaseRecord> result = new List<CaseTableWrapperExample.CaseRecord>();
return CaseTableWrapperExample.getCasesTable(totalRecords);
}
}
CaseTableWrapperExample.cls
Helper class to formulate class from Apex to LWC
public class CaseTableWrapperExample{
public static List<caseRecord> getCasesTable(list<map<string,object>> cases){
List<caseRecord> caseReturnList = new List<caseRecord>();
for(Map<string,object> caseRecord: cases){
caseRecord tempCase = new caseRecord();
tempCase.caseNumber = String.valueOf(caseRecord.get('CaseNumber'));
tempCase.id = String.valueOf(caseRecord.get('Id'));
tempCase.origin = String.valueOf(caseRecord.get('Origin'));
tempCase.status = String.valueOf(caseRecord.get('Status'));
tempCase.subject = String.valueOf(caseRecord.get('Subject'));
tempCase.priority = String.valueOf(caseRecord.get('Priority'));
caseReturnList.add(tempCase);
}
return caseReturnList;
}
public class caseRecord {
@AuraEnabled public string caseNumber;
@AuraEnabled public string id;
@AuraEnabled public string origin;
@AuraEnabled public string status;
@AuraEnabled public string priority;
@AuraEnabled public string subject;
}}
The above code describes a sample scenario whereby you can add LWC as a widget to the Lightning page.
To view the actual Lightning data table, add this code to the
sampleSdkUseCaseWidget.js-meta.xml file. Then add the Archive
Widget to the Lightning Page.
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>55.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__RecordPage</target>
<target>lightning__AppPage</target>
</targets>
<targetConfigs>
<targetConfig targets="lightning__RecordPage">
<supportedFormFactors>
<supportedFormFactor type="Small" />
<supportedFormFactor type="Large" />
</supportedFormFactors>
</targetConfig>
<targetConfig targets="lightning__AppPage">
<supportedFormFactors>
<supportedFormFactor type="Small" />
<supportedFormFactor type="Large" />
</supportedFormFactors>
</targetConfig>
</targetConfigs>
</LightningComponentBundle>
Archive SDK Data Query—Sample Use Case 3
Example use case that shows how Salesforce's queueable mechanism handles tasks without exceeding governor limits, such as CPU limits or Timeout callouts in the Archive managed package.
For more information, see Salesforce documentation on Apex Governor Limits
Use Case
Your business requires that the Tier 3 Support Team has access to view archived Cases with a high-priority status.
This example shows how to work around an error when the number of archived records
retrieved via the SDK is too large. The error reads:
OB_Archiver.ArchiverAccessor.ArchiverAccessorException: Exceeded time
allotted for callout, please contact support.
This use case is created using code from these Apex classes.
ArchiveSecondPageJob
This class processes paginated data from Archive using a scroll ID, making callouts as needed. It runs asynchronously via the Queueable interface, tracking the total execution time to avoid exceeding limits and enqueuing itself for continued processing if necessary. The class handles responses, updates the scroll ID, and logs details about each retrieved data page.
public class ArchiveSecondPageJob implements Queueable,Database.AllowsCallouts {
private String scrollId;
private long totalTime;
private integer pageNumber;
public archiveSecondPageJob(String prevScrollId, integer pageNumber) {
scrollId = prevScrollId;
this.pageNumber = pageNumber;
}
public void execute(QueueableContext context) {
system.debug('executing');
doExecute();
}
private void doExecute() {
OB_Archiver.ArchiverAccessorSearchResponse searchResponse = null;
integer page = 1;
long totalTime = 0;
while(scrollId != '-1')
{
//avoid 12000 ms limit
if(totalTime > 90000){
system.debug('Creating new task');
archiveSecondPageJob q = new archiveSecondPageJob(scrollId,this.pageNumber);
System.enqueueJob(q);
return;
}
//Get the next page using the scrollId
Long startTime = DateTime.now().getTime();
OB_Archiver.ArchiverAccessorResponse nextPage = OB_Archiver.ArchiverAccessor.getNextPage(scrollId);
Long stopTime = DateTime.now().getTime();
totalTime += stopTime - startTime;
if(nextPage.getStatusCode() == 200){
this.pageNumber++;
OB_Archiver.ArchiverAccessorSearchResponse searchResponse2 =nextPage.getSearchResponseMap();
scrollId = searchResponse2.scrollId;
system.debug('count: '+ searchResponse2.totalResultCount);
system.debug('scrollid: '+ searchResponse2.scrollId);
system.debug('list size: '+ searchResponse2.records.size());
system.debug('body: '+ searchResponse2.records);
system.debug('page: '+ pageNumber);
system.debug('time: '+ totalTime);
}
else{
system.debug('response is != 200: '+ nextPage.getStatusCode());
system.debug('body: '+ nextPage.getBody());
return;
}
}
}
}
The ArchiveSearch class performs a search on the Case object using
specific filters, retrieves the results, and logs details such as the total count
and scroll ID. If the search returns a valid scroll ID (indicating more pages of
results), it enqueues the archiveSecondPageJob to process the next
page asynchronously. This class essentially initiates the search and hands off
further processing to the previously defined
archiveSecondPageJob.
The format for the initial search call.
public class ArchiveSearch {
public void doSearch(){
string objectName = 'Case';
list<OB_Archiver.SearchFilter> request = new list<OB_Archiver.SearchFilter>();
OB_Archiver.SearchFilter filter1 = new OB_Archiver.SearchFilter('Priority', 'High');
request.add(filter1);
list<string> fields = new list<string>{'CaseNumber', 'Id'};
OB_Archiver.ArchiverAccessorResponse res = OB_Archiver.ArchiverAccessor.performArchiverGlobalSearch(objectName, request,fields);
if(res.getStatusCode() == 200){
system.debug('code: ' + res.getStatuscode());
OB_Archiver.ArchiverAccessorSearchResponse searchResponse = res.getSearchResponseMap();
system.debug('count: '+ searchResponse.totalResultCount);
system.debug('scrollid: '+ searchResponse.scrollId);
system.debug('list size: '+ searchResponse.records.size());
string scrollId = searchResponse.scrollId;
integer page = 1;
if(scrollId != '-1'){
system.debug('calling next page');
integer pageNumber = 2;
archiveSecondPageJob q = new archiveSecondPageJob(scrollId, pageNumber);
System.enqueueJob(q);
}}}}
For more information, see Salesforce Apex Developer Guide.