---
title: "Solve"
method: POST
path: "/api/solver/solve"
tags: ["solver"]
---

# Solve

`POST /api/solver/solve`

## Request

- Base URL: `https://api.pcbgenerator.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api.pcbgenerator.com/api/solver/solve`
- Auth: none declared

Find optimal PCB stackup configurations for the given constraints.

Enumerates material/thickness/copper combinations, applies hard constraints
(impedance feasibility, total thickness, DFM limits), scores on soft
objectives (cost, insertion loss, manufacturability), and returns ranked
solutions.  Progressively relaxes constraints if no solution is found.

## Request body

- SolverInput: Everything the solver needs to explore the design space.
  - `layer_count` integer, required: Total copper layers (must be even)
  - `frequency` number, required: Operating frequency in GHz
  - `impedance_targets` ImpedanceTarget[]: Impedance targets to satisfy (default: 50 Ω microstrip)
    - `value` number: Target impedance in Ω
    - `topology` 'microstrip' | 'stripline'
    - `differential` boolean: Treat `value` as a differential-pair impedance (Z_diff) solved with the edge-coupled inverse solver instead of single-ended Z0
    - `spacing_mm` number, nullable: Edge-to-edge pair gap for differential targets (mm); defaults to 0.15 mm. Ignored for single-ended targets.
  - `target_thickness_mm` number: Desired finished board thickness in mm
  - `thickness_tolerance_mm` number, nullable: Allowable deviation from the target thickness (mm). OMIT IT and the solver allows 10% of the target, with no minimum. A stated value is used exactly as given, including 0.0 (hit the target exactly). The tolerance the solve used comes back on SolverResult.
  - `allowed_core_materials` string[], nullable: If set, restrict cores to these materials only
  - `allowed_prepreg_materials` string[], nullable: If set, restrict prepregs to these materials only
  - `allowed_inner_core_materials` string[], nullable: Materials a HYBRID board may use for its inner cores (every core but the outer two). A hybrid is searched beside core-outer on 6+ layers when a core outside the FR-4 family is named, or when this is given; omitted, the inner cores are the FR-4 family (FR370HR, VT47, EM827). See solver/stackup/hybrid.py.
  - `copper_weight_oz` number, nullable: If set, restrict the search to this copper weight (oz). Omit to let the solver optimize copper weight freely. This is the FINISHED thickness, with outer-layer plating already included (1 oz = 0.035 mm finished, plated up from 0.5 oz starting foil). A spec written as '0.5 oz base foil + plating, 1.7 mil finished' uses the OTHER convention — pass 1, not 0.5.
  - `cost_priority` 'low' | 'balanced' | 'performance'
  - `build_type` string: single | hdi1 | hdi2 | hdi3 | hdi4
  - `max_solutions` integer: Max solutions to return
  - `reflow_process` 'leaded' | 'lead_free'
  - `construction` 'foil' | 'core_outer' | 'auto', nullable: Lamination: 'foil' (prepreg outermost) or 'core_outer' (a core outermost, so an outer microstrip rides on the core). Omitted, the board is built foil, unless allowed_core_materials names a core, which builds core-outer where core-outer can be built (4+ layers, build_type 'single'); frequency does not change it. 'auto' searches BOTH and lets the score decide. Naming 'foil' or 'core_outer' PINS it — an explicit choice always wins. Each solution reports the construction it was built as.
  - `solder_mask` SolderMask: LPI mask over an outer trace — the covered-microstrip cover. ⚠ Both defaults are TYPICAL values for green LPI, not a measurement of any product: they mirror the frontend's `DEFAULT_SOLDER_MASK_*`. A fab that states its own mask Dk and cured thickness should override them.
    - `thickness_mm` number: Cured mask thickness over the conductor (mm); typical LPI 0.015-0.030
    - `dk` number: Mask dielectric constant; typical green LPI 3.3-3.9

## Response `200`

Successful Response

