HTTP API reference
Every analysis in the app is a thin layer over a clean HTTP API. Run the engine from your own scripts, CI, or spreadsheets.
Every analysis in the app is a thin layer over a clean HTTP API. The web app never computes anything itself — it sends your inputs to the same engine you can call directly from your own scripts, CI pipelines, or spreadsheets. The request and response shapes are defined by a shared schema, so what you see in the app is exactly what the API accepts.
The API is served at https://api.pilecalc.com. All requests and responses are JSON.
Authentication
Requests are authenticated with an API key. Create one from your account (your profile → API keys), then send it as a bearer token on every request:
Authorization: Bearer <your_api_key>Each key is tied to your account and your subscription plan, which together determine your rate limits. Treat keys like passwords — they grant full access to run analyses under your account. You can revoke a key at any time from the same screen.
Keep keys server-side
Endpoints
Each analysis is a POST under /v1 taking the same JSON body the app sends. Two helper GET endpoints round it out.
/v1/health— service status (no key required)./v1/usage— your current plan, quota and remaining calls.Rate limits & quotas
Each plan includes a monthly allowance of API calls (resetting on the 1st, UTC) plus a per-minute burst guard. Successful analysis calls count against your quota; rejected requests (auth or validation failures) do not.
| Plan | Calls / month | Burst / minute |
|---|---|---|
| Free | 5 | 2 |
| Pro | 100 | 20 |
| Business | 300 | 60 |
Successful responses include X-RateLimit-Limit and X-RateLimit-Remaining headers. Call GET /v1/usage any time for a snapshot of where you stand.
Example: lateral analysis
A minimal request to the lateral endpoint:
curl https://api.pilecalc.com/v1/lateral \
-H "Authorization: Bearer $PILECALC_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"pile": { "kind": "uniform", "length": 12, "diameter": 0.6, "ei": 190000 },
"soil": { "layers": [
{ "model": "soft-clay", "top": 0, "bottom": 4, "gamma": 8, "c": 20, "e50": 0.01 },
{ "model": "sand-api", "top": 4, "bottom": 12, "gamma": 10, "phi": 34, "k": 16000 }
]},
"head": { "kind": "shear-moment", "shear": 150, "moment": 0 },
"increments": 120
}'Response (abbreviated):
{
"converged": true,
"head": { "deflection": 0.0310, "slope": -0.0098, "moment": 0, "shear": 150 },
"maxMoment": { "value": 368.0, "depth": 4.2 },
"maxShear": { "value": 150.0, "depth": 0.0 },
"equilibrium": { "residual": 0.0002, "applied": 150 },
"nodes": [ { "depth": 0, "deflection": 0.0310, "moment": 0, "shear": 150 }, ... ]
}Errors
Every error has the shape { "error": { "code", "message", issues? } }. The status code tells you the category:
unauthorized— missing, invalid, or revoked API key.validation_error— the body failed schema validation (see issues).rate_limited— per-minute burst exceeded — retry after the Retry-After header.quota_exceeded— monthly quota reached — upgrade or wait for the reset.engine_error— inputs were valid but the analysis could not complete.Validation errors list each problem keyed by its field path — the same validation that powers the app's inline errors:
{ "error": { "code": "validation_error",
"issues": [ { "path": ["pile","diameter"], "message": "Number must be greater than 0" } ] } }Units
The API is unit-agnostic: supply any self-consistent system and results come back in the same units. There are no hidden conversions, so the rules in Units & sign conventions apply equally to API calls — in particular the water unit weight for axial and shaft analyses.