Body measurement features tend to follow the same pattern in most apps: you need them for sizing, onboarding, or personalization, but you don’t want to build a model, store biometric data, or deal with photo upload pipelines. A REST API that accepts height and weight and returns structured dimensional data solves all three.
This guide walks through a complete Next.js integration — server-side API route for key security, React component for the UI, and response parsing for the data you actually need.
Why server-side matters here
Never call a body measurement API directly from the browser. Your RapidAPI key would be exposed in network requests. In Next.js, route handlers (or pages/api/ routes) run on the server — your key stays private, and you control what data reaches the client.
Step 1: The API route
Create app/api/body-dimensions/route.ts:
import { NextRequest, NextResponse } from 'next/server';
const RAPIDAPI_KEY = process.env.RAPIDAPI_KEY!;
const RAPIDAPI_HOST = 'dimensionspot-bodysize-engine.p.rapidapi.com';
export async function POST(req: NextRequest) {
const { gender, height_cm, weight_kg, region } = await req.json();
// Validate inputs before sending to the API
if (!gender || !height_cm || !weight_kg) {
return NextResponse.json({ error: 'Missing required fields' }, { status: 400 });
}
const payload = {
input_data: {
input_unit_system: 'metric',
subject: {
gender: gender.toLowerCase(),
input_origin_region: region || 'GLOBAL',
},
anchors: {
body_height: height_cm * 10, // API expects mm, not cm
body_mass: weight_kg,
},
},
output_settings: {
calculation: {
calculation_model: 'AUTO',
target_region: region || 'GLOBAL',
body_build_type: 'CIVILIAN',
},
requested_dimensions: { bundle: 'TORSO' },
output_format: {
unit_system: 'metric',
include_range_95: true,
include_iso_codes: false,
},
},
};
const response = await fetch(
`https://${RAPIDAPI_HOST}/v1/predict`,
{
method: 'POST',
headers: {
'X-RapidAPI-Key': RAPIDAPI_KEY,
'X-RapidAPI-Host': RAPIDAPI_HOST,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
}
);
if (!response.ok) {
const error = await response.json();
return NextResponse.json({ error }, { status: response.status });
}
const data = await response.json();
return NextResponse.json(data);
}
Unit note: The API expects
body_heightin millimeters. A user entering 175cm needsheight_cm * 10 = 1750. This is the most common integration mistake — the validator will return a 422 if you send centimeters.
Step 2: The React component
'use client';
import { useState } from 'react';
interface Dimension {
value: number;
unit: string;
confidence_score: number;
range_95: [number, number] | null;
type: string;
}
interface BodyData {
body_dimensions: Record<string, Dimension>;
}
export function BodyProfileForm() {
const [gender, setGender] = useState('female');
const [height, setHeight] = useState('');
const [weight, setWeight] = useState('');
const [result, setResult] = useState<BodyData | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setLoading(true);
setError('');
const res = await fetch('/api/body-dimensions', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
gender,
height_cm: Number(height),
weight_kg: Number(weight),
region: 'GLOBAL',
}),
});
const data = await res.json();
if (!res.ok) {
setError(data.error?.detail || 'Prediction failed');
} else {
setResult(data);
}
setLoading(false);
}
return (
<div>
<form onSubmit={handleSubmit}>
<select value={gender} onChange={e => setGender(e.target.value)}>
<option value="female">Female</option>
<option value="male">Male</option>
</select>
<input
type="number"
placeholder="Height (cm)"
value={height}
onChange={e => setHeight(e.target.value)}
required
/>
<input
type="number"
placeholder="Weight (kg)"
value={weight}
onChange={e => setWeight(e.target.value)}
required
/>
<button type="submit" disabled={loading}>
{loading ? 'Predicting…' : 'Get dimensions'}
</button>
</form>
{error && <p style={{ color: 'red' }}>{error}</p>}
{result && <DimensionTable data={result.body_dimensions} />}
</div>
);
}
function DimensionTable({ data }: { data: Record<string, Dimension> }) {
const KEY_DIMS = ['chest_circumference', 'waist_circumference_natural', 'hip_circumference'];
return (
<table>
<thead>
<tr>
<th>Dimension</th>
<th>Value</th>
<th>Type</th>
<th>Confidence</th>
<th>95% interval</th>
</tr>
</thead>
<tbody>
{KEY_DIMS.filter(k => data[k]).map(key => {
const d = data[key];
const cm = (d.value / 10).toFixed(1);
const range = d.range_95
? `${(d.range_95[0] / 10).toFixed(1)} – ${(d.range_95[1] / 10).toFixed(1)} cm`
: '—';
return (
<tr key={key}>
<td>{key.replace(/_/g, ' ')}</td>
<td>{cm} cm</td>
<td>{d.type}</td>
<td>{d.confidence_score}</td>
<td>{range}</td>
</tr>
);
})}
</tbody>
</table>
);
}
Step 3: Understanding the response shape
A successful response looks like this:
{
"header": {
"gender": "female",
"anchor_tier": "PRIMARY_BOTH",
"target_region": "GLOBAL"
},
"body_dimensions": {
"chest_circumference": {
"value": 893.2,
"unit": "mm",
"type": "FLESH",
"confidence_score": 78,
"range_95": [841.5, 945.0]
},
"waist_circumference_natural": {
"value": 714.8,
"unit": "mm",
"type": "FLESH",
"confidence_score": 77,
"range_95": [671.0, 758.6]
}
},
"system_info": {
"api_version": "1.4.0",
"model_version": "adult_ridge_v4.0",
"computation_time_ms": 4
}
}
Values are in millimeters. Divide by 10 for centimeters. confidence_score runs 0–100 — for height + weight input, FLESH dimensions (circumferences) cluster around 76–80, BONE dimensions (skeletal lengths) around 83–87.
Step 4: Adding accuracy with one extra input
If users know their waist size (many do, from clothing), add it as an anchor:
anchors: {
body_height: height_cm * 10,
body_mass: weight_kg,
waist_circumference_omphalion: waist_cm * 10, // optional
}
This upgrades the request to PRIMARY_RICH tier. FLESH confidence rises by roughly 2–3 points. For a sizing feature, that’s a meaningful improvement at zero UX cost — users entering their waist from existing clothes is a natural onboarding question.
Environment setup
Add to .env.local:
RAPIDAPI_KEY=your_key_here
The free tier on RapidAPI provides 100 requests/month — enough to develop and test the full integration without a credit card.
What to do with the data downstream
The API is stateless — nothing is stored on the provider side. What you store is your decision. For a sizing feature, you typically want to persist only the derived output (e.g., “recommended size: M”), not the raw numerical measurements. This keeps your own data store free of biometric data, which simplifies your GDPR obligations considerably.
For a fitness app building a body profile over time, you’d store the dimensions with a timestamp and user ID — but even then, you own the storage decision entirely.
This integration pattern works identically in standard Next.js API routes (pages/api/) if you’re not using the App Router. The core payload and response structure are the same regardless of framework.