Bodygram

Your first scan (API)

Call the Bodygram Platform REST API directly with your org ID and API key to get body measurements and a 3D avatar from user stats.

The Bodygram Platform exposes a single REST endpoint for creating scans. If you have an Organization ID and an API key, you can call it directly — no SDK required. This page walks through the two input modes, starting with stats-only.

Don't have an account yet? Sign up free at platform.bodygram.com — you get 5 free scans to explore the API.


Two input modes

The POST /api/orgs/{ORG_ID}/scans endpoint accepts two different payload shapes depending on what data you have:

ModeKey in request bodyWhat you get back
Stats onlystatsEstimationsBody measurements, 3D avatar
Stats + photosphotoScanBody measurements, 3D avatar, body composition, posture

This guide covers stats-only first. Photo scanning is covered in the next section.


Stats-only scan

Send the user's age, gender, height (in mm), and weight (in g) under the statsEstimations key:

curl -X POST "https://platform.bodygram.com/api/orgs/${ORG_ID}/scans" \
  --header "Content-Type: application/json" \
  --header "Authorization: ${API_KEY}" \
  --data '{
    "statsEstimations": {
      "age": 29,
      "gender": "female",
      "height": 1640,
      "weight": 54000
    }
  }'
const response = await fetch(
  `https://platform.bodygram.com/api/orgs/${ORG_ID}/scans`,
  {
    method: 'POST',
    headers: {
      'Authorization': API_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      statsEstimations: {
        age: 29,
        gender: 'female',
        height: 1640,  // mm — 164 cm × 10
        weight: 54000, // g  — 54 kg × 1000
      },
    }),
  }
);

const { entry } = await response.json();
console.log(entry.status);       // 'success' | 'failure'
console.log(entry.measurements); // [{ name, unit, value }, ...]
console.log(entry.avatar);       // { data (base64 .obj), format, type }
import requests

url = f"https://platform.bodygram.com/api/orgs/{ORG_ID}/scans"

response = requests.post(
    url,
    headers={"Authorization": API_KEY},
    json={
        "statsEstimations": {
            "age": 29,
            "gender": "female",
            "height": 1640,  # mm — 164 cm × 10
            "weight": 54000, # g  — 54 kg × 1000
        }
    },
)

entry = response.json()["entry"]
print(entry["status"])       # 'success' | 'failure'
print(entry["measurements"]) # [{ name, unit, value }, ...]
print(entry["avatar"])       # { data (base64 .obj), format, type }

Units

height is in millimeters — 164 cm → 1640. weight is in grams — 54 kg → 54000.

Response

A successful response has entry.status: "success" and includes:

  • entry.measurements — array of { name, unit, value } body dimensions (full list)
  • entry.avatar — base64-encoded Wavefront .obj 3D model (how to render it)
{
  "entry": {
    "id": "scan_testIDFHsFggF",
    "status": "success",
    "measurements": [
      { "name": "bustGirth",  "unit": "mm", "value": 895 },
      { "name": "waistGirth", "unit": "mm", "value": 762 },
      { "name": "hipGirth",   "unit": "mm", "value": 905 }
    ],
    "avatar": {
      "data": "<BASE64_ENCODED_OBJ>",
      "format": "obj",
      "type": "highResolution"
    },
    "bodyComposition": null,
    "posture": null
  }
}

Always check entry.status before reading other fields — on "failure" all data fields are null.

All completed scans are saved to your account — view and manage them from your Bodygram dashboard.


Stats + photos scan

Adding two photos unlocks additional outputs that stats alone cannot provide:

Extra fieldWhat it contains
entry.bodyCompositionEstimated body fat %, lean mass, and more
entry.posturePosture angles and alignment data

Photos

You need two JPEG photos of the person being scanned — one front-facing and one right-side-facing — taken in good lighting with the subject standing upright. You can use these sample images to try the endpoint:

FrontRight
Front poseRight pose

Encode to base64

The API expects both photos as base64 strings (RFC 4648) in the request body:

export FRONT_PHOTO_BASE64=$(base64 -i front.jpg)
export RIGHT_PHOTO_BASE64=$(base64 -i right.jpg)
import { readFileSync } from 'fs';

const frontPhotoBase64 = readFileSync('front.jpg').toString('base64');
const rightPhotoBase64 = readFileSync('right.jpg').toString('base64');
import base64

with open('front.jpg', 'rb') as f:
    front_photo_base64 = base64.b64encode(f.read()).decode()

with open('right.jpg', 'rb') as f:
    right_photo_base64 = base64.b64encode(f.read()).decode()

