К содержанию

Публичный JSON API

Оценки сети и отзывы — на вашем сайте

Прежний API v1 возвращает оценки карточек и логотипы без изменений. Дополнительный API v2 даёт оценки всей сети по площадкам, количество оценок и до трёх отзывов с 5★ на площадку — после отдельного разрешения владельца.

GETJSONAPI v1 + v2Без токена

Для разработчика

Быстрый старт

Сделайте обычный GET-запрос по выданному URL. Публичный endpoint поддерживает CORS для GET и OPTIONS.

Endpoint
https://reputescan.ru/api/v1/public/company-ratings/rpf_REPLACE_WITH_YOUR_PUBLIC_ID
cURL
curl --request GET \
  --header "Accept: application/json" \
  "https://reputescan.ru/api/v1/public/company-ratings/rpf_REPLACE_WITH_YOUR_PUBLIC_ID"
200 OKОтвет получен. Массив ratings может быть пустым — это допустимо.
Content-Typeapplication/json
AuthorizationНе передавайте
Cache-Controlno-store

Контракт API v1

Формат ответа

Всегда показывайте rating вместе с rating_scale: отдельные источники могут использовать разные шкалы. Не рассчитывайте на порядок элементов и не объединяйте строки только по platform_slug — у одной площадки могут быть разные филиалы.

JSON
{
  "api_version": "1",
  "company_name": "Example company",
  "ratings": [
    {
      "platform_slug": "yandex_maps",
      "platform_name": "Яндекс Карты",
      "rating": 4.8,
      "rating_scale": 5,
      "profile_url": "https://yandex.ru/maps/org/example/123",
      "logo_url": "https://reputescan.ru/marketing/platforms/yandex-maps.svg",
      "location": {
        "name": "Основная точка",
        "city": "Москва"
      },
      "updated_at": "2026-10-20T12:00:00Z"
    }
  ],
  "updated_at": "2026-10-20T12:00:00Z"
}
Поля верхнего уровня
ПолеТипОписание
api_versionstringВерсия контракта; сейчас всегда «1».
company_namestringПубличное название компании.
ratingsarrayСписок доступных оценок. Может быть пустым.
updated_atstring | nullСамое позднее обновление в ISO 8601 UTC; null для пустой выдачи.
Поля элемента ratings
ПолеТипОписание
platform_slugstringСтабильный машинный код площадки.
platform_namestringНазвание площадки для интерфейса.
ratingnumberАктуальная подтверждённая оценка.
rating_scalenumberМаксимум шкалы, например 5 или 10.
profile_urlstring | nullБезопасная публичная ссылка на карточку. Если null, карточку не делайте кликабельной.
logo_urlstring | nullЛокальный логотип или нейтральный бейдж ReputeScan. Для всех текущих поддерживаемых площадок URL заполнен; всё равно предусмотрите fallback.
locationobject | nullФилиал: nullable name и city. null означает оценку без отдельной точки.
updated_atstringВремя наблюдения этой оценки в ISO 8601 UTC.
О логотипах

Изображения имеют разные пропорции — используйте object-fit: contain. Для части источников возвращаются нейтральные идентификационные бейджи ReputeScan, а не заявление об официальном партнёрстве площадки.

API v2 · widget

Вся сеть: оценки, количество и 5★ отзывы

В кабинете «API для сайта» включите «Публиковать количество оценок» и/или «Публиковать до 3 отзывов с 5★» и сохраните. Используйте дополнительный URL с /widget. Старый URL и его ответ v1 остаются прежними. Без хотя бы одного разрешения v2 возвращает 404. Замена ссылки сбрасывает разрешения, отключение отзывает обе версии.