- SolverResult: Top-level result returned by the solver.
  - `solutions` SolverSolution[], required
    - `rank` integer, required
    - `core_material` string, required
    - `prepreg_material` string, nullable, required
    - `core_thickness_mm` number, required
    - `prepreg_thickness_mm` number, nullable, required: Thickness of ONE prepreg layer (opening) between two coppers, BEFORE lamination: prepreg_sheets × prepreg_sheet_thickness_mm. Summing these per-layer values will NOT reproduce total_thickness_mm: the board total uses the PRESSED thickness, this − PREPREG_COMPRESSION_MM (0.04064 mm), per layer — solver/_constraints.py compute_thickness. None on a board with no prepreg ply (2 layers: one core).
    - `prepreg_sheets` integer, nullable: Prepreg sheets stacked in each prepreg layer, 1 to 3 (NCAB: no more than three at one interface). None on a board with no prepreg ply.
    - `prepreg_sheet_thickness_mm` number, nullable: Thickness of one sheet, a value from the maker's prepreg table (pcbMaterials.json prepregThicknesses). None on a board with no prepreg ply.
    - `copper_weight_oz` number, required
    - `copper_thickness_mm` number, required
    - `total_thickness_mm` number, required
    - `trace_results` TraceWidthResult[], required
      - `target_impedance` number, required
      - `topology` 'microstrip' | 'stripline', required
      - `trace_width_mm` number, required
      - `achieved_impedance` number, required
      - `feasibility` string, required
      - `differential` boolean
      - `spacing_mm` number, nullable: Pair gap the differential solve used (mm); None for single-ended
      - `z_even` number, nullable: Even-mode impedance (Ω); differential targets only
      - `z_odd` number, nullable: Odd-mode impedance (Ω); differential targets only
      - `dielectric_material` string, nullable: Dielectric the trace was solved against: the outer ply for microstrip (the prepreg; the core on a 2-layer or core-outer board), the core for stripline
      - `dielectric_height_mm` number, nullable: Dielectric height the solve used (mm); for stripline the reference planes' spacing b = core + signal copper + prepreg
      - `dk_used` number, nullable: Dielectric constant the solve actually consumed — the catalog value AT `frequency`, or the thickness-weighted value for a buried trace. It is NOT the material's nominal Dk: re-deriving this width from the nominal gives a different impedance (EM528 is 3.95 nominal but 3.9218 at 2.4 GHz, and the same 0.4879 mm trace reads 49.84 Ω instead of the solved 50.00 Ω)
      - `loss` TraceLoss: Loss of one solved trace, in dB per mm, split by cause (issue 519). Set by `stackup/trace_loss.py` from `conductor_loss_equations.calculate_insertion_loss`; the basis (foil roughness, what is left out) is in that module's docstring.
        - `total_db_per_mm` number, required: Copper + dielectric + roughness loss, dB/mm
        - `conductor_db_per_mm` number, required: Smooth-copper (skin-effect) loss, dB/mm
        - `dielectric_db_per_mm` number, required: Board-material (Df) loss, dB/mm
        - `roughness_db_per_mm` number, required: Extra copper loss from foil roughness, dB/mm
        - `df_used` number, required: Loss tangent the dielectric term used
        - `roughness_rms_um` number, required: Foil RMS roughness the roughness term used, µm
    - `score` ScoreBreakdown, required: How a solution scored on each soft objective (0–1, lower is better).
      - `impedance_error` number, required: Normalised impedance error
      - `trace_manufacturability` number, required: Trace width penalty
      - `cost` number, required: Material cost index
      - `thickness_deviation` number, required: Normalised thickness error
      - `insertion_loss` number, required: Insertion-loss proxy: 70% dielectric (average Df) + 30% copper roughness (Hammerstad, standard-ED foil for every candidate)
      - `copper_symmetry` number, required: Copper weight symmetry penalty
      - `emi_quality` number: EMI return-path quality penalty
      - `copper_weight` number: Heavy-copper penalty (etch/impedance-control + foil cost), frequency-scaled
      - `total` number, required: Weighted total score
    - `num_cores` integer, required
    - `num_prepregs` integer, required
    - `construction` 'foil' | 'core_outer': Lamination the solve used (see SolverInput.construction)
    - `core_tg_c` integer, nullable: Core dielectric glass-transition temperature (°C); None if unknown
    - `prepreg_tg_c` integer, nullable: Prepreg dielectric glass-transition temperature (°C); None if unknown
    - `core_foil` FoilRecommendation: Recommended + compatible copper-foil profiles for one dielectric. The recommended profile is the lowest-roughness foil the material's frequency capability calls for; ``compatible`` lists every profile that is acceptable (a smoother foil than recommended is always allowed).
      - `recommended` 'standard_ED' | 'RTF' | 'VLP' | 'HVLP' | 'custom', required
      - `compatible` CopperFoilProfileType[], required: All foil profiles acceptable for this material (recommended included)
    - `prepreg_foil` FoilRecommendation: Recommended + compatible copper-foil profiles for one dielectric. The recommended profile is the lowest-roughness foil the material's frequency capability calls for; ``compatible`` lists every profile that is acceptable (a smoother foil than recommended is always allowed).
      - `recommended` 'standard_ED' | 'RTF' | 'VLP' | 'HVLP' | 'custom', required
      - `compatible` CopperFoilProfileType[], required: All foil profiles acceptable for this material (recommended included)
    - `inner_core_material` string, nullable: Material of every core but the outer two; set on a hybrid only
    - `inner_core_thickness_mm` number, nullable: Thickness of each inner core (mm); set on a hybrid only
    - `inner_core_tg_c` integer, nullable: Inner core glass-transition temperature (°C); hybrid only
  - `total_evaluated` integer, required
  - `total_feasible` integer, required
  - `relaxation` RelaxationInfo: If constraints were relaxed, describes what changed.
    - `was_relaxed` boolean
    - `original_tolerance_mm` number
    - `relaxed_tolerance_mm` number
    - `impedance_tolerance_relaxed` boolean
    - `message` string
  - `diagnostics` string[]
  - `skin_depth_um` number, nullable: Copper skin depth at the solve frequency in µm, to 6 decimal places: `conductor_loss_equations.calculate_skin_depth`, the value the insertion-loss score's roughness term used. Set on every solve, feasible or not.
  - `materials` object: Catalog facts for each core or prepreg material named in `solutions`, keyed by name; empty when no solution is returned.
  - `thickness_tolerance_mm` number: The thickness tolerance this solve actually used, resolved from the request (`_constraints.resolve_thickness_tolerance`). ⚡ Every surface shows THIS value, never its own default: the tolerance also reorders boards through the thickness-deviation score, so a caller that printed a different number would be describing a ranking it did not get.

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/pcbgenerator/apis/pcb-stackup-generator-api.md) · [All operations](https://skmtc.dev/pcbgenerator/apis/pcb-stackup-generator-api/llms.txt) · [OpenAPI document](https://skmtc.dev/pcbgenerator/apis/pcb-stackup-generator-api/revisions/b6084b3ace32?raw)
