К содержимому

LITTLEFOX API · V1

От каталога
до выдачи товара

Подключите LittleFox к своему сайту, боту или приложению. Получайте каталог, покупайте товары с баланса и забирайте данные заказа через API

REST / JSONRUB & USDБезопасные повторы
ВАШ ПЕРВЫЙ ЗАПРОС200 OK
cURL
curl "https://littlefox-api.ru/api/v1/me?currency=RUB" \
  -H "Authorization: Bearer $API_KEY"
Ваш ключВаш балансВаш заказ

В примерах указаны условные данные. Актуальные цены и ID товаров смотрите в каталоге

Base URL
https://littlefox-api.ru/api/v1

Добавляйте к этому адресу путь метода: /me, /products или /orders

01

Первый запрос за три шага

  1. Создайте личный ключ

    В боте откройте «Профиль → API ключ → Создать мой API ключ»

  2. Сохраните ключ на сервере

    Сохраните ключ в переменной окружения API_KEY на своем сервере. В примерах ниже уже указан полный адрес запроса

  3. Проверьте баланс

    Выполните запрос ниже или подключитесь на странице API тест. Просмотр баланса и каталога бесплатный

cURL
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

02

API ключ и баланс

Передавайте API ключ в заголовке каждого запроса. С ним доступны каталог, баланс и заказы владельца ключа

HTTP
Authorization: Bearer YOUR_API_KEY

Также поддерживается заголовок X-API-Key. Ключи в адресной строке не принимаются

GET/api/v1/me

Ответ содержит Telegram ID, логин, баланс и лимит запросов. Баланс общий с ботом; пополнить его можно в профиле

Поле / значениеОписание
balance_kopeksСумма, доступная для покупок. Зарезервированные средства уже исключены из этого значения
held_kopeksСумма в резерве по заказам, которые еще обрабатываются
limits.requests_per_minuteМаксимальное число запросов в минуту для вашего аккаунта
Полный ответ: баланс · 200 OK

Условные данные. В примерах USD используется курс 80 ₽ за $1