Send the request

Use the photoScan key instead of statsEstimations, and include frontPhoto and rightPhoto:

curl -X POST "https://platform.bodygram.com/api/orgs/${ORG_ID}/scans" \
  --header "Content-Type: application/json" \
  --header "Authorization: ${API_KEY}" \
  --data "{
    \"photoScan\": {
      \"age\": 29,
      \"gender\": \"female\",
      \"height\": 1640,
      \"weight\": 54000,
      \"frontPhoto\": \"${FRONT_PHOTO_BASE64}\",
      \"rightPhoto\": \"${RIGHT_PHOTO_BASE64}\"
    }
  }"
const response = await fetch(
  `https://platform.bodygram.com/api/orgs/${ORG_ID}/scans`,
  {
    method: 'POST',
    headers: {
      'Authorization': API_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      photoScan: {
        age: 29,
        gender: 'female',
        height: 1640,      // mm — 164 cm × 10
        weight: 54000,     // g  — 54 kg × 1000
        frontPhoto: frontPhotoBase64,
        rightPhoto: rightPhotoBase64,
      },
    }),
  }
);

const { entry } = await response.json();
console.log(entry.status);          // 'success' | 'failure'
console.log(entry.measurements);    // [{ name, unit, value }, ...]
console.log(entry.bodyComposition); // body fat %, lean mass, ...
console.log(entry.posture);         // posture angles and alignment
import requests

response = requests.post(
    f"https://platform.bodygram.com/api/orgs/{ORG_ID}/scans",
    headers={"Authorization": API_KEY},
    json={
        "photoScan": {
            "age": 29,
            "gender": "female",
            "height": 1640,   # mm — 164 cm × 10
            "weight": 54000,  # g  — 54 kg × 1000
            "frontPhoto": front_photo_base64,
            "rightPhoto": right_photo_base64,
        }
    },
)

entry = response.json()["entry"]
print(entry["status"])           # 'success' | 'failure'
print(entry["measurements"])     # [{ name, unit, value }, ...]
print(entry["bodyComposition"])  # body fat %, lean mass, ...
print(entry["posture"])          # posture angles and alignment

Units

Same as stats-only: height in millimeters, weight in grams.

Response

A successful response has entry.status: "success" and includes:

  • entry.measurements — array of { name, unit, value } body dimensions (full list)
  • entry.avatar — base64-encoded Wavefront .obj 3D model (how to render it)
  • entry.bodyComposition — body composition estimates (fat mass, lean mass, body fat %)
  • entry.posture — posture angle measurements derived from the photos
{
  "entry": {
    "id": "scan_testIDFHsFggF",
    "status": "success",
    "measurements": [
      { "name": "bustGirth",  "unit": "mm", "value": 895 },
      { "name": "waistGirth", "unit": "mm", "value": 762 },
      { "name": "hipGirth",   "unit": "mm", "value": 905 }
    ],
    "avatar": {
      "data": "<BASE64_ENCODED_OBJ>",
      "format": "obj",
      "type": "highResolution"
    },
    "bodyComposition": {
      "bodyFatPercentage": { "unit": "percent", "value": 24.3 },
      "bodyFatMass":       { "unit": "g",       "value": 13122 },
      "leanMass":          { "unit": "g",       "value": 40878 }
    },
    "posture": [
      { "name": "headTilt",        "unit": "degree", "value": 2.1 },
      { "name": "shoulderBalance", "unit": "degree", "value": 1.4 },
      { "name": "hipBalance",      "unit": "degree", "value": 0.8 }
    ]
  }
}

Always check entry.status before reading other fields — on "failure" all data fields are null.


Try it live

Enter your credentials and stats below to run a real stats-only scan against the API.

Interactive Demo

1Credentials

Enter your Bodygram credentials. Find them on your account page. Never call this endpoint directly from the browser in production — your API key must stay server-side.

Your organization ID

Demo only — keep server-side in production

2User Stats

Collect the shopper's stats first. Gender is used both by the estimation call and to pick the correct scanner silhouette in the next step.

years
cm
kg

Upload a front-facing and right-side photo. The person should be standing upright in good lighting.

front.jpg
right.jpg
3Run Scan

Ready to call POST /api/orgs//scans with a photoScan payload.

4Results
CODE
// Step 1 — authenticate with your org ID and API key
const ORG_ID = 'YOUR_ORG_ID';
const API_KEY = 'YOUR_API_KEY';

// Never expose your API key in client-side code.
// In production, make this request from your server.

On this page