wearablesapihealthintegrationdeveloper-guide

Integrating Wearable Health Data with Body Measurement APIs

· 5 min read · Martin Hejda

Wearable devices collect continuous streams of health metrics: heart rate, step count, sleep stages, and — relevant to body dimension prediction — body weight. Some devices also measure height at setup, though it’s rarely updated. A few advanced platforms (Apple Watch with body composition features, smart scales) collect additional circumference approximations.

If your application already integrates a wearable API and needs to add body dimension prediction, the wearable data is a natural input source. Getting the integration right requires understanding what each platform actually provides, the normalization challenges across different data schemas, and how to handle data freshness.


What wearable APIs provide

Apple HealthKit (iOS)

Apple HealthKit exposes user health data via the iOS HealthKit framework and, for server-side access, via the Health Records API (for clinical data) or third-party integrations. Relevant body metrics:

  • HKQuantityTypeIdentifierHeight — user-entered height (rarely updated)
  • HKQuantityTypeIdentifierBodyMass — weight, typically from a connected smart scale or manual entry
  • HKQuantityTypeIdentifierBodyMassIndex — BMI, derived
  • HKQuantityTypeIdentifierWaistCircumference — available but rarely populated outside clinical apps
  • HKQuantityTypeIdentifierLeanBodyMass — from compatible body composition scales

Google Health Connect (Android)

Android’s Health Connect provides similar data types:

  • HeightRecord — height in meters
  • WeightRecord — weight in kilograms, with time series
  • BodyFatRecord — body fat percentage from compatible scales
  • WaistCircumferenceRecord — rarely populated

Garmin Connect API

REST API for Garmin device users who have authorized your application:

  • dailies endpoint includes weightInGrams (if connected to a Garmin Index scale)
  • bodyComps endpoint includes weight, BMI, and body fat if the user has a Garmin Index S2 scale

Oura Ring API

  • No direct body dimensions; provides biometric signals (HRV, sleep, activity)
  • Some users link Oura to a weight app; Oura doesn’t natively capture body dimensions

Normalization challenges

Each platform uses different units, data structures, and update frequencies. Before feeding wearable data into a body measurement prediction, normalize it.

from dataclasses import dataclass
from datetime import datetime
from typing import Optional

@dataclass
class NormalizedBodyMetrics:
    height_cm: Optional[float]
    weight_kg: Optional[float]
    waist_cm: Optional[float]
    body_fat_pct: Optional[float]
    recorded_at: datetime
    source: str  # "apple_health", "google_health_connect", "garmin", "manual"
    height_confidence: str  # "measured" | "user_entered" | "estimated"
    weight_confidence: str  # "scale" | "user_entered" | "estimated"

def normalize_apple_health(raw_data: dict) -> NormalizedBodyMetrics:
    """
    Normalize Apple HealthKit data.
    raw_data: dict of HKQuantityType samples, keyed by type identifier.
    """
    height_sample = raw_data.get("HKQuantityTypeIdentifierHeight")
    weight_sample = raw_data.get("HKQuantityTypeIdentifierBodyMass")
    waist_sample = raw_data.get("HKQuantityTypeIdentifierWaistCircumference")
    
    height_cm = None
    if height_sample:
        # HealthKit returns height in meters by default
        value = height_sample["value"]
        unit = height_sample.get("unit", "m")
        if unit == "m":
            height_cm = value * 100
        elif unit == "cm":
            height_cm = value
        elif unit == "ft":
            height_cm = value * 30.48
    
    weight_kg = None
    if weight_sample:
        value = weight_sample["value"]
        unit = weight_sample.get("unit", "kg")
        if unit == "kg":
            weight_kg = value
        elif unit == "lb":
            weight_kg = value * 0.453592
        elif unit == "g":
            weight_kg = value / 1000
    
    waist_cm = None
    if waist_sample:
        value = waist_sample["value"]
        unit = waist_sample.get("unit", "cm")
        if unit == "cm":
            waist_cm = value
        elif unit == "m":
            waist_cm = value * 100
        elif unit == "in":
            waist_cm = value * 2.54
    
    recorded_at_str = weight_sample.get("endDate") or height_sample.get("endDate", "")
    recorded_at = datetime.fromisoformat(recorded_at_str) if recorded_at_str else datetime.now()
    
    return NormalizedBodyMetrics(
        height_cm=round(height_cm, 1) if height_cm else None,
        weight_kg=round(weight_kg, 2) if weight_kg else None,
        waist_cm=round(waist_cm, 1) if waist_cm else None,
        body_fat_pct=None,
        recorded_at=recorded_at,
        source="apple_health",
        height_confidence="user_entered",  # HealthKit height is almost always manually entered
        weight_confidence="scale" if weight_sample else "user_entered"
    )

