Asynchronous Search APIs
The Search platform in Google Security Operations lets you use asynchronous APIs for long-running queries that return large result sets of up to 1 million results. These APIs let you initiate searches across data sources, including Unified Data Model (UDM) events, detections, data tables, and Entity Context Graph (ECG), without blocking your application. When you run a search query using a long-running operation (LRO) API, you receive an operation ID. You can use this ID to monitor the operation status and get the results page by page.
Prerequisites
To use long-running operation APIs, the calling principal requires specific Identity and Access Management (IAM) permissions.
To perform the following actions, you must have the corresponding IAM permissions:
- Initiate a search:
chronicle.searchSessions.search - List results:
chronicle.searchedResults.liston theSearchSessionresource.
Make sure that the calling principal has a role that grants these permissions, for example, the Chronicle API Viewer, Chronicle API Editor, or Chronicle API Admin role.
Run a search using LRO APIs
Follow these steps to run a search using the LRO APIs:
Initiate the search
Send a POST request to the search custom method on the Google SecOps instance.
- Endpoint:
POST /{$api_version}/projects/{project}/locations/{location}/instances/{instance}:search - Method:
Search - Request body:
SearchRequest
The following example shows a SearchRequest object:
{
"parent": "projects/PROJECT_NUMBER/locations/LOCATION/instances/INSTANCE_ID",
"query": "metadata.event_type = \"USER_LOGIN\"",
"time_range": {},
"start_time": "2026-03-16T14:40:13Z",
"endTime": "2026-03-16T15:40:13Z",
"dialect": "YL2"
}
The request requires the following key parameters:
query: The search query string.time_range: The time interval for the search.dialect: Specifies the language dialect asYL2.- The
SQLdialect isn't supported for asynchronous LRO result retrieval usingListSearchedResults(searchedResults.list). - The
RAW_LOGdialect is in Preview and might return a501 UNIMPLEMENTEDerror if it isn't enabled for your instance.
- The
result_limit: Optional. The maximum number of rows to materialize. The default value is10000, and the maximum value is1000000.simulated_data_visibility(orsimulatedDataVisibilityin JSON): Optional. Controls whether search results include simulated events. Set toSIMULATED_DATA_INCLUDEDto include simulated events in search results. If unspecified, this parameter defaults toSIMULATED_DATA_EXCLUDED, which excludes simulated events.
This call returns a google.longrunning.Operation object.
The following example shows a successful operation response:
{
"name": "projects/PROJECT_NUMBER/locations/LOCATION/instances/INSTANCE_ID/operations/OPERATION_ID",
"metadata": {
"@type": "[type.googleapis.com/google.cloud.chronicle.v1main.SearchOperationMetadata](https://type.googleapis.com/google.cloud.chronicle.v1main.SearchOperationMetadata)",
"state": "RUNNING",
"start_time": "2026-03-13T10:00:00Z"
}
}
The state: RUNNING field indicates that the search is in progress.
Monitor the operation
Poll the status of the LRO by using the standard GetOperation method from the
google.longrunning.Operations service. Use the name value from the previous response.
- Endpoint:
GET /{$api_version}/projects/{project}/locations/{location}/instances/{instance}/operations/{operationID}
Continue polling until the done field in the GetOperation response returns
true.
- If the operation is successful, the
metadata.statefield returnsSUCCEEDED, and the response field contains the createdSearchSessionresource. - If the operation fails, the
donefield returnstrue, and the error field contains the related failure details.
The following example shows a successful GetOperation response:
{
"name": "projects/PROJECT_NUMBER/locations/LOCATION/instances/INSTANCE_ID/operations/OPERATION_ID",
"metadata": {
"@type": "[type.googleapis.com/google.cloud.chronicle.v1main.SearchOperation](https://type.googleapis.com/google.cloud.chronicle.v1main.SearchOperation) Metadata",
"state": "SUCCEEDED",
"startTime": "2026-03-16T15:42:11.037506921Z"
},
"endTime": "2026-03-16T15:42:17.504730842Z",
"expireTime": "2026-03-17T15:42:17.504731874Z",
"progress": 100,
"done": true,
"response": {
"@type": "[type.googleapis.com/google.cloud.chronicle.v1main.SearchSession](https://type.googleapis.com/google.cloud.chronicle.v1main.SearchSession)",
"name": "projects/PROJECT_NUMBER/locations/LOCATION/instances/INSTANCE_ID/searchSessions/SEARCH_SESSION_ID",
"query": "metadata.event_type = \"USER_LOGIN\"",
"timeRange": {},
"startTime": "2026-03-16T14:40:13Z",
"endTime": "2026-03-16T15:40:13Z",
"dialect": "YL2",
"metadata": {
"operationId": "OPERATION_ID",
"startTime": "2026-03-16T15:42:11.037506921Z",
"endTime": "2026-03-16T15:42:17.504730842Z",
"expireTime": "2026-03-17T15:42:17.504731874Z",
"resultRowCount": 10000,
"moreDataAvailable": true
}
}
}
The SearchSession resource name format is
projects/{project}/locations/{location}/instances/{instance}/searchSessions/{search_session}.
The successful response contains the following key fields:
done: When set totrue, the operation is complete.state: When set toSUCCEEDED, the search finished successfully.response.name: The resource name of theSearchSession. Use this value as the parent property in the next step.response.metadata.resultRowCount: Indicates the total number of rows found.response.metadata.moreDataAvailable: Indicates that the number of available results exceeds the defined return limit.
List LRO operations
To list LRO operations, use the ListOperations method from the
google.longrunning.Operations service. Use the name value from the previous response.
- Endpoint:
GET /{$api_version}/projects/{project}/locations/{location}/instances/{instance}
To list search LRO operations from the past 24 hours, specify the name:"operations/s-lro-" filter, including the trailing hyphen.
The following example shows a successful ListOperations request:
google.longrunning.ListOperationsRequest {
name: "projects/PROJECT_NUMBER/locations/LOCATION/instances/INSTANCE_ID"
filter: "name:\"operations/s-lro-\""
page_size: 100
}
The following example shows a successful ListOperations response:
{
operations {
name: "projects/PROJECT_NUMBER/locations/LOCATION/instances/INSTANCE_ID/operations/OPERATION_ID_1"
metadata {
type_url: "[type.googleapis.com/google.cloud.chronicle.v1main.SearchOperation](https://type.googleapis.com/google.cloud.chronicle.v1main.SearchOperation) Metadata"
value: "\b\002\022\f\b\367\304\363\316\006\020\317\352\232\240\003\032\f\b\237\307\363\316\006\020\326\334\351\311\001\"\f\b\237\352\370\316\006\020\352\342\351\311\001(d"
}
done: true
response {
type_url: "[type.googleapis.com/google.cloud.chronicle.v1main.SearchSession](https://type.googleapis.com/google.cloud.chronicle.v1main.SearchSession)"
value: "\n\213\001projects/PROJECT_NUMBER/locations/LOCATION/instances/INSTANCE_ID/searchSessions/OPERATION_ID_11022\bip != \"\"\032\020\n\006\b\251\255\334\316\006\022\006\b\351\345\336\316\006\0012\\\n*OPERATION_ID_1\022\f\b\367\304\363\316\006\020\317\352\232\240\003\032\f\b\237\307\363\316\006\020\326\334\351\311\001\"\f\b\237\352\370\316\006\020\352\342\351\311\001(\300\204=0\001"
}
}
operations {
name: "projects/PROJECT_NUMBER/locations/LOCATION/instances/INSTANCE_ID/operations/OPERATION_ID_2"
metadata {
type_url: "[type.googleapis.com/google.cloud.chronicle.v1main.SearchOperationMetadata](https://type.googleapis.com/google.cloud.chronicle.v1main.SearchOperationMetadata)"
value: "\b\002\022\f\b\200\305\363\316\006\020\324\262\367\225\001\032\v\b\241\307\363\316\006\020\321\261\333?\"\v\b\241\352\370\316\006\020\317\264\333?(d"
}
done: true
response {
type_url: "[type.googleapis.com/google.cloud.chronicle.v1main.SearchSession](https://type.googleapis.com/google.cloud.chronicle.v1main.SearchSession)"
value: "\n\213\001projects/PROJECT_NUMBER/locations/LOCATION/instances/INSTANCE_ID/searchSessions/OPERATION_ID_2\022\bip != \"\"\0321020\n\006\b\251\255\334\316\006\022\006\b\351\345\336\316\006\0012Z\n*OPERATION_ID_2\022\f\b\200\305\363\316\006\0201324\262\367\225\001\032\v\b\241\307\363\316\006\020\321\261\333?\"\v\b\241\352\370\316\006\020\317\264\333?(\300\204=0\001"
}
}
}
Fetch the results
After the operation state returns SUCCEEDED, use the ListSearchedResults() method
to retrieve the search results.
- Endpoint:
GET /{$api_version}/{parent=projects/*/locations/*/instances/*/searchSessions/*}/searchedResults - Method:
ListSearchedResults - Request parameters:
ListSearchedResultsRequest
The following example shows a ListSearchedResultsRequest that retrieves three
results and skips the first five:
// GET
/v1alpha/projects/PROJECT_NUMBER/locations/LOCATION/instances/INSTANCE_ID/searchSessions/SEARCH_SESSION_ID/searchedResults?page_size=3&skip=5
The request supports the following query parameters:
page_size: The maximum number of results to return per page. The default value is 100, and the maximum value is 10000.page_token: The token from a previousListSearchedResultsResponseused to retrieve the next page.order_by(ororderByin JSON): Optional. The field used to sort the results. When you omitorder_by, UDM event results are sorted byudm.metadata.event_timestampin descending order by default, returning the most recent events first. When you specify a field path inorder_bywithout a direction suffix, such asudm.principal.hostname, results are sorted in ascending order by default. Don't appendasc, because theascsuffix isn't supported and returns a400 INVALID_ARGUMENTerror. To sort results in descending order, append thedescsuffix to the field path, such asudm.metadata.event_timestamp desc. Multi-column sorting isn't supported.UDM events (eventRecord): Use fully qualified paths within theudmfield, such asudm.metadata.event_timestamp descorudm.principal.hostname. Column names such ashostname,user,process name, andevent typeare also supported.The default sort order is
udm.metadata.event_timestampin descending order, which returns the most recent events first.Entities / ECG (entityContextRecord): Use paths within theentityfield, such asgraph.entity.iporgraph.entity.ip desc.data tables (dataTableRecord): Use the%<table_alias>.<column_name>format. For example,%dt.user desc.The default value is the first data table column.
Detections (DetectionRecord): Use the keyworddetectionfollowed by the path, for example,detection.id.Joins: For events and entities, use the placeholder variable that defines them. For all other sources, the format remains the same.
For example:
- ECG (UDM-ECG join): Entity:
$e1.graph.entity.hostname - UDM (All joins with UDM):
$e1.principal.ip - Data table (UDM-Data table join):
%<table_alias>.<column_name>
Predefined aliases such as
hostname,user,process name, andevent typeare also supported. For these, use the format$e1.hostname.at.- ECG (UDM-ECG join): Entity:
skip: Optional. The number of results to skip. Don't use this parameter withpage_token.- Default sorting (
udm.metadata.event_timestamp) isn't unique across events that share the same timestamp. In addition, the sort order for the search results is set on the firstListSearchedResults(searchedResults.list) call. - When you paginate with
skip, especially across parallel requests, specify a unique sort field such asudm.metadata.idin theorder_byparameter. Alternatively, fetch the first page before sending parallelskiprequests so that the sort order is already set.
- Default sorting (
The following example shows a successful ListSearchedResultsResponse response:
{
"searchedResults": [
{
"name":
"projects/PROJECT_NUMBER/locations/LOCATION/instances/INSTANCE_ID/searchSessions/SEARCH_SESSION_ID/searchedResults/
RESULT_ID",
"resultRow": {
"eventRecord": {
"event": {
"name":
"projects/PROJECT_NUMBER/locations/LOCATION/instances/INSTANCE_ID/events/EVENT_ID",
"udm": {
"metadata": {
"eventTimestamp": "2026-03-16T14:45:18Z",
"eventType": "USER_LOGIN",
"vendorName": "Microsoft",
"productName": "Azure AD"
}
},
//... other UDM fields
}
//... other UDM fields
"eventLogToken":
"EVENT_LOG_TOKEN"
}
},
{ }
},
{
"name": "projects/PROJECT_NUMBER/locations/
LOCATION/instances/INSTANCE_ID</span>
/searchSessions/SEARCH_SESSION_ID/searchedResults/RESULT_ID", "resultRow": {
"eventRecord": {
//... Similar UDM event structure...
}
}
},
{
""name": "projects/PROJECT_NUMBER/locations/
LOCATION/instances/INSTANCE_ID</span>
/searchSessions/SEARCH_SESSION_ID/searchedResults/RESULT_ID", "resultRow": {
"eventRecord": {
//... Similar UDM event structure...
}
}
}
],
"totalSize": 10000,
"columnNames": [],
"columnSchema": {},
"nextPageToken": "CAKYASAB"
}
To fetch the subsequent page of results, use the returned nextPageToken value
in the page_token query parameter of your next ListSearchedResults request.
The resultRow field contains the actual data.
Continue calling ListSearchedResults with the next_page_token value from
each response. When next_page_token returns empty, all results have
been retrieved.
What's next
For more information about methods, request and response fields, and types, see the following API reference documentation:
Need more help? Get answers from Community members and Google SecOps professionals.