Endpoint v2
https://reputescan.ru/api/v1/public/company-ratings/rpf_REPLACE_WITH_YOUR_PUBLIC_ID/widget
JSON v2 · example
{
  "api_version": "2",
  "company_name": "Example company",
  "total_rating_count": null,
  "known_rating_count": 120,
  "rating_count_complete": false,
  "platforms": [
    {
      "platform_slug": "yandex_maps",
      "platform_name": "Яндекс Карты",
      "logo_url": "https://reputescan.ru/marketing/platforms/yandex-maps.svg",
      "rating": 4.8,
      "rating_scale": 5,
      "rating_method": "unweighted_listing_mean",
      "listing_count": 2,
      "rating_count": null,
      "known_rating_count": 120,
      "rating_count_complete": false,
      "reviews": [
        {
          "author": "Example author",
          "text": "An example of a saved five-star review.",
          "rating": 5,
          "rating_scale": 5,
          "published_at": null,
          "published_date": "2026-10-20",
          "source_url": null,
          "profile_url": "https://yandex.ru/maps/org/example/123",
          "location": {
            "name": "Основная точка",
            "city": "Москва"
          }
        }
      ]
    }
  ],
  "updated_at": "2026-10-25T12:00:00Z"
}
Поля v2 и правила расчёта
ПолеТипОписание
api_versionstringВсегда «2». company_name и updated_at имеют прежний смысл.
total_rating_countnumber | nullСумма оценок всей сети на всех включённых площадках, только когда все количества известны. null — данные неполные или публикация выключена, не ноль.
known_rating_countnumberСумма только известных количеств. При rating_count_complete=false не подписывайте её как итог всей сети. При выключенной публикации 0 — отсутствие раскрытых данных, не измеренный ноль.
rating_count_completebooleanПолнота именно количества; это не доказательство полноты всех отзывов в базе.
platformsarrayОдна строка на площадку: platform_slug, platform_name, logo_url, listing_count, rating, rating_scale, rating_method, rating_count, known_rating_count, rating_count_complete, reviews.
platforms[].ratingnumber | nullСреднее арифметическое доступных оценок карточек после приведения к /5. Без весов по количеству; rating_method=unweighted_listing_mean, rating_scale=5. Это расчёт ReputeScan, не официальная сетевая оценка площадки. Неизвестная оценка — null.
platforms[].rating_countnumber | nullСумма оценок карточек этой площадки при известном количестве каждой; те же правила known_rating_count/completeness. Это оценки, не уникальные люди и не количество текстовых отзывов.
platforms[].reviewsarrayОт 0 до 3 сохранённых полных отзывов с оценкой ровно 5/5, сначала новые, на всю площадку сети, а не по 3 на филиал. При выключенной публикации — []. Нехватка не заполняется выдуманными отзывами или 4★.
reviews[]objectauthor: string|null, text: string, rating:5, rating_scale:5, published_at:string|null (ISO timestamp), published_date:string|null (календарная дата без выдуманного времени), source_url:string|null (сам отзыв), profile_url:string|null (карточка), location: прежний nullable филиал/город. Нельзя выдавать profile_url за прямую ссылку на отзыв.
API читает уже сохранённые данные и не запускает новый сбор. Одна и та же персона может оценить несколько филиалов/площадок: сумма — не аудитория. При неизвестном количестве скрывайте итог или честно подписывайте известный частичный объём. Для отзывов без даты не придумывайте дату, для source_url=null не создавайте ссылку на отзыв.

Мобильная карусель без случайных переходов

Сохраните код как /reviews-widget.js. Сама карточка не кликабельна: переход вынесен в отдельную ссылку «Читать оригинал отзыва» и появляется только при source_url. Данные вставляются через textContent, не innerHTML. Пример не повторяет запросы и не делает polling; для 429 действуют правила ниже.

