Recast Knowledge Base
Breadcrumbs

The Insights API

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

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

  1. GET /reports/[id] - Returns an existing report with its status, form, and available downloads

  2. 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 is true, it uses the latest deployed model for the client regardless of the deployment_id in the form. If this is false, uses the deployment specified by deployment_id in the form

  3. GET /reports - Returns a paginated list of all reports. Supports an optional report_type query parameter to filter results

  4. 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.

JSON
{  "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

compute_insights_overview

Channel-by-channel KPI contribution for the period: impact (shifted/unshifted), ROI, MROI, and each channel's direct contribution to the KPI

start_date, end_date

compute_spend_summary

Spend per channel over the period, in daily/weekly/monthly/total views

start_date, end_date

compute_period_summary

Breakdown of how much KPI was earned during the period vs. how much will be earned after from spend in the period

start_date, end_date

compute_marketing_vs_baseline

Split between marketing-driven KPI and organic/baseline KPI, as an overall percentage and as a daily series with quantiles

start_date, end_date

compute_channel_uncertainty_drivers

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

start_date, end_date

compute_lower_funnel_waterfall

Which upper funnel channels drove spend into lower funnel channels, with rates and KPI effects

start_date, end_date

compute_in_sample_model_fit

Model fit quality: summary statistics, plus daily predicted vs. actual KPI with the model's mean, median and quantiles

start_date, end_date

compute_context_variable_summary

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.

start_date, end_date

compute_baseline_summary

Non-marketing (organic/baseline) contribution to KPI over time, optionally including spike effects

start_date, end_date, include_spikes (boolean)

compute_share_of_spend_vs_effect

Each channel's share-of-effect minus share-of-spend over time, one download per channel. Positive = overperforming, negative = underperforming.

start_date, end_date, granularity ("daily", "weekly", or "monthly")

compute_roi_cpa_waterfall_plots

ROI/CPA for each selected channel broken into its drivers (baseline, context variables, channel saturation, changing saturation, and weekly effects), in one download

start_date, end_date, channels (array of channel name strings)

compute_upper_funnel_details

Daily direct vs. total ROI/CPA for selected channels, plus which lower funnel channels each one drives spend into

start_date, end_date, channels

compute_prior_vs_posterior

The model's prior vs. posterior time shifts for selected channels, and the daily baseline posterior against its prior range

start_date, end_date, channels

compute_daily_spend_vs_kpi

Daily spend per selected channel next to the KPI, raw and scaled, plus correlation coefficients between channels

start_date, end_date, channels

compare_shift_curves

Time delay between when a channel spends and when the effect is realized, as cumulative and non-cumulative curves by day

channels

compute_spend_response_curves

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

date (YYYY-MM-DD), num_days (integer 1--90), channels

compute_fixed_spend_performance

Daily impact, ROI/CPA and mROI/mCPA for selected channels at a fixed daily spend level, as mean and quantiles

start_date, end_date, channels, spend (number), include_lower_funnel_effects ("true" or "false")

compute_grouped_channel_summaries

Channel performance with user-defined groupings: impact, ROI, and MROI per group across daily/weekly/monthly/total granularities

start_date, end_date, channel_groups (array of {name, channels}), include_lower_funnel_effects ("true" or "false")

compare_time_periods

ROI/CPA, mROI/mCPA, baseline, in-period effect, and total outcome compared across two time periods, overall and for each channel, including channel spend

previous_start_date, previous_end_date, period_start_date, period_end_date

compute_spike_detail_summary

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)

compute_backtest_summaries

Out-of-sample backtest results, with daily and cumulative predicted vs. actual KPI for each backtest window

(none)

compute_model_configuration

Model configuration tables: priors, ROI settings, shift parameters, lift tests, spike definitions, channel linkings, and subchannels

(none)

compute_subchannel_spend_over_time

Weekly spend for each subchannel within its parent channel over the trailing 90 days. Only available for models with subchannels

(none)

compute_experiments_summaries

Experiment results summary

(none)

compute_clean_data

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 from results.downloads and 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_waterfall for an ROI model and cpa_waterfall for a CPA model, so read the keys from results.downloads rather 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.

reporter-api.md

Code Examples

R

JavaScript
# 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

Python
# 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)")