Developer Center

Body Measurement API

Extract 30+ body measurements from two smartphone photos. Built for tailors, fashion brands, and e-commerce platforms that need accurate fit data at scale.

Get API Key

API BASE URL

https://korra.work/api/v2

All endpoints accept API key via X-API-Key header. Rate limited.

Getting Started

Quick Start

Get measurements in 3 lines of code. The SDK handles file uploads, async polling, and credit management automatically.

Step 1
Upload Photos
Send front + side photos with height. 1 credit deducted.
Step 2
Get task_id
API returns immediately with a task ID for async processing.
Step 3
Poll Status
Check task status until completed. Or use the SDK's built-in waiter.
Step 4
Get Measurements
Receive 30+ body measurements, body shape, and size recommendation.

JAVASCRIPT SDK

Copy
// Install: npm install @korra/sdk
import Korra from '@korra/sdk';

const korra = new Korra({ apiKey: 'korra_live_xxx' });

// Upload photos and start extraction (deducts 1 credit)
const { task_id } = await korra.extract({
  front: frontPhotoBlob,
  side: sidePhotoBlob,
  height: 175,
  gender: 'male',
});

// Wait for completion (handles polling with backoff)
const result = await korra.waitForCompletion(task_id);
console.log(result.measurements);
// → { "Neck Round": 38.5, "Chest Round": 102.3, ... }

PYTHON SDK

Copy
# Install: pip install aiscan
import aiscan

client = aiscan.Client(api_key="korra_live_xxx")

# Upload photos (deducts 1 credit)
result = client.measurements.extract(
    front="front.jpg",
    side="side.jpg",
    height=175,
    gender="male",
)

# Wait for completion
final = client.measurements.wait_for_completion(result["task_id"])
print(final["measurements"])

CURL

Copy
# Step 1: Extract
curl -X POST "https://korra.work/api/v2/measurements/extract" \
  -H "X-API-Key: korra_live_xxx" \
  -F "front=@front.jpg" \
  -F "side=@side.jpg" \
  -F "height=175" \
  -F "gender=male"
# → { "status": "accepted", "task_id": "a1b2c3d4-..." }

# Step 2: Poll status
curl "https://korra.work/api/v2/measurements/status/a1b2c3d4-..." \
  -H "X-API-Key: korra_live_xxx"
# → { "status": "completed", "measurements": { ... } }
Security

Authentication

Every request requires an API key via the X-API-Key header. Get your key at korra.work/signup.

HEADER FORMAT

X-API-Key: korra_live_xxx

KEY FORMAT

PrefixTypeDescription
korra_live_ProductionLive key. Deducts credits. Use in production.
korra_test_SandboxFree testing. No real credits consumed.
Core Endpoint

Extract Measurements

The core endpoint. Upload front + side photos with height and gender. Returns a task_id for async processing. Each call deducts 1 credit from your account.

POST /measurements/extract

Accepts multipart/form-data with two images. Processing runs asynchronously — returns a task_id immediately.

REQUEST BODY

FieldTypeDescription
front requiredFileFront-facing photo. JPEG/PNG/WebP. Max 10MB.
side requiredFileSide-facing photo. JPEG/PNG/WebP. Max 10MB.
height requiredfloatHeight in centimeters. Range: 100–230.
gender optionalstring"male" or "female". Default: "male".
client_name optionalstringLabel for this scan. Default: "Unnamed Client".

HEADERS

HeaderTypeDescription
X-API-Key requiredstringYour API key (korra_live_xxx)

SUCCESS RESPONSE — 200

