栄養計算 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 を使ってリクエストごとに支払うプロトコルです。
- 有料のエンドポイントを呼ぶと、402 と
PAYMENT-REQUIREDヘッダ(価格・ネットワーク・受取先)が返ります。 - クライアントが USDC の支払いに署名し、
PAYMENT-SIGNATUREヘッダを付けて同じリクエストを送り直します。 - 決済が済むと 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 を受け取ります。
"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 にレシピを加えます。鶏むね肉・ごはん・にんじん・ブロッコリーの例です。
{
"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
{
"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
{
"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 が付きます。
{
…,
"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- ・食品成分データを取得できない
{
"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 を見る
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 です。
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 -dAPI のコードを本番と同じ設定で動かして得た中身です(説明文と extensions は省略)。食材 4 件なので amount は 28000($0.028)です。
{
"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 を受けると、支払いと再リクエストを自動で行います。
npm install @x402/fetch @x402/evm viemimport { 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)を使います。
- ・食品の成分値は、文部科学省「日本食品標準成分表」の値です。
- ・わんぱくキッチンのレシピの栄養表示と同じエンジンです。