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

POST
https://api.pilecalc.com/v1/lateral
GET
/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.

FieldTypeRequiredConstraints
unitSystem"si" | "us"yes
pileobjectyes
pile.kind = "uniform"variantno
pile.lengthnumberyes> 0
pile.diameternumberyes> 0
pile.einumberyes> 0
pile.groundSurfaceDepthnumberno≥ 0
pile.kind = "sectioned"variantyes
pile.sectionsobject | object[]yesmin 1 item
pile.groundSurfaceDepthnumberno≥ 0
soilobjectyes
soil.layers[]object[]yesmin 1 item
soil.layers[].model = "soft-clay"variantyes
soil.layers[].topnumberyes≥ 0
soil.layers[].bottomnumberyes> 0
soil.layers[].gammanumberyes> 0
soil.layers[].gammaBottomnumberno> 0
soil.layers[].cnumberyes> 0
soil.layers[].cBottomnumberno> 0
soil.layers[].e50numberyes> 0
soil.layers[].e50Bottomnumberno> 0
soil.layers[].jnumberno> 0
soil.layers[].model = "stiff-clay-above-water"variantyes
soil.layers[].topnumberyes≥ 0
soil.layers[].bottomnumberyes> 0
soil.layers[].gammanumberyes> 0
soil.layers[].gammaBottomnumberno> 0
soil.layers[].cnumberyes> 0
soil.layers[].cBottomnumberno> 0
soil.layers[].e50numberyes> 0
soil.layers[].e50Bottomnumberno> 0
soil.layers[].jnumberno> 0
soil.layers[].knumberno> 0
soil.layers[].kBottomnumberno> 0
soil.layers[].model = "stiff-clay-below-water"variantyes
soil.layers[].topnumberyes≥ 0
soil.layers[].bottomnumberyes> 0
soil.layers[].gammanumberyes> 0
soil.layers[].gammaBottomnumberno> 0
soil.layers[].cnumberyes> 0
soil.layers[].cBottomnumberno> 0
soil.layers[].e50numberyes> 0
soil.layers[].e50Bottomnumberno> 0
soil.layers[].knumberyes> 0
soil.layers[].kSource"gec9-table-a1"noUI 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[].kBottomnumberno> 0
soil.layers[].model = "sand-reese"variantyes
soil.layers[].topnumberyes≥ 0
soil.layers[].bottomnumberyes> 0
soil.layers[].gammanumberyes> 0
soil.layers[].gammaBottomnumberno> 0
soil.layers[].phinumberyes0 – 60
soil.layers[].phiBottomnumberno0 – 60
soil.layers[].knumberyes> 0
soil.layers[].kBottomnumberno> 0
soil.layers[].model = "sand-api"variantyes
soil.layers[].topnumberyes≥ 0
soil.layers[].bottomnumberyes> 0
soil.layers[].gammanumberyes> 0
soil.layers[].gammaBottomnumberno> 0
soil.layers[].phinumberyes0 – 60
soil.layers[].phiBottomnumberno0 – 60
soil.layers[].knumberyes> 0
soil.layers[].kBottomnumberno> 0
soil.layers[].model = "weak-rock"variantyes
soil.layers[].topnumberyes≥ 0
soil.layers[].bottomnumberyes> 0
soil.layers[].gammanumberyes> 0
soil.layers[].gammaBottomnumberno> 0
soil.layers[].qunumberyes> 0
soil.layers[].quBottomnumberno> 0
soil.layers[].eirnumberyes> 0
soil.layers[].eirBottomnumberno> 0
soil.layers[].rqdnumberyes0 – 100
soil.layers[].rqdBottomnumberno0 – 100
soil.layers[].krmnumberno> 0
soil.layers[].model = "elastic"variantyes
soil.layers[].topnumberyes≥ 0
soil.layers[].bottomnumberyes> 0
soil.layers[].gammanumberyes> 0
soil.layers[].gammaBottomnumberno> 0
soil.layers[].esTopnumberyes≥ 0
soil.layers[].esBottomnumberno≥ 0
soil.layers[].model = "user"variantyes
soil.layers[].topnumberyes≥ 0
soil.layers[].bottomnumberyes> 0
soil.layers[].gammanumberyes> 0
soil.layers[].gammaBottomnumberno> 0
soil.layers[].esnumberno≥ 0
soil.layers[].curves[]object[]no
soil.layers[].curves[].depthnumberyes≥ 0
soil.layers[].curves[].points[]object[]yesmin 2 items
soil.layers[].curves[].points[].ynumberyes≥ 0
soil.layers[].curves[].points[].pnumberyes≥ 0
soil.layers[].model = "liquefied-sand"variantyes
soil.layers[].topnumberyes≥ 0
soil.layers[].bottomnumberyes> 0
soil.layers[].gammanumberyes> 0
soil.layers[].gammaBottomnumberno> 0
soil.layers[].units"SI" | "US"yes
soil.layers[].model = "silt-cphi"variantyes
soil.layers[].topnumberyes≥ 0
soil.layers[].bottomnumberyes> 0
soil.layers[].gammanumberyes> 0
soil.layers[].gammaBottomnumberno> 0
soil.layers[].cnumberyes> 0
soil.layers[].cBottomnumberno> 0
soil.layers[].phinumberyes0 – 60
soil.layers[].phiBottomnumberno0 – 60
soil.layers[].knumberyes> 0
soil.layers[].kBottomnumberno> 0
soil.layers[].jnumberno> 0
soil.layers[].model = "piedmont-residual"variantyes
soil.layers[].topnumberyes≥ 0
soil.layers[].bottomnumberyes> 0
soil.layers[].gammanumberyes> 0
soil.layers[].gammaBottomnumberno> 0
soil.layers[].esinumberyes> 0
soil.layers[].esiBottomnumberno> 0
soil.layers[].lambdanumberno> 0
soil.layers[].model = "iso-clay"variantyes
soil.layers[].topnumberyes≥ 0
soil.layers[].bottomnumberyes> 0
soil.layers[].gammanumberyes> 0
soil.layers[].gammaBottomnumberno> 0
soil.layers[].sunumberyes> 0
soil.layers[].suBottomnumberno> 0
soil.layers[].alphanumberno0 – 1
soil.layers[].gap"open" | "closed"no
soil.layers[].clayType"soft" | "stiff"no
soil.cyclesnumbernointeger, > 0Cycle 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.
headobjectyes
head.kind = "shear-moment"variantyes
head.shearnumberyes
head.momentnumberyes
head.kind = "shear-slope"variantyes
head.shearnumberyes
head.slopenumberyes
head.kind = "shear-rotational"variantyes
head.shearnumberyes
head.rotationalStiffnessnumberyes≥ 0
head.kind = "deflection-moment"variantyes
head.deflectionnumberyes
head.momentnumberyes
head.kind = "deflection-slope"variantyes
head.deflectionnumberyes
head.slopenumberyes
tipobjectno
tip.shearnumberno
tip.momentnumberno
axialLoadnumberno
loadType"static" | "cyclic"no
incrementsnumbernointeger, 10 – 800
layering"direct" | "georgiadis"no
groundSlopeDegnumberno0 – 45
pMultipliernumberno0 – 1
pMultiplierProfile[]object[]nomin 2 items
pMultiplierProfile[].depthnumberyes≥ 0
pMultiplierProfile[].factornumberyes0 – 1
tipShearCurve[]object[]nomin 2 items
tipShearCurve[].deflectionnumberyes≥ 0
tipShearCurve[].shearnumberyes≥ 0
distributedLoadobjectno
distributedLoad.intensitynumberyes
distributedLoad.topnumberno≥ 0
distributedLoad.bottomnumberno≥ 0
soilMovementobjectno
soilMovement.magnitudenumberyes
soilMovement.slidingDepthnumberyes> 0
sweepobjectno
sweep.stepsnumberyesinteger, 2 – 50
sweep.maxShearnumberno
sweep.maxMomentnumberno
sectionobjectno
section.kind = "solid-circular"variantyes
section.diameternumberyes> 0
section.kind = "pipe"variantyes
section.outerDiameternumberyes> 0
section.wallThicknessnumberyes> 0
section.kind = "rectangular"variantyes
section.widthnumberyes> 0
section.heightnumberyes> 0
section.kind = "round-concrete"variantyes
section.diameternumberyes> 0
section.cageobjectno
section.cage.countnumberyesinteger, 1 – 200
section.cage.areanumberyes> 0
section.cage.barDiameternumberyes> 0
section.cage.barCircleRadiusnumberyes> 0
section.cage.yieldStressnumberyes> 0
section.cage.elasticModulusnumberyes> 0
section.cage.effectivePrestrainnumberno≥ 0
section.cage.hardeningRationumberno0 – 1
section.cage.tensionRuptureStrainnumberno> 0
section.rebarConcreteDisplacement"subtract" | "overlap"no
section.casingobjectno
section.casing.outerDiameternumberyes> 0
section.casing.wallThicknessnumberyes> 0
section.casing.yieldStressnumberyes> 0
section.casing.elasticModulusnumberyes> 0
section.casing.hardeningRationumberno0 – 1
section.casing.tensionRuptureStrainnumberno> 0
section.coreobjectno
section.core.outerDiameternumberyes> 0
section.core.wallThicknessnumberyes> 0
section.core.yieldStressnumberyes> 0
section.core.elasticModulusnumberyes> 0
section.core.hardeningRationumberno0 – 1
section.core.tensionRuptureStrainnumberno> 0
section.kind = "rectangular-concrete"variantyes
section.widthnumberyes> 0
section.heightnumberyes> 0
section.reinforcementobjectno
section.reinforcement.layout"perimeter"yes
section.reinforcement.barsPerFacenumberyesinteger, 2 – 100
section.reinforcement.barsPerSidenumberyesinteger, 0 – 99
section.reinforcement.areanumberyes> 0
section.reinforcement.barDiameternumberyes> 0
section.reinforcement.clearCovernumberyes> 0
section.reinforcement.yieldStressnumberyes> 0
section.reinforcement.elasticModulusnumberyes> 0
section.reinforcement.effectivePrestrainnumberno≥ 0
section.reinforcement.hardeningRationumberno0 – 1
section.reinforcement.tensionRuptureStrainnumberno> 0
section.rebarConcreteDisplacement"subtract" | "overlap"no
section.confinedCoreobjectno
section.confinedCore.coverDepthnumberyes> 0
section.confinedCore.materialobjectyes
section.confinedCore.material.kind"concrete"yes
section.confinedCore.material.compressiveStrengthnumberyes> 0
section.confinedCore.material.elasticModulusnumberno> 0
section.confinedCore.material.peakStrainnumberno> 0
section.confinedCore.material.ultimateStrainnumberno> 0
section.confinedCore.material.descendingStrainnumberno> 0
section.confinedCore.material.tensileStrengthnumberno≥ 0
section.confinedCore.material.model"todeschini" | "hognestad" | "concrete01"no
section.confinedCore.material.residualStrengthnumberno≥ 0
materialobjectno
material.kind = "concrete"variantyes
material.compressiveStrengthnumberyes> 0
material.elasticModulusnumberno> 0
material.peakStrainnumberno> 0
material.ultimateStrainnumberno> 0
material.descendingStrainnumberno> 0
material.tensileStrengthnumberno≥ 0
material.model"todeschini" | "hognestad" | "concrete01"no
material.residualStrengthnumberno≥ 0
material.kind = "steel"variantno
material.elasticModulusnumberyes> 0
material.yieldStressnumberyes> 0
material.hardeningRationumberno0 – 1
material.ultimateStrainnumberno> 0
eiCurve[]object[]nomin 2 items
eiCurve[].momentnumberyes> 0
eiCurve[].curvaturenumberyes> 0
momentCapacitynumber | object[]no
lrfdobjectno
lrfd.loads[]object[]yesmin 1 item
lrfd.loads[].type"dead" | "live" | "earthquake" | "impact" | "wind" | "water" | "ice" | "soil-pressure" | "live-roof" | "rain" | "snow" | "temperature" | "special"yes
lrfd.loads[].shearnumberyes
lrfd.loads[].axialnumberno
lrfd.loads[].momentnumberno
lrfd.loads[].distributednumberno
lrfd.cases[]object[]nomin 1 item
lrfd.cases[].namestringyes
lrfd.cases[].factorsobjectyes
lrfd.cases[].phiFlexurenumberyes0 – 1
lrfd.cases[].phiShearnumberno0 – 1
lrfd.phiFlexurenumberno0 – 1
lrfd.phiShearnumberno0 – 1
lrfd.nominalMomentnumberno> 0
lrfd.shearCapacitynumberno> 0