HTML v2
<p id="rating-total" aria-live="polite"></p>
<div id="network-ratings" aria-live="polite"></div>
<section id="five-star-reviews" class="review-carousel" aria-label="Five-star reviews" tabindex="0"></section>
<script type="module" src="/reviews-widget.js"></script>
JavaScript v2
const API_URL = "https://reputescan.ru/api/v1/public/company-ratings/rpf_REPLACE_WITH_YOUR_PUBLIC_ID/widget";
const scores = document.querySelector("#network-ratings");
const carousel = document.querySelector("#five-star-reviews");
const total = document.querySelector("#rating-total");
const safeLink = (value) => {
  try {
    const url = new URL(value);
    return ["http:", "https:"].includes(url.protocol) && !url.username && !url.password ? url.href : null;
  } catch { return null; }
};
try {
  const response = await fetch(API_URL, { headers: { Accept: "application/json" } });
  if (!response.ok) throw new Error("Widget API: " + response.status);
  const data = await response.json();
  // null means unknown or not published, not zero.
  total.textContent = data.total_rating_count !== null
    ? "Всего оценок: " + data.total_rating_count
    : data.known_rating_count > 0
      ? "Известно оценок (неполные данные): " + data.known_rating_count
      : "";
  scores.replaceChildren();
  carousel.replaceChildren();
  for (const platform of data.platforms) {
    const score = document.createElement("p");
    score.textContent = platform.platform_name + ": " + (platform.rating === null
      ? "Оценка недоступна"
      : platform.rating + " / " + platform.rating_scale);
    scores.append(score);
    for (const review of platform.reviews) {
      const card = document.createElement("article");
      card.className = "review-slide";
      const title = document.createElement("h3");
      title.textContent = platform.platform_name + " · 5 / 5";
      const author = document.createElement("p");
      author.textContent = review.author || "Автор отзыва";
      const text = document.createElement("p");
      text.textContent = review.text;
      const date = document.createElement("small");
      date.textContent = review.published_date || review.published_at || "";
      card.append(title, author, text, date);
      const href = safeLink(review.source_url);
      if (href) {
        const link = document.createElement("a");
        link.href = href;
        link.target = "_blank";
        link.rel = "noopener noreferrer";
        link.textContent = "Читать оригинал отзыва";
        // Only this link is clickable, not the swipable card.
        card.append(link);
      }
      carousel.append(card);
    }
  }
} catch (error) {
  console.error(error);
  scores.textContent = "Оценки временно недоступны.";
  carousel.replaceChildren();
  total.textContent = "";
}
CSS v2
.review-carousel { display: flex; gap: 16px; overflow-x: auto; scroll-snap-type: x mandatory; padding: 12px 0; }
.review-slide { flex: 0 0 min(85vw, 360px); scroll-snap-align: start; border: 1px solid #e2e8f1; border-radius: 12px; padding: 20px; overflow-wrap: anywhere; }
.review-slide a { display: inline-block; padding: 12px 0; }
.review-slide p { white-space: pre-wrap; }

Без библиотек

Готовый пример для сайта

Добавьте контейнер, сохраните JavaScript как /ratings.js, подключите стили и замените API_URL на ссылку из кабинета. type=module запускает код после разбора HTML. Пример создаёт DOM-элементы безопасно, не вставляет данные через innerHTML и убирает изображение, если логотип не загрузился.

HTML
<div id="reputescan-ratings" aria-live="polite"></div>
<script type="module" src="/ratings.js"></script>
JavaScript
const API_URL =
  "https://reputescan.ru/api/v1/public/company-ratings/rpf_REPLACE_WITH_YOUR_PUBLIC_ID";

const root = document.querySelector("#reputescan-ratings");

function createRatingCard(source) {
  const card = document.createElement(source.profile_url ? "a" : "article");
  card.className = "rating-card";

  if (source.profile_url) {
    card.href = source.profile_url;
    card.target = "_blank";
    card.rel = "noopener noreferrer";
  }

  if (source.logo_url) {
    const logo = document.createElement("img");
    logo.src = source.logo_url;
    logo.alt = "";
    logo.width = 88;
    logo.height = 36;
    logo.addEventListener("error", () => logo.remove(), { once: true });
    card.append(logo);
  }

  const name = document.createElement("span");
  name.textContent = source.platform_name;

  const score = document.createElement("strong");
  score.textContent = `${source.rating} / ${source.rating_scale}`;

  card.append(name, score);

  const location = [source.location?.name, source.location?.city]
    .filter(Boolean)
    .join(", ");
  if (location) {
    const place = document.createElement("small");
    place.textContent = location;
    card.append(place);
  }

  return card;
}

async function loadRatings() {
  root.textContent = "Загружаем оценки…";

  try {
    const response = await fetch(API_URL, {
      headers: { Accept: "application/json" },
    });

    if (response.status === 429) {
      const seconds = response.headers.get("Retry-After") || "60";
      throw new Error(`Слишком много запросов. Повторите через ${seconds} сек.`);
    }
    if (!response.ok) throw new Error(`API вернул ${response.status}`);

    const data = await response.json();
    if (!data.ratings.length) {
      root.textContent = "Актуальные оценки скоро появятся.";
      return;
    }

    root.replaceChildren(...data.ratings.map(createRatingCard));
  } catch (error) {
    console.error(error);
    root.textContent = "Не удалось загрузить оценки.";
  }
}

loadRatings();
CSS
.rating-card {
  display: grid;
  grid-template-columns: 96px 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 14px;
  border: 1px solid #e2e8f1;
  border-radius: 12px;
  color: #172640;
  text-decoration: none;
}

.rating-card img {
  width: 88px;
  height: 36px;
  object-fit: contain;
}

.rating-card small {
  grid-column: 2 / -1;
  color: #53627a;
}
Рекомендация: загружайте данные один раз при открытии страницы. Не запускайте частый polling; если обновление в открытой вкладке действительно нужно, делайте его не чаще одного раза в минуту.

Надёжная интеграция

Ошибки, лимиты и пустая выдача

Блок оценок не должен ломать основную страницу сайта. Покажите спокойное резервное состояние и оставьте повторную попытку следующей загрузке страницы.

200

Пустой ratings

Это валидный ответ: сейчас нет оценки, которую можно достоверно опубликовать. Скрывайте блок или показывайте нейтральный текст; не подставляйте 0.

404

Ссылка недействительна

API отключён, ссылка заменена или URL неверен. Запросите у владельца новую ссылку из кабинета.

429

Слишком много запросов

Пример автоматически не повторяет запрос. Если добавляете повтор на сервере, используйте Retry-After; в cross-origin JavaScript этот заголовок может быть недоступен, поэтому задайте паузу не менее 60 секунд.

5xx

Временная ошибка

Не показывайте устаревшие данные как свежие. Сохраните интерфейс сайта рабочим и повторите позже.

120запросов в минуту с одного IP
1 200запросов в минуту на одну API-ссылку

Лимиты защищают доступность API и не являются авторизацией. Не добавляйте URL в публичный репозиторий как секрет: это именно отзываемая публичная ссылка для чтения.

Частые вопросы

Есть ли логотип у каждой площадки?

Да, для всех текущих поддерживаемых источников logo_url заполнен. Там, где официальный знак нельзя использовать без отдельного разрешения, ReputeScan отдаёт нейтральный локальный бейдж с названием источника. Оставьте визуальный fallback на случай нового источника или временной ошибки изображения.

Почему оценка иногда не по пятибалльной шкале?

API сохраняет родную шкалу площадки. Всегда выводите «rating / rating_scale» или нормализуйте значение у себя осознанно.

Почему одна площадка встречается несколько раз?

У компании может быть несколько филиалов и отдельная карточка площадки для каждого из них. Используйте location и не удаляйте строки только из-за одинакового platform_slug.

Когда обновляются данные?

API показывает последний достоверный снимок, доступный в ReputeScan. Точное время есть в updated_at у каждой строки и на верхнем уровне ответа. GET-запрос сам не запускает новый сбор.

Можно ли получить количество или тексты отзывов?

Да, через дополнительный /widget API v2 после отдельного разрешения владельца: количество оценок и до трёх полных отзывов ровно с 5/5 на каждую площадку. Прежний v1 остаётся rating-only; его URL и формат не меняются.

Готово к подключению

Скопируйте API-ссылку в кабинете и отправьте эту страницу разработчику

Постоянный адрес инструкции: reputescan.ru/docs/ratings-api

Войти в ReputeScanВернуться к примеру