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.
API BASE URL
https://korra.work/api/v2
All endpoints accept API key via X-API-Key header. Rate limited.
Quick Start
Get measurements in 3 lines of code. The SDK handles file uploads, async polling, and credit management automatically.
JAVASCRIPT SDK
// 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
# 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
# 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": { ... } }
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
| Prefix | Type | Description |
|---|---|---|
| korra_live_ | Production | Live key. Deducts credits. Use in production. |
| korra_test_ | Sandbox | Free testing. No real credits consumed. |
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.
Accepts multipart/form-data with two images. Processing runs asynchronously — returns a task_id immediately.
REQUEST BODY
| Field | Type | Description |
|---|---|---|
| front required | File | Front-facing photo. JPEG/PNG/WebP. Max 10MB. |
| side required | File | Side-facing photo. JPEG/PNG/WebP. Max 10MB. |
| height required | float | Height in centimeters. Range: 100–230. |
| gender optional | string | "male" or "female". Default: "male". |
| client_name optional | string | Label for this scan. Default: "Unnamed Client". |
HEADERS
| Header | Type | Description |
|---|---|---|
| X-API-Key required | string | Your API key (korra_live_xxx) |
SUCCESS RESPONSE — 200
{
"status": "accepted",
"task_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
ERROR RESPONSES
| Code | Reason |
|---|---|
| 401 | Missing or invalid API key |
| 402 | Insufficient credits. Add credits in your dashboard. |
| 400 | Empty images or height out of range (100–230 cm) |
SDK METHOD
// 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 )
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.
Returns the current state of the task. No API key required.
PATH PARAMS
| Param | Type | Description |
|---|---|---|
| task_id required | string | UUID returned from /extract |
RESPONSE STATUSES
{
"status": "processing",
"created_at": "2026-08-31T12:00:00",
"height": 175.0,
"gender": "male"
}
{
"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
}
{
"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).
// 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
// 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);
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.
Same as /extract but for merchant-embedded widgets. Supports Paystack single-scan billing or merchant credit deduction.
REQUEST BODY
| Field | Type | Description |
|---|---|---|
| front required | File | Front-facing photo. |
| side required | File | Side-facing photo. |
| height required | float | Height in cm (100–230). |
| merchant_id required | string | Your merchant user ID (from auth). |
| gender optional | string | "male" or "female". Default: "male". |
| client_name optional | string | Default: "Widget Customer". |
| payment_reference optional | string | Paystack reference for pay-per-scan billing. |
| client_user_id optional | string | Client'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
// 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", )
Height Estimate
Get estimated measurements from height only. No photos required. Free to call — does not deduct credits.
Uses ANSUR II anthropometric ratios. Returns 13 body measurements plus body shape and size recommendation. No credits deducted.
REQUEST BODY
| Field | Type | Description |
|---|---|---|
| height required | float | Height in cm (100–230). |
| gender optional | string | "male" or "female". Default: "male". |
SUCCESS RESPONSE — 200
{
"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
// 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"])
Key Provisioning
Generate API keys for your users. Use this when building a platform that provisions Korra access for sub-accounts.
Creates a korra_live_ API key for an authenticated user. Requires a Supabase JWT via Bearer token.
HEADERS
| Header | Type | Description |
|---|---|---|
| Authorization required | string | Bearer token: "Bearer {supabase_jwt}" |
QUERY PARAMS
| Param | Type | Description |
|---|---|---|
| regenerate optional | bool | If true, revoke existing keys and create new one. Default: false. |
SUCCESS RESPONSE — 200
{
"key": "korra_live_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"tier": "tailor_pro",
"created": true
}
SDK METHOD
// JavaScript const { key, tier, created } = await korra.provisionKey({ jwt: 'eyJhbGciOiJIUzI1NiIs...', regenerate: false, }); # Python result = client.measurements.provision_key( jwt="eyJhbGciOiJIUzI1NiIs...", regenerate=False, )
List & Retrieve
Query past extractions. All endpoints require API key authentication and follow standard credit rules.
List all measurements. Returns paginated results.
// JavaScript const list = await korra.measurements.list({ limit: 20, offset: 0 }); # Python list = client.measurements.list(limit=20, offset=0)
Get a specific measurement by ID.
Download a PDF report of the measurement.
Poll task status. No auth required. See Poll Task Status above.
SDK Reference
Install the SDK for your platform. Handles auth, retries, file uploads, and async polling automatically.
JavaScript / TypeScript
npm install @korra/sdk
Python
pip install aiscan
ALL EXTRACTION METHODS
| Method | Credits | Description |
|---|---|---|
| extract() | 1 credit | Upload front + side photos. Returns task_id. |
| extractWidget() | 1 credit | Widget flow. Deducts from merchant or Paystack. |
| status(taskId) | free | Poll task status. No auth needed. |
| waitForCompletion(taskId) | free | Auto-poll with backoff until done. |
| estimate({height}) | free | Height-only estimate. No photos needed. |
| provisionKey({jwt}) | free | Generate API key for a user. |
| list() | free | List past measurements. |
| get(id) | free | Get specific measurement. |
| delete(id) | free | Delete a measurement. |
| pdf(id) | free | Download PDF report. |
Error Codes
The API uses standard HTTP status codes. Error responses include a detail field with a human-readable message.
| Code | Meaning | Action |
|---|---|---|
| 400 | Bad Request | Check height range (100–230) and image files. |
| 401 | Unauthorized | Check your API key. Get one at /signup. |
| 402 | Payment Required | Add credits in your dashboard. |
| 404 | Not Found | Resource does not exist. |
| 429 | Rate Limited | Wait and retry. Check Retry-After header. |
| 500 | Server Error | Retry with backoff. Contact support if persistent. |
ERROR RESPONSE FORMAT
{
"detail": "Insufficient credits for extraction."
}
SDK ERROR HANDLING
// 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
Test Your Integration
Test endpoints, inspect full schemas, and verify your API keys in the browser.
Launch Swagger UI