Investigate API user guide
Using the RUX Investigate (Metadata) API Manually (e.g., with Postman)
Please Note:
This only applies to the new Vectra AI experience (RUX).
Overview
The Vectra AI Investigations API (also called the Metadata API) enables you to run SQL-like queries directly against network metadata and cloud logs captured by your Vectra sensors. This is the same data that powers Vectra's built-in detections, giving you full access to underlying evidence for threat hunting, incident response, and ad-hoc investigation.
Key capabilities:
Query up to 14 days of raw network session data (connections, DNS, HTTP, TLS, SMB, Kerberos, LDAP, RDP, SSH, beacons, and more)
Query cloud audit logs: AWS CloudTrail, Azure ARM, Microsoft 365 (Entra ID, SharePoint, Exchange, Teams), and Entra ID sign-ins
Filter by host entity IDs, IP addresses, timestamps, protocol fields, MITRE technique indicators, and any field documented in the schema reference
Retrieve up to 10,000 rows per query with full pagination support
Use aggregation functions (COUNT, SUM, AVG, etc.) and grouping for statistical analysis
The API is asynchronous: you submit a query, receive a request_id, then poll for completion and retrieve paginated results.
Prerequisites & Authentication
What You Need
Vectra Brain URL
Your Vectra deployment URL, e.g., https://your-brain.vectra.ai
API Client Credentials
OAuth 2.0 Client ID and Client Secret (created in Vectra Admin → API Clients)
Access Token
Bearer token obtained via the OAuth token endpoint
Postman (optional)
Version 9+ recommended; import the collection or build requests manually
Obtaining an Access Token
The Investigations API uses OAuth 2.0 Bearer Token authentication. Obtain a token before making API calls.
Token endpoint:
Postman setup:
Create a new request → set method to
POSTURL:
https://{your-brain}/oauth2/tokenBody → select
x-www-form-urlencodedand add:
grant_type
client_credentials
client_id
{your_client_id}
client_secret
{your_client_secret}
Send the request. Copy the
access_tokenvalue from the response.
Token response example:
json
Using the token in subsequent requests:
In Postman, go to the Authorization tab → select Bearer Token → paste the access token. Alternatively, add a header manually:
Tip: In Postman, store the token as a collection variable (
{{access_token}}) and reference it withBearer {{access_token}}. You can automate token refresh using a pre-request script.
How It Works — Submit, Poll, Retrieve
The Investigations API uses an asynchronous pattern. Queries may take several seconds or longer depending on data volume and time range.
Status values:
meta.query_status
Meaning
RUNNING
Query is actively executing — keep polling
SUCCESS
Query finished — data array contains your results
FAILED
Query failed — check the response body for error details (exact structure unconfirmed)
Recommended polling interval: 1–3 seconds. Most queries complete within 5–15 seconds.
Postman Automation Tip: Add this snippet to the Tests tab of your POST request to automatically capture the
request_idinto a collection variable:javascript
Your GET request can then reference
{{request_id}}in the URL path automatically — no manual copy/paste needed. See Appendix B for the full script.
API Reference
POST /api/v3.4/investigations/ — Submit a Query
Full URL: https://{your-brain}/api/v3.4/investigations/
Method: POST
Headers:
Authorization
Bearer {access_token}
Content-Type
application/json
Request body (JSON):
query
string
Yes
SQL-like query string. See Query Language Reference.
version
string
No
Query language version. Use "1.0" (default).
Example request body:
json
Success response — 200 OK:
json
Error responses:
400 Bad Request
Invalid query syntax, unsupported clause, or missing ._all suffix on table name
401 Unauthorized
Missing or expired Bearer token
403 Forbidden
Token does not have Investigations API permission
429 Too Many Requests
Rate limit exceeded (5 POST requests/minute)
The API surfaces two distinct error patterns depending on when the error is detected.
Syntax errors — returned directly in the POST response (query fails to parse before execution):
json
error.errorCode
SYNTAX_ERROR for parse failures
error.errorId
Unique ID for this error — include when reporting issues
error.extra[].line
Line number in the query where the error occurred
error.extra[].column
Character position of the error
error.extra[].offending_symbol
The token that triggered the parse failure
error.extra[].message
Human-readable description of the parse error
Common cause: Missing
._allsuffix on a table name produces aSYNTAX_ERRORbecause the parser expects a.after the table name and encountersWHEREinstead.
Runtime errors — returned in the GET response (query parses successfully but fails during execution):
json
error.errorCode
DATABASE_ERROR for runtime failures
error.errorId
Unique ID for this error — include when reporting issues
error.extra[].column
The field name that caused the error
error.extra[].error_name
Specific error type (e.g. COLUMN_NOT_FOUND)
error.extra[].error_type
USER_ERROR indicates the query itself is the problem
Common error_name values for runtime errors:
error_name
column present
Meaning
COLUMN_NOT_FOUND
Yes — names the offending field
A field name in SELECT or WHERE does not exist in the table — check the schema reference for correct field names
TYPE_MISMATCH
No
A value, cast, or function argument has an incompatible type — check comparisons (e.g. string field vs. numeric literal), CAST expressions, and aggregate function inputs
GET /api/v3.4/investigations/{request_id}/ — Get Results
Full URL: https://{your-brain}/api/v3.4/investigations/{request_id}/
Method: GET
Headers:
Authorization
Bearer {access_token}
Query parameters:
page
integer
1
Page number (1-indexed)
page_size
integer
50
Rows per page (max 10,000)
Success response — 200 OK (completed):
json
Response fields:
request_id
top level
Unique ID for this query job
data
top level
Array of result rows — your query results are here
meta.query_status
meta
"RUNNING" while executing, "SUCCESS" when complete
meta.num_rows_available
meta
Total rows returned by the query
meta.page
meta
Current page number
meta.page_size
meta
Rows returned on this page
meta.estimated_file_size_bytes
meta
Estimated size of the full result set in bytes
meta.columns
meta
Array of [column_name, [{type}, hint]] tuples describing the result schema
Checking for more pages: Compare
meta.num_rows_availableagainstmeta.page_size. Ifnum_rows_available > page_size, there are additional pages to retrieve. Increment thepageparameter until you have collected all rows.
Still running — 202 OK:
json
Polling tip: The response structure is identical whether the query is running or complete —
dataandcolumnsare simply empty arrays while the query is in progress. Poll untilmeta.query_statusequals"SUCCESS"before readingdata.
Failed query response (expected structure):
json
Query Language Reference
Table Naming Convention
All table names must end with the ._all suffix:
sql
Supported Clauses
SELECT
✅
Use * or specific fields
FROM
✅
One table per query (no JOIN)
WHERE
✅
Standard filters + functions
ORDER BY
✅
ASC or DESC
LIMIT
✅
Recommended; maximum 10,000
GROUP BY
✅
For aggregation queries
HAVING
✅
Filter on aggregate results
UNION ALL
✅
Combine results from multiple queries
Subqueries
✅
Supported
JOIN
❌
Not supported — use UNION ALL instead
Plain UNION
❌
Use UNION ALL
INSERT / UPDATE / DELETE
❌
Read-only API
Field Notation Rules
Struct fields must use dot-notation in WHERE and ORDER BY clauses. Using a flat field name in a filter position returns 400 Bad Request.
sql
Note: In
SELECT, either form works —SELECT id.orig_hreturns the column asorig_hin results.
Supported Functions
Aggregate functions: COUNT, MAX, MIN, SUM, AVG, STDDEV, STDDEV_SAMP, STDDEV_POP
String functions: LOWER, UPPER, LENGTH, ABS, CONCAT, CONTAINS, COALESCE
Time functions: DATE, NOW, DATE_ADD, DATE_DIFF, FROM_ISO8601_TIMESTAMP, FROM_UNIXTIME, TO_UNIXTIME
Regex functions: REGEXP_COUNT, REGEXP_EXTRACT_ALL, REGEXP_EXTRACT, REGEXP_LIKE, REGEXP_POSITION, REGEXP_REPLACE, REGEXP_SPLIT
Type casting: TRY_CAST, CAST
Array / predicate functions: ANY_MATCH, ALL_MATCH, DISTINCT, ARRAY_AGG, CARDINALITY
Common Time Filter Pattern
sql
Common Fields (All Network Tables)
timestamp
timestamp
Record timestamp — always use this (not ts)
id.orig_h
string
Source (originator) IP address
id.orig_p
integer
Source port
id.resp_h
string
Destination (responder) IP address
id.resp_p
integer
Destination port
local_orig
boolean
true if source IP is internal
local_resp
boolean
true if destination IP is internal
orig_hostname.id
integer
Vectra host entity ID for source host
orig_hostname.name
string
Display name of source host
uid
string
Unique session ID
For complete field listings by table, see the Table Schema Reference.
Sample Queries
All samples below are formatted as complete Postman request bodies (JSON). Submit each as the body of POST /api/v3.4/investigations/.
Basic Queries
1. Recent Outbound Connections (Last 24 Hours)
Returns outbound sessions from internal hosts to external destinations.
json
2. DNS Queries from a Specific Host
Returns all DNS lookups made by a host identified by its IP address.
json
Replace
1.2.3.4with the actual Vectra host IP.
3. HTTP Activity to a Suspicious Domain
Finds all HTTP requests to a specific host header value.
json
4. Failed Kerberos Authentication Attempts
Returns failed Kerberos events which may indicate brute force or password spraying.
json
5. Large Outbound Data Transfers
Identifies sessions where an internal host sent more than 10 MB to an external destination.
json
Advanced Queries
6. Top External Destinations by Connection Count
Aggregates outbound connections grouped by destination IP, ordered by volume. Useful for identifying beaconing or bulk data transfer patterns.
json
7. DNS Tunneling Indicators
Flags suspiciously long DNS query names (potential data exfiltration via DNS tunneling) combined with TXT record lookups.
json
8. Lateral Movement — Internal RDP Sessions
Finds RDP connections between internal hosts, a primary lateral movement indicator.
json
9. AWS IAM Changes in the Last 7 Days
Identifies mutating IAM API calls (user/role/policy changes) in AWS CloudTrail — a key persistence and privilege escalation indicator.
json
10. Entra ID Sign-ins from High-Risk Countries (UNION ALL)
Combines sign-in events from multiple high-risk geographic locations using UNION ALL.
json
Pagination — Retrieving Large Result Sets
When a query matches more rows than your page_size, results are split across multiple pages. Use the next_page field in the response to retrieve subsequent pages.
Step-by-Step Pagination in Postman
Step 1 — Submit the query (POST):
json
Step 2 — Poll for completion (GET):
Step 3 — Retrieve page 1 (GET):
Step 4 — Continue if next_page is not null:
Stop when you've collected all rows — compare meta.num_rows_available (total rows) against meta.page * meta.page_size. When the rows you've collected equals meta.num_rows_available, you're done.
Performance tip: Use a
page_sizeof 500–1,000 for large result sets to balance payload size and number of round trips. Your results will be in thedataarray of each response.
Troubleshooting
SYNTAX_ERROR in POST response
Missing ._all suffix — parser expects . before WHERE
Change FROM network.isession to FROM network.isession._all
SYNTAX_ERROR in POST response
Malformed SQL — bad clause order, unmatched quotes, unsupported keyword
Check extra[].offending_symbol and extra[].message for the exact location
COLUMN_NOT_FOUND in GET response
Field name doesn't exist in the table
Check the schema reference for correct field names — e.g. orig_ip_bytes not orig_bytes
COLUMN_NOT_FOUND in GET response
Flat field name used in WHERE clause
Change WHERE resp_h = '...' to WHERE id.resp_h = '...'
TYPE_MISMATCH in GET response
Incompatible types in a comparison, cast, or function
Check that field types match their usage — e.g. wrap array fields with CAST(... AS VARCHAR) before using REGEXP_LIKE, avoid comparing string fields to numeric literals
400 Bad Request
Unsupported clause (e.g., JOIN)
Remove JOIN; use UNION ALL to combine results from multiple queries
401 Unauthorized
Missing or expired token
Re-authenticate via POST /oauth2/token and update the Bearer token
403 Forbidden
Insufficient API client permissions
Ensure the API client has Investigations API access in Vectra Admin → API Clients
429 Too Many Requests
Rate limit exceeded
Wait 60 seconds before retrying; the limit is 5 POST requests/minute
error object present in GET response
Runtime error (e.g. bad field name)
Check error.extra[].error_name and error.extra[].column for the specific cause
Query returns 0 results
Time range too narrow or wrong table
Widen the time range; verify the correct table name in the schema reference
Query times out
Query spans too much data
Add a more specific WHERE clause to narrow the result set; reduce time range
Struct field returns null
External IP has no Vectra host record
orig_hostname / resp_hostname fields are null for external IPs — this is expected
UNION ALL returns duplicate columns
Column alias mismatch between branches
Ensure both SELECT branches have identical column count and matching aliases
Rate Limits & Constraints
POST requests (submit query)
5 per minute
Maximum rows per query
10,000
Maximum lookback period
14 days
Minimum polling interval
1 second (recommended: 2–3 seconds)
Authentication token lifetime
3,600 seconds (1 hour) — refresh before expiry
Bulk data retrieval: If you need more than 10,000 rows, split your query into multiple time windows and submit them sequentially. For example, query 4-hour windows over a 24-hour period to retrieve up to 60,000 rows total.
Appendix A — Where to Find Table Schemas
This guide covers the query mechanics and provides sample queries, but does not document every available table and field. Full schema documentation — including all available tables, field names, types, and example values — is maintained separately.
📄 See the Vectra Investigate API Query Schema Reference for complete field listings:
Appendix B — Quick Reference: Endpoint Summary
Obtain access token
POST
https://{brain}/oauth2/token
Submit a query
POST
https://{brain}/api/v3.4/investigations/
Poll for results
GET
https://{brain}/api/v3.4/investigations/{request_id}/
Get page 2+
GET
https://{brain}/api/v3.4/investigations/{request_id}/?page=2&page_size=500
Appendix C — Postman Tests Script (Auto-Capture Request ID)
Paste the following into the Tests tab of your POST request in Postman to automatically store the returned request_id as a collection variable. This eliminates the need to manually copy the ID between requests.
javascript
How to set this up in Postman:
Open your POST
/api/v3.4/investigations/requestClick the Tests tab (next to Headers, Body, etc.)
Paste the script above
In your GET request URL, use:
https://{{VECTRA_BRAIN}}/api/v3.4/investigations/{{request_id}}/Every time you run the POST request,
request_idis automatically updated — then just send the GET request to poll for results
Collection variables to configure:
VECTRA_BRAIN
your-brain.vectra.ai
Your Vectra Brain hostname
VECTRA_TOKEN
eyJhbGci...
OAuth Bearer token (refresh every hour)
request_id
(auto-populated by test script)
Captured from POST response automatically
Token auto-refresh tip: Add a pre-request script to your collection to check token expiry and re-authenticate automatically if needed. Store the token expiry timestamp in a
TOKEN_EXPIRYcollection variable and compare againstDate.now()before each request.
Vectra AI — Investigations API User Guide | Version 1.0 | March 2026
Last updated
Was this helpful?