Response fields

FieldTypeDescription
convergedbooleanWhether the nonlinear p-y iteration converged. Treat a non-converged result as unusable.
iterationsnumberIterations the solver needed.
head.deflectionnumberPile-head lateral deflection (m).
head.slopenumberPile-head slope (rad).
head.momentnumberMoment at the head (kN·m).
head.shearnumberShear at the head (kN).
maxMoment.valuenumberLargest-magnitude bending moment along the pile (kN·m).
maxMoment.depthnumberDepth of the maximum moment, from the pile head (m).
maxDeflection.value / .depthnumberLargest-magnitude deflection (m) and its depth (m).
maxShear.value / .depthnumberLargest-magnitude shear (kN) and its depth (m).
equilibrium.residualnumberUnbalanced force after convergence (kN) — a solution-quality check against equilibrium.applied.
equilibrium.appliednumberTotal applied lateral load (kN).
nodes[]object[]The full solution, one entry per finite-difference node from head to tip.
nodes[].depthnumberDepth from the pile head (m).
nodes[].soilDepthnumberDepth below the ground surface (m) — differs from depth when the head is above grade.
nodes[].deflectionnumberLateral deflection y (m).
nodes[].slopenumberSlope dy/dz (rad).
nodes[].momentnumberBending moment (kN·m).
nodes[].shearnumberShear force (kN).
nodes[].soilReactionnumberMobilized 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.
sectionPropertiesobjectPresent 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).

POST /v1/lateral
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 converged first. 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.
  • nodes has increments + 1 entries (default 121). For plots, use nodes[].depth vs. deflection/moment/shear.
  • equilibrium.residual should be small relative to equilibrium.applied; a large residual with converged: true is a red flag worth reporting.

Sizing runs

For load–deflection curves, set 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.