SELECT only) against a small, well-defined set of virtual tables built from your collections. It is not a general database you write to — it is a read-only analytical view over your own extracted data.
Ways to Query
Synchronous runs return small-to-moderate result sets in one round trip; for anything large, aggregate server-side, raise
max_rows, or use a background export.
When to Use Query vs Search
Running a Query
Send one or more collection IDs and a single read-onlySELECT statement. Each query costs 2 credits; failed runs are refunded automatically. Retrieving results is free.
query_result object with the echoed SQL, columns, rows, and usage:
GET /query/{id} and browse history with GET /query.
The Virtual Schema
Every query executes against three virtual tables built on the fly from the collections you select.The
entities and segment_entities tables carry each file’s most recent
completed extraction only. This is deliberately different from the
collection entities
endpoint, which
merges all extraction jobs — analytics must not double-count re-extractions.files
One row per (file, collection) in the selected collections — a file that belongs to more than one selected collection appears once per collection. Files without extractions appear here but have no entities or segment_entities rows, so use LEFT JOIN when joining those tables, and join on both file_id and collection_id (see Joining across tables).
entities
File-level extracted entities, one row per (field, value element), from each file’s most recent completed extraction only. Files without a completed extraction have no rows here — LEFT JOIN from files on both file_id and collection_id to keep them.
segment_entities
Segment-level extracted entities, one row per segment, from each file’s most recent completed extraction only (same job as the entities table). Files without a completed extraction have no rows here — LEFT JOIN from files on both file_id and collection_id to keep them.
Discovering Your Fields
Before writing SQL, callGET /query/schema to see the virtual tables plus what each collection actually extracts:
fieldsis a flat summary: each extracted field’s name, type, and level.extract_schemais the collection’s extract schema verbatim — it shows the nested value shapes, so you know which JSON paths exist before writingjson_extractexpressions.promptis the collection’s extraction prompt verbatim, when one was configured.
Where each field lives
Thelevel on each field tells you which table to query:
-
level: "file"— the field appears as rows in theentitieslong table. Query it viafield/value/value_text: -
level: "segment"— the field lives inside thesegment_entities.entitiesJSON column. Usejson_extract/json_extract_stringwith a path derived from theextract_schema: -
Metadata collections have
extract_schema: null— there is no extraction to query. Query them through thefilestable’smetadataandsource_metadataJSON columns instead.
Joining across tables
The same file can belong to more than one of the collections you select, in which case it appears once per collection infiles (and its entities appear once per collection in entities / segment_entities). Always join on both file_id and collection_id so a shared file doesn’t cross-join across collections:
Dialect and Restrictions
A query must be a single read-onlySELECT statement. Everything else is rejected:
- DDL and DML (
CREATE,INSERT,UPDATE,DELETE,DROP, …) ATTACH,COPY,SET,PRAGMA,INSTALL,LOAD- Multiple statements in one request
json_extract, json_extract_string, ->, ->>), analytics functions (date_trunc, window functions), and CTEs. See the worked examples below for the JSON extraction syntax. BIGINT values within the safe integer range are returned as JSON numbers.
Worked Examples
Structured queries span file attributes (duration, dimensions, size, timestamps), user metadata, connector source metadata, and extracted entities — you can filter, aggregate, and join across all of them. Count files per ingestion source:topic field in the extract schema):
project key set (use IS NOT NULL to test for a key’s presence):
[*] wildcard path extracts the whole list of email values, then list_contains tests membership:
LEFT JOIN keeps them):
Natural Language Queries
Instead ofsql, send a query in plain English. Cloudglue compiles it to SQL against the same virtual schema and runs it, returning the compiled statement in sql so you can inspect, tweak, and resubmit it as a regular sql query.
query_result shape as a SQL query, with sql set to the compiled statement. Natural language runs cost 4 credits (refunded if compilation fails). Provide exactly one of sql or query.
Dry Run
Adddry_run: true to validate — and, for a natural-language query, compile — a statement and get back the effective SQL plus its output column schema without executing it over any data. No rows are produced, and it is billed at a reduced rate: 1 credit for sql, 2 for a natural-language query.
dry_run cannot be combined with background.
Background Exports
For result sets too large to return inline, run the query as a background export. It streams the full result to a gzipped CSV or JSONL file in cloud storage and hands back a 24-hour signed download URL. Setbackground: true and a format:
status: "in_progress" and an id. Poll GET /query/{id} until status is completed (or failed), then download from download_url:
- The URL is valid for 24 hours and is not refreshed on read — if it expires, re-run the export.
- Output is capped at 2 GB compressed; a run that would exceed it fails with an actionable error and is refunded.
- Natural-language (
query) exports are supported too — the query is compiled server-side before streaming. - Billing: exports reserve 4 credits, reconciled to +1 credit per 100 MB of compressed output on completion. A failed or cancelled export is refunded.
Cancelling an export
POST /query/{id}/cancel stops an in-progress export — the run is aborted mid-stream (the partial upload is discarded) and the reserved credits are refunded:
Querying from the Agent
The Responses API agent (nimbus-002-preview) can run structured queries on your behalf. When an entity collection is part of its knowledge base, the model is given get_structured_data_schema and run_sql_query tools and decides when to query — e.g. to answer “how many meetings had more than three attendees?” it writes and runs the SQL itself. Each query it runs is surfaced as a cloudglue_query_call item in the response output (with the SQL and row counts; result rows are not included), so you never call /v1/query directly.
Limits, Credits, and Errors
Query shape — the number of output columns, joins, CTEs, and nesting depth — is not capped explicitly. Instead it is bounded by the dataset and execution-time limits above: a query that is too large or too complex fails with a
408 (time) or a 400 rather than a dedicated limit error. Aggregate server-side and select only the columns you need to stay well within these bounds.
Caveats
- Latest extraction only.
entitiesandsegment_entitiesreflect each file’s most recent completed extraction. Re-extracting a file replaces its rows in query results; historical extractions are not visible here. - Truncation. Results are truncated at
max_rows, and very large results are also capped when stored — re-fetching such a run viaGET /query/{id}replays the truncated rows. Check thetruncatedflag; if it istrue, narrow the query, aggregate further, or run a background export.