開発者向け

栄養計算 API(x402)

わんぱくキッチンでレシピの栄養を評価しているエンジンを、そのまま API にしたものです。犬の情報から 1 日の必要エネルギーを出し、日本食品標準成分表(文部科学省)の食品番号とグラム数で書いたレシピを、FEDIAF 2025 の栄養基準に照らして評価します。アカウント登録も API キーも不要で、リクエストごとに少額の USDC を支払って使います。

できること

  • ・体重・月齢・体型などから、RER と MER(1 日の必要エネルギー)を幅つきで出す
  • ・レシピのカロリー、PFC バランス、1 食分の目標に対する割合を出す
  • ・約 40 の栄養素を 1000 kcal あたりの量にして、FEDIAF 2025 の最小値・最大値と比べる
  • ・栄養素ごとに、食品成分値のばらつきから不確かさの幅と判定の確度を付ける
  • ・骨格・筋肉・肌など 6 軸のレーダースコアを出す

x402 で支払う

x402 は、HTTP の 402 Payment Required を使ってリクエストごとに支払うプロトコルです。

  1. 有料のエンドポイントを呼ぶと、402 と PAYMENT-REQUIRED ヘッダ(価格・ネットワーク・受取先)が返ります。
  2. クライアントが USDC の支払いに署名し、PAYMENT-SIGNATURE ヘッダを付けて同じリクエストを送り直します。
  3. 決済が済むと 200 と結果が返り、PAYMENT-RESPONSE ヘッダに決済結果が入ります。
ベース URL
https://nutrition-calc.wanpak.uk
通貨
USDC
ネットワーク
Base(eip155:8453)
スキーム
exact
ファシリテーター
Coinbase CDP

エンドポイントと料金

  • POST/energy$0.005

    ワンちゃん 1 頭の 1 日の必要エネルギーを返します。

    • ・ボディは { dog } だけです。
    • ・RER と MER(low・point・high、kcal ME/日)、ライフステージ、計算に使った係数を返します。
  • POST/evaluate$0.02 + $0.002 × 食材数

    レシピのエネルギー、PFC バランス、栄養素ごとの充足を評価します。

    • ・食材は 1〜30 件。料金は $0.022〜$0.08 です。
    • ・栄養素ごとに、値と不確かさの幅、データの網羅率、FEDIAF の基準値、判定を返します。
  • POST/evaluate/radar/evaluate + $0.01

    /evaluate の結果に、6 軸のレーダースコア(0〜100)を加えて返します。

    • ・料金は $0.032〜$0.09 です。
  • GET/無料

    サービスの説明、バージョン、使っている基準と単位、ルートと料金を JSON(英語)で返します。

  • GET/spec無料

    判定に使う基準値、判定の幅、栄養素ごとの信頼度の設定を返します。

    • ・死活確認は GET /health(無料)。

料金はボディの食材数で決まります。例: 食材 4 件の /evaluate は $0.028、/evaluate/radar は $0.038。

リクエスト

どの有料エンドポイントも dog を受け取ります。

JSON
"dog": {
  "weightKg": 8,
  "ageMonths": 48,
  "bcs": 3,
  "size": "small",
  "gender": "male",
  "isNeutered": true,
  "bodyGoal": "maintain"
}
weightKg
体重(kg)。0 より大きく 100 以下。必須
ageMonths
月齢。0〜360。必須
size
small / medium / large / giant。必須
gender
male / female。必須
isNeutered
避妊・去勢済みなら true。必須
bcs
ボディコンディションスコア(5 段階)。省略時は 3
bodyGoal
maintain / lose / gain。省略時は maintain

/evaluate と /evaluate/radar は、dog にレシピを加えます。鶏むね肉・ごはん・にんじん・ブロッコリーの例です。

