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 -
use_latest_deployments--- (default:true) if this istrue, it uses the latest deployed model for the client regardless of thedeployment_idin the form. If this isfalse, uses the deployment specified bydeployment_idin the form
-
-
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,
"use_latest_deployments": true,
"form": {
"deployment_id": 5716,
"report_type": "compute_insights_overview",
"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 for the period: impact (shifted/unshifted), ROI, MROI, and each channel's direct contribution to the KPI |
|
|
|
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, as an overall percentage and as a daily series with quantiles |
|
|
|
Each channel's lack-of-signal score and spend, plus a per-channel breakdown of how much of its uncertainty comes from other channels, baseline, spikes, or lack of signal |
|
|
|
Which upper funnel channels drove spend into lower funnel channels, with rates and KPI effects |
|
|
|
Model fit quality: summary statistics, plus daily predicted vs. actual KPI with the model's mean, median and quantiles |
|
|
|
Daily effect of each contextual variable (e.g. price, brand awareness) on baseline and marketing, alongside the variable's value. 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, one download per channel. Positive = overperforming, negative = underperforming. |
|
|
|
ROI/CPA for each selected channel broken into its drivers (baseline, context variables, channel saturation, changing saturation, and weekly effects), in one download |
|
|
|
Daily direct vs. total ROI/CPA for selected channels, plus which lower funnel channels each one drives spend into |
|
|
|
The model's prior vs. posterior time shifts for selected channels, and the daily baseline posterior against its prior range |
|
|
|
Daily spend per selected channel next to the KPI, raw and scaled, plus correlation coefficients between channels |
|
|
|
Time delay between when a channel spends and when the effect is realized, as cumulative and non-cumulative curves by day |
|
|
|
Impact, ROI/CPA, and MROI/MCPA at various spend levels for selected channels, anchored to actual spend in the lookback window, as tables and full curves |
|
|
|
Daily impact, ROI/CPA and mROI/mCPA for selected channels at a fixed daily spend level, as mean and quantiles |
|
|
|
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, overall and for each channel, including channel spend |
|
|
|
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, with daily and cumulative predicted vs. actual KPI for each backtest window |
(none) |
|
|
Model configuration tables: priors, ROI settings, shift parameters, lift tests, spike definitions, channel linkings, and subchannels |
(none) |
|
|
Weekly spend for each subchannel within its parent channel over the trailing 90 days. Only available for models with subchannels |
(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 include the data behind the plots on each report, so you get the same numbers you see in the app's charts. A few things to know when working with them:
-
Some reports return a large number of downloads, for example one uncertainty breakdown file is provided per channel in
compute_channel_uncertainty_drivers. Pick the keys you need fromresults.downloadsand download only those, rather than downloading everything and searching through it. -
Some keys depend on the model type. For example, the waterfall download is
roi_waterfallfor an ROI model andcpa_waterfallfor a CPA model, so read the keys fromresults.downloadsrather than hard-coding them.
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: Fetch latest deployment โ Create report โ Poll โ Download CSVs
#
# Prerequisites:
# install.packages(c("httr2", "jsonlite"))
# Add to .Renviron: API_PAT=gr_your_token_here
# Generate your token: log into the Recast app โ click your email (top right) โ Generate API token
# Store it in ~/.Renviron as: API_PAT=gr_your_token_here
# Never hard-code or share this token.
library(httr2)
library(jsonlite)
# โโ Edit these โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
CLIENT_SLUG <- "myslug" # Your client slug (visible in the app URL)
REPORT_TYPE <- "compute_insights_overview"
START_DATE <- "2024-01-01"
END_DATE <- "2024-03-31"
SHOW_IN_UI <- FALSE # TRUE to see this report in the app
USE_LATEST_DEPLOYMENTS <- TRUE # FALSE to pin to the deployment_id in the form
# โโ Rarely needs changing โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
BASE_URL <- "https://api.getrecast.com"
PAT <- Sys.getenv("API_PAT") # Set in ~/.Renviron, never hard-coded
# โโ Helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
parse <- \(resp) resp |> resp_body_string() |> fromJSON(simplifyVector = FALSE)
check <- function(resp, expected = 200) {
if (resp_status(resp) != expected) {
body <- tryCatch(resp_body_string(resp), error = \(e) "(no body)")
stop(sprintf("HTTP %d: %s", resp_status(resp), body))
}
resp
}
api_request <- function(...) {
request(BASE_URL) |>
req_url_path_append("v1", "clients", CLIENT_SLUG, ...) |>
req_auth_bearer_token(PAT) |>
req_headers(Accept = "application/json") |>
req_error(is_error = \(resp) FALSE)
}
# โโ Step 1: Fetch the latest deployment โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# deployment_id is required in the form even when USE_LATEST_DEPLOYMENTS = True.
# In this case, the API uses the latest deployment at run time, but still validates that
# deployment_id is present and belongs to this client.
deployment_id <- api_request("deployments") |>
req_perform() |> check() |> parse() |>
(\(r) r$data[[1]]$id)()
cat(sprintf("Using deployment ID: %d\n", deployment_id))
# โโ Step 2: Create a report โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
resp <- api_request("reports") |>
req_method("POST") |>
req_headers(`Content-Type` = "application/json") |>
req_body_json(list(
name = paste("Channel Attribution -", Sys.Date()),
show_in_ui = SHOW_IN_UI,
use_latest_deployments = USE_LATEST_DEPLOYMENTS,
form = list(
deployment_id = deployment_id,
report_type = REPORT_TYPE,
start_date = START_DATE,
end_date = END_DATE
)
), auto_unbox = TRUE) |>
req_perform() |>
check(expected = 201)
report_id <- parse(resp)$id
cat(sprintf("Created report %d\n", report_id))
# โโ Step 3: Poll until complete (typically 1--5 minutes) โโโโโโโโโโโโโโโโโโโโโโ
repeat {
result <- api_request("reports", 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 4: Download all available CSVs โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
downloads <- result$results$downloads
cat(sprintf("Downloading %d files...\n", length(downloads)))
for (dl in downloads) {
csv_resp <- api_request("reports", 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
# Recast Insights API --- Python Usage Example
#
# Workflow: Fetch latest deployment โ Create report โ Poll โ Download CSVs
#
# Prerequisites:
# pip install requests pandas
# Generate your token: log into the Recast app โ click your email (top right) โ Generate API token
# Set as an environment variable: export API_PAT=gr_your_token_here
# Never hard-code or share this token.
import os
import time
import io
import requests
import pandas as pd
from datetime import date
# โโ Edit these โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
CLIENT_SLUG = "myslug" # Your client slug (visible in the app URL)
REPORT_TYPE = "compute_insights_overview"
START_DATE = "2024-01-01"
END_DATE = "2024-03-31"
SHOW_IN_UI = False # True to see this report in the app
USE_LATEST_DEPLOYMENTS = True # False to pin to the deployment_id in the form
# โโ Rarely needs changing โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
BASE_URL = "https://api.getrecast.com"
PAT = os.environ["API_PAT"] # Set via: export API_PAT=gr_your_token_here
BASE_PATH = f"{BASE_URL}/v1/clients/{CLIENT_SLUG}"
HEADERS = {"Authorization": f"Bearer {PAT}", "Accept": "application/json"}
# โโ Helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
def check(resp, expected=200):
if resp.status_code != expected:
body = resp.text or "(no body)"
raise Exception(f"HTTP {resp.status_code}: {body}")
return resp
# โโ Step 1: Fetch the latest deployment โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# deployment_id is required in the form even when USE_LATEST_DEPLOYMENTS = True.
# In this case, the API uses the latest deployment at run time, but still validates that
# deployment_id is present and belongs to this client.
deployments = check(requests.get(f"{BASE_PATH}/deployments", headers=HEADERS)).json()
deployment_id = deployments["data"][0]["id"]
print(f"Using deployment ID: {deployment_id}")
# โโ Step 2: Create a report โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
resp = check(requests.post(
f"{BASE_PATH}/reports",
headers={**HEADERS, "Content-Type": "application/json"},
json={
"name": f"Channel Attribution - {date.today()}",
"show_in_ui": SHOW_IN_UI,
"use_latest_deployments": USE_LATEST_DEPLOYMENTS,
"form": {
"deployment_id": deployment_id,
"report_type": REPORT_TYPE,
"start_date": START_DATE,
"end_date": END_DATE,
},
},
), expected=201)
report_id = resp.json()["id"]
print(f"Created report {report_id}")
# โโ Step 3: Poll until complete (typically 1--5 minutes) โโโโโโโโโโโโโโโโโโโโโโ
while True:
result = check(requests.get(f"{BASE_PATH}/reports/{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 4: 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}/reports/{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)")