Skip to content

API Reference

ReformLab exposes two interfaces: a Python API for programmatic use in notebooks and scripts, and a REST API (FastAPI) for integration with external tools and the no-code GUI.


Import from the reformlab package top-level (from reformlab import ...).

FunctionDescription
run_scenario(config)Execute a complete multi-year simulation; returns SimulationResult
load_population(path)Load population data from CSV or Parquet; returns PopulationData
create_scenario(name, template, policy, ...)Create and optionally register a scenario; returns ScenarioConfig
clone_scenario(name, new_name)Clone an existing registered scenario; returns ScenarioConfig
list_scenarios()List all registered scenario names; returns list[str]
get_scenario(name)Get a scenario from the registry; returns ScenarioConfig
generate_population(sources, merge_method, ...)Generate synthetic population via data fusion; returns PopulationData
check_memory_requirements(config)Preflight memory-risk check; returns MemoryCheckResult
run_benchmarks()Run benchmark validation suite; returns benchmark report
Key return types

SimulationResult — immutable result container.

Attribute / MethodTypeDescription
successboolWhether the run completed without error
panel_outputpa.TableHousehold-by-year panel dataset
manifestdictFull run manifest (seeds, hashes, assumptions)
.indicators()methodCompute indicators from panel output
.export_csv(path)methodExport panel to CSV
.export_parquet(path)methodExport panel to Parquet
.export_replication_package(path)methodExport full replication bundle

ScenarioConfig — scenario definition (template, policy, years, population, seed).

MemoryCheckResult — memory pre-check (should_warn: bool, estimate: MemoryEstimate, message: str).


The backend runs on FastAPI. All endpoints are under /api/. Error responses use a consistent structure:

{
"what": "user-facing error title",
"why": "root cause explanation",
"fix": "actionable guidance"
}
MethodPathDescription
POST/api/runs/Execute a simulation run
POST/api/runs/memory-checkPreflight memory-risk check
Request and response models

RunRequest

template_name: str | None
policy: dict[str, Any]
start_year: int = 2025
end_year: int = 2030
population_id: str | None
seed: int | None
baseline_id: str | None
portfolio_name: str | None
policy_type: str | None

RunResponse

run_id: str
success: bool
scenario_id: str
years: list[int]
row_count: int
manifest_id: str

MemoryCheckRequest

template_name: str # required (not optional)
policy: dict[str, Any] = {}
start_year: int # required (no default)
end_year: int # required (no default)
population_id: str | None

MemoryCheckResponse

should_warn: bool
estimated_gb: float
available_gb: float
message: str

MethodPathDescription
GET/api/scenarios/List all registered scenarios
GET/api/scenarios/{name}Get scenario detail
POST/api/scenarios/Create a new scenario
POST/api/scenarios/{name}/cloneClone an existing scenario
Request and response models

CreateScenarioRequest

name: str
policy_type: str | None # carbon_tax | subsidy | rebate | feebate
policy: dict[str, Any]
start_year: int
end_year: int
description: str = ""
baseline_ref: str | None

ScenarioResponse

name: str
policy_type: str
description: str
version: str
policy: dict[str, Any]
year_schedule: dict[str, int]
baseline_ref: str | None

CloneRequestnew_name: str


MethodPathDescription
GET/api/templates/List all available templates
GET/api/templates/{name}Get template detail and parameter schema
POST/api/templates/customRegister a custom template
DELETE/api/templates/custom/{name}Unregister a custom template
Request and response models

TemplateListItem

id: str
name: str
type: str
parameter_count: int
description: str
parameter_groups: list[str]
is_custom: bool = False

TemplateDetailResponse — extends TemplateListItem with default_policy: dict[str, Any].

CreateCustomTemplateRequest

name: str
description: str = ""
parameters: list[CustomTemplateParameterSpec]
# CustomTemplateParameterSpec: {name, type, default, unit, min, max}

CustomTemplateResponse

name: str
description: str
parameter_count: int
is_custom: bool = True

MethodPathDescription
GET/api/portfolios/List all portfolios
GET/api/portfolios/{name}Get portfolio detail
POST/api/portfolios/Create a new portfolio
PUT/api/portfolios/{name}Update an existing portfolio
DELETE/api/portfolios/{name}Delete a portfolio
POST/api/portfolios/validateValidate portfolio for conflicts
POST/api/portfolios/{name}/cloneClone a portfolio
Request and response models

CreatePortfolioRequest

name: str
description: str = ""
policies: list[PortfolioPolicyRequest]
resolution_strategy: str = "error"
# PortfolioPolicyRequest: {name, policy_type, rate_schedule, exemptions, thresholds, covered_categories, extra_params}