JSON
{
  "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 ключ перестает работать до повторного включения. После перевыпуска старый ключ недействителен, а баланс и история сохраняются

03

Каталог, наличие и цены

GET/api/v1/products?page=1&limit=50&lang=ru&currency=RUB
Поле / значениеОписание
page / limitНомер страницы и число товаров на ней. limit — от 1 до 100, по умолчанию 50. Ответ содержит items, page, limit, total, pages и has_more
available=trueТолько товары в наличии. Без фильтра возвращаются и временно отсутствующие товары
lang=ru / enЯзык названия и описания. Не влияет на валюту
JSON · пример товара
{
  "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, которое не превышает количество в заказе. При создании заказа сервер снова проверит цену и наличие

GET/api/v1/products/{id}

Обновляйте каталог периодически, соблюдая лимит запросов. Автоматических уведомлений об изменениях нет. Если stock_unlimited=true, наличие не ограничено, а stock равен null. Услуги с дополнительными параметрами доступны в боте

04

Валюты и расчет стоимости

Добавьте 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
JSON · пример USD при курсе 80
{
  "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 с тем же ключом операции

05

Описания для сайта и бота

Параметр lang задает язык названия и описания. Описание доступно в двух форматах, готовых для использования на вашем сайте или в боте

Поле / значениеОписание
descriptionОбычный текст. Ссылки сохраняют подпись и адрес; анимированные эмодзи заменены обычными символами
description_htmlHTML с тегами strong, em, u, s, code, pre, blockquote, a и br. Скрипты, обработчики событий и стили удалены. Текст под спойлерами виден сразу
JSON
{
  "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 передаются без преобразований. Сохраняйте их целиком, включая переносы строк

06

Покупка с вашего баланса

POST/api/v1/orders?currency=RUB

В одном заказе можно купить от 1 до 100 единиц одного товара. Стоимость списывается с баланса владельца API ключа. До отправки запроса сохраните его параметры и Idempotency-Key — уникальный ключ операции

cURL
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_idID товара из актуального каталога. 42 в примере — условное значение
quantityКоличество единиц товара: целое число от 1 до 100, не больше доступного остатка
max_total_kopeksМаксимальная сумма всего заказа в копейках: от 1 до 100000000. Поле обязательно. Если итоговая стоимость выше лимита, покупка отклоняется
client_order_idНеобязательный номер или метка заказа в вашей системе, до 128 символов. Не заменяет Idempotency-Key
201Выдан200Повтор заказа202В обработке

Выдавайте данные покупателю только при status=delivered. Поле data.delivery_data содержит полный результат: текст, несколько строк или JSON в виде строки

Полный ответ: товар выдан · 201 Created

Условные данные. В примерах USD используется курс 80 ₽ за $1

JSON
{
  "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 до запуска. Значения ниже условные; запрос создает реальную покупку

operation.json
{
  "idempotency_key": "store-order-1042",
  "body": {
    "product_id": 42,
    "quantity": 1,
    "max_total_kopeks": 19900,
    "client_order_id": "1042"
  }
}
Node.js
// 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.

Одна сохраненная операция — одна покупка. Повторный запуск использует тот же файл. Для новой покупки создайте отдельный файл и новый ключ. Не перезаписывайте файл, пока предыдущая операция не завершена

07

Тестовая покупка

Тестовый товар доступен всем API-клиентам. Найдите is_test=true в каталоге и используйте полученный id. Наличие неограниченно: stock=null, stock_unlimited=true, available=true; api_only=true

Покупка за 1 ₽ с тестовыми данными

Цена единицы — 100 копеек (1 ₽). В USD ответ показывает эквивалент по курсу магазина. В ответе будет уникальная вымышленная пара email | password на домене example.com. Эти данные не дают доступа к реальным аккаунтам. Заказ попадет в «Мои покупки», историю API и статистику

  1. Отправьте обычный POST /orders с ID тестового товара, quantity=1 и max_total_kopeks=100
  2. Дождитесь delivered и сохраните delivery_data. При повторе используйте прежний Idempotency-Key

На странице API тест можно подтвердить покупку, проверить результат, скопировать или скачать полученные данные. API ключ хранится только в памяти вкладки. Для повторной проверки сохраняются параметры операции, но не сам API ключ

Открыть API тест
08

Один заказ — один ключ операции

Если ответ не пришел, повторите исходный POST с тем же Idempotency-Key и параметрами. Новый ключ создает новую покупку; изменение параметров прежнего ключа вернет 409 idempotency_conflict. Выбор другой валюты ответа не создает новый заказ

GET/api/v1/orders/{id}

Если ID уже известен, проверяйте этот заказ через GET не чаще одного раза в 5 секунд

Поле / значениеОписание
processingЗаказ принят и обрабатывается; средства могут быть в резерве
recoveringСистема автоматически уточняет результат. Дождитесь завершения, не создавайте новую покупку
deliveredПокупка завершена. Сумма списана, delivery_data заполнено
failedПокупка отклонена до списания. Причина в failure_code и failure_message
refundedПокупка не выполнена. Зарезервированная сумма полностью возвращена на баланс
Полный ответ: заказ обрабатывается · 202 Accepted

Условные данные. В примерах USD используется курс 80 ₽ за $1

JSON
{
  "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

JSON
{
  "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 могут создать разные заказы
Если ответ не пришел

До подтверждения результата система не повторяет покупку и не возвращает резерв. Проверка продолжается после перезапуска. Уже принятые заказы обрабатываются даже при отключении ключа

09

История заказов и движение средств

GET/api/v1/orders?page=1&limit=20

Список покупок владельца ключа через API. Для поиска по своей метке используйте client_order_id. Данные выдачи доступны в отдельном запросе заказа по его ID

GET/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

JSON
{
  "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

JSON
{
  "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. Для показа пользователю преобразуйте дату в его часовой пояс

10

Ошибки и лимиты запросов

JSON
{
  "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
503API отключен или курс недоступен. При 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 ключ передавать не нужно