# API КБЖУ продуктов perek.us > Справочник калорий, белков, жиров и углеводов для приложений, ботов и сайтов: 70 732 продукта, из них 69 187 товаров 6 038 брендов России и Казахстана, 1 162 позиции из меню сетей, 52 780 штрихкодов. Поиск понимает русский язык с опечатками, транслитом и неправильной раскладкой. Ответы в JSON. Справка написана для ИИ-агентов (Cursor, Claude Code и других), которые встраивают API в проект. Человеку удобнее страница https://perek.us/developers. ## Главное - Адрес API: `https://perek.us/api/v1/foods` - Ключ: заголовок `X-Api-Key: <ключ>` или `Authorization: Bearer <ключ>`. Ключ бесплатный, его выпускают в кабинете https://app.perek.us/developers после входа по почте, через Telegram или MAX. Без ключа API отвечает 401. - Ключ хранится в переменной окружения `PEREK_API_KEY` и уходит в API только с сервера проекта: из кода страницы или мобильного приложения его достанут за минуту. CORS закрыт, запрос из браузера не пройдёт. В браузерном проекте (Vite, Next.js, Lovable) нужен свой серверный маршрут или функция, которая ходит в API. - Числа `per100` даны на 100 г, а если `unit` равно `"мл"`, на 100 мл. КБЖУ порции: `per100 × граммы / 100`. - `id` продукта не меняется. Храни его у себя и открывай карточку по нему, переименованный продукт найдётся и по старому `id`. - На бесплатном тарифе рядом с цифрами из API показывай ссылку «Данные: perek.us» на поле `url` продукта. Бесплатный тариф только для некоммерческих проектов: если приложение берёт оплату, показывает рекламу или работает на бизнес, нужен Premium. - Сутки лимитов считаются по UTC. ## Быстрый старт ```bash curl -H "X-Api-Key: $PEREK_API_KEY" "https://perek.us/api/v1/foods/search?q=гречка&limit=5" curl -H "X-Api-Key: $PEREK_API_KEY" "https://perek.us/api/v1/foods/burger-king/vopper" curl -H "X-Api-Key: $PEREK_API_KEY" "https://perek.us/api/v1/foods/barcode/4606068171360" ``` ```python import os, requests r = requests.get( "https://perek.us/api/v1/foods/search", params={"q": "гречка", "limit": 5}, headers={"X-Api-Key": os.environ["PEREK_API_KEY"]}, timeout=10, ) r.raise_for_status() for food in r.json()["foods"]: print(food["name"], food["per100"]["kcal"], "ккал на 100", food["unit"]) ``` ```javascript // Node.js 18+, только на сервере const url = new URL("https://perek.us/api/v1/foods/search"); url.searchParams.set("q", "гречка"); const res = await fetch(url, { headers: { "X-Api-Key": process.env.PEREK_API_KEY } }); const { foods } = await res.json(); ``` ## Эндпоинты ### GET /search?q= Поиск по названию, бренду, сети или штрихкоду. Понимает названия блюд целиком, как их пишут люди: «Плов с курицей и овощами», «Авокадо (половина)». - `q`: от 2 символов. Длиннее 80 знаков обрезается по слову, длиннее 300 даёт 400. Штрихкод в `q` (8–14 цифр, можно внутри текста) ищется как штрихкод. - `limit`: 1–20, по умолчанию 10. Больше 20 урезается до 20. - `offset`: 0–80, листает выдачу. - `market`: `ru` или `kz`. - `type`: `food` (обычные продукты), `branded` (товары брендов), `chain` (блюда сетей), `dish` (напитки и блюда без бренда: морс, компот). Можно несколько через запятую. - `brand`: `brand.id` из ответа, например `?q=бургер&brand=burger-king`. - `full=true`: карточки целиком вместо коротких, второй запрос за составом не нужен. На бесплатном тарифе карточка урезана, поэтому `full=true` там считается обычным поиском, а ответ получает `"card": "basic"` и `notice`. Ответ: `{"query", "similar", "offset", "more", "foods": [...]}`. `similar: true` значит, что точного совпадения нет и пришло похожее. `more: true` значит, что дальше есть ещё, следующие отдаст `offset`. Если в `q` был штрихкод: `"barcode": true`, в `foods` одна карточка целиком или пусто, а если товар по коду известен без чисел, его название придёт в `product`. Короткая позиция в `foods`: ```json { "id": "burger-king/vopper", "name": "Воппер", "type": "chain", "brand": {"id": "burger-king", "name": "Бургер Кинг", "kind": "chain"}, "category": null, "section": null, "market": "ru", "unit": "г", "per100": {"kcal": 262.77, "protein": 9.85, "fat": 16.06, "carbs": 19.34}, "servings": [{"label": "Порция", "grams": 274.0}], "url": "https://perek.us/food/burger-king/vopper", "license": null } ``` ### GET /suggest?q= Подсказки при наборе по первым буквам: на «моро» мороженое, а не смородина. Отдаёт только `id`, названия и бренды без чисел, поэтому лимит разных продуктов не тратит. Параметры `q`, `limit` (1–20, по умолчанию 8), `market`, `type`, `brand`. Пробел в конце `q` значит, что слово дописано. Ответ: `{"query", "foods": [{"id", "name", "type", "brand"}], "brands": [{"id", "name", "kind", "count"}]}`. Цифры выбранного продукта бери карточкой по `id`. ### GET /{id} Карточка продукта целиком: `{"food": {...}}`. Нет такого `id`: 404 `not_found`. ### GET ?ids=a,b,c До 20 карточек одним запросом, например весь дневник за день: `https://perek.us/api/v1/foods?ids=pelmeni,burger-king/vopper`. Ответ: `{"foods": [...], "missing": [id, которых нет]}`. ### GET /barcode/{code} КБЖУ по штрихкоду EAN-13, EAN-8, UPC-A или GTIN-14: `{"food": {...}}`. Если чисел по коду нет, 404 `not_found`, а если товар известен, в ошибке будет `product` с названием и брендом: найди его поиском по `product.name`. Если не сходится контрольная цифра и кода нигде нет, 400 `bad_barcode`. У карточки из таблицы кодов (Open Food Facts, магазины) `id` равен `null`: страницы у неё нет. ### POST /parse Разбор еды словами и по фото тарелки. JSON `{"text": "200 г гречки и 2 яйца"}`, фото в `image` (base64 JPEG, PNG или WebP, до 8 МБ, можно `data:`-адрес) или оба. `text` до 1000 символов. Ответ: `{"items": [...], "total": {...}, "question": null, "parses_left": N}`. Позиция: `name`, `brand`, `amount` и `unit` (г или мл), `grams`, `weight` (откуда вес: `stated`, `pack`, `serving`, `unit`, `estimate`), `per100`, `nutrients` (на эту порцию), `barcode`, `confidence` от 0 до 1. Если без уточнения не посчитать, `items` пуст, а в `question` вопрос человеку. Если еды нет, `"no_food": true`. Ответ ждёт до 25 секунд. Не успел: 202 с `id`, `"status": "processing"` и `poll_url`, а готовый результат отдаст `GET /parse/{id}` (хранится час). Бесплатно 50 разборов в месяц, Premium 50 в сутки, Diamond 200 в сутки. Лимит разных продуктов разбор не тратит. ### POST /label КБЖУ с фото таблицы на упаковке: `{"image": "", "text": "название, если есть"}`. Ответ: `{"food": {"name", "brand", "barcode", "unit", "per100", "net_weight"}, "verified": true, "labels_left": N}`. В `per100` калории и БЖУ, а если напечатаны, ещё `fiber`, `sugar`, `saturated_fat`, `salt`. Числа приходят, только если таблицу подтвердило второе, независимое чтение снимка. Не читается: 422 `label_unreadable`, и фото не списывается. Бесплатно 3 фото в месяц, Premium 150 фото в месяц, Diamond 1 000. Ожидание и `GET /label/{id}` как у разбора. ## Карточка продукта целиком Сверх полей короткой позиции: - `aliases`: другие названия - `barcodes`: штрихкоды - `micronutrients`: витамины и минералы, если известны (Premium) - `product`: `maker`, `country`, `ingredients` (состав с упаковки, Premium), `allergens` (Premium), `shelf_life` - `per100` у части продуктов содержит ещё `fiber`, `sugar`, `saturated_fat`, `salt` (Premium) - `sources`: откуда цифры, `[{"title", "url"}]` - `checked`: когда цифры сверили с источником, `verified`: когда мы проверили их сами - `license`: лицензия открытой базы, если цифры оттуда (`ODbL` у Open Food Facts, `CC BY 4.0`, `NLOD 2.0`); такие позиции можно распространять на её условиях - `premium_only` (бесплатный тариф): что из скрытого у продукта есть ## Ошибки Тело любой ошибки: `{"error": {"code": "...", "message": "..."}}`. `message` по-русски, его можно показать человеку. - 400: `bad_query`, `bad_limit`, `bad_offset`, `bad_market`, `bad_type`, `bad_brand`, `bad_ids`, `bad_barcode`, `bad_body`, `bad_text`, `bad_image`, `empty` - 401 `unauthorized`: нет ключа или он отозван - 403 `label_unavailable`: чтение этикеток не входит в тариф - 404 `not_found`: продукта нет (у штрихкода может прийти `product`) - 413 `image_too_large` - 422 `label_unreadable`, `failed`: фото или текст не разобрать, повтор того же не поможет - 429 `rate_limited`, `parse_limit`, `label_limit`: лимит. Жди столько секунд, сколько в `Retry-After`. В теле `plan`, `upgrade_url`, `upgrade_plan`, `upgrade_price_rub`, если на тарифе выше лимит больше - `mcp_diamond_only` (только в MCP): инструменты с данными работают на тарифе Diamond - 503 `disabled`, `parse_unavailable`, `parse_busy`, `label_busy`: временно, повтори позже ## Заголовки ответа - `X-Api-Plan`: тариф аккаунта - `X-RateLimit-Remaining`, `X-RateLimit-Remaining-Day`: сколько запросов осталось на минуту и на сутки - `X-Search-Foods-Remaining-Day`: разных продуктов в выдаче поиска на сутки - `X-Foods-Remaining-Day`: карточек разных продуктов на сутки - `Retry-After`: у ответа 429 ## Лимиты и тарифы Лимиты общие на аккаунт, сколько бы ключей в нём ни было. Продукт, который уже приходил аккаунту сегодня, лимит разных продуктов не тратит, поэтому кэшировать карточки на своей стороне выгодно. Выдача поиска и карточки считаются отдельно: карточка, штрихкод и `full=true` тратят лимит карточек. С одного IP не больше 2 000 запросов в сутки (Premium 20 000). Похожее на выкачивание справочника урезает лимиты на сутки. **Бесплатный, 0 ₽** - 500 запросов в сутки и 20 в минуту - 150 разных продуктов в выдаче поиска за сутки - 50 карточек разных продуктов и штрихкодов за сутки - 50 разборов еды в месяц - 3 фото этикеток в месяц - 3 ключа на аккаунт - Калории, БЖУ, порции и штрихкоды - Для некоммерческих проектов со ссылкой на нас **Premium, 299 ₽ в месяц** - 10 000 запросов в сутки и 180 в минуту - 5 000 разных продуктов в выдаче поиска за сутки - 1 500 карточек разных продуктов и штрихкодов за сутки - 50 разборов еды в сутки - 150 фото этикеток в месяц - 10 ключей на аккаунт - Состав, аллергены, клетчатка, сахар, соль, витамины и минералы - Для коммерческих проектов, ссылка на нас по желанию **Diamond, 990 ₽ в месяц** - 80 000 запросов в сутки и 1 440 в минуту - 40 000 разных продуктов в выдаче поиска за сутки - 12 000 карточек разных продуктов и штрихкодов за сутки - 200 разборов еды в сутки - 1 000 фото этикеток в месяц - 10 ключей на аккаунт - Всё, что даёт Premium - MCP-сервер для Cursor и Claude - Тариф только для разработчиков Premium и Diamond оформляются в кабинете https://app.perek.us/developers. Кому мало и Diamond, пишут на почту, лимит подберут. ## Условия - Ссылка «Данные: perek.us» на `url` продукта рядом с цифрами обязательна на бесплатном тарифе, с Premium по желанию. - Бесплатный тариф для некоммерческих проектов. Аккаунты с первым ключом до 21 октября 2026 года остаются на прежних условиях. - Ключ только на сервере. - Справочник нельзя выкачивать целиком, раздавать или продавать как свою базу: такой ключ отзывают. - Цифры средние. Источник у каждой позиции в `sources`. ## MCP-сервер `https://perek.us/mcp` по Streamable HTTP для аккаунтов на тарифе Diamond. Ключ в заголовке `X-Api-Key` (или `Authorization: Bearer`), запросы тратят те же лимиты, что и API. На других тарифах и без ключа открыты список инструментов и `api_docs`, а вызов с данными отвечает ошибкой `mcp_diamond_only` с ценой Diamond и ссылкой на оформление: там те же данные отдаёт REST API. Инструменты: `search_foods`, `suggest_foods`, `get_foods`, `food_by_barcode`, `parse_meal`, `read_label`, `get_job` и `api_docs` (эта справка, без ключа). Cursor, файл `.cursor/mcp.json`: ```json { "mcpServers": { "perek": { "url": "https://perek.us/mcp", "headers": { "X-Api-Key": "${env:PEREK_API_KEY}" } } } } ``` Claude Code: ```bash claude mcp add --transport http perek https://perek.us/mcp --header "X-Api-Key: $PEREK_API_KEY" ``` ## Как обычно встраивают - Счётчик калорий: строка поиска на `/suggest`, по выбору карточка `/{id}`, вес порции из `servings` или граммами от человека, КБЖУ = `per100 × граммы / 100`. В дневнике хранится `id` и граммы, дневник за день открывается одним `?ids=`. - Сканер штрихкодов: `/barcode/{code}`. На 404 с `product` предложи поиск по `product.name`, на 404 без него ручной ввод. - Еда словами или голосом: распознанный текст в `POST /parse`, показать `items` человеку для правки, сохранить `total`. Если пришёл `question`, задать его человеку и отправить ответ вместе с исходным текстом. - Кэш: карточки по `id` меняются редко, их можно держать у себя сутки и дольше. - Ошибки: 429 с `Retry-After` повторять позже, а не сразу; 404 не повторять.