Recast Operators Knowledge Base
Breadcrumbs

Run Launching API Endpoint

The Run Launching API lets you programmatically launch Recast runs without going through the admin UI. This is an alternative to the manual process of visiting the Launch Run page in the admin, uploading a CSV, and a prior settings Google Sheet link.

See the Swagger documentation for full schema details. See Authentication section in the MMM knowledge base for details on how to gain access.

The Endpoints

  1. POST /v1/clients/{client_slug}/runs: Launches a new model run

  2. GET /v1/clients/{client_slug}/runs/{id}: Returns the status, metadata, and form for a specific run

  3. GET /v1/clients/{client_slug}/runs: Returns a paginated list of runs

  4. GET /v1/clients/{client_slug}/runs/{id}/downloads/clean_data: Returns the clean data CSV the run was trained on

The Form

The request body wraps all run settings inside a form object. The client is identified by the {client_slug} path parameter in the URL:

{
  "form": {
    "run_type": "model_run",
    "google_sheet_priors_url": "https://docs.google.com/spreadsheets/d/abc/edit",
    "clean_data": "date,channel_1,...\n2024-1-1,1000,...\n"
  }
}

Required Inputs

Field

Type

Description

google_sheet_priors_url

string

URL pointing to the prior settings Google Sheet

clean_data

string

Raw CSV text (UTF-8) with \n line endings

run_type

string

One of: model_run, parameter_recovery, stability_loop, holdout

Optional Inputs

Stability Loop

The following fields are only used when run_type is stability_loop:

Field

Type

Description

n_holdout

integer

Number of stability loop periods to run

days_per_holdout

integer

Number of days to hold out in each period

Holdout Test

The following field is only used when run_type is holdout:

Field

Type

Description

holdout_days

array of integers

Number of days to holdout in each test period

Run Types

run_type

What it does

model_run

Standard model run

parameter_recovery

Tests that the model can recover known parameters

stability_loop

Runs the model across consecutive holdout periods to assess stability

holdout

Runs the model holding out specific numbers of days to evaluate out-of-sample performance

Response

A successful POST returns HTTP 201 with the new run's ID:

JSON
{
  "id": 2786
}

Use id to poll for status and retrieve results.

Rate Limits

  • 100 runs per day

  • 20 runs per minute

Exceeding either limit returns HTTP 429. Rate limits are in place to protect against scripts accidentally launching runs. Space out batch submissions accordingly.

Status Polling

Model runs are asynchronous. After launching, poll GET /v1/clients/{client_slug}/runs/{id} until status is no longer an in-progress value. Typical run times vary by run type and data size β€” model runs generally take 20–60 minutes.

In-progress statuses (keep polling):

Status

Meaning

launching

Run has been accepted and will begin processing shortly

waiting

Run is queued

running

Run is actively processing

processing

Run is actively processing

Terminal statuses (stop polling):

Status

Meaning

success

Run completed successfully

error

Run failed

aborted

Run was aborted

syncing_failed

Run failed during data sync (treated as aborted)

Downloading Clean Data

Once a run exists, you can retrieve the clean data CSV it was trained on:

GET /v1/clients/{client_slug}/runs/{id}/downloads/clean_data

This returns text/csv. When the clean_data field is submitted via the POST endpoint the format should be a CSV string with new line characters (\n).

Skill File (Beta)

We have a skill file to help AI coding assistants get up to speed on the Model Launching API. It is in beta and is best used for writing boilerplate code. Please review any generated code before running it.

run-launching-api.md

Code Examples

Python

Python
# Recast Model Launching API β€” Python Usage Example
#
# Workflow: Submit a model run β†’ Poll for completion
#
# Prerequisites:
#   pip install requests
#   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 requests

# ── Edit these ────────────────────────────────────────────────────────────────
CLIENT_SLUG  = "my-client"                                    # Client slug (visible in the app URL)
PRIORS_URL   = "https://docs.google.com/spreadsheets/d/..."  # Google Sheet URL
CLEAN_DATA   = "clean_data.csv"                               # Path to your clean data CSV
RUN_TYPE     = "model_run"  # model_run | parameter_recovery | holdout | stability_loop

# ── 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}", "Content-Type": "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: Read CSV and normalize line endings ───────────────────────────────
with open(CLEAN_DATA, "r", newline="") as f:
    clean_data = f.read().replace("\r\n", "\n")

# ── Step 2: Submit the run ────────────────────────────────────────────────────
resp = check(requests.post(
    f"{BASE_PATH}/runs",
    headers=HEADERS,
    json={
        "form": {
            "run_type":                RUN_TYPE,
            "google_sheet_priors_url": PRIORS_URL,
            "clean_data":              clean_data,
        },
    },
), expected=201)