JSON
{
  "dog": { "weightKg": 8, "ageMonths": 48, "bcs": 3, "size": "small", "gender": "male", "isNeutered": true },
  "ingredients": [
    { "mextCode": "11220", "grams": 120 },
    { "mextCode": "01088", "grams": 80 },
    { "mextCode": "06212", "grams": 30 },
    { "mextCode": "06263", "grams": 20 }
  ],
  "mealPlan": { "mealsPerDay": 2, "recipeMeals": 1 },
  "category": "daily",
  "addedWaterGrams": 0
}
ingredients
{ mextCode, grams } を 1〜30 件。mextCode は成分表の 5 桁の食品番号、grams は与える状態の重さ(5,000 g まで)
mealPlan
mealsPerDay は 1 日の食事回数(1〜10、省略時 2)、recipeMeals はこのレシピで作る食数(1〜60、省略時 1)。おやつでは mealsPerDay を日数として扱います
category
daily(主食)/ treat(おやつ)/ event(行事食)。省略時は daily
addedWaterGrams
加える水(g)。0〜5,000。任意

レスポンス

計算エンジンのテスト(golden テスト)で、上のリクエストを実際に計算した結果です。nutrients は 39 件のうち 3 件だけ載せています。

POST /energy

JSON(例)
{
  "specVersion": "ssot-1.0",
  "engineVersion": "dog-recipe@4c383b9198cd",
  "dataHash": "30c28f313b51450473b0c463c5af67229727644310d33f76a92de0d7e2667a0a",
  "sourceCommitDate": "2026-10-30T19:45:05+09:00",
  "dog": {
    "weightKg": 8,
    "ageMonths": 48,
    "bcs": 3,
    "size": "small",
    "gender": "male",
    "isNeutered": true,
    "bodyGoal": "maintain"
  },
  "lifeStage": "adult",
  "profile": { "lifeStage": "adult", "neutered": true },
  "energy": {
    "unit": "kcal ME/day",
    "rer": 332.98,
    "mer": { "low": 372.94, "point": 466.17, "high": 559.4 },
    "calculationWeightKg": 8,
    "bcsWeightFactor": 1,
    "stateFactor": 1.4,
    "stateFactorLabelJa": "成犬・去勢済み",
    "maturityAgeMonths": 10
  }
}

POST /evaluate

JSON(例)
{
  "specVersion": "ssot-1.0",
  "engineVersion": "dog-recipe@4c383b9198cd",
  "dataHash": "30c28f313b51450473b0c463c5af67229727644310d33f76a92de0d7e2667a0a",
  "sourceCommitDate": "2026-10-30T19:45:05+09:00",
  "category": "daily",
  "dog": {
    "weightKg": 8,
    "ageMonths": 48,
    "bcs": 3,
    "size": "small",
    "gender": "male",
    "isNeutered": true,
    "bodyGoal": "maintain"
  },
  "lifeStage": "adult",
  "energy": {
    "unit": "kcal ME",
    "recipeKcal": 283,
    "targetKcal": 233,
    "percentOfTarget": 122,
    "merPerDay": { "low": 372.94, "point": 466.17, "high": 559.4 },
    "meDensityKcalPerG": 1.1331,
    "digestibility": 92.3568,
    "outsideValidatedDomain": false,
    "estimateDegraded": false
  },
  "pfc": {
    "proteinPct": 46.6,
    "fatPct": 8.3,
    "carbPct": 45.1,
    "proteinG": 31.25,
    "fatG": 2.7,
    "carbG": 33.76,
    "balance": "HIGH_PROTEIN"
  },
  "grade": "fair",
  "deficient": ["カルシウム", "リン", "ビタミンD", "ヨウ素", …],
  "excessive": [],
  "nutrients": [
    {
      "id": "カルシウム",
      "key": "calcium",
      "unit": "g/Mcal",
      "evaluationType": "range",
      "value": 0.0904,
      "range": { "low": 0.0813, "high": 0.0994 },
      "cv": 0.1,
      "coverage": 1,
      "precision": "high",
      "reference": {
        "min": 1.45,
        "max": 6.25,
        "bandMin": 1.595,
        "bandMax": 5.625,
        "column": "成犬 95 kcal列",
        "dailyCeiling": null
      },
      "percentOfMinimum": 6.2,
      "status": "deficient",
      "certainty": "definite",
      "confidence": "A",
      "tier": 1
    },
    {
      "id": "タンパク質",
      "key": "protein",
      "unit": "g/Mcal",
      "evaluationType": "sufficiency",
      "value": 110.312,
      "range": { "low": 99.2808, "high": 121.3432 },
      "cv": 0.1,
      "coverage": 1,
      "precision": "high",
      "reference": {
        "min": 52.1,
        "max": null,
        "bandMin": 57.31,
        "bandMax": null,
        "column": "成犬 95 kcal列",
        "dailyCeiling": null
      },
      "percentOfMinimum": 211.7,
      "status": "adequate",
      "certainty": "ok_stable",
      "confidence": "A",
      "tier": 3
    },
    {
      "id": "Ca:P比",
      "key": "ca_p_ratio",
      "unit": "",
      "evaluationType": "ratio",
      "value": 0.0798,
      "range": { "low": 0.0638, "high": 0.0957 },
      "cv": 0.2,
      "coverage": 1,
      "precision": "medium",
      "reference": {
        "min": 1,
        "max": 2,
        "bandMin": 1.2,
        "bandMax": 1.6,
        "column": "成犬 95 kcal列",
        "dailyCeiling": null
      },
      "percentOfMinimum": 8,
      "status": "deficient",
      "certainty": "definite",
      "confidence": "A",
      "tier": 2,
      "ratio": { "numerator": 0.0256, "denominator": 0.321 }
    },
    …
  ],
  "ingredientWarnings": {
    "contraindicated": [],
    "cautionary": [],
    "iodineEstimated": [],
    "seleniumEstimated": [
      { "mextCode": "06212", "nameJa": "(にんじん類) にんじん 根 皮つき 生" }
    ]
  }
}

