LITTLEFOX API · V1
От каталога
до выдачи товара
Подключите LittleFox к своему сайту, боту или приложению. Получайте каталог, покупайте товары с баланса и забирайте данные заказа через API
curl "https://littlefox-api.ru/api/v1/me?currency=RUB" \
-H "Authorization: Bearer $API_KEY"В примерах указаны условные данные. Актуальные цены и ID товаров смотрите в каталоге
https://littlefox-api.ru/api/v1Добавляйте к этому адресу путь метода: /me, /products или /orders
Первый запрос за три шага
- Создайте личный ключ
В боте откройте «Профиль → API ключ → Создать мой API ключ»
- Сохраните ключ на сервере
Сохраните ключ в переменной окружения API_KEY на своем сервере. В примерах ниже уже указан полный адрес запроса
- Проверьте баланс
Выполните запрос ниже или подключитесь на странице API тест. Просмотр баланса и каталога бесплатный
curl "https://littlefox-api.ru/api/v1/me?currency=RUB" \
-H "Authorization: Bearer $API_KEY"Базовый путь — /api/v1. Используйте HTTPS; ответы приходят в JSON. Успешный ответ содержит ok: true и data, ошибка — ok: false и error
API ключ и баланс
Передавайте API ключ в заголовке каждого запроса. С ним доступны каталог, баланс и заказы владельца ключа
Authorization: Bearer YOUR_API_KEYТакже поддерживается заголовок X-API-Key. Ключи в адресной строке не принимаются
/api/v1/meОтвет содержит Telegram ID, логин, баланс и лимит запросов. Баланс общий с ботом; пополнить его можно в профиле
| Поле / значение | Описание |
|---|---|
balance_kopeks | Сумма, доступная для покупок. Зарезервированные средства уже исключены из этого значения |
held_kopeks | Сумма в резерве по заказам, которые еще обрабатываются |
limits.requests_per_minute | Максимальное число запросов в минуту для вашего аккаунта |
Полный ответ: баланс · 200 OK
Условные данные. В примерах USD используется курс 80 ₽ за $1
{
"ok": true,
"data": {
"telegram_id": "123456789",
"username": "example_user",
"balance_kopeks": 100000,
"held_kopeks": 19900,
"limits": {
"requests_per_minute": 60
},
"api_access": "enabled",
"balance": "1000.00",
"held": "199.00",
"currency": "RUB",
"settlement_currency": "RUB",
"exchange_rate": null
}
}Не публикуйте ключ в коде, логах или репозитории. При отключении API ключ перестает работать до повторного включения. После перевыпуска старый ключ недействителен, а баланс и история сохраняются
Каталог, наличие и цены
/api/v1/products?page=1&limit=50&lang=ru¤cy=RUB| Поле / значение | Описание |
|---|---|
page / limit | Номер страницы и число товаров на ней. limit — от 1 до 100, по умолчанию 50. Ответ содержит items, page, limit, total, pages и has_more |
available=true | Только товары в наличии. Без фильтра возвращаются и временно отсутствующие товары |
lang=ru / en | Язык названия и описания. Не влияет на валюту |
{
"id": 42,
"name": "Example product",
"price_kopeks": 19900,
"price": "199.00",
"currency": "RUB",
"stock": 12,
"stock_unlimited": false,
"available": true,
"category": {
"id": 3,
"name": "Subscriptions"
},
"wholesale_tiers": [
{
"min_quantity": 5,
"unit_price_kopeks": 17900,
"unit_price": "179.00",
"currency": "RUB"
}
]
}Товары не в наличии
При stock=0 и available=false отключите покупку. Название, цена, описание и ID сохраняются. После пополнения тот же товар снова доступен
Оптовые цены
Из wholesale_tiers выберите цену с наибольшим min_quantity, которое не превышает количество в заказе. При создании заказа сервер снова проверит цену и наличие
/api/v1/products/{id}Обновляйте каталог периодически, соблюдая лимит запросов. Автоматических уведомлений об изменениях нет. Если stock_unlimited=true, наличие не ограничено, а stock равен null. Услуги с дополнительными параметрами доступны в боте
Валюты и расчет стоимости
Добавьте currency=RUB или currency=USD к запросам баланса, каталога, заказов и операций. По умолчанию — RUB. Язык и валюта выбираются независимо; отдельный долларовый счет не создается
| Поле / значение | Описание |
|---|---|
price / total / balance | Суммы в выбранной валюте в виде строк с двумя знаками после запятой. Это также относится к held, unit_price, charged, refunded и amount |
…_kopeks | Точные суммы в копейках: 100 = 1 ₽. Используйте их для расчета общей стоимости и лимита покупки |
exchange_rate | Курс магазина: rate — число рублей за 1 USD в виде строки, updated_at — время обновления. Для всех сумм одного ответа используется один курс. При currency=RUB поле равно null |
{
"currency": "USD",
"settlement_currency": "RUB",
"exchange_rate": {
"base": "USD",
"quote": "RUB",
"rate": "80",
"updated_at": "2026-09-20T12:00:00.000Z"
},
"items": [
{
"id": 42,
"price_kopeks": 19900,
"price": "2.49",
"currency": "USD"
}
]
}Рассчитывайте max_total_kopeks по цене в копейках с учетом количества и оптовой скидки. Округленные суммы в USD предназначены для отображения. Небольшая цена может отображаться как 0.00 USD, но покупка все равно оплачивается по точной цене в копейках
Цену определяет сервер. max_total_kopeks ограничивает расходы, но не задает стоимость. Поля price, currency и exchange_rate в теле заказа не принимаются. Валюта ответа не меняет списание
В истории суммы в USD пересчитываются по курсу на момент запроса. Если нужно сохранить этот расчет, сохраните ответ вместе с курсом. Когда курс недоступен, запрос в USD вернет 503 exchange_rate_unavailable до создания заказа. Его можно повторить в RUB с тем же ключом операции
Описания для сайта и бота
Параметр lang задает язык названия и описания. Описание доступно в двух форматах, готовых для использования на вашем сайте или в боте
| Поле / значение | Описание |
|---|---|
description | Обычный текст. Ссылки сохраняют подпись и адрес; анимированные эмодзи заменены обычными символами |
description_html | HTML с тегами strong, em, u, s, code, pre, blockquote, a и br. Скрипты, обработчики событий и стили удалены. Текст под спойлерами виден сразу |
{
"description": "Product description\nGuide (https://example.com/guide)",
"description_html": "<strong>Product description</strong><br><a href=\"https://example.com/guide\" rel=\"noopener noreferrer\">Guide</a>"
}Данные выдачи delivery_data передаются без преобразований. Сохраняйте их целиком, включая переносы строк
Покупка с вашего баланса
/api/v1/orders?currency=RUBВ одном заказе можно купить от 1 до 100 единиц одного товара. Стоимость списывается с баланса владельца API ключа. До отправки запроса сохраните его параметры и Idempotency-Key — уникальный ключ операции
curl -X POST "https://littlefox-api.ru/api/v1/orders?currency=RUB" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: store-order-1042" \
-d '{
"product_id": 42,
"quantity": 1,
"max_total_kopeks": 19900,
"client_order_id": "1042"
}'| Поле / значение | Описание |
|---|---|
Idempotency-Key | Обязательный ключ операции длиной 8–128 символов. Допустимы латинские буквы, цифры, двоеточие, подчеркивание и дефис. Для нового заказа создавайте новый ключ, при повторе запроса сохраняйте прежний |
product_id | ID товара из актуального каталога. 42 в примере — условное значение |
quantity | Количество единиц товара: целое число от 1 до 100, не больше доступного остатка |
max_total_kopeks | Максимальная сумма всего заказа в копейках: от 1 до 100000000. Поле обязательно. Если итоговая стоимость выше лимита, покупка отклоняется |
client_order_id | Необязательный номер или метка заказа в вашей системе, до 128 символов. Не заменяет Idempotency-Key |
Выдавайте данные покупателю только при status=delivered. Поле data.delivery_data содержит полный результат: текст, несколько строк или JSON в виде строки
Полный ответ: товар выдан · 201 Created
Условные данные. В примерах USD используется курс 80 ₽ за $1
{
"ok": true,
"data": {
"id": "LF-API-EXAMPLE1-A1B2C",
"client_order_id": "store-1042",
"status": "delivered",
"channel": "api",
"is_test": false,
"product": {
"id": 42,
"name": "Example product"
},
"quantity": 1,
"unit_price_kopeks": 19900,
"total_kopeks": 19900,
"held_kopeks": 0,
"charged_kopeks": 19900,
"refunded_kopeks": 0,
"failure_code": null,
"failure_message": null,
"created_at": "2026-09-21 09:00:00",
"updated_at": "2026-09-21 09:00:02",
"delivery_data": "example@example.com | FictionalPassword123",
"unit_price": "199.00",
"held": "0.00",
"total": "199.00",
"charged": "199.00",
"refunded": "0.00",
"currency": "RUB",
"settlement_currency": "RUB",
"exchange_rate": null
}
}Пример на серверном JavaScript · Node.js
Получите товар через GET /products/{id}, выберите оптовую ступень и умножьте unit_price_kopeks (или price_kopeks) на количество. Сохраните параметры и уникальный ключ в operation.json до запуска. Значения ниже условные; запрос создает реальную покупку
{
"idempotency_key": "store-order-1042",
"body": {
"product_id": 42,
"quantity": 1,
"max_total_kopeks": 19900,
"client_order_id": "1042"
}
}// Node.js 22+. Keep API_KEY on your server.
// Save operation.json before the first request; reuse it after a restart.
import { readFile } from "node:fs/promises";
const base = "https://littlefox-api.ru/api/v1";
const apiKey = process.env.API_KEY;
const operation = JSON.parse(await readFile("operation.json", "utf8"));
if (!apiKey || !operation.idempotency_key || !operation.body) {
throw new Error("API_KEY and a saved operation are required");
}
const body = JSON.stringify(operation.body);
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
let orderId;
let delivery;
for (let attempt = 0; attempt < 6; attempt++) {
let response, result;
try {
response = await fetch(base + (orderId
? "/orders/" + encodeURIComponent(orderId) : "/orders"), {
method: orderId ? "GET" : "POST",
headers: {
Authorization: "Bearer " + apiKey,
"Content-Type": "application/json",
"Idempotency-Key": operation.idempotency_key,
},
...(orderId ? {} : { body }),
signal: AbortSignal.timeout(30000),
});
result = await response.json();
} catch {
// The result is unknown. Never replace the saved operation.
await sleep(Math.min(5000 * 2 ** attempt, 60000));
continue;
}
const order = result.data;
if (order?.id) orderId = order.id;
if (order?.status === "delivered") {
delivery = order.delivery_data;
break;
}
if (["failed", "refunded"].includes(order?.status)) {
throw new Error(order.status + ": " + order.failure_code);
}
const pending = ["processing", "recovering"].includes(order?.status);
if (pending || response.status === 429 || response.status >= 500) {
const retry = Number(response.headers.get("Retry-After")) || 5;
await sleep(Math.max(5, retry, Math.min(2 ** attempt * 5, 60)) * 1000);
continue;
}
throw new Error(result.error?.code || "unexpected_response");
}
if (delivery === undefined) {
throw new Error("Still unresolved; resume with the SAME operation.json");
}
// Save the entire delivery securely; do not log it or split its format.
// The value in delivery is the completed result.
Одна сохраненная операция — одна покупка. Повторный запуск использует тот же файл. Для новой покупки создайте отдельный файл и новый ключ. Не перезаписывайте файл, пока предыдущая операция не завершена
Тестовая покупка
Тестовый товар доступен всем API-клиентам. Найдите is_test=true в каталоге и используйте полученный id. Наличие неограниченно: stock=null, stock_unlimited=true, available=true; api_only=true
Цена единицы — 100 копеек (1 ₽). В USD ответ показывает эквивалент по курсу магазина. В ответе будет уникальная вымышленная пара email | password на домене example.com. Эти данные не дают доступа к реальным аккаунтам. Заказ попадет в «Мои покупки», историю API и статистику
- Отправьте обычный POST /orders с ID тестового товара, quantity=1 и max_total_kopeks=100
- Дождитесь delivered и сохраните delivery_data. При повторе используйте прежний Idempotency-Key
На странице API тест можно подтвердить покупку, проверить результат, скопировать или скачать полученные данные. API ключ хранится только в памяти вкладки. Для повторной проверки сохраняются параметры операции, но не сам API ключ
Открыть API тестОдин заказ — один ключ операции
Если ответ не пришел, повторите исходный POST с тем же Idempotency-Key и параметрами. Новый ключ создает новую покупку; изменение параметров прежнего ключа вернет 409 idempotency_conflict. Выбор другой валюты ответа не создает новый заказ
/api/v1/orders/{id}Если ID уже известен, проверяйте этот заказ через GET не чаще одного раза в 5 секунд
| Поле / значение | Описание |
|---|---|
processing | Заказ принят и обрабатывается; средства могут быть в резерве |
recovering | Система автоматически уточняет результат. Дождитесь завершения, не создавайте новую покупку |
delivered | Покупка завершена. Сумма списана, delivery_data заполнено |
failed | Покупка отклонена до списания. Причина в failure_code и failure_message |
refunded | Покупка не выполнена. Зарезервированная сумма полностью возвращена на баланс |
Полный ответ: заказ обрабатывается · 202 Accepted
Условные данные. В примерах USD используется курс 80 ₽ за $1
{
"ok": true,
"data": {
"id": "LF-API-EXAMPLE1-A1B2C",
"client_order_id": "store-1042",
"status": "processing",
"channel": "api",
"is_test": false,
"product": {
"id": 42,
"name": "Example product"
},
"quantity": 1,
"unit_price_kopeks": 19900,
"total_kopeks": 19900,
"held_kopeks": 19900,
"charged_kopeks": 0,
"refunded_kopeks": 0,
"failure_code": null,
"failure_message": null,
"created_at": "2026-09-21 09:00:00",
"updated_at": "2026-09-21 09:00:00",
"delivery_data": null,
"unit_price": "199.00",
"held": "199.00",
"total": "199.00",
"charged": "0.00",
"refunded": "0.00",
"currency": "RUB",
"settlement_currency": "RUB",
"exchange_rate": null
}
}Полный ответ GET: средства возвращены · 200 OK
Условные данные. В примерах USD используется курс 80 ₽ за $1
{
"ok": true,
"data": {
"id": "LF-API-EXAMPLE1-A1B2C",
"client_order_id": "store-1042",
"status": "refunded",
"channel": "api",
"is_test": false,
"product": {
"id": 42,
"name": "Example product"
},
"quantity": 1,
"unit_price_kopeks": 19900,
"total_kopeks": 19900,
"held_kopeks": 0,
"charged_kopeks": 0,
"refunded_kopeks": 19900,
"failure_code": "purchase_rejected",
"failure_message": "The purchase could not be completed. Any reserved funds have been returned.",
"created_at": "2026-09-21 09:00:00",
"updated_at": "2026-09-21 09:00:02",
"delivery_data": null,
"unit_price": "199.00",
"held": "0.00",
"total": "199.00",
"charged": "0.00",
"refunded": "199.00",
"currency": "RUB",
"settlement_currency": "RUB",
"exchange_rate": null
}
}GET заказа возвращает 200 и его текущий status, в том числе failed или refunded. Повтор POST для заказа, который завершился отказом или возвратом, вернет ошибку с данными того же заказа: 402 при недостатке средств, 404 при отсутствии товара, в остальных случаях 409
| Поле / значение | Описание |
|---|---|
Одновременные заказы | Для одного владельца допускается один незавершенный заказ на один товар. Повтор прежнего ключа возвращает его; новый ключ до завершения получает order_in_progress. Другие товары имеют отдельные операции |
Idempotency-Key | Ключ операции закреплен за аккаунтом и сохраняется после перевыпуска API ключа. Он действует, пока хранится запись операции, в том числе со статусом failed или refunded. После отказа или возврата для новой покупки нужен новый ключ операции |
client_order_id | Метка для поиска заказа. Она не защищает от повторной покупки: разные ключи операции с одинаковым client_order_id могут создать разные заказы |
До подтверждения результата система не повторяет покупку и не возвращает резерв. Проверка продолжается после перезапуска. Уже принятые заказы обрабатываются даже при отключении ключа
История заказов и движение средств
/api/v1/orders?page=1&limit=20Список покупок владельца ключа через API. Для поиска по своей метке используйте client_order_id. Данные выдачи доступны в отдельном запросе заказа по его ID
/api/v1/transactions?page=1&limit=50Все операции по вашему балансу. amount_kopeks со знаком: плюс — зачисление, минус — списание или резерв. Тип purchase_hold меняется на purchase после выдачи; purchase_refund — возврат
В обоих списках limit — от 1 до 100. Поле has_more показывает, есть ли следующая страница. Для валюты используйте currency=RUB или currency=USD
Полный ответ: список заказов
Условные данные. В примерах USD используется курс 80 ₽ за $1
{
"ok": true,
"data": {
"items": [
{
"id": "LF-API-EXAMPLE1-A1B2C",
"client_order_id": "store-1042",
"status": "delivered",
"channel": "api",
"is_test": false,
"product": {
"id": 42,
"name": "Example product"
},
"quantity": 1,
"unit_price_kopeks": 19900,
"total_kopeks": 19900,
"held_kopeks": 0,
"charged_kopeks": 19900,
"refunded_kopeks": 0,
"failure_code": null,
"failure_message": null,
"created_at": "2026-09-21 09:00:00",
"updated_at": "2026-09-21 09:00:02",
"unit_price": "199.00",
"held": "0.00",
"total": "199.00",
"charged": "199.00",
"refunded": "0.00",
"currency": "RUB",
"settlement_currency": "RUB"
}
],
"page": 1,
"limit": 20,
"has_more": false,
"currency": "RUB",
"settlement_currency": "RUB",
"exchange_rate": null
}
}Полный ответ: операции баланса
Условные данные. В примерах USD используется курс 80 ₽ за $1
{
"ok": true,
"data": {
"items": [
{
"id": 1042,
"amount_kopeks": -19900,
"type": "purchase",
"order_id": "LF-API-EXAMPLE1-A1B2C",
"channel": "api",
"created_at": "2026-09-21 09:00:00",
"amount": "-199.00",
"currency": "RUB",
"settlement_currency": "RUB"
}
],
"page": 1,
"limit": 30,
"has_more": false,
"currency": "RUB",
"settlement_currency": "RUB",
"exchange_rate": null
}
}created_at и updated_at в заказах и операциях — UTC в формате YYYY-MM-DD HH:mm:ss. Не интерпретируйте их как локальное время. exchange_rate.updated_at содержит часовой пояс в ISO 8601. Для показа пользователю преобразуйте дату в его часовой пояс
Ошибки и лимиты запросов
{
"ok": false,
"error": {
"code": "insufficient_balance",
"message": "Your available balance is too low."
}
}Обрабатывайте error.code, а не текст message. Ответ с ошибкой покупки может содержать data с заказом. При сетевой ошибке или 5xx сохраняйте прежний ключ операции
| Поле / значение | Описание |
|---|---|
400 / 415 | Проверьте JSON, поля, Content-Type и Idempotency-Key |
401 / 403 | Ключ неверен, отключен, перевыпущен или доступ ограничен |
402 | Недостаточно средств. Пополните баланс в боте |
404 | Товар или ваш заказ не найден |
409 | Изменилась цена, недостаточно остатка, конфликт параметров или другой заказ обрабатывается. Уточните error.code |
429 | Достигнут лимит. Подождите Retry-After секунд; ваш лимит указан в /me |
503 | API отключен или курс недоступен. При exchange_rate_unavailable можно повторить в RUB с тем же ключом операции |
Коды ошибок и дальнейшие действия
| Поле / значение | Описание |
|---|---|
401 · invalid_api_key | Скопируйте действующий ключ из бота. Передайте один ключ в заголовке. После исправления повторите с прежним ключом операции |
403 · key_disabled / account_disabled | Включите ключ в боте или обратитесь в поддержку. Не создавайте замену незавершенному заказу |
429 · auth_rate_limit_exceeded | Запросы с вашего IP временно ограничены. Остановите ошибочные запросы, проверьте API ключ и подождите указанное в Retry-After число секунд. Ключ операции сохраняйте прежним |
429 · rate_limit_exceeded | Превышен лимит владельца ключа. Подождите Retry-After секунд и повторите исходный запрос |
400 · invalid_json / invalid_order / idempotency_required | Исправьте JSON, поля или заголовок Idempotency-Key. Такой запрос не создает заказ |
415 / 413 · json_required / body_too_large | Используйте application/json и тело до 8192 байт в UTF-8. Заказ еще не создан |
400 · invalid_currency | В строке запроса укажите ровно один currency=RUB или currency=USD; повторите с тем же ключом операции |
503 · exchange_rate_unavailable / api_disabled | При недоступном курсе повторите в RUB с тем же ключом операции. При отключенном API дождитесь восстановления доступа |
402 · insufficient_balance | Пополните баланс. Если в data вернулся заказ со статусом failed, для новой попытки создайте новый ключ операции |
404 · product_not_found / order_not_found | Проверьте ID и владельца ключа. Обновите каталог. Не заменяйте заказ с неизвестным результатом новой покупкой |
409 · idempotency_conflict | Повторите запрос с исходными параметрами и прежним ключом операции. Новый ключ нужен только для новой покупки |
409 · order_in_progress | На вашем аккаунте уже обрабатывается заказ на этот товар. Найдите его в истории и дождитесь статуса delivered, failed или refunded |
409 · price_changed / out_of_stock | Обновите товар и стоимость. Повторно оформить покупку с новым ключом можно после статуса failed или refunded у предыдущего заказа |
409 · order_not_reserved / stock_changed / access_changed | Проверьте data.status и условия покупки. Повтор с прежним ключом вернет тот же результат; новая покупка доступна после статуса failed или refunded |
409 · temporarily_unavailable / purchase_rejected / interrupted | Проверьте data: при failed или refunded можно позже создать новую попытку. При processing или recovering продолжайте проверять этот заказ |
500 / 5xx · internal_error / 5xx / network error | Результат может быть неизвестен. Сохраните параметры, повторите POST с прежним ключом или GET известного заказа. Постепенно увеличивайте интервал между повторами |
Ограничения
| Поле / значение | Описание |
|---|---|
POST /orders | До 8192 байт JSON в UTF-8, включая пробелы; при превышении — 413 body_too_large. Content-Type: application/json |
/me → limits | Лимит авторизованных запросов общий для владельца. Его текущее значение возвращает /me; при превышении — 429 rate_limit_exceeded |
429 auth_rate_limit_exceeded | До авторизации действует предел 1200 запросов за 60 секунд с одного IP (IPv6 — сеть /64). После 10 неверных или отсутствующих ключей доступ с этого адреса приостанавливается до конца 60-секундного окна. Общий IP означает общий предел; точная пауза указана в Retry-After |
202 / 429 / 5xx | После 202 проверяйте известный заказ не чаще раза в 5 секунд; после 429 соблюдайте Retry-After. При сетевых ошибках и ответах 5xx увеличивайте интервал между повторами, сохраняя параметры и ключ исходной операции |
Нужна помощь? Напишите в поддержку через бота. Приложите номер заказа и error.code; полный API ключ передавать не нужно