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. -
result_limit: Optional. The maximum number of rows to materialize. The default value is10000, and the maximum value is1000000.
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 all LRO operations from the past 24 hours, add the filter name: "operations/s-lro"
.
The following example shows a successful ListOperations
request:
google.lo
n
gru
nn
i
n
g.Lis
t
Opera
t
io
ns
Reques
t
{
na
me
:
"projects/ PROJECT_NUMBER
/locations/ LOCATION
/instances/ INSTANCE_ID
"
f
il
ter
:
"name:\"operations/lro\""
page_size
:
100
}
The following example shows a successful ListOperations
response:
{
opera
t
io
ns
{
na
me
:
"projects/ PROJECT_NUMBER
/locations/ LOCATION
/instances/ INSTANCE_ID
/operations/ OPERATION_ID_1
"
me
ta
da
ta
{
t
ype_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"
}
do
ne
:
true
respo
nse
{
t
ype_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_1
1022\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"
}
}
opera
t
io
ns
{
na
me
:
"projects/ PROJECT_NUMBER
/locations/ LOCATION
/instances/ INSTANCE_ID
/operations/ OPERATION_ID_2
"
me
ta
da
ta
{
t
ype_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"
}
do
ne
:
true
respo
nse
{
t
ype_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: Optional. The field is used to sort the results.-
UDM events (eventRecord): Use paths within theudmfield, for example,udm.metadata.timestamp descorudm.principal.hostname asc. Column names includinghostname,user,process name, andevent typeare also supported.The default value is
udm.metadata.event_timestamp. -
Entities / ECG (entityContextRecord): Use paths within theentityfield, for example,graph.entity.ip asc. -
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 if usingpage_token.
-
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
/searchSessions/ SEARCH_SESSION_ID
/searchedResults/ RESULT_ID
"
,
"resultRow"
:
{
"eventRecord"
:
{
//... Similar UDM event structure...
}
}
},
{
""
na
me
": "
projec
ts
/ PROJECT_NUMBER
/loca
t
io
ns
/
LOCATION
/i nstan
ces/ INSTANCE_ID
/searchSessio
ns
/ SEARCH_SESSION_ID
/searchedResul
ts
/ RESULT_ID
", "
resul
t
Row
": {
"
eve
nt
Record
": {
//... Similar UDM event structure...
}
}
}
],
"
t
o
tal
Size
": 10000,
"
colum
n
Names
": [],
"
colum
n
Schema
": {},
"
ne
x
t
PageToke
n
": "
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.
Limitations
LRO APIs don't support statistics and aggregations.
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.