Copy
{
  "status": "accepted",
  "task_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

ERROR RESPONSES

CodeReason
401Missing or invalid API key
402Insufficient credits. Add credits in your dashboard.
400Empty images or height out of range (100–230 cm)

SDK METHOD

Copy
// JavaScript
const { task_id } = await korra.extract({
  front: frontBlob,       // File, Blob, or Buffer
  side: sideBlob,
  height: 175,            // cm
  gender: 'male',         // "male" | "female"
  client_name: 'John',    // optional
});

# Python
result = client.measurements.extract(
    front="front.jpg",    # file path, file object, or bytes
    side="side.jpg",
    height=175,
    gender="male",
    client_name="John",   # optional
)
Async Processing

Poll Task Status

Check the status of an extraction task. No authentication required — the task_id is the only secret. Tasks expire after 24 hours.

GET /measurements/status/{task_id}

Returns the current state of the task. No API key required.

PATH PARAMS

ParamTypeDescription
task_id requiredstringUUID returned from /extract

RESPONSE STATUSES

QUEUED / PROCESSING
Copy
{
  "status": "processing",
  "created_at": "2026-08-31T12:00:00",
  "height": 175.0,
  "gender": "male"
}
COMPLETED
Copy
{
  "status": "completed",
  "measurements": {
    "Neck Round": 38.5,
    "Chest Round": 102.3,
    "Waist Round": 86.1,
    "Hip Round": 98.7,
    "Shoulder": 45.2,
    // ... 30+ measurements
  },
  "body_shape": "Athletic",
  "size_recommendation": "L",
  "mesh_url": "/meshes/korra_twin_a1b2...obj",
  "clinical_realism_index": 0.95
}
FAILED
Copy
{
  "status": "failed",
  "error": "AI Subprocess Crashed (RC -1)"
}

SDK: WAIT FOR COMPLETION

The SDK handles polling with exponential backoff (2s → 4s → 8s → 30s cap). Default: 30 attempts (~5 min timeout).

Copy
// JavaScript — automatic polling
const result = await korra.waitForCompletion(task_id, {
  onStatus: (s) => console.log(s.status),  // optional callback
  pollIntervalMs: 2000,                     // initial interval
  maxAttempts: 30,                          // max polls
});

# Python — automatic polling
final = client.measurements.wait_for_completion(
    task_id,
    on_status=lambda s: print(s["status"]),
    poll_interval=2.0,
    max_attempts=30,
)

SDK: MANUAL POLLING

Copy
// JavaScript — manual polling
let status;
do {
  status = await korra.status(task_id);
  if (status.status === 'failed') throw new Error(status.error);
  if (status.status !== 'completed') await sleep(3000);
} while (status.status !== 'completed');

console.log(status.measurements);
Embeddable Widget

Widget Extraction

Embed body scanning in your own app without requiring an API key. The merchant_id is passed as a form field. Deducts credits from the merchant's account.

POST /measurements/extract-widget

Same as /extract but for merchant-embedded widgets. Supports Paystack single-scan billing or merchant credit deduction.

REQUEST BODY

FieldTypeDescription
front requiredFileFront-facing photo.
side requiredFileSide-facing photo.
height requiredfloatHeight in cm (100–230).
merchant_id requiredstringYour merchant user ID (from auth).
gender optionalstring"male" or "female". Default: "male".
client_name optionalstringDefault: "Widget Customer".
payment_reference optionalstringPaystack reference for pay-per-scan billing.
client_user_id optionalstringClient's user ID for dual-account linking.

BILLING LOGIC

1. Paystack reference provided? → Verifies payment. If successful, no credit deducted.
2. No reference? → Deducts 1 credit from merchant_id's account.
3. 0 credits + no reference? → Returns 402 "Payment Required".

NO AUTH HEADER REQUIRED

This endpoint does not use X-API-Key. The merchant_id is provided as a form field. The caller asserts their identity via the merchant_id value.

SDK METHOD

Copy
// JavaScript
const { task_id } = await korra.extractWidget({
  front: frontBlob,
  side: sideBlob,
  height: 165,
  gender: 'female',
  merchant_id: 'my-merchant-uuid',
  payment_reference: 'pay_ref_xxx',  // optional: pay-per-scan
});

# Python
result = client.measurements.extract_widget(
    front="front.jpg",
    side="side.jpg",
    height=165,
    merchant_id="my-merchant-uuid",
    payment_reference="pay_ref_xxx",
)
Quick Estimates

Height Estimate

Get estimated measurements from height only. No photos required. Free to call — does not deduct credits.

POST /measurements/estimate FREE

Uses ANSUR II anthropometric ratios. Returns 13 body measurements plus body shape and size recommendation. No credits deducted.

REQUEST BODY

FieldTypeDescription
height requiredfloatHeight in cm (100–230).
gender optionalstring"male" or "female". Default: "male".

SUCCESS RESPONSE — 200

Copy
{
  "status": "success",
  "estimation_mode": "height_only",
  "measurements": {
    "chest": 89.3,
    "waist": 73.5,
    "hip": 93.1,
    "shoulder": 77.0,
    "inseam": 91.0,
    "outseam": 129.5,
    "sleeve": 66.5,
    "neck": 35.0,
    "bicep": 28.0,
    "wrist": 17.5,
    "thigh": 54.3,
    "calf": 35.0,
    "ankle": 22.8
  },
  "body_shape": "Hourglass",
  "size_recommendation": "M",
  "confidence": "estimated",
  "note": "Estimates based on standard anthropometric ratios."
}

SDK METHOD

Copy
// JavaScript
const estimate = await korra.estimate({ height: 175, gender: 'male' });
console.log(estimate.measurements.chest);  // ~95.2

# Python
estimate = client.measurements.estimate(height=175, gender="male")
print(estimate["measurements"]["chest"])
Platform Builders

Key Provisioning

Generate API keys for your users. Use this when building a platform that provisions Korra access for sub-accounts.

POST /keys/provision

Creates a korra_live_ API key for an authenticated user. Requires a Supabase JWT via Bearer token.

HEADERS

HeaderTypeDescription
Authorization requiredstringBearer token: "Bearer {supabase_jwt}"

QUERY PARAMS

ParamTypeDescription
regenerate optionalboolIf true, revoke existing keys and create new one. Default: false.

SUCCESS RESPONSE — 200

Copy
{
  "key": "korra_live_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "tier": "tailor_pro",
  "created": true
}

SDK METHOD

Copy
// JavaScript
const { key, tier, created } = await korra.provisionKey({
  jwt: 'eyJhbGciOiJIUzI1NiIs...',
  regenerate: false,
});

# Python
result = client.measurements.provision_key(
    jwt="eyJhbGciOiJIUzI1NiIs...",
    regenerate=False,
)
Data Access

List & Retrieve

Query past extractions. All endpoints require API key authentication and follow standard credit rules.

GET /measurements

List all measurements. Returns paginated results.

Copy
// JavaScript
const list = await korra.measurements.list({ limit: 20, offset: 0 });

# Python
list = client.measurements.list(limit=20, offset=0)
GET /measurements/{id}

Get a specific measurement by ID.

GET /measurements/{id}/pdf

Download a PDF report of the measurement.

GET /measurements/status/{task_id}

Poll task status. No auth required. See Poll Task Status above.

Libraries

SDK Reference

Install the SDK for your platform. Handles auth, retries, file uploads, and async polling automatically.

JavaScript / TypeScript

Copy
npm install @korra/sdk

Python

Copy
pip install aiscan

ALL EXTRACTION METHODS

MethodCreditsDescription
extract()1 creditUpload front + side photos. Returns task_id.
extractWidget()1 creditWidget flow. Deducts from merchant or Paystack.
status(taskId)freePoll task status. No auth needed.
waitForCompletion(taskId)freeAuto-poll with backoff until done.
estimate({height})freeHeight-only estimate. No photos needed.
provisionKey({jwt})freeGenerate API key for a user.
list()freeList past measurements.
get(id)freeGet specific measurement.
delete(id)freeDelete a measurement.
pdf(id)freeDownload PDF report.
Troubleshooting

Error Codes

The API uses standard HTTP status codes. Error responses include a detail field with a human-readable message.

CodeMeaningAction
400Bad RequestCheck height range (100–230) and image files.
401UnauthorizedCheck your API key. Get one at /signup.
402Payment RequiredAdd credits in your dashboard.
404Not FoundResource does not exist.
429Rate LimitedWait and retry. Check Retry-After header.
500Server ErrorRetry with backoff. Contact support if persistent.

ERROR RESPONSE FORMAT

Copy
{
  "detail": "Insufficient credits for extraction."
}

SDK ERROR HANDLING

Copy
// JavaScript
import Korra, { InsufficientCreditsError, AuthenticationError } from '@korra/sdk';

try {
  await korra.extract({ ... });
} catch (e) {
  if (e instanceof InsufficientCreditsError) {
    // Redirect to billing
  } else if (e instanceof AuthenticationError) {
    // Re-authenticate
  }
}

# Python
from aiscan.exceptions import InsufficientCreditsError, AuthenticationError

try:
    client.measurements.extract(...)
except InsufficientCreditsError:
    # Redirect to billing
except AuthenticationError:
    # Re-authenticate
Interactive Reference

Test Your Integration

Test endpoints, inspect full schemas, and verify your API keys in the browser.

Launch Swagger UI