FORMA API
Send one camera frame, get body measurements back. Circumferences, landmarks and a parametric mesh, from FORMA's own measurement engine — no third-party model in the path.
No image is ever stored. Pixels are measured in the request and discarded; only the derived numbers are kept.
Try it without paying
Keys beginning forma_test_ run the identical engine and are never counted against an allowance. Build the whole integration on a test key, then swap it for forma_live_ when you go live. Nothing else changes.
Create one at API keys. It is shown once and hashed at rest, so it cannot be shown again — lose it and revoke it rather than hunting for it.
Measure a frame
Two formats. Prefer the binary one: a 1280×1280 frame is 6.5 MB of pixels, and base64 inside JSON makes it 8.7 MB plus a parse and a decode before any measuring starts.
# Raw RGBA in the body — no base64, no JSON parse curl -X POST https://myforma3d.com/api/v1/scans \ -H "authorization: Bearer forma_test_YOUR_KEY" \ -H "content-type: application/octet-stream" \ -H "x-forma-profile-id: PROFILE_ID" \ -H "x-forma-width: 720" \ -H "x-forma-height: 1280" \ --data-binary @frame.rgba
Or JSON, if that is easier to produce:
curl -X POST https://myforma3d.com/api/v1/scans \
-H "authorization: Bearer forma_test_YOUR_KEY" \
-H "content-type: application/json" \
-d '{"profileId":"PROFILE_ID","width":720,"height":1280,"rgbaBase64":"..."}'The response:
{
"ok": true,
"requestId": "req_9f2c…",
"data": {
"scanId": "…",
"environment": "test",
"billable": false,
"measurements": {
"height_cm": 178.0, "chest_cm": 98.4, "waist_cm": 84.1,
"hips_cm": 99.2, "shoulders_cm": 45.6, "left_thigh_cm": 56.3
},
"scores": { "quality": 0.83, "confidence": 0.79, "lighting": 0.71 }
}
}Capture quality
A frame that cannot be measured is refused rather than guessed at. Quality below 0.15 returns 422 scan_quality_too_low with the individual scores so you can tell the user what to fix. Weak framing, lighting or pose returns 400 with softIssues — resubmit with confirmLowFraming to accept the lower confidence.
Neither is billed. A capture we could not measure is ours to explain, not yours to pay for.
Limits
| Plan | Live scans / month | Requests / min | Live keys |
|---|---|---|---|
free | — | 30 | sandbox only |
trial | — | 30 | sandbox only |
home | — | 60 | sandbox only |
home_plus | — | 60 | sandbox only |
business | 2000 | 300 | yes |
Live API access is part of the Business plan. Every plan can use test keys without limit on allowance. Check your own numbers any time with GET /api/v1/account.
Errors
Every failure returns a stable code and a requestId. Branch on the code — the message is for humans and may be reworded. Quote the requestId in any support request.
| Code | HTTP | Meaning |
|---|---|---|
missing_api_key | 401 | No API key was supplied. Send Authorization: Bearer forma_live_… |
malformed_api_key | 401 | That does not look like a FORMA key. Keys begin forma_live_ or forma_test_. |
invalid_api_key | 401 | The API key is not recognised. |
revoked_api_key | 401 | That API key has been revoked. |
plan_required | 403 | Your plan does not include live API access. |
quota_exceeded | 429 | Monthly scan allowance used up. |
rate_limited | 429 | Too many requests. Slow down and retry. |
invalid_request | 400 | The request body or parameters were not valid. |
payload_too_large | 413 | The frame is larger than this plan allows. |
not_found | 404 | No such resource. |
scan_quality_too_low | 422 | The capture was not good enough to measure. |
store_unavailable | 503 | The durable store is unreachable, so this request cannot be recorded. |
internal_error | 500 | Something went wrong on our side. |
Machine-readable at /api/v1/errors.
Reference
- OpenAPI 3.1 specification — import into Postman or generate a client
GET /api/v1/account— plan, limits and usageGET /api/v1/usage— allowance used this periodGET /api/v1/scans?profileId=…— history for a profileGET /api/v1/scans/{id}— one scan with landmarks and mesh
Business allowance is 2000 live scans a month at 300 requests a minute.
Talk to us
API access is $299/month for 2,000 live scans. Higher volume is priced per use case — tell us what you are building and we will size it.
Something broken? Report it here and include the requestId from the response — it identifies the exact request in our logs and turns a guessing game into a lookup.
You can build the entire integration on a sandbox key before speaking to anyone. That is deliberate: we would rather you knew it worked first.