run_id = resp.json()["id"]
print(f"Launched run {run_id}")

# ── Step 3: Poll until complete ───────────────────────────────────────────────
timeout_seconds = 90 * 60  # model runs can take 20–60 minutes
started_at = time.time()

while True:
    if time.time() - started_at > timeout_seconds:
        raise TimeoutError(f"Timed out waiting for run {run_id}")
    result = check(requests.get(f"{BASE_PATH}/runs/{run_id}", headers=HEADERS)).json()
    print(f"Status: {result['status']}")
    if result["status"] not in ("launching", "waiting", "running", "processing"):
        break
    time.sleep(60)

print(f"Run {run_id} finished with status: {result['status']}")

Stability Loop Example (Python)

Python
with open(CLEAN_DATA, "r", newline="") as f:
    clean_data = f.read().replace("\r\n", "\n")

resp = check(requests.post(
    f"{BASE_PATH}/runs",
    headers=HEADERS,
    json={
        "form": {
            "run_type":                "stability_loop",
            "google_sheet_priors_url": PRIORS_URL,
            "clean_data":              clean_data,
            "n_holdout":               6,
            "days_per_holdout":        30,
        },
    },
), expected=201)

print(f"Launched stability loop: {resp.json()['id']}")

Holdout Test Example (Python)

Python
with open(CLEAN_DATA, "r", newline="") as f:
    clean_data = f.read().replace("\r\n", "\n")

resp = check(requests.post(
    f"{BASE_PATH}/runs",
    headers=HEADERS,
    json={
        "form": {
            "run_type":                "holdout",
            "google_sheet_priors_url": PRIORS_URL,
            "clean_data":              clean_data,
            "holdout_days":            [14, 28, 42],
        },
    },
), expected=201)

print(f"Launched holdout: {resp.json()['id']}")

Downloading Clean Data (Python)

Python
import io, pandas as pd

csv_resp = check(requests.get(
    f"{BASE_PATH}/runs/{run_id}/downloads/clean_data",
    headers={"Authorization": f"Bearer {PAT}"},
))
df = pd.read_csv(io.StringIO(csv_resp.text))
df.to_csv("downloaded_clean_data.csv", index=False)
print(f"Downloaded clean data: {len(df)} rows, {len(df.columns)} columns")

R

R
# Recast Model Launching API β€” R Usage Example
#
# Prerequisites:
#   install.packages(c("httr2", "jsonlite"))
#   Add to .Renviron: API_PAT=gr_your_token_here

library(httr2)
library(jsonlite)

# ── Edit these ────────────────────────────────────────────────────────────────
CLIENT_SLUG <- "my-client"
PRIORS_URL  <- "https://docs.google.com/spreadsheets/d/..."
CLEAN_DATA  <- "clean_data.csv"
RUN_TYPE    <- "model_run"

# ── Rarely needs changing ─────────────────────────────────────────────────────
BASE_URL <- "https://api.getrecast.com"
PAT      <- Sys.getenv("API_PAT")

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
}

# ── Step 1: Read CSV and normalize line endings ───────────────────────────────
clean_data <- paste(readLines(CLEAN_DATA), collapse = "\n")

# ── Step 2: Submit the run ────────────────────────────────────────────────────
resp <- request(BASE_URL) |>
  req_url_path_append("v1", "clients", CLIENT_SLUG, "runs") |>
  req_auth_bearer_token(PAT) |>
  req_error(is_error = \(resp) FALSE) |>
  req_body_json(list(
    form = list(
      run_type                = RUN_TYPE,
      google_sheet_priors_url = PRIORS_URL,
      clean_data              = clean_data
    )
  ), auto_unbox = TRUE) |>
  req_perform() |>
  check(expected = 201)

run_id <- parse(resp)$id
cat(sprintf("Launched run %d\n", run_id))

# ── Step 3: Poll until complete ───────────────────────────────────────────────
timeout_seconds <- 90 * 60
started_at <- proc.time()["elapsed"]

repeat {
  if ((proc.time()["elapsed"] - started_at) > timeout_seconds) {
    stop(sprintf("Timed out waiting for run %d", run_id))
  }
  result <- request(BASE_URL) |>
    req_url_path_append("v1", "clients", CLIENT_SLUG, "runs", run_id) |>
    req_auth_bearer_token(PAT) |>
    req_error(is_error = \(resp) FALSE) |>
    req_perform() |> check() |> parse()
  cat(sprintf("Status: %s\n", result$status))
  if (!result$status %in% c("launching", "waiting", "running", "processing")) break
  Sys.sleep(60)
}

cat(sprintf("Run %d finished with status: %s\n", run_id, result$status))