Queries and gap analysis
These endpoints run SQL queries across all states and return the SQL schema. They also find the cloud resources that no state manages, and generate import blocks for them.
SQL queries
Execute SQL query
GET /api/v1/mql
Runs an SQL query across all states.
| Parameter | Type | Required | Description |
|---|---|---|---|
q |
query | Yes | SQL query string |
page |
query | No | Pagination cursor |
tz |
query | No | Timezone for date formatting |
curl "$STATEGRAPH_API_BASE/api/v1/mql?q=SELECT%20*%20FROM%20resources%20WHERE%20type%20%3D%20%27aws_instance%27" \
-H "Authorization: Bearer $STATEGRAPH_API_KEY"
Response:
[
{
"address": "aws_instance.web",
"type": "aws_instance",
...
}
]
Result size:
- The
LIMITclause of the query sets the number of rows, up to 1000. The endpoint ignores alimitquery parameter: put the bound inLIMIT. - A query without
LIMITreturns at most 20 rows. When this default truncates the result, the response has anmql-default-limit-appliedheader, so that clients can detect the truncation and not undercount. - To get more rows, add an explicit
LIMIT, or paginate withORDER BY.
Get SQL schema reports the active default_limit and max_limit.
Cursor pagination:
Cursor pagination uses an RFC 5988 Link response header. It needs an ORDER BY clause, because deterministic paging requires a stable sort order.
- With
ORDER BY: when more rows remain, the response has aLinkheader withrel="next"(andrel="prev"when you page through results). Follow theLinkURL for the next page.
Link: <https://app.stategraph.cloud/api/v1/mql?page=...&q=...>; rel="next"
- Without
ORDER BY: there is noLinkheader. Themql-pagination-error: ORDER_BY_MISSINGheader tells you that the result cannot be paginated. AddORDER BYto page.
Get SQL schema
GET /api/v1/mql/schema
Returns the SQL schema for autocomplete. With the tables object, it reports default_limit (the row limit for a query without its own LIMIT) and max_limit (the cap on an explicit LIMIT).
Gap analysis
Get gap analysis config
GET /api/v1/tenants/{tenant_id}/gaps/config
Returns the status of the gap analysis configuration.
| Parameter | Type | Required | Description |
|---|---|---|---|
provider |
query | Yes | Cloud provider (for example aws) |
Response:
{
"provider": "aws",
"status": "ready",
"ready_for_gap_analysis": true,
"ready_for_terraform_import": true,
"has_aggregator": true,
"aggregator_region": "us-east-1",
"indexed_regions": ["us-east-1", "us-west-2"],
"index_count": 2,
"warnings": []
}
Run gap analysis
GET /api/v1/tenants/{tenant_id}/gaps
Returns unmanaged resources. Gap analysis runs asynchronously:
- The first request for a provider starts a background scan and returns
{ "status": "running", "started_at": <ts> }. - When the scan completes, repeat the request to get the result.
- Later calls get the result from the cache.
| Parameter | Type | Required | Description |
|---|---|---|---|
provider |
query | Yes | Cloud provider |
source |
query | No | cache (default) or no-cache |
Response:
{
"summary": {
"total_aws_resources": 1500,
"managed_by_stategraph": 1200,
"unmanaged": 300,
"phantom_filtered": 8
},
"unmanaged_resources": [ ... ],
"fetched_at": 1705312800
}
Generate import
POST /api/v1/tenants/{tenant_id}/gaps/import
Generates Terraform import blocks.
Request body:
{
"provider": "aws",
"resources": [
{
"arn": "arn:aws:s3:::bucket-name",
"service": "s3",
"resource_type": "s3:bucket",
"region": "us-east-1",
"owning_account_id": "123456789012"
}
]
}
Response:
{
"import_blocks": "import { ... }",
"provider_hcl": "provider \"aws\" { ... }",
"generated_hcl": "resource \"aws_s3_bucket\" { ... }",
"supported_count": 1,
"unsupported_count": 0,
"unsupported_resources": []
}