The Insights API lets you programmatically run any report available in the Insights tab and Reporter tab of the Recast app, retrieve the results, and download the underlying data as CSVs. This is useful for automating recurring analyses, integrating Recast outputs into your own reporting pipelines, or running the same report across multiple date ranges at once.
See the Swagger documentation for full schema details: https://app.getrecast.com/api-docs
Recommended Workflow
The simplest way to get started is to run the report you want in the Recast UI first. Once it completes successfully, use GET /reports/[id] to retrieve the form that was used. You can then resubmit that form, changing only the fields you need, rather than constructing the request from scratch.
The Endpoints
-
GET /reports/[id] - Returns an existing report with its status, form, and available downloads
-
POST /reports - Creates a new report
-
name- optional label for the report -
show_in_ui- whether to show it in the Reporter tab of the app
-
-
GET /reports - Returns a paginated list of all reports. Supports an optional
report_typequery parameter to filter results -
GET /reports/[id]/downloads/[key] - Returns a CSV download for a completed report
The Form
The form contains all the inputs for a report. It always requires deployment_id and report_type. Additional fields depend on the report type.
{ "name": "My Report",
"show_in_ui": false,
"form": {
"deployment_id": 5716,
"report_type": "compute_waterfall_summary",
"start_date": "2024-01-01",
"end_date": "2024-03-31"
}
}
Report Types
The table below lists every available report type and the additional form fields each one requires. deployment_id and report_type are always required and are not repeated in the table.
|
Report type |
What it shows |
Additional required fields |
|---|---|---|
|
|
Channel-by-channel KPI contribution — impact (shifted/unshifted), ROI, and MROI for the period |
|
|
|
Spend per channel over the period, in daily/weekly/monthly/total views |
|
|
|
Breakdown of how much KPI was earned during the period vs. how much will be earned after from spend in the period |
|
|
|
Split between marketing-driven KPI and organic/baseline KPI over time |
|
|
|
Which upper funnel channels drove spend into lower funnel channels, with rates and KPI effects |
|
|
|
Model fit quality — predicted vs. actual KPI in-sample, with summary statistics |
|
|
|
Effect of contextual variables (e.g. price, brand awareness) on marketing effectiveness over time. Only available for models with contextual variables configured. |
|
|
|
|
|
|
|
Non-marketing (organic/baseline) contribution to KPI over time, optionally including spike effects |
|
|
|
Each channel's share-of-effect minus share-of-spend over time. Positive = overperforming, negative = underperforming. |
|
|
|
ROI/CPA decomposed into its drivers: baseline ROI, context variables, saturation, changing saturation, and seasonal effects |
|
|
|
Direct ROI vs. Total ROI over time for selected channels, showing how much of total ROI is driven by lower funnel activity |
|
|
|
Shows the difference between eh models' priors and posterior estimate |
|
|
|
Daily spend per channel vs. modeled KPI, plus correlation coefficients between channels |
|
|
|
Time delay between when a channel spends and when the effect is realized |
|
|
|
Impact, ROI/CPA, and MROI/MCPA at various spend levels for selected channels, anchored to actual spend in the lookback window |
|
|
|
Channel performance estimated at a fixed spend level |
|
|
|
Channel performance with user-defined groupings: impact, ROI, and MROI per group across daily/weekly/monthly/total granularities |
|
|
|
ROI/CPA, mROI/mCPA, baseline, in-period effect, and total outcome compared across two time periods. |
|
|
|
Day-by-day effect of promotions, holidays, and spikes on KPI, including pull-forward and pull-back. Provides both individual and grouped spike views. |
(none) |
|
|
Out-of-sample backtest results showing how well the model predicts data it was not trained on |
(none) |
|
|
Model configuration tables: priors, ROI settings, shift parameters, spike definitions, and channel linkings |
(none) |
|
|
Experiment results summary |
(none) |
|
|
The clean input data used by the model, exported as a CSV |
(none) |
Polling
Reports run asynchronously. After creating a report, poll GET /reports/[id] until status is no longer "ready" or "processing". Typical run times are 1–5 minutes. The results field which contains the list of available download keys is only present when status == "success".
Downloads
Each download key returns a CSV. The available keys are listed in results.downloads on the show response. For report types where the keys depend on inputs (e.g. compute_grouped_channel_summaries generates one CSV per group × metric × granularity combination), inspect results.downloads to see the full list of available downloads.
The downloads currently do not include the plots available on each insights report.
SKILL.md (Beta)
We have a skill file that can help AI coding assistants get up to speed on the Insights API. It is in beta and is best used for writing boilerplate code. Please review any generated code before running it.
Code Examples
R
# Recast Insights API — R Usage Example
#
# Workflow: Create report → Poll → List downloads → Download CSVs
#
# Prerequisites:
# install.packages(c("httr2", "jsonlite"))
# Add to .Renviron: API_PAT=gr_your_token_here
library(httr2)
library(jsonlite)
BASE_URL <- "https://api.getrecast.com"
CLIENT_SLUG <- "democlient" # Update to your client slug
PAT <- Sys.getenv("API_PAT")
parse <- \(resp) resp |> resp_body_string() |> fromJSON(simplifyVector = FALSE)
check <- function(resp, expected = 200) {
if (resp_status(resp) != expected) {
stop(sprintf("HTTP %d: %s", resp_status(resp), resp_body_string(resp)))
}
resp
}
report_request <- function(...) {
request(BASE_URL) |>
req_url_path_append("v1", "clients", CLIENT_SLUG, "reports", ...) |>
req_auth_bearer_token(PAT) |>
req_headers(Accept = "application/json") |>
req_error(is_error = \(resp) FALSE)
}
# Step 1: Create a report
# report_type goes inside form, not at the top level
resp <- report_request() |>
req_method("POST") |>
req_headers(`Content-Type` = "application/json") |>
req_body_json(list(
name = paste("Channel Attribution -", Sys.Date()),
show_in_ui = FALSE,
form = list(
deployment_id = 5716, # Update to your deployment ID
report_type = "compute_waterfall_summary",
start_date = "2024-01-01",
end_date = "2024-03-31"
)
), auto_unbox = TRUE) |>
req_perform() |>
check(expected = 201)
report_id <- parse(resp)$id
cat(sprintf("Created report %d\n", report_id))
# Step 2: Poll until complete (typically 1–5 minutes)
repeat {
result <- report_request(report_id) |> req_perform() |> check() |> parse()
cat(sprintf("Status: %s\n", result$status))
if (!result$status %in% c("ready", "processing")) break
Sys.sleep(30)
}
if (result$status != "success") {
stop(sprintf("Report ended with status: %s", result$status))
}
# Step 3: Download all available CSVs
downloads <- result$results$downloads
cat(sprintf("Downloading %d files...\n", length(downloads)))
for (dl in downloads) {
csv_resp <- report_request(report_id, "downloads", dl$key) |>
req_perform() |>
check()
df <- read.csv(text = resp_body_string(csv_resp), check.names = FALSE)
write.csv(df, file = paste0(dl$key, ".csv"), row.names = FALSE)
cat(sprintf(" Saved %s.csv (%d rows)\n", dl$key, nrow(df)))
}
Python
Recast Insights API — Python Usage Example
#
# Workflow: Create report → Poll → List downloads → Download CSVs
#
# Prerequisites:
# pip install requests pandas
# Set environment variable: export API_PAT=gr_your_token_here
import os
import time
import io
import requests
import pandas as pd
from datetime import date
BASE_URL = "https://api.getrecast.com"
CLIENT_SLUG = "democlient" # Update to your client slug
PAT = os.environ["API_PAT"]
BASE_PATH = f"{BASE_URL}/v1/clients/{CLIENT_SLUG}/reports"
HEADERS = {"Authorization": f"Bearer {PAT}", "Accept": "application/json"}
def check(resp, expected=200):
if resp.status_code != expected:
raise Exception(f"HTTP {resp.status_code}: {resp.text}")
return resp
# Step 1: Create a report
# report_type goes inside form, not at the top level
resp = check(requests.post(
BASE_PATH,
headers={**HEADERS, "Content-Type": "application/json"},
json={
"name": f"Channel Attribution - {date.today()}",
"show_in_ui": False,
"form": {
"deployment_id": 5716, # Update to your deployment ID
"report_type": "compute_waterfall_summary",
"start_date": "2024-01-01",
"end_date": "2024-03-31",
},
},
), expected=201)
report_id = resp.json()["id"]
print(f"Created report {report_id}")
# Step 2: Poll until complete (typically 1–5 minutes)
while True:
result = check(requests.get(f"{BASE_PATH}/{report_id}", headers=HEADERS)).json()
print(f"Status: {result['status']}")
if result["status"] not in ("ready", "processing"):
break
time.sleep(30)
if result["status"] != "success":
raise Exception(f"Report ended with status: {result['status']}")
# Step 3: Download all available CSVs
downloads = result["results"]["downloads"]
print(f"Downloading {len(downloads)} files...")
for dl in downloads:
csv_resp = check(requests.get(
f"{BASE_PATH}/{report_id}/downloads/{dl['key']}",
headers=HEADERS,
))
df = pd.read_csv(io.StringIO(csv_resp.text))
df.to_csv(f"{dl['key']}.csv", index=False)
print(f" Saved {dl['key']}.csv ({len(df)} rows)")