kalori Catalog API v1
外食・コンビニ商品の栄養データ(カロリー・たんぱく質・脂質・炭水化物)を参照する API です。
ベース URL: https://catalog-api.kalori.jp/v1
利用条件は 利用規約、稼働の目安とサポート窓口は サービスレベルの目安 にあります。
クイックスタート
curl "https://catalog-api.kalori.jp/v1/search?q=%E3%81%8A%E3%81%AB%E3%81%8E%E3%82%8A&limit=5" \
-H "Authorization: Bearer kdk_xxxxxxxx" \
-H "X-Kalori-End-User: 5f1c0a9e2b7d4c3a"
- 鍵は コンソール で発行します(発行時に一度だけ表示)。
X-Kalori-End-Userは貴社サービスの末端ユーザーごとの値です(下記)。- 呼び出しは貴社のサーバからお願いします。 ブラウザやアプリから直接呼ぶ形(CORS)は許可していません — 鍵が利用者の手元に露出し、そのまま第三者に使われるためです。
認証
Authorization: Bearer kdk_…(コンソールで発行した API 鍵)X-Kalori-End-User: <opaque>— データ系ルート(/shops, /search, /products/:id)で必須。 貴社サービスの末端ユーザーごとに安定した識別子を、貴社側で salt 付きハッシュ化した値 (^[A-Za-z0-9_-]{8,64}$)。メールアドレスや生の ID は送らないでください。 kalori はこの値を逆引きせず、末端ユーザー単位の利用上限と月間アクティブ数(MAU)の計測にだけ使います。
末端ユーザー ID の作り方(例)
貴社だけが知る salt を鍵にした HMAC を、URL-safe な Base64 に変えて送ります。同じ利用者には常に同じ値になり、
値から利用者を逆算することはできません。
// Node.js
import { createHmac } from "node:crypto";
const endUser = createHmac("sha256", process.env.KALORI_END_USER_SALT)
.update(String(userId))
.digest("base64url"); // 43 文字・[A-Za-z0-9_-]
- 利用者ごとに別の値にしてください。複数の利用者を 1 つの ID にまとめると、その ID が 1 日の上限に先に当たります。 課金は呼び出し回数ではなく MAU なので、実際の人数をそのまま送るのが最も安くなります。
- ログインしていない利用者には、端末やセッション単位の安定した識別子をハッシュ化して使ってください。
ルート
| ルート | 用途 |
|---|---|
GET /v1/me | 契約状態・上限・本日の使用量・当月 MAU |
GET /v1/shops | 掲載チェーン一覧 |
GET /v1/search?q=&shop=&limit=&offset= | 商品検索(q または shop のどちらか必須) |
GET /v1/products/:id | 商品 1 件 |
/v1/search
q: 商品名の検索語(100 文字まで)。shop: チェーン ID(/v1/shops のid)。limit: 1〜50(既定 20)。offset: 0〜200。 これより深いページは取れません(全件取得のエンドポイントはありません)。- 応答:
{ items: Product[], total: number, next_offset: number|null }
Product
{
"id": 12345,
"shop": { "id": "seven", "name": "セブン-イレブン" },
"name": "商品名",
"nutrition": { "calories": 512, "protein": 18.5, "fat": 12, "carbs": 70.2 },
"source": "official",
"url": "https://kalori.jp/ja/shops/seven/products/12345/"
}
nutritionは 1 食あたり。不明な項目はnull。source:official(チェーン公表値)/operator(事業者が kalori へ直接登録した公式値)/estimated(kalori の推定値)/mixed(項目ごとに混在)/null。urlは kalori.jp 上の出典ページです。表示時は出典として kalori へのリンクを併記してください(利用規約)。estimated/mixedの値を出すときは、推定値である旨も併せて表示してください。- ⚠️
urlは必ずこの値を使ってください(idから URL を組み立てないでください)。 API で配信している商品には kalori.jp に個別ページが無いものがあり、その場合urlはチェーンのページ、それも無い場合はトップページを指します。 組み立てた URL は存在しないページに当たります。
上限と応答ヘッダ
データ系ルート(/shops, /search, /products/:id)の応答に X-Quota-Used / X-Quota-Limit /X-Quota-Remaining / X-Quota-Reset / X-Quota-Scope(developer_day / developer_hour / end_user_day)
が付きます。超過時は 429。認証で弾かれた応答(401 / 403)には付きません。
X-Quota-Resetは ISO 8601(UTC)の時刻です。429 を受けたらこの時刻まで同じ窓の呼び出しを止めてください (即時の再試行は上限を消費するだけで成功しません)。- 日次の窓は日本時間の 0 時、時間の窓は毎正時(UTC)にリセットされます。
- 上限の値はご契約ごとに異なります。現在の値は
GET /v1/meかコンソールで確認できます。
データの保存(原則禁止)
- データは表示のたびに API から取得してください。 応答を貴社側に保存・蓄積すること(データベースへの取り込み・ サーバ側の共有キャッシュ・CDN でのキャッシュを含む)は利用規約で禁止しています。
- 許されるのは、同じ末端ユーザーの同じ画面を描くために必要な一時的な保持(1 回の応答をその画面で使い回す程度)だけです。
- 末端ユーザーごとの 1 日の上限は、この前提(表示ごとに 1 回)で足りる値にしています。足りない場合は上限の引き上げを相談してください。
エラー
{ "error": { "code": "...", "message": "..." } } + HTTP ステータス。
| code | HTTP | 意味 |
|---|---|---|
unauthorized | 401 | Authorization ヘッダが無い |
invalid_format | 401 | Bearer kdk_… の形式ではない |
unknown_key | 401 | 鍵が存在しない |
key_revoked | 401 | 失効した鍵(コンソールで再発行) |
key_expired | 401 | 有効期限を過ぎた鍵 |
developer_suspended | 403 | 契約が停止中 |
end_user_required | 400 | X-Kalori-End-User が無い |
end_user_invalid | 400 | X-Kalori-End-User が形式に合わない |
bad_request | 400 | パラメータ不正(q / shop 欠落、limit / offset の範囲外など) |
unknown_shop | 400 | shop が /v1/shops に無い |
not_found | 404 | 商品が無い |
quota_exceeded | 429 | 上限超過(X-Quota-Scope にどの窓かが出る) |
internal | 500 | こちらの障害 |
5xx はこちらの障害です。数秒おいて指数バックオフで再試行してください(再試行の上限は 3 回程度を推奨)。
400 番台は再試行しても同じ結果になります(429 を除く)。
- パラメータ不正(400)や商品が無い(404)の呼び出しも上限を消費します。値の検証は呼ぶ前に済ませてください。
- 応答には
Cache-Control: no-storeが付きます(上の「データの保存(原則禁止)」に合わせています)。
版の運用
- パスの major(
/v1/)だけが版です。 - フィールドの追加は非破壊。削除・改名・意味変更・必須化は v2 として別パスで提供し、v1 には
Deprecation/Sunsetヘッダで期日を出します(廃止は最短でも 6 か月前に告知)。 - 変更履歴: CHANGELOG
Markdown 版
https://catalog-api.kalori.jp/v1/help で同じ内容を Markdown のまま取得できます(認証不要)。