API
Базовый адрес: https://api.desperatemeasure.tech
API только на чтение и без авторизации. Нет токенов, сессий и заголовков аутентификации — каталог публичный и замороженный. Ограничений по частоте запросов нет.
Схема OpenAPI доступна по /openapi.json, интерактивная документация — по /docs.
Общие правила
Кодировка. Все ответы в UTF-8, Content-Type: application/json.
Названия и описания на русском языке.
Кэширование. Ответы отдаются с Cache-Control: public, max-age=300.
Изображения — с max-age=31536000, immutable, потому что путь меняется
вместе с содержимым. 3D-модели — max-age=86400, поскольку файл может
быть заменён на месте.
Цены — целые числа в рублях, без копеек. Значение null в price_from
означает «цена по запросу»; в этом случае price_on_request равно true.
Пути к файлам относительные (/media/..., /models/...).
Дописывайте базовый адрес на стороне клиента.
Ошибки возвращаются в формате FastAPI:
{ "detail": "Товар «nope» не найден" }
| Код | Когда |
|---|---|
200 |
Успех |
404 |
Товар с таким slug не существует |
422 |
Некорректный параметр запроса (например, price_min=-5) |
GET /health
Проверка живости. Не кэшируется, годится для мониторинга.
curl https://api.desperatemeasure.tech/health
{ "status": "ok", "products": 114, "materials": 221 }
GET /v1/products
Список товаров в краткой форме — без конфигуратора, отделок и галереи. Отсортирован по категории, внутри категории по возрастанию цены.
Параметры
| Параметр | Тип | Описание |
|---|---|---|
category |
string | Идентификатор категории, см. /v1/categories |
price_min |
int ≥ 0 | Минимальная цена «от» |
price_max |
int ≥ 0 | Максимальная цена «от» |
is_new |
bool | Только новинки либо только не-новинки |
has_ar |
bool | Только товары с 3D-моделью либо только без неё |
Параметры комбинируются по «и». Товары с ценой по запросу (price_from: null)
из результата исключаются, если задан любой из ценовых фильтров.
Пример
curl "https://api.desperatemeasure.tech/v1/products?category=chairs&price_max=80000"
[
{
"slug": "scarlett-light",
"title": "SCARLETT LIGHT / Скарлетт лайт",
"category": "chairs",
"is_new": false,
"price_from": 63500,
"price_on_request": false,
"has_ar": false,
"image": {
"thumb": "/media/scarlett-light/1-400.webp",
"detail": "/media/scarlett-light/1-1200.webp",
"full": "/media/scarlett-light/1-2000.webp"
}
}
]
Поле image содержит первое изображение товара или null, если их нет.
На текущих данных изображение есть у всех 114 товаров.
GET /v1/products/{slug}
Полная карточка: описание, галерея, конфигуратор с правилами расчёта цены, отделки.
curl https://api.desperatemeasure.tech/v1/products/elio
Структура ответа
| Поле | Тип | Описание |
|---|---|---|
slug |
string | Идентификатор, он же часть URL |
title |
string | Название, обычно латиницей и кириллицей |
category |
string | Идентификатор категории |
is_new |
bool | Товар в разделе «Новинки» |
description |
string | Описание; абзацы разделены \n\n |
price_from |
int | null | Минимальная возможная конфигурация |
price_to |
int | null | Максимальная: дорогие материалы и все опции |
price_on_request |
bool | Цены на сайте нет |
price_warnings |
string[] | Замечания к данным, см. ниже |
images |
Image[] | Галерея, три размера на каждое изображение |
has_ar |
bool | Есть ли 3D-модель на сервере |
model_url |
string | null | Путь к модели |
base_group |
BaseGroup | Базовая группа выбора |
delta_groups |
ChoiceGroup[] | Группы надбавок |
extras |
Extra[] | Опции-галочки |
finishes |
FinishSection[] | Доступные отделки |
Конфигуратор
Это главная часть ответа, и устроена она так, чтобы клиент считал цену одной формулой без ветвлений по типу товара.
base_group — ровно одна группа с абсолютными ценами. У столов это размер,
у стульев — ткань. Выбор в ней обязателен и всегда единственный.
"base_group": {
"id": "size",
"title": "Выберите Размер, мм (высота - 750)",
"options": [
{
"id": "1000",
"label": "⌀ 1000",
"price": 194000,
"dimensions": {
"shape": "round",
"diameter_mm": 1000,
"diameter_extended_mm": null,
"width_mm": null,
"width_extended_mm": null,
"depth_mm": null,
"height_mm": 750,
"height_options_mm": [],
"parsed": true
},
"price_matrix": {
"materials": { "derevo": 0, "keramika": 40000, "keramika_v_dereve": 84000 }
},
"option_prices": {
"vtoroy_vraschayuschiysya_uroven": 35000,
"okras_podstolya": 15000
}
}
]
}
price_matrix — надбавки за варианты из delta_groups, уже разрешённые
под этот конкретный базовый вариант. Ключ первого уровня — id группы,
второго — id варианта. На сайте надбавка за материал зависит от размера
(керамика в дереве стоит 84 000 ₽ на диаметре 1000 и 106 000 ₽ на 1600),
поэтому матрица своя у каждого базового варианта.
option_prices — то же самое для опций-галочек.
delta_groups описывают только состав и подписи; цены в них не дублируются.
"delta_groups": [
{
"id": "materials",
"title": "Выберите материал столешницы",
"options": [
{ "id": "derevo", "label": "Дерево", "swatches": [] },
{ "id": "keramika", "label": "Керамика", "swatches": [] }
]
}
]
Возможные id групп: materials (материал столешницы или ткань),
facade (материал фасада, встречается у 8 товаров).
extras — опции с множественным выбором. Поле price в них годится
для показа по умолчанию; фактическая цена берётся из option_prices
выбранного базового варианта.
Расчёт цены
итог = base_group.options[b].price
+ Σ по выбранным группам g: price_matrix[g][выбранный вариант]
+ Σ по отмеченным опциям o: option_prices[o]
Проверка на ELIO: диаметр 1600, керамика в дереве, обе опции —
373000 + 106000 + 35000 + 15000 = 529000 ₽.
Готовая реализация на Swift — в IOS.md.
Габариты
dimensions заполняется по мере распознавания; parsed говорит, удалось ли
извлечь что-то пригодное для дополненной реальности.
| Поле | Когда заполняется |
|---|---|
shape |
round, rect или other |
diameter_mm |
Круглые столешницы |
diameter_extended_mm |
Раздвижные круглые: второй диаметр |
width_mm, depth_mm |
Прямоугольные |
width_extended_mm |
Раздвижные: ширина в разложенном виде |
height_mm |
Высота; для стульев — общая высота изделия |
height_options_mm |
Несколько высот, например [600, 700] |
Габариты распознаны у 485 вариантов из 495. Оставшиеся 10 — это четыре товара
без конфигуратора и два барных стула, у которых размеров нет на самом сайте.
Проверяйте parsed перед тем, как строить геометрию.
Предупреждения о данных
price_warnings содержит замечания к качеству исходных данных. Показывать
их пользователю не нужно, но при отладке они экономят время.
| Текст | Товары |
|---|---|
цена на сайте не указана |
lazy-susan, zoe, alice-mirror, amelie |
подозрительная цена ... на фоне максимума ... |
harry |
У harry размер 2400 мм стоит 35 900 ₽ при соседних 325 000 и 392 000 ₽ —
на сайте потеряна цифра. Значение оставлено как в источнике.
Отделки
"finishes": [
{
"part": "Столешница",
"category": "Керамика - 1 категория",
"items": [
{ "id": "bianco_michelangelo", "label": "Bianco Michelangelo",
"image": "/media/_finishes/5-bianco-michelangelo-1-400.webp" }
]
}
]
part — часть изделия (Столешница, Подстолье), category — серия отделки.
Изображения образцов общие для всех товаров и лежат в /media/_finishes/,
поэтому кэшируются один раз на всё приложение.
GET /v1/categories
Категории с числом товаров. Пустые категории не возвращаются.
[
{ "id": "tables", "title": "Столы", "count": 43 },
{ "id": "coffee_tables", "title": "Журнальные столики", "count": 17 },
{ "id": "tables_sliding", "title": "Раздвижные столы", "count": 9 }
]
Полный список: tables, tables_sliding, chairs, bar_stools,
coffee_tables, consoles, mirrors, accessories, commodes,
shelving, cabinets, desks.
GET /v1/materials
Сводный каталог отделок по всем товарам. Один образец встречается у многих
изделий, поэтому записи объединены, а used_in перечисляет товары.
Параметры
| Параметр | Тип | Описание |
|---|---|---|
category |
string | Фильтр по серии отделки |
[
{
"id": "blek",
"label": "Блэк",
"category": "Бизнес - Серия",
"image": "/media/_finishes/1_black-400.webp",
"used_in": ["james-2", "mellow", "eric"]
}
]
221 запись в 17 сериях.
Статические файлы
Раздаются Caddy напрямую с диска, минуя приложение.
Изображения
/media/{slug}/{n}-{размер}.webp фото товара
/media/_finishes/{имя}-400.webp образец отделки
Размеры: 400, 1200, 2000 пикселей по длинной стороне, формат WebP.
Образцы отделок только в 400.
Пути приходят готовыми в полях thumb, detail, full — собирать их
вручную не нужно.
3D-модели
/models/{slug}.usdz
/models/{slug}.glb
Отдаются с типом model/vnd.usdz+zip и model/gltf-binary соответственно.
Первый обязателен, иначе iOS не откроет AR Quick Look.
Наличие модели проверяется на диске в момент запроса, поэтому новый файл
подхватывается без перезапуска сервиса. Пока файла нет, has_ar равно false
и model_url равно null.