curl --request POST \
--url https://oleander.dev/api/v1/query \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data @- <<EOF
{
"query": "SELECT species, count(*) AS n FROM oleander.default.flowers GROUP BY 1",
"script": "result = events.group_by('day').len()",
"tables": [],
"engine": "auto",
"destination": "default.daily_counts",
"write_mode": "overwrite",
"explain": false
}
EOFimport requests
url = "https://oleander.dev/api/v1/query"
payload = {
"query": "SELECT species, count(*) AS n FROM oleander.default.flowers GROUP BY 1",
"script": "result = events.group_by('day').len()",
"tables": [],
"engine": "auto",
"destination": "default.daily_counts",
"write_mode": "overwrite",
"explain": False
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
query: 'SELECT species, count(*) AS n FROM oleander.default.flowers GROUP BY 1',
script: 'result = events.group_by(\'day\').len()',
tables: [],
engine: 'auto',
destination: 'default.daily_counts',
write_mode: 'overwrite',
explain: false
})
};
fetch('https://oleander.dev/api/v1/query', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://oleander.dev/api/v1/query",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'query' => 'SELECT species, count(*) AS n FROM oleander.default.flowers GROUP BY 1',
'script' => 'result = events.group_by(\'day\').len()',
'tables' => [
],
'engine' => 'auto',
'destination' => 'default.daily_counts',
'write_mode' => 'overwrite',
'explain' => false
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://oleander.dev/api/v1/query"
payload := strings.NewReader("{\n \"query\": \"SELECT species, count(*) AS n FROM oleander.default.flowers GROUP BY 1\",\n \"script\": \"result = events.group_by('day').len()\",\n \"tables\": [],\n \"engine\": \"auto\",\n \"destination\": \"default.daily_counts\",\n \"write_mode\": \"overwrite\",\n \"explain\": false\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://oleander.dev/api/v1/query")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"query\": \"SELECT species, count(*) AS n FROM oleander.default.flowers GROUP BY 1\",\n \"script\": \"result = events.group_by('day').len()\",\n \"tables\": [],\n \"engine\": \"auto\",\n \"destination\": \"default.daily_counts\",\n \"write_mode\": \"overwrite\",\n \"explain\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://oleander.dev/api/v1/query")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"query\": \"SELECT species, count(*) AS n FROM oleander.default.flowers GROUP BY 1\",\n \"script\": \"result = events.group_by('day').len()\",\n \"tables\": [],\n \"engine\": \"auto\",\n \"destination\": \"default.daily_counts\",\n \"write_mode\": \"overwrite\",\n \"explain\": false\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"explain": true,
"engine_decision": {
"engine": "bloom",
"execution_mode": "interactive",
"reasons": [
"not a script",
"no external connection tables",
"not DDL",
"no destination table",
"estimated input 1.2 GiB",
"sandbox 2 vCPU / 4 GB"
],
"size_band": "small",
"estimated_input_bytes": "1288490188",
"vcpus": 2,
"sandbox": {
"vcpus": 2,
"memory_gb": 4
},
"input_tables": [
"oleander.default.events"
]
},
"results": {
"columns": [
"<string>"
],
"column_types": [
"<string>"
],
"rows": [
[
"<unknown>"
]
]
},
"row_count": 123,
"execution_time": "42ms",
"state": "COMPLETE",
"run_id": "<string>",
"output_table": "<string>",
"compute": {},
"job": {
"namespace": "oleander.lake",
"name": "query_9f2c1ab4"
}
}{
"success": false,
"error": "query and script are mutually exclusive."
}{
"success": false,
"error": "Unauthorized"
}{
"success": false,
"error": "A payment method is required to run this query"
}{
"success": false,
"error": "This query requires a plan upgrade"
}{
"success": false,
"error": "Query execution failed"
}Query (routed)
The unified query endpoint. oleander parses the SQL, estimates how much data the referenced tables hold, and picks the engine (DuckDB, Polars, Bloom, or Spark) and machine size to match. Leave engine on auto unless you need a specific one.
Interactive reads return rows on the call. A query with a destination, a statement that names its own target, or a read too large for an interactive engine is submitted asynchronously and returns state: "SUBMITTED" with a run_id to poll.
This endpoint supersedes /api/v1/warehouse/query, which pins DuckDB and is sized by the org default rather than by the query.
curl --request POST \
--url https://oleander.dev/api/v1/query \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data @- <<EOF
{
"query": "SELECT species, count(*) AS n FROM oleander.default.flowers GROUP BY 1",
"script": "result = events.group_by('day').len()",
"tables": [],
"engine": "auto",
"destination": "default.daily_counts",
"write_mode": "overwrite",
"explain": false
}
EOFimport requests
url = "https://oleander.dev/api/v1/query"
payload = {
"query": "SELECT species, count(*) AS n FROM oleander.default.flowers GROUP BY 1",
"script": "result = events.group_by('day').len()",
"tables": [],
"engine": "auto",
"destination": "default.daily_counts",
"write_mode": "overwrite",
"explain": False
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
query: 'SELECT species, count(*) AS n FROM oleander.default.flowers GROUP BY 1',
script: 'result = events.group_by(\'day\').len()',
tables: [],
engine: 'auto',
destination: 'default.daily_counts',
write_mode: 'overwrite',
explain: false
})
};
fetch('https://oleander.dev/api/v1/query', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://oleander.dev/api/v1/query",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'query' => 'SELECT species, count(*) AS n FROM oleander.default.flowers GROUP BY 1',
'script' => 'result = events.group_by(\'day\').len()',
'tables' => [
],
'engine' => 'auto',
'destination' => 'default.daily_counts',
'write_mode' => 'overwrite',
'explain' => false
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://oleander.dev/api/v1/query"
payload := strings.NewReader("{\n \"query\": \"SELECT species, count(*) AS n FROM oleander.default.flowers GROUP BY 1\",\n \"script\": \"result = events.group_by('day').len()\",\n \"tables\": [],\n \"engine\": \"auto\",\n \"destination\": \"default.daily_counts\",\n \"write_mode\": \"overwrite\",\n \"explain\": false\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://oleander.dev/api/v1/query")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"query\": \"SELECT species, count(*) AS n FROM oleander.default.flowers GROUP BY 1\",\n \"script\": \"result = events.group_by('day').len()\",\n \"tables\": [],\n \"engine\": \"auto\",\n \"destination\": \"default.daily_counts\",\n \"write_mode\": \"overwrite\",\n \"explain\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://oleander.dev/api/v1/query")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"query\": \"SELECT species, count(*) AS n FROM oleander.default.flowers GROUP BY 1\",\n \"script\": \"result = events.group_by('day').len()\",\n \"tables\": [],\n \"engine\": \"auto\",\n \"destination\": \"default.daily_counts\",\n \"write_mode\": \"overwrite\",\n \"explain\": false\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"explain": true,
"engine_decision": {
"engine": "bloom",
"execution_mode": "interactive",
"reasons": [
"not a script",
"no external connection tables",
"not DDL",
"no destination table",
"estimated input 1.2 GiB",
"sandbox 2 vCPU / 4 GB"
],
"size_band": "small",
"estimated_input_bytes": "1288490188",
"vcpus": 2,
"sandbox": {
"vcpus": 2,
"memory_gb": 4
},
"input_tables": [
"oleander.default.events"
]
},
"results": {
"columns": [
"<string>"
],
"column_types": [
"<string>"
],
"rows": [
[
"<unknown>"
]
]
},
"row_count": 123,
"execution_time": "42ms",
"state": "COMPLETE",
"run_id": "<string>",
"output_table": "<string>",
"compute": {},
"job": {
"namespace": "oleander.lake",
"name": "query_9f2c1ab4"
}
}{
"success": false,
"error": "query and script are mutually exclusive."
}{
"success": false,
"error": "Unauthorized"
}{
"success": false,
"error": "A payment method is required to run this query"
}{
"success": false,
"error": "This query requires a plan upgrade"
}{
"success": false,
"error": "Query execution failed"
}Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Body
Provide either query or script, never both.
The SQL to run.
"SELECT species, count(*) AS n FROM oleander.default.flowers GROUP BY 1"
A Polars DataFrame script that assigns result. Mutually exclusive with query; forces the Polars engine.
"result = events.group_by('day').len()"
Tables a script reads, as "alias=namespace.table" or {alias, table}. Script mode only.
Ask for a specific engine. An impossible combination returns an engine capability error rather than rerouting.
auto, duckdb, polars, bloom, spark Table to write the result to, as a dotted identifier such as namespace.table. Presence of a destination makes this a write.
"default.daily_counts"
Applies to a destination write. A statement that names its own target carries its own semantics.
overwrite, append Return the routing decision without executing anything or spending compute.
Response
Query executed, submitted, or explained.
true
Present and true when explain was requested. No compute was spent and no other result fields are returned.
How the query router chose the engine and machine for this run.
Show child attributes
Show child attributes
Rows, for interactive reads only. Absent on submitted runs.
Show child attributes
Show child attributes
"42ms"
COMPLETE means the write already landed. SUBMITTED means a job is running and run_id must be polled.
COMPLETE, SUBMITTED Run to poll when state is SUBMITTED.
Fully qualified destination table for a write.
The sandbox or cluster that actually served the run. A warm larger sandbox can serve a smaller request, and that is the machine the org is billed for.
Lineage job identity for this query.
Show child attributes
Show child attributes
Was this page helpful?