def normalize_garmin(dailies_data: dict, body_comps_data: dict) -> NormalizedBodyMetrics:
    """Normalize Garmin Connect API data."""
    weight_kg = None
    weight_grams = dailies_data.get("weightInGrams")
    if weight_grams:
        weight_kg = weight_grams / 1000
    
    body_fat_pct = body_comps_data.get("bodyFat")
    
    return NormalizedBodyMetrics(
        height_cm=None,  # Garmin doesn't provide height in most endpoints
        weight_kg=round(weight_kg, 2) if weight_kg else None,
        waist_cm=None,
        body_fat_pct=body_fat_pct,
        recorded_at=datetime.now(),
        source="garmin",
        height_confidence="estimated",
        weight_confidence="scale" if weight_grams else "user_entered"
    )

Data freshness and staleness

The biggest practical problem with wearable body metrics is that they’re often stale. Users enter their height once during app setup and never update it. Weight is more current if the user has a smart scale, but many users update weight manually and infrequently.

Track the age of each metric and communicate this to users:

from datetime import datetime, timedelta, timezone

def assess_data_freshness(metrics: NormalizedBodyMetrics) -> dict:
    """
    Returns a freshness assessment for body metrics.
    """
    now = datetime.now(timezone.utc)
    age_days = (now - metrics.recorded_at.replace(tzinfo=timezone.utc)).days
    
    freshness = {}
    
    if metrics.height_cm:
        # Height changes very slowly in adults; stale is acceptable
        if metrics.height_confidence == "user_entered":
            freshness["height"] = {
                "age_days": age_days,
                "status": "acceptable",
                "note": "Height rarely changes for adults; user-entered value is typically reliable"
            }
    
    if metrics.weight_kg:
        # Weight can change significantly week-to-week
        if age_days > 30:
            status = "stale"
            note = f"Weight was recorded {age_days} days ago. Ask user to confirm current weight."
        elif age_days > 7:
            status = "moderate"
            note = f"Weight recorded {age_days} days ago. Reasonably current."
        else:
            status = "fresh"
            note = "Weight recently recorded."
        
        freshness["weight"] = {"age_days": age_days, "status": status, "note": note}
    
    return freshness

When weight data is stale (>30 days), prompt the user to confirm or update before generating a size recommendation. A 5kg weight change can shift size predictions by one size.


Combining wearable data with body dimension prediction

Once you have normalized, fresh metrics, the integration with a body dimension prediction API is straightforward. The wearable data provides height and weight; the prediction API returns the full dimensional profile.

import requests

def get_dimensions_from_wearable(
    metrics: NormalizedBodyMetrics,
    gender: str,
    region: str = "GLOBAL"
) -> dict:
    """
    Combine wearable body metrics with a prediction API.
    Requires height and weight at minimum.
    """
    if not metrics.height_cm or not metrics.weight_kg:
        raise ValueError("Height and weight are required for body dimension prediction")
    
    anchors = {
        "body_height": int(metrics.height_cm * 10),  # cm → mm
        "body_mass": metrics.weight_kg
    }
    
    # If waist is available from wearable (rare but possible), include it
    if metrics.waist_cm:
        anchors["waist_circumference"] = int(metrics.waist_cm * 10)  # cm → mm
    
    response = requests.post(
        "https://dimensionspot-bodysize-engine.p.rapidapi.com/v1/predict",
        json={
            "input_data": {
                "input_unit_system": "metric",
                "subject": {
                    "gender": gender,
                    "input_origin_region": region
                },
                "anchors": anchors
            },
            "output_settings": {
                "calculation": {"target_region": region, "body_build_type": "CIVILIAN"},
                "requested_dimensions": {"bundle": "TORSO"},
                "output_format": {
                    "include_range_95": True,
                    "confidence_score_threshold": 60
                }
            }
        },
        headers={
            "X-RapidAPI-Key": "YOUR_API_KEY",
            "X-RapidAPI-Host": "dimensionspot-bodysize-engine.p.rapidapi.com"
        }
    )
    
    result = response.json()
    result["data_source"] = {
        "wearable": metrics.source,
        "height_confidence": metrics.height_confidence,
        "weight_confidence": metrics.weight_confidence,
        "data_age_days": (datetime.now(timezone.utc) - metrics.recorded_at.replace(tzinfo=timezone.utc)).days
    }
    
    return result

Privacy considerations for wearable data integration

Wearable APIs gate access behind user consent and OAuth scopes. Best practices:

Request minimal scopes. If you only need height and weight, request only HKQuantityTypeIdentifierHeight and HKQuantityTypeIdentifierBodyMass. Don’t request full health data access for a sizing feature.

Don’t persist wearable data. Use the wearable data to generate a prediction, present the result, and discard the raw metrics from the wearable. Store only what your application needs (the prediction inputs, not the full wearable health record).

Be explicit about the data flow. Users should understand that their wearable data is being used for sizing. This is typically not surprising if framed clearly in the UI, but it should be stated: “We’re using your height and weight from Apple Health to recommend your size.”

Handle revoked access gracefully. Users can revoke wearable permissions at any time. Build a fallback to manual height/weight input that’s always available.


The practical value of wearable integration for sizing is primarily convenience: users who already have their height and weight logged don’t need to re-enter it in your sizing form. The dimensional prediction is the same regardless of whether the inputs came from manual entry or a smart scale — the wearable integration just removes friction from data collection.

Try DimensionsPot

Free tier — 100 requests/month, no credit card required.

Get API on RapidAPI