PortfolioDetailResponse

name: str
description: str
version_id: str
policies: list[PortfolioPolicyItem]
resolution_strategy: str
policy_count: int

ValidatePortfolioRequest

policies: list[PortfolioPolicyRequest]
resolution_strategy: str = "error"

ValidatePortfolioResponse

conflicts: list[PortfolioConflict]
is_compatible: bool

MethodPathDescription
GET/api/results/List all stored results
GET/api/results/{run_id}Get result detail and status
DELETE/api/results/{run_id}Delete a stored result
GET/api/results/{run_id}/export/csvDownload panel as CSV
GET/api/results/{run_id}/export/parquetDownload panel as Parquet
Request and response models

ResultDetailResponse

run_id: str
timestamp: str
run_kind: str # "scenario" | "portfolio"
start_year: int
end_year: int
population_id: str | None
seed: int | None
row_count: int
manifest_id: str
scenario_id: str
status: str
data_available: bool
template_name: str | None
policy_type: str | None
portfolio_name: str | None
indicators: dict[str, Any] | None # only when data_available is True
columns: list[str] | None
column_count: int | None

ResultListItem

run_id: str
timestamp: str
run_kind: str
start_year: int
end_year: int
row_count: int
status: str
data_available: bool
template_name: str | None
policy_type: str | None
portfolio_name: str | None

MethodPathDescription
POST/api/indicators/distributionalCompute per-decile distributional indicators
POST/api/indicators/geographicCompute per-region geographic indicators
POST/api/indicators/fiscalCompute fiscal indicators (revenue, cost, balance)
Request and response models

IndicatorRequest

run_id: str
income_field: str = "income"
by_year: bool = False

IndicatorResponse

indicator_type: str
data: dict[str, list[Any]] # Columnar data (PyArrow-style)
metadata: dict[str, Any]
warnings: list[str]
excluded_count: int

MethodPathDescription
POST/api/comparison/Compare a baseline run against a reform run
POST/api/comparison/portfoliosCompare multiple portfolios side by side
Request and response models

ComparisonRequest

baseline_run_id: str
reform_run_id: str
welfare_field: str = "disposable_income"
threshold: float = 0.0

PortfolioComparisonRequest

run_ids: list[str] # 2–5 run IDs
baseline_run_id: str | None # defaults to first run_id
indicator_types: list[str] = ["distributional", "fiscal"]
include_welfare: bool = True
include_deltas: bool = True
include_pct_deltas: bool = True

PortfolioComparisonResponse

comparisons: dict[str, ComparisonData] # keyed by indicator type
cross_metrics: list[CrossMetricItem]
portfolio_labels: list[str]
metadata: dict[str, Any]
warnings: list[str]

MethodPathDescription
GET/api/data-fusion/sourcesList available data sources
GET/api/data-fusion/sources/{provider}/{dataset_id}Get source detail and field schema
GET/api/data-fusion/merge-methodsList available merge strategies
POST/api/data-fusion/generateGenerate a synthetic population
Request and response models

GeneratePopulationRequest

sources: list[DataFusionSourceSelection] # {provider, dataset_id}
merge_method: str = "uniform" # "uniform" | "ipf" | "conditional"
seed: int = 42
ipf_constraints: list[IPFConstraintRequest] = []
strata_columns: list[str] = []

GeneratePopulationResponse

success: bool
summary: PopulationSummary # {record_count, column_count, columns}
step_log: list[StepLogItem]
assumption_chain: list[AssumptionRecordItem]
validation_result: ValidationResultResponse | None

MethodPathDescription
GET/api/populations/List available pre-built populations
Response model

PopulationItem

id: str
name: str
households: int
source: str
year: int

MethodPathDescription
POST/api/decisions/summaryGenerate behavioral decision summary for a run
Request and response models

DecisionSummaryRequest

run_id: str
domain_name: str | None # None = all domains
group_by: str | None # "decile" or None
group_value: str | None # e.g., "3" for D3; None = all
year: int | None # If set, include mean probabilities

DecisionSummaryResponse

run_id: str
domains: list[DomainSummary] # Per-domain yearly outcomes
metadata: dict[str, Any]
warnings: list[str]

MethodPathDescription
POST/api/exports/csvExport a result panel as CSV
POST/api/exports/parquetExport a result panel as Parquet
Request and response models

ExportRequest

run_id: str

Returns a file download response (Content-Disposition: attachment).


For the full interactive API documentation with live request testing, run the backend in development mode (REFORMLAB_ENV=dev) and visit http://localhost:8000/api/docs.