OpenAPI Cross-Site Data Query¶
This document explains how to use query_data to query data from other sites that the current Workspace has been authorized to access, and how to correctly handle the async_id returned by asynchronous endpoints.
Applicable Endpoints¶
| Scenario | Endpoint | Description |
|---|---|---|
| Synchronous query (recommended) | POST /api/v1/df/query_data_v1 |
Preferred for new integrations |
| Asynchronous query | POST /api/v1/df/asynchronous/query_data |
Returns async_id only when the task is still running |
| Legacy compatibility | GET/POST /api/v1/df/query_data |
GET uses body=<URL-encoded JSON>; not recommended for new integrations |
| Query authorized targets | GET /api/v1/wksp_share/granted_ws_list |
Obtain the target workspaceUUID and targetRegion |
| Query site configuration | GET /api/v1/workspace/website/list |
Only for viewing site registration information; does not imply data authorization |
External API's POST /api/v1/df/{workspace_uuid}/query_data is a synchronous entry point and does not accept async_id. It uses the same set of DQL query fields underneath, but authentication is performed using the External API AK/SK signature.
Request Model and Limits¶
Cross-site queries still call the OpenAPI Endpoint of the current site and use the current Workspace's DF-API-KEY. There is no need to directly request the target site, and you should not replace the current Endpoint with the target site's Endpoint.
The structure of each queries[i] is:
{
"qtype": "dql",
"query": {
"q": "L::re(`.*`):(`message`)",
"workspaceUUIDs": ["wksp_target"],
"targetRegion": "region_code"
}
}
The following rules must be followed:
- Non-empty
workspaceUUIDstakes precedence overworkspaceUUID; whenworkspaceUUIDsis an empty array ornull, it falls back toworkspaceUUID. It is recommended not to pass both fields. When neither is passed, the current Workspace is queried. - For cross-site queries, explicitly pass the same
targetRegioninside thequeryobject of each query, not only at the top level of the request. - All
queries[*]in one request can only point to one site. Multiple sites must be split into multiple requests, and the results are merged by the client. workspaceUUIDs: ["*"]orworkspaceUUID: "*"means querying all authorized workspaces visible to the current Workspace within the specifiedtargetRegion; in this case,targetRegionis required.targetRegionis theregionCodeof the site to which the authorizing Workspace belongs, nottoRegionCode.- Multiple target workspaces in the same request must belong to the same
targetRegion. It is recommended to group target workspaces by site before constructing requests.
Time Range and Pagination Cursor¶
The start and end times of query.timeRange must use the same unit (seconds, milliseconds, microseconds, or nanoseconds). Mixing units returns HTTP 400, ft.TimeRangeUnitMismatch; the server does not automatically correct it. The request examples in this document use milliseconds; replace them with your actual query time range.
Pagination parameters such as cursor_time and cursor_token are independent of the query time range: when paging, pass the response cursor back as-is according to the API convention, keep the original query's timeRange unchanged, and do not convert the cursor to unify digit lengths.
Obtain Target Workspace and targetRegion¶
Call:
curl '<Endpoint>/api/v1/wksp_share/granted_ws_list?namespace=logging&pageIndex=1&pageSize=100' \
-H 'DF-API-KEY: <DF-API-KEY>' \
--compressed
namespace filters authorizations by data type, for example:
- Logs:
logging - Tracing:
tracing - Metrics:
metric - Real User Monitoring (RUM):
rum - Synthetic Monitoring:
dialtest
The response is grouped by authorizing site:
{
"code": 200,
"content": [
{
"regionCode": "region_a",
"regionName": "Site A",
"data": [
{
"workspaceUUID": "wksp_target_a",
"workspaceName": "Target workspace A",
"regionCode": "region_a",
"toWorkspaceUUID": "wksp_current",
"toRegionCode": "region_current",
"type": ["logging"],
"indexes": ["*"]
}
],
"pageInfo": {
"pageIndex": 1,
"pageSize": 100,
"totalCount": 1
}
}
],
"success": true
}
When organizing parameters:
- Use
data[*].workspaceUUIDas the value ofquery.workspaceUUIDorquery.workspaceUUIDs. - Use the
content[*].regionCodeof the group containing that workspace (or theregionCodeof the same data item) asquery.targetRegion. - Do not use
toWorkspaceUUIDas the query target; it is usually the grantee Workspace to which the current API Key belongs. - Do not use
toRegionCodeastargetRegion; it is usually the current site. granted_ws_listpaginates independently per site group.pageInfo.countis only the number of items on the current page; do not use it to compare withtotalCountand increment the page indefinitely.- On the first call, you may omit
regionCodeand usepageIndex=1&pageSize=100to discover authorizing sites; afterward, explicitly pass theregionCodefor each site you need to query, and start paging from page 1 for each site. - Stop when the cumulative number of unique authorizations obtained for a site reaches that group's
pageInfo.totalCount, or whenpageIndex * pageSize >= totalCount. When merging page data, deduplicate by the authorization recorduuid, and validate the target mapping with(regionCode, workspaceUUID); if conflicting authorizations appear for the same mapping, do not expand the permission scope on your own.
For example, to fetch only page 2 of region_a:
curl '<Endpoint>/api/v1/wksp_share/granted_ws_list?namespace=logging®ionCode=region_a&pageIndex=2&pageSize=100' \
-H 'DF-API-KEY: <DF-API-KEY>' \
--compressed
The content[*].regionCode returned by GET /api/v1/workspace/website/list can only be used as a site code candidate. A site appearing in the site list does not mean the current Workspace has data authorization for that site; the final result must be based on granted_ws_list.
Synchronous Cross-Site Query¶
The following example queries wksp_target_a in the region_a site:
curl '<Endpoint>/api/v1/df/query_data_v1' \
-H 'Content-Type: application/json' \
-H 'DF-API-KEY: <DF-API-KEY>' \
--data-raw '{
"queries": [
{
"qtype": "dql",
"query": {
"q": "L::re(`.*`):(`message`)",
"timeRange": [1772516130000, 1772519730000],
"limit": 100,
"workspaceUUIDs": ["wksp_target_a"],
"targetRegion": "region_a"
}
}
]
}' \
--compressed
Query all authorized workspaces within a site:
{
"queries": [
{
"qtype": "dql",
"query": {
"q": "L::re(`.*`):(`message`)",
"timeRange": [1772516130000, 1772519730000],
"limit": 100,
"workspaceUUIDs": ["*"],
"targetRegion": "region_a"
}
}
]
}
If you need to query both region_a and region_b, send two separate requests:
Target list
├─ region_a: [wksp_a1, wksp_a2] -> Request 1
└─ region_b: [wksp_b1] -> Request 2
↓
Client merges results
Do not mix different targetRegion values in the same queries array; otherwise the API will return ft.UnsupportMultiSiteQuery.
Asynchronous Query and async_id Lifecycle¶
async_id is a handle for a single query task, not a session ID, a fixed client ID, or a pagination cursor. A new query must never carry a historical async_id.
1. First Submission: Omit async_id¶
curl '<Endpoint>/api/v1/df/asynchronous/query_data' \
-H 'Content-Type: application/json' \
-H 'DF-API-KEY: <DF-API-KEY>' \
--data-raw '{
"queries": [
{
"qtype": "dql",
"query": {
"q": "L::re(`.*`):(`message`)",
"timeRange": [1772516130000, 1772519730000],
"limit": 100,
"workspaceUUIDs": ["wksp_target_a"],
"targetRegion": "region_a"
}
}
]
}' \
--compressed
If the task is still running, example response:
{
"code": 200,
"content": {
"data": [
{
"async_id": "async_task_001",
"is_running": true
}
]
},
"success": true,
"traceId": "TRACE-XXXX"
}
2. Polling: Only Pass Back Running Task IDs¶
Poll only when the same result item satisfies both of the following conditions:
Place the ID back into queries[i].async_id at the same array index, and keep qtype, query.q, the time range, target workspace, and targetRegion unchanged:
{
"queries": [
{
"async_id": "async_task_001",
"qtype": "dql",
"query": {
"q": "L::re(`.*`):(`message`)",
"timeRange": [1772516130000, 1772519730000],
"limit": 100,
"workspaceUUIDs": ["wksp_target_a"],
"targetRegion": "region_a"
}
}
]
}
Use incremental backoff and set an overall timeout, for example, wait 1 second first, then gradually increase to 2 seconds, 4 seconds; do not send continuous requests without intervals.
3. Completion: Stop Carrying async_id¶
When content.data[i].is_running is false, the task has ended. The caller should read the final data and stop polling immediately. Whether or not the async_id field still appears in the response object, the next new query must omit the old ID.
The following behaviors are incorrect:
- Hard-coding the previous task's
async_idinto all subsequent requests. - Reusing the old ID after modifying DQL, time range, workspace, or
targetRegion. - Placing
content.data[0].async_idintoqueries[1]. - Using
async_idinstead ofsearch_after,cursor_time, orcursor_tokenfor pagination.
If is_running=true but async_id is empty, do not backfill with a locally saved historical ID. Record the response traceId and reissue the same logical query with a finite retry strategy; if it persists, contact technical support.
In batch asynchronous queries, content.data[i] corresponds to queries[i] by array index. To reduce the risk of mismatching IDs with query items, it is recommended to submit only one query per asynchronous call.
Difference Between Pagination and Asynchronous Tasks¶
After an asynchronous task completes, pagination still uses the pagination fields returned by the final query result:
| Scenario | Next Request Parameter |
|---|---|
| Log deep pagination | Pass the response search_after into the next query's query.search_after |
| Segmented time query | First set query.cursor_time to the end time, then pass the response next_cursor_time |
| Safe pagination for identical timestamps | Pass the response next_cursor_token into the next query's query.cursor_token |
Pagination is a new query request and should not continue carrying the async_id of a completed task. If a pagination request becomes an asynchronous task again, start a new lifecycle from "omit async_id".
Client-Side Merging Recommendations¶
The server does not support querying across multiple sites in one query_data request. When merging on the client side, it is recommended to:
- First group target workspaces by
targetRegion. - Each site uses independent requests, independent pagination, and independent asynchronous task handling.
- Add client-side metadata to each batch of results, such as
sourceRegionandqueriedWorkspaceUUIDs. Multi-workspace queries do not guarantee that every record carries a reliable source workspace, so you cannot label the entire batch as a singlesourceWorkspaceUUID; only when each query targets a single workspace can you do so. When record-level source is required, query each workspace individually, or have DQL return a source dimension that the business has confirmed is reliable. - Sort and deduplicate log-type results by
time,date_ns, and a stable unique identifier; for metric results, first confirm that the time granularity, aggregation function, and tag set are consistent before merging. - When any site fails, keep the successful results from other sites and return per-site error details; do not wrap partial success as full success.
Common Errors¶
| Error Code / Symptom | Cause | Resolution |
|---|---|---|
ft.TimeRangeUnitMismatch |
The start and end times in timeRange use different units |
Reconstruct the time range with the same unit; still pass pagination cursors back as-is from the response |
ft.UnsupportMultiSiteQuery |
A single request mixes multiple target sites | Split requests by targetRegion |
ft.workspaceUnauthorized |
The target Workspace is not authorized to the current Workspace | Re-query granted_ws_list and confirm the authorization status and UUID |
ft.NotFoundWorkspaceAuthorizationCfg |
The target site has no available authorization configuration, commonly when using * but the site has no authorization |
Check targetRegion and confirm that a valid authorization exists on the target site |
ft.NoInitOtherNodeCfg |
Cross-organization site authorization exists, but the target site's Front Endpoint / node configuration is missing | Do not blindly retry; contact the site administrator to check and complete the target node configuration |
ft.InvalidWorkspace |
The target Workspace on the current site is invalid or disabled | Refresh the workspace list and confirm its status |
| Keeps polling old results | The client keeps reusing the async_id of a completed task |
Delete the local ID after is_running=false; do not pass async_id in new queries |
| Site exists but no data is found | Only workspace/website/list was checked; authorization was not confirmed |
Use wksp_share/granted_ws_list as the source of truth |
DQLDataAccessScopeRestricted warning |
Data access rules allow only some indexes or do not allow the relevant indexes | Check warnings[].details[].metadata.restriction and data access rules |
Pre-Launch Checklist¶
- Request the current site's OpenAPI Endpoint using the current Workspace's
DF-API-KEY. - Obtain the target
workspaceUUIDand its group'sregionCodefromgranted_ws_list. - Every cross-site query explicitly passes the same
targetRegion. - Multi-site targets have been split into multiple requests by
targetRegion. - New asynchronous queries do not pass
async_id. - Poll only when
is_running=trueand the ID is non-empty, and bind tasks by array index. - Delete the task ID after
is_running=false; pagination starts from a new asynchronous lifecycle. - Set polling backoff, overall timeout, pagination limit, and per-site error handling.
- When merging results, preserve the source site/workspace and deduplicate by the business primary key.