targetKcal は 1 食分の目標(MER ÷ 食事回数 × 食数)で、percentOfTarget はそれに対するレシピのカロリーの割合です。grade(excellent / good / fair / poor)は daily のときだけ返します。

id / key
栄養素名(日本語)と英語キー
value / unit
1000 kcal ME あたりの量(g/Mcal など)。おやつは 1 日あたりの絶対量
range
食品成分値のばらつきから出した不確かさの幅(low〜high)
cv / precision
変動係数と、その精度区分(high / medium / low)
coverage
成分値がわかっている食材の割合(0〜1)
reference
FEDIAF の最小値・最大値(min / max)と、判定に使う幅(bandMin / bandMax)
status
deficient / adequate / excessive
certainty
definite(幅全体が基準外)/ possible(値は基準外、幅は基準にかかる)/ ok_stable / ok_marginal
confidence / tier
成分データの信頼度(A〜C)と、指摘の重要度(1 が最も高い)

POST /evaluate/radar

/evaluate と同じ内容に radar が付きます。

JSON(例)
{
  …,
  "radar": [
    { "axisId": "bone_teeth", "labelJa": "骨格・歯", "score": 0.4 },
    { "axisId": "muscle_build", "labelJa": "筋肉・体づくり", "score": 10 },
    { "axisId": "skin_coat", "labelJa": "肌・毛並み", "score": 2.4 },
    { "axisId": "energy_metabolism", "labelJa": "元気・代謝", "score": 5 },
    { "axisId": "immunity_antioxidants", "labelJa": "免疫・抗酸化", "score": 4.2 },
    { "axisId": "organ_load", "labelJa": "ミネラルバランス", "score": 2.7 }
  ]
}

支払い前に返すエラー

入力の検証と食品番号の確認は 402 より先に行います。次の場合は 402 を返さず、課金されません。

400
  • ・JSON として読めない、必須項目がない、値が範囲外、食材が 0 件または 31 件以上
  • ・成分表にない食品番号(unknownMextCodes に番号が入ります)
  • ・reproductiveState が pregnant / lactating(妊娠・授乳中のワンちゃんは対象外)
502
  • ・食品成分データを取得できない
JSON(400 の例)
{
  "error": "Bad Request",
  "message": "Unknown MEXT food codes",
  "unknownMextCodes": ["99999"]
}

バージョン

