API: laterally loaded pile
POST /v1/lateral — request and response fields for the COM624P p-y analysis, with runnable curl, JavaScript, and Python examples.
Solves a laterally loaded single pile by the p-y method — a beam supported by nonlinear soil springs, solved with the COM624P finite-difference formulation (Wang & Reese, FHWA-SA-91-048), the same engine behind LPILE and RSPile. It returns head response, maxima, and the full deflection / moment / shear profile. The method and every input are explained in Laterally loaded piles (p-y); the same analysis runs interactively at the lateral tool.
Endpoint
https://api.pilecalc.com/v1/lateral/v1/lateral?key=…&req=…&fields=…(for header-less callers)The request has three required parts: the pile (uniform, or sectioned with depth-varying diameter/ei), the soil profile (a stack of layers, each tagged with a p-y model — Matlock soft clay, Reese stiff clay, Reese or API sand, weak rock, elastic, or user-defined curves), and the head boundary condition (five variants covering free, fixed, and rotationally restrained heads). Optional fields add an axial load, cyclic loading, distributed loads, ground slope, a p-multiplier, and free-field soil movement. Two more options change what comes back: sweep solves a series of proportionally scaled head loads (a pushover curve) in one call, and section + material replace the constant pile EI with the section's fiber-integrated moment–curvature stiffness (cracked-section nonlinear EI) — see Load sweep and Cracked section.
For cyclic loading, soil.cycles applies only to profiles containing Welch–Reese stiff clay above water (default N = 100). Omit it for fixed cyclic branches such as Matlock, RCK and sand. RCK layer.k and optional kBottom are initial moduli for the active loading state, not automatically reduced static inputs. Choose cyclic values from FHWA GEC 9 Table A-1 or justified site data.
Authentication, rate limits, and the error envelope are shared by every endpoint — see the API overview. Requests are unit-agnostic: any self-consistent unit system works, and results come back in the same units. The examples below use SI (m, kN, kPa, kN/m³).
Request fields
This table is generated from the same schema that validates the request, so it cannot drift. Where a field appears once per variant row, it belongs to that variant only.
| Field | Type | Required | Constraints |
|---|---|---|---|
unitSystem | "si" | "us" | yes | |
pile | object | yes | |
pile.kind = "uniform" | variant | no | |
pile.length | number | yes | > 0 |
pile.diameter | number | yes | > 0 |
pile.ei | number | yes | > 0 |
pile.groundSurfaceDepth | number | no | ≥ 0 |
pile.kind = "sectioned" | variant | yes | |
pile.sections | object | object[] | yes | min 1 item |
pile.groundSurfaceDepth | number | no | ≥ 0 |
soil | object | yes | |
soil.layers[] | object[] | yes | min 1 item |
soil.layers[].model = "soft-clay" | variant | yes | |
soil.layers[].top | number | yes | ≥ 0 |
soil.layers[].bottom | number | yes | > 0 |
soil.layers[].gamma | number | yes | > 0 |
soil.layers[].gammaBottom | number | no | > 0 |
soil.layers[].c | number | yes | > 0 |
soil.layers[].cBottom | number | no | > 0 |
soil.layers[].e50 | number | yes | > 0 |
soil.layers[].e50Bottom | number | no | > 0 |
soil.layers[].j | number | no | > 0 |
soil.layers[].model = "stiff-clay-above-water" | variant | yes | |
soil.layers[].top | number | yes | ≥ 0 |
soil.layers[].bottom | number | yes | > 0 |
soil.layers[].gamma | number | yes | > 0 |
soil.layers[].gammaBottom | number | no | > 0 |
soil.layers[].c | number | yes | > 0 |
soil.layers[].cBottom | number | no | > 0 |
soil.layers[].e50 | number | yes | > 0 |
soil.layers[].e50Bottom | number | no | > 0 |
soil.layers[].j | number | no | > 0 |
soil.layers[].k | number | no | > 0 |
soil.layers[].kBottom | number | no | > 0 |
soil.layers[].model = "stiff-clay-below-water" | variant | yes | |
soil.layers[].top | number | yes | ≥ 0 |
soil.layers[].bottom | number | yes | > 0 |
soil.layers[].gamma | number | yes | > 0 |
soil.layers[].gammaBottom | number | no | > 0 |
soil.layers[].c | number | yes | > 0 |
soil.layers[].cBottom | number | no | > 0 |
soil.layers[].e50 | number | yes | > 0 |
soil.layers[].e50Bottom | number | no | > 0 |
soil.layers[].k | number | yes | > 0 |
soil.layers[].kSource | "gec9-table-a1" | no | UI source-default selection provenance. k remains the explicit active-loading modulus used by the engine; the UI restores source selection only when k matches the default for the original loading and unit system. |
soil.layers[].kBottom | number | no | > 0 |
soil.layers[].model = "sand-reese" | variant | yes | |
soil.layers[].top | number | yes | ≥ 0 |
soil.layers[].bottom | number | yes | > 0 |
soil.layers[].gamma | number | yes | > 0 |
soil.layers[].gammaBottom | number | no | > 0 |
soil.layers[].phi | number | yes | 0 – 60 |
soil.layers[].phiBottom | number | no | 0 – 60 |
soil.layers[].k | number | yes | > 0 |
soil.layers[].kBottom | number | no | > 0 |
soil.layers[].model = "sand-api" | variant | yes | |
soil.layers[].top | number | yes | ≥ 0 |
soil.layers[].bottom | number | yes | > 0 |
soil.layers[].gamma | number | yes | > 0 |
soil.layers[].gammaBottom | number | no | > 0 |
soil.layers[].phi | number | yes | 0 – 60 |
soil.layers[].phiBottom | number | no | 0 – 60 |
soil.layers[].k | number | yes | > 0 |
soil.layers[].kBottom | number | no | > 0 |
soil.layers[].model = "weak-rock" | variant | yes | |
soil.layers[].top | number | yes | ≥ 0 |
soil.layers[].bottom | number | yes | > 0 |
soil.layers[].gamma | number | yes | > 0 |
soil.layers[].gammaBottom | number | no | > 0 |
soil.layers[].qu | number | yes | > 0 |
soil.layers[].quBottom | number | no | > 0 |
soil.layers[].eir | number | yes | > 0 |
soil.layers[].eirBottom | number | no | > 0 |
soil.layers[].rqd | number | yes | 0 – 100 |
soil.layers[].rqdBottom | number | no | 0 – 100 |
soil.layers[].krm | number | no | > 0 |
soil.layers[].model = "elastic" | variant | yes | |
soil.layers[].top | number | yes | ≥ 0 |
soil.layers[].bottom | number | yes | > 0 |
soil.layers[].gamma | number | yes | > 0 |
soil.layers[].gammaBottom | number | no | > 0 |
soil.layers[].esTop | number | yes | ≥ 0 |
soil.layers[].esBottom | number | no | ≥ 0 |
soil.layers[].model = "user" | variant | yes | |
soil.layers[].top | number | yes | ≥ 0 |
soil.layers[].bottom | number | yes | > 0 |
soil.layers[].gamma | number | yes | > 0 |
soil.layers[].gammaBottom | number | no | > 0 |
soil.layers[].es | number | no | ≥ 0 |
soil.layers[].curves[] | object[] | no | |
soil.layers[].curves[].depth | number | yes | ≥ 0 |
soil.layers[].curves[].points[] | object[] | yes | min 2 items |
soil.layers[].curves[].points[].y | number | yes | ≥ 0 |
soil.layers[].curves[].points[].p | number | yes | ≥ 0 |
soil.layers[].model = "liquefied-sand" | variant | yes | |
soil.layers[].top | number | yes | ≥ 0 |
soil.layers[].bottom | number | yes | > 0 |
soil.layers[].gamma | number | yes | > 0 |
soil.layers[].gammaBottom | number | no | > 0 |
soil.layers[].units | "SI" | "US" | yes | |
soil.layers[].model = "silt-cphi" | variant | yes | |
soil.layers[].top | number | yes | ≥ 0 |
soil.layers[].bottom | number | yes | > 0 |
soil.layers[].gamma | number | yes | > 0 |
soil.layers[].gammaBottom | number | no | > 0 |
soil.layers[].c | number | yes | > 0 |
soil.layers[].cBottom | number | no | > 0 |
soil.layers[].phi | number | yes | 0 – 60 |
soil.layers[].phiBottom | number | no | 0 – 60 |
soil.layers[].k | number | yes | > 0 |
soil.layers[].kBottom | number | no | > 0 |
soil.layers[].j | number | no | > 0 |
soil.layers[].model = "piedmont-residual" | variant | yes | |
soil.layers[].top | number | yes | ≥ 0 |
soil.layers[].bottom | number | yes | > 0 |
soil.layers[].gamma | number | yes | > 0 |
soil.layers[].gammaBottom | number | no | > 0 |
soil.layers[].esi | number | yes | > 0 |
soil.layers[].esiBottom | number | no | > 0 |
soil.layers[].lambda | number | no | > 0 |
soil.layers[].model = "iso-clay" | variant | yes | |
soil.layers[].top | number | yes | ≥ 0 |
soil.layers[].bottom | number | yes | > 0 |
soil.layers[].gamma | number | yes | > 0 |
soil.layers[].gammaBottom | number | no | > 0 |
soil.layers[].su | number | yes | > 0 |
soil.layers[].suBottom | number | no | > 0 |
soil.layers[].alpha | number | no | 0 – 1 |
soil.layers[].gap | "open" | "closed" | no | |
soil.layers[].clayType | "soft" | "stiff" | no | |
soil.cycles | number | no | integer, > 0 — Cycle count N for Welch–Reese stiff clay above water only; omitted defaults to 100. Matlock, RCK and sand cyclic branches do not depend on N. |
head | object | yes | |
head.kind = "shear-moment" | variant | yes | |
head.shear | number | yes | |
head.moment | number | yes | |
head.kind = "shear-slope" | variant | yes | |
head.shear | number | yes | |
head.slope | number | yes | |
head.kind = "shear-rotational" | variant | yes | |
head.shear | number | yes | |
head.rotationalStiffness | number | yes | ≥ 0 |
head.kind = "deflection-moment" | variant | yes | |
head.deflection | number | yes | |
head.moment | number | yes | |
head.kind = "deflection-slope" | variant | yes | |
head.deflection | number | yes | |
head.slope | number | yes | |
tip | object | no | |
tip.shear | number | no | |
tip.moment | number | no | |
axialLoad | number | no | |
loadType | "static" | "cyclic" | no | |
increments | number | no | integer, 10 – 800 |
layering | "direct" | "georgiadis" | no | |
groundSlopeDeg | number | no | 0 – 45 |
pMultiplier | number | no | 0 – 1 |
pMultiplierProfile[] | object[] | no | min 2 items |
pMultiplierProfile[].depth | number | yes | ≥ 0 |
pMultiplierProfile[].factor | number | yes | 0 – 1 |
tipShearCurve[] | object[] | no | min 2 items |
tipShearCurve[].deflection | number | yes | ≥ 0 |
tipShearCurve[].shear | number | yes | ≥ 0 |
distributedLoad | object | no | |
distributedLoad.intensity | number | yes | |
distributedLoad.top | number | no | ≥ 0 |
distributedLoad.bottom | number | no | ≥ 0 |
soilMovement | object | no | |
soilMovement.magnitude | number | yes | |
soilMovement.slidingDepth | number | yes | > 0 |
sweep | object | no | |
sweep.steps | number | yes | integer, 2 – 50 |
sweep.maxShear | number | no | |
sweep.maxMoment | number | no | |
section | object | no | |
section.kind = "solid-circular" | variant | yes | |
section.diameter | number | yes | > 0 |
section.kind = "pipe" | variant | yes | |
section.outerDiameter | number | yes | > 0 |
section.wallThickness | number | yes | > 0 |
section.kind = "rectangular" | variant | yes | |
section.width | number | yes | > 0 |
section.height | number | yes | > 0 |
section.kind = "round-concrete" | variant | yes | |
section.diameter | number | yes | > 0 |
section.cage | object | no | |
section.cage.count | number | yes | integer, 1 – 200 |
section.cage.area | number | yes | > 0 |
section.cage.barDiameter | number | yes | > 0 |
section.cage.barCircleRadius | number | yes | > 0 |
section.cage.yieldStress | number | yes | > 0 |
section.cage.elasticModulus | number | yes | > 0 |
section.cage.effectivePrestrain | number | no | ≥ 0 |
section.cage.hardeningRatio | number | no | 0 – 1 |
section.cage.tensionRuptureStrain | number | no | > 0 |
section.rebarConcreteDisplacement | "subtract" | "overlap" | no | |
section.casing | object | no | |
section.casing.outerDiameter | number | yes | > 0 |
section.casing.wallThickness | number | yes | > 0 |
section.casing.yieldStress | number | yes | > 0 |
section.casing.elasticModulus | number | yes | > 0 |
section.casing.hardeningRatio | number | no | 0 – 1 |
section.casing.tensionRuptureStrain | number | no | > 0 |
section.core | object | no | |
section.core.outerDiameter | number | yes | > 0 |
section.core.wallThickness | number | yes | > 0 |
section.core.yieldStress | number | yes | > 0 |
section.core.elasticModulus | number | yes | > 0 |
section.core.hardeningRatio | number | no | 0 – 1 |
section.core.tensionRuptureStrain | number | no | > 0 |
section.kind = "rectangular-concrete" | variant | yes | |
section.width | number | yes | > 0 |
section.height | number | yes | > 0 |
section.reinforcement | object | no | |
section.reinforcement.layout | "perimeter" | yes | |
section.reinforcement.barsPerFace | number | yes | integer, 2 – 100 |
section.reinforcement.barsPerSide | number | yes | integer, 0 – 99 |
section.reinforcement.area | number | yes | > 0 |
section.reinforcement.barDiameter | number | yes | > 0 |
section.reinforcement.clearCover | number | yes | > 0 |
section.reinforcement.yieldStress | number | yes | > 0 |
section.reinforcement.elasticModulus | number | yes | > 0 |
section.reinforcement.effectivePrestrain | number | no | ≥ 0 |
section.reinforcement.hardeningRatio | number | no | 0 – 1 |
section.reinforcement.tensionRuptureStrain | number | no | > 0 |
section.rebarConcreteDisplacement | "subtract" | "overlap" | no | |
section.confinedCore | object | no | |
section.confinedCore.coverDepth | number | yes | > 0 |
section.confinedCore.material | object | yes | |
section.confinedCore.material.kind | "concrete" | yes | |
section.confinedCore.material.compressiveStrength | number | yes | > 0 |
section.confinedCore.material.elasticModulus | number | no | > 0 |
section.confinedCore.material.peakStrain | number | no | > 0 |
section.confinedCore.material.ultimateStrain | number | no | > 0 |
section.confinedCore.material.descendingStrain | number | no | > 0 |
section.confinedCore.material.tensileStrength | number | no | ≥ 0 |
section.confinedCore.material.model | "todeschini" | "hognestad" | "concrete01" | no | |
section.confinedCore.material.residualStrength | number | no | ≥ 0 |
material | object | no | |
material.kind = "concrete" | variant | yes | |
material.compressiveStrength | number | yes | > 0 |
material.elasticModulus | number | no | > 0 |
material.peakStrain | number | no | > 0 |
material.ultimateStrain | number | no | > 0 |
material.descendingStrain | number | no | > 0 |
material.tensileStrength | number | no | ≥ 0 |
material.model | "todeschini" | "hognestad" | "concrete01" | no | |
material.residualStrength | number | no | ≥ 0 |
material.kind = "steel" | variant | no | |
material.elasticModulus | number | yes | > 0 |
material.yieldStress | number | yes | > 0 |
material.hardeningRatio | number | no | 0 – 1 |
material.ultimateStrain | number | no | > 0 |
eiCurve[] | object[] | no | min 2 items |
eiCurve[].moment | number | yes | > 0 |
eiCurve[].curvature | number | yes | > 0 |
momentCapacity | number | object[] | no | |
lrfd | object | no | |
lrfd.loads[] | object[] | yes | min 1 item |
lrfd.loads[].type | "dead" | "live" | "earthquake" | "impact" | "wind" | "water" | "ice" | "soil-pressure" | "live-roof" | "rain" | "snow" | "temperature" | "special" | yes | |
lrfd.loads[].shear | number | yes | |
lrfd.loads[].axial | number | no | |
lrfd.loads[].moment | number | no | |
lrfd.loads[].distributed | number | no | |
lrfd.cases[] | object[] | no | min 1 item |
lrfd.cases[].name | string | yes | |
lrfd.cases[].factors | object | yes | |
lrfd.cases[].phiFlexure | number | yes | 0 – 1 |
lrfd.cases[].phiShear | number | no | 0 – 1 |
lrfd.phiFlexure | number | no | 0 – 1 |
lrfd.phiShear | number | no | 0 – 1 |
lrfd.nominalMoment | number | no | > 0 |
lrfd.shearCapacity | number | no | > 0 |
Response fields
| Field | Type | Description |
|---|---|---|
converged | boolean | Whether the nonlinear p-y iteration converged. Treat a non-converged result as unusable. |
iterations | number | Iterations the solver needed. |
head.deflection | number | Pile-head lateral deflection (m). |
head.slope | number | Pile-head slope (rad). |
head.moment | number | Moment at the head (kN·m). |
head.shear | number | Shear at the head (kN). |
maxMoment.value | number | Largest-magnitude bending moment along the pile (kN·m). |
maxMoment.depth | number | Depth of the maximum moment, from the pile head (m). |
maxDeflection.value / .depth | number | Largest-magnitude deflection (m) and its depth (m). |
maxShear.value / .depth | number | Largest-magnitude shear (kN) and its depth (m). |
equilibrium.residual | number | Unbalanced force after convergence (kN) — a solution-quality check against equilibrium.applied. |
equilibrium.applied | number | Total applied lateral load (kN). |
nodes[] | object[] | The full solution, one entry per finite-difference node from head to tip. |
nodes[].depth | number | Depth from the pile head (m). |
nodes[].soilDepth | number | Depth below the ground surface (m) — differs from depth when the head is above grade. |
nodes[].deflection | number | Lateral deflection y (m). |
nodes[].slope | number | Slope dy/dz (rad). |
nodes[].moment | number | Bending moment (kN·m). |
nodes[].shear | number | Shear force (kN). |
nodes[].soilReaction | number | Mobilized soil reaction p (kN/m); opposes the deflection. |
sweep[] | object[] | Present when the request set sweep — one entry per pushover step, each an independent solution at the proportionally scaled head load: shear, moment, headDeflection, headSlope, maxMoment, maxMomentDepth, maxDeflection, converged. |
sectionProperties | object | Present when the request set section + material (cracked-section nonlinear EI): the fiber-integrated section's elasticEI (kN·m²), yieldMoment, and plasticMoment (kN·m). |
Code examples
A complete, runnable request. Replace YOUR_API_KEY (or set PILECALC_API_KEY in your environment).
curl https://api.pilecalc.com/v1/lateral \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"unitSystem": "si",
"pile": {
"kind": "uniform",
"length": 15,
"diameter": 0.61,
"ei": 143000,
"groundSurfaceDepth": 0
},
"soil": {
"layers": [
{
"model": "soft-clay",
"top": 0,
"bottom": 4,
"gamma": 8.5,
"c": 24,
"e50": 0.02
},
{
"model": "sand-reese",
"top": 4,
"bottom": 15,
"gamma": 9.5,
"phi": 34,
"k": 16300
}
]
},
"head": {
"kind": "shear-moment",
"shear": 312,
"moment": 0
},
"axialLoad": 1200,
"loadType": "cyclic",
"layering": "direct",
"increments": 120
}'The GET variant returns head.deflection,maxMoment.value as a plain-text CSV line — made for Excel's WEBSERVICE() and Sheets' IMPORTDATA() (see Use PileCalc in Excel). Omit fields to get the full JSON response.
Interpreting results
- Check
convergedfirst. If false, the pile is likely failing (soil fully mobilized) or too short — the numbers are not an equilibrium solution. - Deflections and moments follow the sign of the applied load; see Units & sign conventions.
nodeshasincrements + 1entries (default 121). For plots, usenodes[].depthvs.deflection/moment/shear.equilibrium.residualshould be small relative toequilibrium.applied; a large residual withconverged: trueis a red flag worth reporting.
Sizing runs
sweep (e.g. { "steps": 20 }) instead of sweeping head.shear across calls — the whole pushover curve comes back in sweep[] and costs a single request of your monthly quota.