Документация API
Базовый адрес: https://priceapi.ru. Все ответы — JSON в UTF-8, имена полей в snake_case, время в UTC (ISO 8601).
Начало работы
- Зарегистрируйтесь и выберите тариф в разделе «Тариф и оплата».
- Создайте ключ в разделе «Ключи» — он показывается один раз, сохраните его.
- Отправьте запрос со ссылкой на страницу товара.
Авторизация
Ключ передаётся в заголовке Authorization: Bearer pa_… или X-Api-Key: pa_…. Не передавайте ключ в адресе запроса и не встраивайте его в код, который выполняется в браузере ваших пользователей.
Цена по ссылке
| Параметр | Описание |
|---|---|
url | Ссылка на страницу товара, http или https, до 2000 символов. Обязательный. Метки utm_*, yclid, gclid и т. п. отбрасываются — ссылки с разными метками считаются одной страницей. |
max_age | Сколько секунд сохранённый результат считается свежим. По умолчанию 3600 (час), минимум 300, максимум 604800 (неделя). Если ссылку кто-то запрашивал недавно, ответ придёт сразу из кеша. |
wait | Сколько секунд ждать загрузки новой страницы, от 0 до 120, по умолчанию 60. Не дождались — ответ 202. |
То же самое можно отправить методом POST /v1/price с телом {"url": "…", "max_age": 3600, "wait": 60} и заголовком Content-Type: application/json — удобно для длинных ссылок.
Ответ
200 OK — цена найдена или страница сообщает, что товара нет в наличии:
{
"url": "https://shop.example.ru/product/12345",
"price": 12990.00,
"currency": "RUB",
"in_stock": true,
"title": "Смартфон …",
"source": "jsonld",
"checked_at": "2026-10-31T20:15:04Z",
"cached": false
}
| Поле | Описание |
|---|---|
url | Ссылка после нормализации: без меток и фрагмента, параметры отсортированы. |
price | Текущая цена для покупателя, число. null, если товара нет в наличии и страница не показывает цену. |
currency | Код валюты ISO 4217 (RUB, USD, …) или null, если магазин его не указал. |
in_stock | true — можно купить, false — нет в наличии или только предзаказ, null — страница об этом не сообщает. |
title | Название товара со страницы. |
source | Откуда взята цена: jsonld, microdata, meta — разметка страницы; vision — распознана по скриншоту и сверена с текстом страницы. |
checked_at | Когда страница была загружена. |
cached | true, если ответ взят из сохранённой загрузки не старше max_age. |
Долгая загрузка: 202
Если страница не загрузилась за wait секунд, API отвечает 202 Accepted с заголовком Retry-After, а загрузка продолжается. Повторите тот же запрос через несколько секунд — он подхватит идущую загрузку или вернёт её результат. Ответ 202 в лимит не засчитывается.
{
"url": "https://shop.example.ru/product/12345",
"status": "pending",
"message": "Страница загружается, повторите тот же запрос через несколько секунд",
"retry_after": 10
}
Ошибки
Ответ с ошибкой содержит объект error с машинным кодом и описанием:
{
"url": "https://shop.example.ru/product/12345",
"error": {
"code": "no_price",
"message": "No structured price on the page …",
"page_title": "Доступ ограничен"
},
"checked_at": "2026-10-31T20:15:04Z",
"cached": false
}
| HTTP | Код | Что случилось |
|---|---|---|
| 400 | invalid_url, invalid_json | Нет ссылки, она не http(s) или тело POST не JSON. |
| 401 | invalid_key | Нет ключа, ключ неверный или отозван. |
| 402 | no_subscription | Нет оплаченного тарифа или срок оплаты закончился. |
| 429 | rate_limited | Превышен лимит запросов в минуту; через сколько секунд повторить — в заголовке Retry-After. |
| 429 | quota_exceeded | Ответы тарифа на этот месяц закончились. |
| 422 | not_loaded | Страница не загрузилась (сайт недоступен или отдал ошибку). |
| 422 | too_large | Страница слишком большая. |
| 422 | no_answer | Загрузка не завершилась за отведённое время. |
| 422 | no_price, price_not_in_text, no_screenshot, model_failed | Страница загрузилась, но цену на ней найти не удалось. Посмотрите page_title: часто это страница защиты от ботов или «товар не найден». |
| 503 | unavailable | Загрузка страниц временно недоступна, повторите позже. |
Ошибка по одной и той же ссылке сохраняется на 10 минут: повторный запрос в это время вернёт ту же ошибку без новой загрузки.
Остаток лимита
Тариф, оплаченный период и сколько ответов осталось в текущем месяце. Этот запрос в лимит не засчитывается.
{
"plan": "business",
"active": true,
"paid_until": "2026-12-01T10:00:00Z",
"month_start": "2026-11-01T10:00:00Z",
"month_end": "2026-12-01T10:00:00Z",
"requests_limit": 50000,
"requests_used": 1234,
"requests_left": 48766,
"per_minute": 120
}
Лимиты и учёт
- В месячный лимит засчитываются только ответы
200— с ценой или с «нет в наличии», в том числе из кеша. - Ответы 202, 4xx и 5xx не засчитываются.
- Месяц считается от даты оплаты. При оплате того же тарифа заранее оплаченный срок продлевается на месяц, при смене тарифа новый тариф начинает действовать сразу.
- Лимит в минуту считается по всем ключам аккаунта вместе.
Примеры
curl -G "https://priceapi.ru/v1/price" \ -H "Authorization: Bearer pa_ваш_ключ" \ --data-urlencode "url=https://shop.example.ru/product/12345"
import time
import requests
def get_price(url):
while True:
r = requests.get("https://priceapi.ru/v1/price",
params={"url": url},
headers={"Authorization": "Bearer pa_ваш_ключ"},
timeout=130)
if r.status_code == 202: # страница ещё загружается
time.sleep(int(r.headers.get("Retry-After", 10)))
continue
return r.status_code, r.json()
status, data = get_price("https://shop.example.ru/product/12345")
if status == 200:
print(data["price"], data["currency"], data["in_stock"])
else:
print(data["error"]["code"], data["error"]["message"])// Node.js 18+ или браузер (ключ не стоит отдавать в браузер пользователям)
async function getPrice(url) {
const api = new URL("https://priceapi.ru/v1/price");
api.searchParams.set("url", url);
for (;;) {
const r = await fetch(api, { headers: { Authorization: "Bearer pa_ваш_ключ" } });
if (r.status === 202) {
await new Promise(ok => setTimeout(ok, 10_000));
continue;
}
return { status: r.status, data: await r.json() };
}
}
const { status, data } = await getPrice("https://shop.example.ru/product/12345");
console.log(status, data.price, data.currency, data.in_stock);<?php
function getPrice(string $url): array {
$api = "https://priceapi.ru/v1/price?" . http_build_query(["url" => $url]);
do {
$ch = curl_init($api);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 130,
CURLOPT_HTTPHEADER => ["Authorization: Bearer pa_ваш_ключ"],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($status == 202) sleep(10);
} while ($status == 202);
return [$status, json_decode($body, true)];
}
[$status, $data] = getPrice("https://shop.example.ru/product/12345");
if ($status == 200) echo $data["price"], " ", $data["currency"];using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
var http = new HttpClient { Timeout = TimeSpan.FromSeconds(130) };
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "pa_ваш_ключ");
var api = "https://priceapi.ru/v1/price?url=" + Uri.EscapeDataString("https://shop.example.ru/product/12345");
HttpResponseMessage r;
while ((r = await http.GetAsync(api)).StatusCode == System.Net.HttpStatusCode.Accepted)
await Task.Delay(TimeSpan.FromSeconds(10));
var data = await r.Content.ReadFromJsonAsync<JsonElement>();
if (r.IsSuccessStatusCode)
Console.WriteLine($"{data.GetProperty("price")} {data.GetProperty("currency")}");Ссылка = "https://shop.example.ru/product/12345";
Соединение = Новый HTTPСоединение("priceapi.ru", 443, , , , 130,
Новый ЗащищенноеСоединениеOpenSSL);
// wait=120: ждать загрузки страницы до двух минут
Запрос = Новый HTTPЗапрос("/v1/price?wait=120&url=" + КодироватьСтроку(Ссылка, СпособКодированияСтроки.КодировкаURL));
Запрос.Заголовки.Вставить("Authorization", "Bearer pa_ваш_ключ");
Ответ = Соединение.Получить(Запрос);
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(Ответ.ПолучитьТелоКакСтроку());
Данные = ПрочитатьJSON(Чтение);
Если Ответ.КодСостояния = 200 Тогда
Сообщить("Цена: " + Данные.price + " " + Данные.currency);
ИначеЕсли Ответ.КодСостояния = 202 Тогда
Сообщить("Страница ещё загружается, повторите запрос через минуту");
Иначе
Сообщить(Данные.error.message);
КонецЕсли;