有料・無料を問わず、応答に計算の版が付きます。結果を保存するときは一緒に残すと、どの版で計算したかを追えます。

specVersion
判定基準(栄養プロファイル)の版(例: ssot-1.0)
engineVersion
計算エンジンのコピー元になった、わんぱくキッチン本体のコミット(例: dog-recipe@4c383b9198cd)
dataHash
基準値などのデータファイルの SHA-256
sourceCommitDate
そのコミットの日時

呼び出し例

上の「リクエスト」の JSON を recipe.json に保存して呼ぶ例です。

curl で 402 を見る

shell
curl -i -X POST https://nutrition-calc.wanpak.uk/evaluate \
  -H 'Content-Type: application/json' \
  -d @recipe.json

# HTTP/2 402
# content-type: application/json
# payment-required: eyJ4NDAyVmVyc2lvbiI6MiwiZXJyb3IiOiJQYXltZW50IHJlcXVpcmVkIiwi...

PAYMENT-REQUIRED の中身は Base64 の JSON です。

shell
curl -s -D - -o /dev/null -X POST https://nutrition-calc.wanpak.uk/evaluate \
  -H 'Content-Type: application/json' -d @recipe.json \
  | grep -i '^payment-required:' | cut -d' ' -f2 | tr -d '\r' \
  | base64 -d

API のコードを本番と同じ設定で動かして得た中身です(説明文と extensions は省略)。食材 4 件なので amount は 28000($0.028)です。

JSON
{
  "x402Version": 2,
  "error": "Payment required",
  "resource": {
    "url": "https://nutrition-calc.wanpak.uk/evaluate",
    "description": "Evaluate a dog food recipe. …",
    "mimeType": "application/json",
    "serviceName": "dog-recipe-nutrition-api",
    "tags": ["dog", "pet-food", "nutrition", "fediaf", "nrc", "mext", "japan"]
  },
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:8453",
      "amount": "28000",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "payTo": "0xCfB9c9608AF3E400954E15fFFbC5663436300Fd4",
      "maxTimeoutSeconds": 300,
      "extra": { "name": "USD Coin", "version": "2" }
    }
  ],
  "extensions": { "bazaar": { … } }
}

JavaScript(@x402/fetch)

402 を受けると、支払いと再リクエストを自動で行います。

shell
npm install @x402/fetch @x402/evm viem
TypeScript
import { wrapFetchWithPaymentFromConfig, decodePaymentResponseHeader } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";

const BASE = "https://nutrition-calc.wanpak.uk";

// Base 上の USDC を持つウォレット
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);

const fetchWithPayment = wrapFetchWithPaymentFromConfig(fetch, {
  schemes: [{ network: "eip155:*", client: new ExactEvmScheme(account) }],
});

const dog = { weightKg: 8, ageMonths: 48, bcs: 3, size: "small", gender: "male", isNeutered: true };

// 必要エネルギー: $0.005
const energy = await fetchWithPayment(`${BASE}/energy`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ dog }),
});
console.log(await energy.json());

// レシピ評価とレーダー(食材 2 件): $0.02 + 2 × $0.002 + $0.01 = $0.034
const res = await fetchWithPayment(`${BASE}/evaluate/radar`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    dog,
    ingredients: [
      { mextCode: "11220", grams: 120 },
      { mextCode: "01088", grams: 80 },
    ],
    mealPlan: { mealsPerDay: 2, recipeMeals: 1 },
  }),
});
const result = await res.json();
console.log(result.engineVersion, result.grade, result.radar);

const receipt = res.headers.get("PAYMENT-RESPONSE");
if (receipt) console.log(decodePaymentResponseHeader(receipt));

計算について

  • ・栄養素の最小値・最大値は FEDIAF Nutritional Guidelines 2025 を使います。
  • ・RER・MER と、FEDIAF に上限がない栄養素の安全上限は NRC(2006)を使います。
  • ・食品の成分値は、文部科学省「日本食品標準成分表」の値です。
  • ・わんぱくキッチンのレシピの栄養表示と同じエンジンです。

関連リンク