curl --request PUT \
--url https://api.dune.com/api/v1/queries/{query_id}/schedule \
--header 'Content-Type: application/json' \
--header 'X-Dune-Api-Key: <x-dune-api-key>' \
--data '
{
"cron_expression": "0 6 * * *",
"performance": "medium",
"expires_at": "<string>"
}
'import requests
url = "https://api.dune.com/api/v1/queries/{query_id}/schedule"
payload = {
"cron_expression": "0 6 * * *",
"performance": "medium",
"expires_at": "<string>"
}
headers = {
"X-Dune-Api-Key": "<x-dune-api-key>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PUT',
headers: {'X-Dune-Api-Key': '<x-dune-api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({cron_expression: '0 6 * * *', performance: 'medium', expires_at: '<string>'})
};
fetch('https://api.dune.com/api/v1/queries/{query_id}/schedule', 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://api.dune.com/api/v1/queries/{query_id}/schedule",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'cron_expression' => '0 6 * * *',
'performance' => 'medium',
'expires_at' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Dune-Api-Key: <x-dune-api-key>"
],
]);
$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://api.dune.com/api/v1/queries/{query_id}/schedule"
payload := strings.NewReader("{\n \"cron_expression\": \"0 6 * * *\",\n \"performance\": \"medium\",\n \"expires_at\": \"<string>\"\n}")
req, _ := http.NewRequest("PUT", url, payload)
req.Header.Add("X-Dune-Api-Key", "<x-dune-api-key>")
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.put("https://api.dune.com/api/v1/queries/{query_id}/schedule")
.header("X-Dune-Api-Key", "<x-dune-api-key>")
.header("Content-Type", "application/json")
.body("{\n \"cron_expression\": \"0 6 * * *\",\n \"performance\": \"medium\",\n \"expires_at\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.dune.com/api/v1/queries/{query_id}/schedule")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["X-Dune-Api-Key"] = '<x-dune-api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"cron_expression\": \"0 6 * * *\",\n \"performance\": \"medium\",\n \"expires_at\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"created": true,
"cron_expression": "0 6 * * *",
"dashboard_id": 123,
"expires_at": "<string>",
"next_execution_time": "<string>",
"performance": "medium",
"query_id": 1234567,
"type": "query"
}{
"error": "Bad Request"
}{
"error": "Invalid API Key"
}{
"error": "This API request would exceed your configured limits per billing cycle."
}{
"error": "Not allowed to execute query. Query is archived, unsaved or not enough permissions"
}{
"error": "Object not found"
}{
"error": "Conflict - The request conflicts with another request."
}{
"error": "Internal error"
}Set Query Schedule
Run a saved query on a schedule. Creates the schedule, or replaces all the settings of the one the query has. Every run is charged credits at the chosen performance tier. You need to own the query or be an editor or admin of the team that owns it; a team API key can only schedule its own team’s queries.
curl --request PUT \
--url https://api.dune.com/api/v1/queries/{query_id}/schedule \
--header 'Content-Type: application/json' \
--header 'X-Dune-Api-Key: <x-dune-api-key>' \
--data '
{
"cron_expression": "0 6 * * *",
"performance": "medium",
"expires_at": "<string>"
}
'import requests
url = "https://api.dune.com/api/v1/queries/{query_id}/schedule"
payload = {
"cron_expression": "0 6 * * *",
"performance": "medium",
"expires_at": "<string>"
}
headers = {
"X-Dune-Api-Key": "<x-dune-api-key>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PUT',
headers: {'X-Dune-Api-Key': '<x-dune-api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({cron_expression: '0 6 * * *', performance: 'medium', expires_at: '<string>'})
};
fetch('https://api.dune.com/api/v1/queries/{query_id}/schedule', 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://api.dune.com/api/v1/queries/{query_id}/schedule",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'cron_expression' => '0 6 * * *',
'performance' => 'medium',
'expires_at' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Dune-Api-Key: <x-dune-api-key>"
],
]);
$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://api.dune.com/api/v1/queries/{query_id}/schedule"
payload := strings.NewReader("{\n \"cron_expression\": \"0 6 * * *\",\n \"performance\": \"medium\",\n \"expires_at\": \"<string>\"\n}")
req, _ := http.NewRequest("PUT", url, payload)
req.Header.Add("X-Dune-Api-Key", "<x-dune-api-key>")
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.put("https://api.dune.com/api/v1/queries/{query_id}/schedule")
.header("X-Dune-Api-Key", "<x-dune-api-key>")
.header("Content-Type", "application/json")
.body("{\n \"cron_expression\": \"0 6 * * *\",\n \"performance\": \"medium\",\n \"expires_at\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.dune.com/api/v1/queries/{query_id}/schedule")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["X-Dune-Api-Key"] = '<x-dune-api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"cron_expression\": \"0 6 * * *\",\n \"performance\": \"medium\",\n \"expires_at\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"created": true,
"cron_expression": "0 6 * * *",
"dashboard_id": 123,
"expires_at": "<string>",
"next_execution_time": "<string>",
"performance": "medium",
"query_id": 1234567,
"type": "query"
}{
"error": "Bad Request"
}{
"error": "Invalid API Key"
}{
"error": "This API request would exceed your configured limits per billing cycle."
}{
"error": "Not allowed to execute query. Query is archived, unsaved or not enough permissions"
}{
"error": "Object not found"
}{
"error": "Conflict - The request conflicts with another request."
}{
"error": "Internal error"
}Read/WriteUse Cases
- Keep Results Fresh: Run a query every few hours, then read the latest result without executing it yourself.
- Change a Schedule: Change the frequency or performance tier. The response has
created: falsewhen it replaced a schedule. - Safe Retries: The call is idempotent. Sending the same settings again leaves one schedule with those settings.
Headers
API Key for the service
Path Parameters
Query ID
Body
Schedule settings
CronExpression is a 5-part cron expression in UTC. Supported: every 15, 20 or 30 minutes ("*/15 * * * *"), hourly ("0 * * * *"), every 2, 3, 4, 6, 8 or 12 hours ("0 */6 * * *"), daily ("M H * * *") and weekly ("M H * * D"), where M is 0 or 30.
"0 6 * * *"
Performance is the engine tier each run uses and must be available on your plan. Every run is charged credits at this tier.
small, medium, large "medium"
ExpiresAt stops the schedule after this time. Omitted means no expiration; the free plan caps it at 3 months.
Response
OK
Created is true when the request created the schedule, false when it replaced one.
CronExpression is a 5-part cron expression in UTC.
"0 6 * * *"
DashboardID is set for dashboard schedules.
ExpiresAt is absent when the schedule does not expire.
Performance is the engine tier each run uses.
small, medium, large "medium"
QueryID is set for query and materialized view schedules.
1234567
Type is what the schedule runs. Materialized view refreshes are managed through the /v1/materialized-views endpoints.
query, dashboard, materialized_view "query"
Was this page helpful?