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.
Python API
Section titled “Python API”Import from the reformlab package top-level (from reformlab import ...).
| Function | Description |
|---|---|
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 / Method | Type | Description |
|---|---|---|
success | bool | Whether the run completed without error |
panel_output | pa.Table | Household-by-year panel dataset |
manifest | dict | Full run manifest (seeds, hashes, assumptions) |
.indicators() | method | Compute indicators from panel output |
.export_csv(path) | method | Export panel to CSV |
.export_parquet(path) | method | Export panel to Parquet |
.export_replication_package(path) | method | Export full replication bundle |
ScenarioConfig — scenario definition (template, policy, years, population, seed).
MemoryCheckResult — memory pre-check (should_warn: bool, estimate: MemoryEstimate, message: str).
REST API
Section titled “REST API”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"}Runs — /api/runs
Section titled “Runs — /api/runs”| Method | Path | Description |
|---|---|---|
POST | /api/runs/ | Execute a simulation run |
POST | /api/runs/memory-check | Preflight memory-risk check |
Request and response models
RunRequest
template_name: str | Nonepolicy: dict[str, Any]start_year: int = 2025end_year: int = 2030population_id: str | Noneseed: int | Nonebaseline_id: str | Noneportfolio_name: str | Nonepolicy_type: str | NoneRunResponse
run_id: strsuccess: boolscenario_id: stryears: list[int]row_count: intmanifest_id: strMemoryCheckRequest
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 | NoneMemoryCheckResponse
should_warn: boolestimated_gb: floatavailable_gb: floatmessage: strScenarios — /api/scenarios
Section titled “Scenarios — /api/scenarios”| Method | Path | Description |
|---|---|---|
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}/clone | Clone an existing scenario |
Request and response models
CreateScenarioRequest
name: strpolicy_type: str | None # carbon_tax | subsidy | rebate | feebatepolicy: dict[str, Any]start_year: intend_year: intdescription: str = ""baseline_ref: str | NoneScenarioResponse
name: strpolicy_type: strdescription: strversion: strpolicy: dict[str, Any]year_schedule: dict[str, int]baseline_ref: str | NoneCloneRequest — new_name: str
Templates — /api/templates
Section titled “Templates — /api/templates”| Method | Path | Description |
|---|---|---|
GET | /api/templates/ | List all available templates |
GET | /api/templates/{name} | Get template detail and parameter schema |
POST | /api/templates/custom | Register a custom template |
DELETE | /api/templates/custom/{name} | Unregister a custom template |
Request and response models
TemplateListItem
id: strname: strtype: strparameter_count: intdescription: strparameter_groups: list[str]is_custom: bool = FalseTemplateDetailResponse — extends TemplateListItem with default_policy: dict[str, Any].
CreateCustomTemplateRequest
name: strdescription: str = ""parameters: list[CustomTemplateParameterSpec]# CustomTemplateParameterSpec: {name, type, default, unit, min, max}CustomTemplateResponse
name: strdescription: strparameter_count: intis_custom: bool = TruePortfolios — /api/portfolios
Section titled “Portfolios — /api/portfolios”| Method | Path | Description |
|---|---|---|
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/validate | Validate portfolio for conflicts |
POST | /api/portfolios/{name}/clone | Clone a portfolio |
Request and response models
CreatePortfolioRequest
name: strdescription: str = ""policies: list[PortfolioPolicyRequest]resolution_strategy: str = "error"# PortfolioPolicyRequest: {name, policy_type, rate_schedule, exemptions, thresholds, covered_categories, extra_params}PortfolioDetailResponse
name: strdescription: strversion_id: strpolicies: list[PortfolioPolicyItem]resolution_strategy: strpolicy_count: intValidatePortfolioRequest
policies: list[PortfolioPolicyRequest]resolution_strategy: str = "error"ValidatePortfolioResponse
conflicts: list[PortfolioConflict]is_compatible: boolResults — /api/results
Section titled “Results — /api/results”| Method | Path | Description |
|---|---|---|
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/csv | Download panel as CSV |
GET | /api/results/{run_id}/export/parquet | Download panel as Parquet |
Request and response models
ResultDetailResponse
run_id: strtimestamp: strrun_kind: str # "scenario" | "portfolio"start_year: intend_year: intpopulation_id: str | Noneseed: int | Nonerow_count: intmanifest_id: strscenario_id: strstatus: strdata_available: booltemplate_name: str | Nonepolicy_type: str | Noneportfolio_name: str | Noneindicators: dict[str, Any] | None # only when data_available is Truecolumns: list[str] | Nonecolumn_count: int | NoneResultListItem
run_id: strtimestamp: strrun_kind: strstart_year: intend_year: introw_count: intstatus: strdata_available: booltemplate_name: str | Nonepolicy_type: str | Noneportfolio_name: str | NoneIndicators — /api/indicators
Section titled “Indicators — /api/indicators”| Method | Path | Description |
|---|---|---|
POST | /api/indicators/distributional | Compute per-decile distributional indicators |
POST | /api/indicators/geographic | Compute per-region geographic indicators |
POST | /api/indicators/fiscal | Compute fiscal indicators (revenue, cost, balance) |
Request and response models
IndicatorRequest
run_id: strincome_field: str = "income"by_year: bool = FalseIndicatorResponse
indicator_type: strdata: dict[str, list[Any]] # Columnar data (PyArrow-style)metadata: dict[str, Any]warnings: list[str]excluded_count: intComparison — /api/comparison
Section titled “Comparison — /api/comparison”| Method | Path | Description |
|---|---|---|
POST | /api/comparison/ | Compare a baseline run against a reform run |
POST | /api/comparison/portfolios | Compare multiple portfolios side by side |
Request and response models
ComparisonRequest
baseline_run_id: strreform_run_id: strwelfare_field: str = "disposable_income"threshold: float = 0.0PortfolioComparisonRequest
run_ids: list[str] # 2–5 run IDsbaseline_run_id: str | None # defaults to first run_idindicator_types: list[str] = ["distributional", "fiscal"]include_welfare: bool = Trueinclude_deltas: bool = Trueinclude_pct_deltas: bool = TruePortfolioComparisonResponse
comparisons: dict[str, ComparisonData] # keyed by indicator typecross_metrics: list[CrossMetricItem]portfolio_labels: list[str]metadata: dict[str, Any]warnings: list[str]Data Fusion — /api/data-fusion
Section titled “Data Fusion — /api/data-fusion”| Method | Path | Description |
|---|---|---|
GET | /api/data-fusion/sources | List available data sources |
GET | /api/data-fusion/sources/{provider}/{dataset_id} | Get source detail and field schema |
GET | /api/data-fusion/merge-methods | List available merge strategies |
POST | /api/data-fusion/generate | Generate a synthetic population |
Request and response models
GeneratePopulationRequest
sources: list[DataFusionSourceSelection] # {provider, dataset_id}merge_method: str = "uniform" # "uniform" | "ipf" | "conditional"seed: int = 42ipf_constraints: list[IPFConstraintRequest] = []strata_columns: list[str] = []GeneratePopulationResponse
success: boolsummary: PopulationSummary # {record_count, column_count, columns}step_log: list[StepLogItem]assumption_chain: list[AssumptionRecordItem]validation_result: ValidationResultResponse | NonePopulations — /api/populations
Section titled “Populations — /api/populations”| Method | Path | Description |
|---|---|---|
GET | /api/populations/ | List available pre-built populations |
Response model
PopulationItem
id: strname: strhouseholds: intsource: stryear: intDecisions — /api/decisions
Section titled “Decisions — /api/decisions”| Method | Path | Description |
|---|---|---|
POST | /api/decisions/summary | Generate behavioral decision summary for a run |
Request and response models
DecisionSummaryRequest
run_id: strdomain_name: str | None # None = all domainsgroup_by: str | None # "decile" or Nonegroup_value: str | None # e.g., "3" for D3; None = allyear: int | None # If set, include mean probabilitiesDecisionSummaryResponse
run_id: strdomains: list[DomainSummary] # Per-domain yearly outcomesmetadata: dict[str, Any]warnings: list[str]Exports — /api/exports
Section titled “Exports — /api/exports”| Method | Path | Description |
|---|---|---|
POST | /api/exports/csv | Export a result panel as CSV |
POST | /api/exports/parquet | Export a result panel as Parquet |
Request and response models
ExportRequest
run_id: strReturns 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.