Preview an unsaved config
Run an insight config in-memory and return the same payload as the saved-insight runner — without persisting anything. Use it for a builder's live "Run" on a draft. Requires an agency key (ak_) and ?location_id=. Charges the query rate-limit allowance. Identical drafts are served from a 120-second cache so keystroke-driven previews are free to repeat.
curl -X POST "https://mythic-analytics.gulp.workers.dev/builder/insights/preview?location_id=acme-retail" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{
"insight_type": "number",
"config": {
"query": {
"kind": "trend",
"date_range": {
"preset": "30d"
},
"interval": "day",
"series": [
{
"event": "$pageview",
"math": "dau",
"label": "Visitors"
}
],
"compare": "previous_period"
}
},
"dateRange": {},
"granularity": "hour"
}'
import requests
import json
url = "https://mythic-analytics.gulp.workers.dev/builder/insights/preview?location_id=acme-retail"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN"
}
data = {
"insight_type": "number",
"config": {
"query": {
"kind": "trend",
"date_range": {
"preset": "30d"
},
"interval": "day",
"series": [
{
"event": "$pageview",
"math": "dau",
"label": "Visitors"
}
],
"compare": "previous_period"
}
},
"dateRange": {},
"granularity": "hour"
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const response = await fetch("https://mythic-analytics.gulp.workers.dev/builder/insights/preview?location_id=acme-retail", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN"
},
body: JSON.stringify({
"insight_type": "number",
"config": {
"query": {
"kind": "trend",
"date_range": {
"preset": "30d"
},
"interval": "day",
"series": [
{
"event": "$pageview",
"math": "dau",
"label": "Visitors"
}
],
"compare": "previous_period"
}
},
"dateRange": {},
"granularity": "hour"
})
});
const data = await response.json();
console.log(data);
package main
import (
"fmt"
"net/http"
"bytes"
"encoding/json"
)
func main() {
data := []byte(`{
"insight_type": "number",
"config": {
"query": {
"kind": "trend",
"date_range": {
"preset": "30d"
},
"interval": "day",
"series": [
{
"event": "$pageview",
"math": "dau",
"label": "Visitors"
}
],
"compare": "previous_period"
}
},
"dateRange": {},
"granularity": "hour"
}`)
req, err := http.NewRequest("POST", "https://mythic-analytics.gulp.workers.dev/builder/insights/preview?location_id=acme-retail", bytes.NewBuffer(data))
if err != nil {
panic(err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer YOUR_API_TOKEN")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
fmt.Println("Response Status:", resp.Status)
}
require 'net/http'
require 'json'
uri = URI('https://mythic-analytics.gulp.workers.dev/builder/insights/preview?location_id=acme-retail')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Content-Type'] = 'application/json'
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request.body = '{
"insight_type": "number",
"config": {
"query": {
"kind": "trend",
"date_range": {
"preset": "30d"
},
"interval": "day",
"series": [
{
"event": "$pageview",
"math": "dau",
"label": "Visitors"
}
],
"compare": "previous_period"
}
},
"dateRange": {},
"granularity": "hour"
}'
response = http.request(request)
puts response.body
{}
{
"error": "Bad Request",
"message": "The request contains invalid parameters or malformed data",
"code": 400,
"details": [
{
"field": "email",
"message": "Invalid email format"
}
]
}
{
"error": "Forbidden",
"message": "You don't have permission to access this resource",
"code": 403
}
{
"error": "Too Many Requests",
"message": "Rate limit exceeded. Please try again later",
"code": 429,
"retryAfter": 3600
}
/insights/preview
Target server for requests. Edit to use your own host.
Builder key as a bearer token. Use an agency key (Bearer ak_...) for writes, or a viewer key (Bearer sk_...) for reads. Scoped keys (mcp_) with insights:read/insights:write are accepted too.
Bearer ak_...) for writes, or a viewer key (Bearer sk_...) for reads. Scoped keys (mcp_) with insights:read/insights:write are accepted too.Client location to scope the request to. Required when authenticating with an agency key (ak_). Ignored for viewer keys (sk_).
The media type of the request body
Visualization only. funnel draws a kind: funnel spec; table, bar and number can also draw kind: attribution; the others draw a kind: trend spec (line/table want an interval, number/bar/pie usually don't). Analysis panels: constraints → table/number/bar, correlation → line/table, scanner_summary → pie/bar/line/table/number.
Exactly one of query or analysis. query is an explore spec — the same object POST /client/v1/data/query takes (trend, funnel, or attribution for ad spend, CPA and ROAS); the full grammar is on the Explore queries page and a per-type walkthrough on the Insight config reference. analysis runs a constraint finder, correlation or scanner summary as the viewer (not on public or embedded dashboards). Any other key is stored verbatim as display settings and never read by the runner, except metric on an attribution number.
Optional override of config.query.date_range, e.g. { "preset": "7d" } or { "preset": "custom", "from": "2026-08-01", "to": "2026-08-31" }. Presets: 24h, 7d, 14d, 30d, 90d, 365d, custom.
Optional override of config.query.interval (trends only).
Request Preview
Response
Response will appear here after sending the request
Authentication
Bearer token. Builder key as a bearer token. Use an agency key (Bearer ak_...) for writes, or a viewer key (Bearer sk_...) for reads. Scoped keys (mcp_) with insights:read/insights:write are accepted too.
Query Parameters
Client location to scope the request to. Required when authenticating with an agency key (ak_). Ignored for viewer keys (sk_).
acme-retailBody
Visualization only. funnel draws a kind: funnel spec; table, bar and number can also draw kind: attribution; the others draw a kind: trend spec (line/table want an interval, number/bar/pie usually don't). Analysis panels: constraints → table/number/bar, correlation → line/table, scanner_summary → pie/bar/line/table/number.
numberlinebarpietablefunnelExactly one of query or analysis. query is an explore spec — the same object POST /client/v1/data/query takes (trend, funnel, or attribution for ad spend, CPA and ROAS); the full grammar is on the Explore queries page and a per-type walkthrough on the Insight config reference. analysis runs a constraint finder, correlation or scanner summary as the viewer (not on public or embedded dashboards). Any other key is stored verbatim as display settings and never read by the runner, except metric on an attribution number.
{"query":{"kind":"trend","date_range":{"preset":"30d"},"interval":"day","series":[{"event":"$pageview","math":"dau","label":"Visitors"}],"compare":"previous_period"}}Optional override of config.query.date_range, e.g. { "preset": "7d" } or { "preset": "custom", "from": "2026-08-01", "to": "2026-08-31" }. Presets: 24h, 7d, 14d, 30d, 90d, 365d, custom.
Optional override of config.query.interval (trends only).
hourdayweekmonthResponses
Query result for the draft — the explore response (series + buckets for a trend, steps for a funnel) with meta, the echoed config, the compiled sql, and cached. Documented under the Queries group (POST /insights/{id}/query).