Документация API
Справочник REST API v1 Pandus: аутентификация, лимиты, ошибки, пагинация, каждый эндпоинт с параметрами и примерами на curl, JavaScript, PHP и Python, подписанные вебхуки.
- Версия:
- 1.1
- Базовый URL:
- https://pandus.online/api/v1
- Обновлено:
Спецификация OpenAPI (YAML) Для чего нужен API
API возвращает результаты автоматической проверки, а не полного аудита. Это не юридическая консультация.
Начало работы
API Pandus даёт те же автоматические проверки доступности, что и кабинет, в ваших собственных инструментах: в скрипте деплоя, панели агентства, системе задач. Каждый ответ — это автоматическая проверка, а не полный аудит, и не юридическая консультация.
- API входит в тарифы Pro и Agency, смотрите цены.
- В кабинете откройте API и webhooks и создайте токен. Скопируйте его сразу: он показывается только один раз.
- Храните токен на своём сервере, например в переменной окружения
PANDUS_TOKEN, и отправьте первый запрос:
curl https://pandus.online/api/v1/account \
-H "Authorization: Bearer $PANDUS_TOKEN" \
-H "Accept: application/json"
Ответ показывает ваш тариф, его лимиты и сколько из них использовано. Дальше типичная интеграция добавляет сайт (POST /sites), ждёт проверку (GET /scans/{scan} или вебхук scan.completed) и читает отчёт в JSON или PDF.
Все файлы проверки есть и в API: PDF-отчёт, экспорт проблем (CSV, JSON, задачи GitHub, импорт в Jira) и на тарифах Agency — черновик ACR в JSON, HTML или PDF. GET /sites/{site}/export возвращает файл последней завершённой проверки сайта, не ища проверку отдельно. PDF создаются синхронно, в первый раз — до минуты: держите тайм-аут чтения клиента 120 секунд.
Все адреса ниже относительны к базовому URL https://pandus.online/api/v1. Запросы и ответы — JSON в UTF-8 (PDF, скриншоты и экспорты — файлы); даты — ISO 8601 с часовым поясом. Машиночитаемое описание всего на этой странице — спецификация OpenAPI.
Аутентификация
Отправляйте токен в заголовке Authorization каждого запроса:
Authorization: Bearer 12|pNd4sY…
Accept: application/json
- Токен принадлежит команде, а не человеку, поэтому он работает и тогда, когда кто-то уходит из команды.
- Права.
readдаёт все запросыGET.write— добавление и удаление сайтов и запуск проверок. Для панели достаточно токена только для чтения. - Отзыв. Удалите токен в кабинете, и он сразу перестанет работать. Cookie сессии кабинета API никогда не принимает.
- Тариф. Если команда переходит на тариф без API, её токены отвечают
403, пока тариф снова не будет включать API; их не удаляют. - Держите его в секрете. Не вставляйте токен в код, который выполняется в браузере, и в публичный репозиторий. Используйте отдельный токен для каждой интеграции, чтобы отозвать один, не трогая остальные.
Лимиты
- Запросы: 60 в минуту на токен. PDF-отчёт, экспорт проблем и файлы ACR: 20 в минуту.
- Каждый ответ содержит
X-RateLimit-LimitиX-RateLimit-Remaining. Сверх лимита вы получите429сRetry-After(сколько секунд ждать) иX-RateLimit-Reset(время Unix). - Лимиты тарифа действуют как в кабинете: число сайтов, страниц за проверку, сколько хранятся проверки (история) и как часто можно запускать проверку вручную: раз в час для сайта и никогда, пока идёт проверка этого сайта. Запрос сверх лимита возвращает
422с сообщением. GET /accountвозвращает лимиты и использование, в том числеmanual_checks_available: число сайтов, на которых проверку вручную можно запустить прямо сейчас.
Ошибки
Ошибки — это JSON с полем message на английском. Ошибки валидации и лимиты тарифа (422) добавляют errors с проблемами по полям:
{
"message": "The url field is required. (and 1 more error)",
"errors": {
"url": ["The url field is required."],
"report_locale": ["The report locale field is required."]
}
}
| Статус | Значение |
|---|---|
401 |
Нет токена, или токен неизвестен либо удалён. |
403 |
У токена нет нужного права, тариф не включает API или инструмент, или команда заблокирована. |
404 |
Не найдено в вашей команде или старше истории тарифа. Тело всегда {"message": "Not found."}. |
409 |
Проверка ещё не завершена. |
422 |
Запрос некорректен, или его останавливает лимит тарифа. |
429 |
Слишком много запросов: подождите Retry-After секунд. |
503 |
PDF сейчас не удалось подготовить: попробуйте через минуту. |
Ориентируйтесь на код статуса, а не на текст message: формулировки могут меняться.
Пагинация
Списки (GET /sites и GET /sites/{site}/scans) приходят страницами. per_page задаёт размер от 1 до 100 (по умолчанию 50 сайтов или 20 проверок), page — номер страницы. У каждого списка три ключа:
data: элементы этой страницы;links:first,last,prevиnext, полные URL илиnull;meta:current_page,last_page,per_page,total,from,toиpath.
Чтобы прочитать всё, переходите по links.next, пока он не станет null:
let url = 'https://pandus.online/api/v1/sites?per_page=100';
const sites = [];
while (url) {
const response = await fetch(url, {
headers: {
Authorization: `Bearer ${process.env.PANDUS_TOKEN}`,
Accept: 'application/json',
},
});
const page = await response.json();
sites.push(...page.data);
url = page.links.next;
}
Вебхуки
Вебхуки сообщают вашему серверу, когда что-то происходит, поэтому не нужно постоянно опрашивать API. Добавьте эндпоинт в кабинете в разделе API и webhooks: публичный адрес https и события, которые он слушает. У команды может быть до 5 эндпоинтов. Секрет (whsec_…) показывается один раз, при создании эндпоинта.
- Доставка.
POSTс телом JSON и заголовкамиPandus-Event,Pandus-DeliveryиPandus-Signature. Перенаправления не выполняются; частные и локальные адреса отклоняются. - Отвечайте любым
2xxв течение 10 секунд. Медленную работу делайте после ответа, например в очереди. - Повторы. Другие ответы и тайм-ауты повторяются через 1 минуту, 5 минут, 30 минут, 2 часа и 6 часов.
Pandus-Deliveryодинаков в каждом повторе: по нему отбрасывайте дубликаты. После 20 доставок, не удавшихся ни в одной попытке, эндпоинт отключается; включите его снова в кабинете. - Подпись.
Pandus-Signature: t=<время unix>,v1=<hex>.v1— это HMAC-SHA256 строки"<t>.<сырое тело>"с секретом эндпоинта. Считайте его по сырому телу, до разбора JSON, сравнивайте за постоянное время и отклоняйте метки времени старше 5 минут.
Дальше — события, их данные и проверка подписи на четырёх языках.
scan.completed · Проверка завершена
Sent for every completed check of a site of the team, scheduled or started by hand. A failed check sends no event. data.scan.links has ready-to-use API links to the report, the PDF, every export and the ACR draft.
Заголовки
| Имя | Описание |
|---|---|
| Pandus-Event | The event type, the same as type in the body. |
| Pandus-Delivery | ID of this delivery; the same on every retry, so you can ignore repeats. |
| Pandus-Signature | t=<unix time>,v1=<hex>. v1 is HMAC-SHA256 of "<t>.<raw body>" with the endpoint's secret (whsec_…). Compare it in constant time and refuse old timestamps (for example older than 5 minutes) against replays. |
Данные
{
"id": "evt_01j9z4c3x5v7b9n1m3k5j7h9g2",
"type": "scan.completed",
"created_at": "2026-10-14T06:03:42+00:00",
"data": {
"scan": {
"id": "01j9z4a7d2f4g6h8j0k2l4z6x8",
"site_id": "01j9z3m2c5v7b9n1m3k5j7h9g1",
"kind": "scheduled",
"status": "completed",
"pages_requested": 2000,
"pages_scanned": 37,
"issues": 12,
"previous_issues": 15,
"by_impact": {
"critical": 2,
"serious": 7,
"moderate": 3
},
"error": null,
"queued_at": "2026-10-14T06:00:02+00:00",
"started_at": "2026-10-14T06:00:05+00:00",
"finished_at": "2026-10-14T06:03:41+00:00",
"links": {
"self": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8",
"report": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/report",
"pdf": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/pdf",
"exports": {
"json": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/export?format=json",
"csv": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/export?format=csv",
"github": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/export?format=github",
"jira": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/export?format=jira"
},
"acr": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/acr"
}
},
"site": {
"id": "01j9z3m2c5v7b9n1m3k5j7h9g1",
"url": "https://shop.example/",
"domain": "shop.example",
"name": "Example Shop",
"platform": "shopify",
"stack": [
"shopify"
],
"report_locale": "de",
"status": "active",
"tags": [
"client-a"
],
"next_scan_at": "2026-10-21T06:00:00+00:00",
"created_at": "2026-09-02T12:31:08+00:00"
},
"new_issues": 2
}
}
Any 2xx answer within 10 seconds marks the delivery as received. Other answers and timeouts are retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours (6 attempts). After 20 deliveries that failed every attempt the endpoint is switched off.
issues.new · Проверка нашла новые проблемы
Sent after scan.completed when the check found problems that the previous check did not have (new_issues > 0). Same payload.
Заголовки
| Имя | Описание |
|---|---|
| Pandus-Event | The event type, the same as type in the body. |
| Pandus-Delivery | ID of this delivery; the same on every retry, so you can ignore repeats. |
| Pandus-Signature | t=<unix time>,v1=<hex>. v1 is HMAC-SHA256 of "<t>.<raw body>" with the endpoint's secret (whsec_…). Compare it in constant time and refuse old timestamps (for example older than 5 minutes) against replays. |
Данные
{
"id": "evt_01j9z4c3x5v7b9n1m3k5j7h9g3",
"type": "issues.new",
"created_at": "2026-10-14T06:03:42+00:00",
"data": {
"scan": {
"id": "01j9z4a7d2f4g6h8j0k2l4z6x8",
"site_id": "01j9z3m2c5v7b9n1m3k5j7h9g1",
"kind": "scheduled",
"status": "completed",
"pages_requested": 2000,
"pages_scanned": 37,
"issues": 12,
"previous_issues": 15,
"by_impact": {
"critical": 2,
"serious": 7,
"moderate": 3
},
"error": null,
"queued_at": "2026-10-14T06:00:02+00:00",
"started_at": "2026-10-14T06:00:05+00:00",
"finished_at": "2026-10-14T06:03:41+00:00",
"links": {
"self": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8",
"report": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/report",
"pdf": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/pdf",
"exports": {
"json": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/export?format=json",
"csv": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/export?format=csv",
"github": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/export?format=github",
"jira": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/export?format=jira"
},
"acr": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/acr"
}
},
"site": {
"id": "01j9z3m2c5v7b9n1m3k5j7h9g1",
"url": "https://shop.example/",
"domain": "shop.example",
"name": "Example Shop",
"platform": "shopify",
"stack": [
"shopify"
],
"report_locale": "de",
"status": "active",
"tags": [
"client-a"
],
"next_scan_at": "2026-10-21T06:00:00+00:00",
"created_at": "2026-09-02T12:31:08+00:00"
},
"new_issues": 2
}
}
Any 2xx answer within 10 seconds marks the delivery as received. Other answers and timeouts are retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours (6 attempts). After 20 deliveries that failed every attempt the endpoint is switched off.
ping · Тестовое событие
Sent when you press "Send a test event" next to an endpoint in the cabinet. Every endpoint receives it, whatever events it listens to.
Заголовки
| Имя | Описание |
|---|---|
| Pandus-Event | The event type, the same as type in the body. |
| Pandus-Delivery | ID of this delivery; the same on every retry, so you can ignore repeats. |
| Pandus-Signature | t=<unix time>,v1=<hex>. v1 is HMAC-SHA256 of "<t>.<raw body>" with the endpoint's secret (whsec_…). Compare it in constant time and refuse old timestamps (for example older than 5 minutes) against replays. |
Данные
{
"id": "evt_01j9z5d9k1l3m5n7b9v1c3x5z7",
"type": "ping",
"created_at": "2026-10-14T09:20:11+00:00",
"data": {
"message": "Test event from Pandus."
}
}
Any 2xx answer within 10 seconds marks the delivery as received. Other answers and timeouts are retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours (6 attempts). After 20 deliveries that failed every attempt the endpoint is switched off.
Проверка подписи
Вычислите HMAC-SHA256 от метки времени, точки и сырого тела запроса с секретом эндпоинта, сравните с v1 за постоянное время и отклоняйте метки времени старше пяти минут.
Shell
# SIGNATURE: the Pandus-Signature header; body.json: the raw request body.
t=$(printf '%s' "$SIGNATURE" | sed -E 's/.*t=([0-9]+).*/\1/')
v1=$(printf '%s' "$SIGNATURE" | sed -E 's/.*v1=([0-9a-f]+).*/\1/')
expected=$({ printf '%s.' "$t"; cat body.json; } \
| openssl dgst -sha256 -hmac "$PANDUS_WEBHOOK_SECRET" -r | cut -d' ' -f1)
[ "$expected" = "$v1" ] && echo "valid" || echo "invalid"
JavaScript
import crypto from 'node:crypto';
import express from 'express';
const app = express();
// The signature covers the raw body: read it before any JSON parser does.
app.post('/pandus/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const parts = Object.fromEntries(
(req.get('Pandus-Signature') ?? '').split(',').map((part) => part.split('=')),
);
const expected = crypto
.createHmac('sha256', process.env.PANDUS_WEBHOOK_SECRET)
.update(`${parts.t}.${req.body}`)
.digest('hex');
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
const valid =
fresh &&
typeof parts.v1 === 'string' &&
parts.v1.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
if (!valid) return res.sendStatus(400);
const event = JSON.parse(req.body);
// event.type: "scan.completed", "issues.new" or "ping"
res.sendStatus(204);
});
PHP
// routes/api.php: no CSRF token on this route.
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::post('pandus/webhook', function (Request $request) {
parse_str(str_replace(',', '&', (string) $request->header('Pandus-Signature')), $parts);
$expected = hash_hmac('sha256', ($parts['t'] ?? '').'.'.$request->getContent(), config('services.pandus.webhook_secret'));
abort_unless(
isset($parts['t'], $parts['v1'])
&& abs(time() - (int) $parts['t']) < 300
&& hash_equals($expected, (string) $parts['v1']),
400,
);
$event = $request->json()->all();
// $event['type']: scan.completed, issues.new or ping
return response()->noContent();
});
Python
import hashlib
import hmac
import os
import time
from flask import Flask, abort, request
app = Flask(__name__)
@app.post("/pandus/webhook")
def pandus_webhook():
header = request.headers.get("Pandus-Signature", "")
parts = dict(part.split("=", 1) for part in header.split(",") if "=" in part)
body = request.get_data() # the raw body, before any parsing
secret = os.environ["PANDUS_WEBHOOK_SECRET"].encode()
signed = parts.get("t", "").encode() + b"." + body
expected = hmac.new(secret, signed, hashlib.sha256).hexdigest()
fresh = parts.get("t", "").isdigit() and abs(time.time() - int(parts["t"])) < 300
if not fresh or not hmac.compare_digest(expected, parts.get("v1", "")):
abort(400)
event = request.get_json()
# event["type"]: "scan.completed", "issues.new" or "ping"
return "", 204
Аккаунт
Команда, которой принадлежит токен, её тариф, лимиты и использование.
Тариф, лимиты и использование команды
GET
/account
Право токена: read
The team the token belongs to with its plan, what the plan includes and how much of it is used. Check usage before you add a site or start a check.
Ответ
200 The team, plan, limits and usage.
application/json
{
"data": {
"team": {
"id": "01j9z3k8m4q2w7e5r6t8y0u1i2",
"name": "Nova Studio"
},
"plan": {
"code": "agency_10",
"name": "Agency 10",
"status": "active",
"ends_at": "2026-11-14T09:00:00+00:00",
"trial_ends_at": null
},
"features": {
"api": true,
"exports": true,
"white_label": true,
"statement_drafts": true,
"acr_drafts": true
},
"limits": {
"sites": 10,
"pages_per_check": 2000,
"check_frequency": "weekly",
"history_months": 24,
"seats": 5,
"manual_check_interval": "PT1H",
"requests_per_minute": 60
},
"usage": {
"sites": {
"used": 4,
"allowed": 10
},
"seats": {
"used": 2,
"allowed": 5
},
"manual_checks_available": 3
}
}
}
| Статус | Когда | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | The token lacks the ability for this request, the team's plan does not include the API, or the team is blocked. | Invalid ability provided. / The API is not included in the current plan of this team. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
Примеры кода
curl
curl "https://pandus.online/api/v1/account" \
-H "Authorization: Bearer $PANDUS_TOKEN" \
-H "Accept: application/json"
JavaScript
const response = await fetch('https://pandus.online/api/v1/account', {
headers: {
Authorization: `Bearer ${process.env.PANDUS_TOKEN}`,
Accept: 'application/json',
},
});
if (!response.ok) throw new Error(`Pandus API: HTTP ${response.status}`);
const { data } = await response.json();
PHP
use Illuminate\Support\Facades\Http;
$response = Http::withToken(env('PANDUS_TOKEN'))
->acceptJson()
->get('https://pandus.online/api/v1/account')
->throw();
$data = $response->json('data');
Python
import os
import requests
response = requests.get(
"https://pandus.online/api/v1/account",
headers={"Authorization": f"Bearer {os.environ['PANDUS_TOKEN']}", "Accept": "application/json"},
timeout=30,
)
response.raise_for_status()
data = response.json()["data"]
Сайты
Сайты команды. Добавление сайта запускает его первую проверку.
Список сайтов команды
GET
/sites
Право токена: read
Sites in alphabetical order of their domain, 50 a page by default.
Параметры
| Имя | Где | Тип | Описание |
|---|---|---|---|
| tag | query | string | Only sites with this tag, e.g. the name of an agency's client. |
| per_page | query | integer (1–100) |
Items per page, 1 to 100.
По умолчанию: 50 |
| page | query | integer |
Page number, from 1.
По умолчанию: 1 |
Ответ
200 One page of sites.
application/json
{
"data": [
{
"id": "01j9z3m2c5v7b9n1m3k5j7h9g1",
"url": "https://shop.example/",
"domain": "shop.example",
"name": "Example Shop",
"platform": "shopify",
"stack": [
"shopify"
],
"report_locale": "de",
"status": "active",
"tags": [
"client-a"
],
"next_scan_at": "2026-10-21T06:00:00+00:00",
"created_at": "2026-09-02T12:31:08+00:00"
}
],
"links": {
"first": "https://pandus.online/api/v1/sites?page=1",
"last": "https://pandus.online/api/v1/sites?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"links": [
{
"url": null,
"label": "« Previous",
"page": null,
"active": false
},
{
"url": "https://pandus.online/api/v1/sites?page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": null,
"label": "Next »",
"page": null,
"active": false
}
],
"path": "https://pandus.online/api/v1/sites",
"per_page": 50,
"to": 1,
"total": 1
}
}
| Статус | Когда | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | The token lacks the ability for this request, the team's plan does not include the API, or the team is blocked. | Invalid ability provided. / The API is not included in the current plan of this team. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
Примеры кода
curl
curl "https://pandus.online/api/v1/sites?tag=client-a" \
-H "Authorization: Bearer $PANDUS_TOKEN" \
-H "Accept: application/json"
JavaScript
const response = await fetch('https://pandus.online/api/v1/sites?tag=client-a', {
headers: {
Authorization: `Bearer ${process.env.PANDUS_TOKEN}`,
Accept: 'application/json',
},
});
if (!response.ok) throw new Error(`Pandus API: HTTP ${response.status}`);
const { data } = await response.json();
PHP
use Illuminate\Support\Facades\Http;
$response = Http::withToken(env('PANDUS_TOKEN'))
->acceptJson()
->get('https://pandus.online/api/v1/sites', [
'tag' => 'client-a',
])
->throw();
$data = $response->json('data');
Python
import os
import requests
response = requests.get(
"https://pandus.online/api/v1/sites",
headers={"Authorization": f"Bearer {os.environ['PANDUS_TOKEN']}", "Accept": "application/json"},
params={
"tag": "client-a",
},
timeout=30,
)
response.raise_for_status()
data = response.json()["data"]
Добавить сайт
POST
/sites
Право токена: write
Adds a site to the team and queues its first check, unless scan is false. The address must be public: private networks, local addresses and blocked domains are refused with 422. The number of sites is limited by the plan.
Тело запроса application/json
| Поле | Тип | Описание |
|---|---|---|
| url(обязательный) | string (max. 500) |
The address; https:// is added when left out.
|
| name | string | null (max. 120) | |
| report_locale(обязательный) | string |
Значения: en, uk, de, pl, cs, ro, ru |
| platform | string | null |
One platform key (older form of stack).
|
| stack | array of string (max. 8) |
Platform keys, e.g. shopify, woocommerce, wordpress, laravel, vue. Fixing tips follow the first shop platform.
|
| tags | array of string (max. 40) (max. 10) | |
| scan | boolean |
Start the first check right away.
По умолчанию: true |
Пример
{
"url": "shop.example",
"name": "Example Shop",
"report_locale": "de",
"stack": [
"shopify"
],
"tags": [
"client-a"
],
"scan": true
}
Ответ
201 The new site.
application/json
{
"data": {
"id": "01j9z3m2c5v7b9n1m3k5j7h9g1",
"url": "https://shop.example",
"domain": "shop.example",
"name": "Example Shop",
"platform": "shopify",
"stack": [
"shopify"
],
"report_locale": "de",
"status": "active",
"tags": [
"client-a"
],
"next_scan_at": "2026-10-21T06:00:00+00:00",
"created_at": "2026-10-14T09:12:44+00:00"
}
}
| Статус | Когда | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | The token lacks the ability for this request, the team's plan does not include the API, or the team is blocked. | Invalid ability provided. / The API is not included in the current plan of this team. |
| 422 | The request is not valid or a plan limit stops it. | The url field is required. (and 1 more error) |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
Примеры кода
curl
curl -X POST "https://pandus.online/api/v1/sites" \
-H "Authorization: Bearer $PANDUS_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"url": "shop.example",
"name": "Example Shop",
"report_locale": "de",
"stack": [
"shopify"
],
"tags": [
"client-a"
],
"scan": true
}'
JavaScript
const response = await fetch('https://pandus.online/api/v1/sites', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.PANDUS_TOKEN}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'shop.example',
name: 'Example Shop',
report_locale: 'de',
stack: ['shopify'],
tags: ['client-a'],
scan: true,
}),
});
if (!response.ok) throw new Error(`Pandus API: HTTP ${response.status}`);
const { data } = await response.json();
PHP
use Illuminate\Support\Facades\Http;
$response = Http::withToken(env('PANDUS_TOKEN'))
->acceptJson()
->post('https://pandus.online/api/v1/sites', [
'url' => 'shop.example',
'name' => 'Example Shop',
'report_locale' => 'de',
'stack' => ['shopify'],
'tags' => ['client-a'],
'scan' => true,
])
->throw();
$data = $response->json('data');
Python
import os
import requests
response = requests.post(
"https://pandus.online/api/v1/sites",
headers={"Authorization": f"Bearer {os.environ['PANDUS_TOKEN']}", "Accept": "application/json"},
json={
"url": "shop.example",
"name": "Example Shop",
"report_locale": "de",
"stack": ["shopify"],
"tags": ["client-a"],
"scan": True,
},
timeout=30,
)
response.raise_for_status()
data = response.json()["data"]
Один сайт
GET
/sites/{site}
Право токена: read
Параметры
| Имя | Где | Тип | Описание |
|---|---|---|---|
| site(обязательный) | path | string |
The site's id.
|
Ответ
200 The site.
application/json
{
"data": {
"id": "01j9z3m2c5v7b9n1m3k5j7h9g1",
"url": "https://shop.example/",
"domain": "shop.example",
"name": "Example Shop",
"platform": "shopify",
"stack": [
"shopify"
],
"report_locale": "de",
"status": "active",
"tags": [
"client-a"
],
"next_scan_at": "2026-10-21T06:00:00+00:00",
"created_at": "2026-09-02T12:31:08+00:00"
}
}
| Статус | Когда | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | The token lacks the ability for this request, the team's plan does not include the API, or the team is blocked. | Invalid ability provided. / The API is not included in the current plan of this team. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
Примеры кода
curl
curl "https://pandus.online/api/v1/sites/01j9z3m2c5v7b9n1m3k5j7h9g1" \
-H "Authorization: Bearer $PANDUS_TOKEN" \
-H "Accept: application/json"
JavaScript
const response = await fetch('https://pandus.online/api/v1/sites/01j9z3m2c5v7b9n1m3k5j7h9g1', {
headers: {
Authorization: `Bearer ${process.env.PANDUS_TOKEN}`,
Accept: 'application/json',
},
});
if (!response.ok) throw new Error(`Pandus API: HTTP ${response.status}`);
const { data } = await response.json();
PHP
use Illuminate\Support\Facades\Http;
$response = Http::withToken(env('PANDUS_TOKEN'))
->acceptJson()
->get('https://pandus.online/api/v1/sites/01j9z3m2c5v7b9n1m3k5j7h9g1')
->throw();
$data = $response->json('data');
Python
import os
import requests
response = requests.get(
"https://pandus.online/api/v1/sites/01j9z3m2c5v7b9n1m3k5j7h9g1",
headers={"Authorization": f"Bearer {os.environ['PANDUS_TOKEN']}", "Accept": "application/json"},
timeout=30,
)
response.raise_for_status()
data = response.json()["data"]
Удалить сайт
DELETE
/sites/{site}
Право токена: write
Deletes the site with all its checks, reports and screenshots. This cannot be undone.
Параметры
| Имя | Где | Тип | Описание |
|---|---|---|---|
| site(обязательный) | path | string |
The site's id.
|
Ответ
204 Deleted; no body.
| Статус | Когда | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | The token lacks the ability for this request, the team's plan does not include the API, or the team is blocked. | Invalid ability provided. / The API is not included in the current plan of this team. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
Примеры кода
curl
curl -X DELETE "https://pandus.online/api/v1/sites/01j9z3m2c5v7b9n1m3k5j7h9g1" \
-H "Authorization: Bearer $PANDUS_TOKEN" \
-H "Accept: application/json"
JavaScript
const response = await fetch('https://pandus.online/api/v1/sites/01j9z3m2c5v7b9n1m3k5j7h9g1', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.PANDUS_TOKEN}`,
Accept: 'application/json',
},
});
if (!response.ok) throw new Error(`Pandus API: HTTP ${response.status}`);
// 204 No Content
PHP
use Illuminate\Support\Facades\Http;
$response = Http::withToken(env('PANDUS_TOKEN'))
->acceptJson()
->delete('https://pandus.online/api/v1/sites/01j9z3m2c5v7b9n1m3k5j7h9g1')
->throw();
// 204 No Content
Python
import os
import requests
response = requests.delete(
"https://pandus.online/api/v1/sites/01j9z3m2c5v7b9n1m3k5j7h9g1",
headers={"Authorization": f"Bearer {os.environ['PANDUS_TOKEN']}", "Accept": "application/json"},
timeout=30,
)
response.raise_for_status()
# 204 No Content
Проверки
Запустите проверку и следите за её статусом.
Проверки сайта
GET
/sites/{site}/scans
Право токена: read
Checks within the plan's history, newest first, 20 a page by default. The latest completed check of a site stays available even when it is older than the history.
Параметры
| Имя | Где | Тип | Описание |
|---|---|---|---|
| site(обязательный) | path | string |
The site's id.
|
| per_page | query | integer (1–100) |
Items per page, 1 to 100.
По умолчанию: 20 |
| page | query | integer |
Page number, from 1.
По умолчанию: 1 |
Ответ
200 One page of checks.
application/json
{
"data": [
{
"id": "01j9z4a7d2f4g6h8j0k2l4z6x8",
"site_id": "01j9z3m2c5v7b9n1m3k5j7h9g1",
"kind": "scheduled",
"status": "completed",
"pages_requested": 2000,
"pages_scanned": 37,
"issues": 12,
"previous_issues": 15,
"by_impact": {
"critical": 2,
"serious": 7,
"moderate": 3
},
"error": null,
"queued_at": "2026-10-14T06:00:02+00:00",
"started_at": "2026-10-14T06:00:05+00:00",
"finished_at": "2026-10-14T06:03:41+00:00",
"links": {
"self": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8",
"report": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/report",
"pdf": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/pdf",
"exports": {
"json": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/export?format=json",
"csv": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/export?format=csv",
"github": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/export?format=github",
"jira": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/export?format=jira"
},
"acr": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/acr"
}
}
],
"links": {
"first": "https://pandus.online/api/v1/sites/01j9z3m2c5v7b9n1m3k5j7h9g1/scans?page=1",
"last": "https://pandus.online/api/v1/sites/01j9z3m2c5v7b9n1m3k5j7h9g1/scans?page=3",
"prev": null,
"next": "https://pandus.online/api/v1/sites/01j9z3m2c5v7b9n1m3k5j7h9g1/scans?page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 3,
"links": [],
"path": "https://pandus.online/api/v1/sites/01j9z3m2c5v7b9n1m3k5j7h9g1/scans",
"per_page": 1,
"to": 1,
"total": 3
}
}
| Статус | Когда | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | The token lacks the ability for this request, the team's plan does not include the API, or the team is blocked. | Invalid ability provided. / The API is not included in the current plan of this team. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
Примеры кода
curl
curl "https://pandus.online/api/v1/sites/01j9z3m2c5v7b9n1m3k5j7h9g1/scans" \
-H "Authorization: Bearer $PANDUS_TOKEN" \
-H "Accept: application/json"
JavaScript
const response = await fetch('https://pandus.online/api/v1/sites/01j9z3m2c5v7b9n1m3k5j7h9g1/scans', {
headers: {
Authorization: `Bearer ${process.env.PANDUS_TOKEN}`,
Accept: 'application/json',
},
});
if (!response.ok) throw new Error(`Pandus API: HTTP ${response.status}`);
const { data } = await response.json();
PHP
use Illuminate\Support\Facades\Http;
$response = Http::withToken(env('PANDUS_TOKEN'))
->acceptJson()
->get('https://pandus.online/api/v1/sites/01j9z3m2c5v7b9n1m3k5j7h9g1/scans')
->throw();
$data = $response->json('data');
Python
import os
import requests
response = requests.get(
"https://pandus.online/api/v1/sites/01j9z3m2c5v7b9n1m3k5j7h9g1/scans",
headers={"Authorization": f"Bearer {os.environ['PANDUS_TOKEN']}", "Accept": "application/json"},
timeout=30,
)
response.raise_for_status()
data = response.json()["data"]
Запустить проверку сейчас
POST
/sites/{site}/scans
Право токена: write
Queues a check of the site with the plan's number of pages. One check by hand per site an hour (a month on Free), never while a check of the site is running: both are answered with 422. Follow the status with "Status of a check" or a webhook.
Параметры
| Имя | Где | Тип | Описание |
|---|---|---|---|
| site(обязательный) | path | string |
The site's id.
|
Ответ
202 The check is queued.
application/json
{
"data": {
"id": "01j9z5c1v3b5n7m9q1w3e5r7t9",
"site_id": "01j9z3m2c5v7b9n1m3k5j7h9g1",
"kind": "manual",
"status": "queued",
"pages_requested": 2000,
"pages_scanned": 0,
"issues": null,
"previous_issues": null,
"by_impact": null,
"error": null,
"queued_at": "2026-10-14T09:15:00+00:00",
"started_at": null,
"finished_at": null,
"links": {
"self": "https://pandus.online/api/v1/scans/01j9z5c1v3b5n7m9q1w3e5r7t9",
"report": null,
"pdf": null,
"exports": null,
"acr": null
}
}
}
| Статус | Когда | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | The token lacks the ability for this request, the team's plan does not include the API, or the team is blocked. | Invalid ability provided. / The API is not included in the current plan of this team. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 422 | A check of the site is running, or one was started by hand too recently. | You started a check of this site less than an hour ago. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
Примеры кода
curl
curl -X POST "https://pandus.online/api/v1/sites/01j9z3m2c5v7b9n1m3k5j7h9g1/scans" \
-H "Authorization: Bearer $PANDUS_TOKEN" \
-H "Accept: application/json"
JavaScript
const response = await fetch('https://pandus.online/api/v1/sites/01j9z3m2c5v7b9n1m3k5j7h9g1/scans', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.PANDUS_TOKEN}`,
Accept: 'application/json',
},
});
if (!response.ok) throw new Error(`Pandus API: HTTP ${response.status}`);
const { data } = await response.json();
PHP
use Illuminate\Support\Facades\Http;
$response = Http::withToken(env('PANDUS_TOKEN'))
->acceptJson()
->post('https://pandus.online/api/v1/sites/01j9z3m2c5v7b9n1m3k5j7h9g1/scans')
->throw();
$data = $response->json('data');
Python
import os
import requests
response = requests.post(
"https://pandus.online/api/v1/sites/01j9z3m2c5v7b9n1m3k5j7h9g1/scans",
headers={"Authorization": f"Bearer {os.environ['PANDUS_TOKEN']}", "Accept": "application/json"},
timeout=30,
)
response.raise_for_status()
data = response.json()["data"]
Статус проверки
GET
/scans/{scan}
Право токена: read
The status (queued, running, completed, failed), the number of pages and problems.
Параметры
| Имя | Где | Тип | Описание |
|---|---|---|---|
| scan(обязательный) | path | string |
The check's id.
|
Ответ
200 The check.
application/json
{
"data": {
"id": "01j9z4a7d2f4g6h8j0k2l4z6x8",
"site_id": "01j9z3m2c5v7b9n1m3k5j7h9g1",
"kind": "scheduled",
"status": "completed",
"pages_requested": 2000,
"pages_scanned": 37,
"issues": 12,
"previous_issues": 15,
"by_impact": {
"critical": 2,
"serious": 7,
"moderate": 3
},
"error": null,
"queued_at": "2026-10-14T06:00:02+00:00",
"started_at": "2026-10-14T06:00:05+00:00",
"finished_at": "2026-10-14T06:03:41+00:00",
"links": {
"self": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8",
"report": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/report",
"pdf": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/pdf",
"exports": {
"json": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/export?format=json",
"csv": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/export?format=csv",
"github": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/export?format=github",
"jira": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/export?format=jira"
},
"acr": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/acr"
}
}
}
| Статус | Когда | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | The token lacks the ability for this request, the team's plan does not include the API, or the team is blocked. | Invalid ability provided. / The API is not included in the current plan of this team. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
Примеры кода
curl
curl "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8" \
-H "Authorization: Bearer $PANDUS_TOKEN" \
-H "Accept: application/json"
JavaScript
const response = await fetch('https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8', {
headers: {
Authorization: `Bearer ${process.env.PANDUS_TOKEN}`,
Accept: 'application/json',
},
});
if (!response.ok) throw new Error(`Pandus API: HTTP ${response.status}`);
const { data } = await response.json();
PHP
use Illuminate\Support\Facades\Http;
$response = Http::withToken(env('PANDUS_TOKEN'))
->acceptJson()
->get('https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8')
->throw();
$data = $response->json('data');
Python
import os
import requests
response = requests.get(
"https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8",
headers={"Authorization": f"Bearer {os.environ['PANDUS_TOKEN']}", "Accept": "application/json"},
timeout=30,
)
response.raise_for_status()
data = response.json()["data"]
Отчёты
Результаты завершённой проверки: JSON, PDF, файлы для разработчиков или сравнение.
Экспорт последней проверки сайта
GET
/sites/{site}/export
Право токена: read
The same files as "Export the issues for developers", for the latest completed check of the site (the one its report shows), so you do not have to look up the check first. The Pandus-Scan header names the check the file is from. format=pdf is the PDF report (every plan with the API); the other formats come with paid plans. The PDF is prepared synchronously on the first request for a language (up to a minute, then kept); keep the read timeout of your client at 120 seconds. 20 requests a minute.
Параметры
| Имя | Где | Тип | Описание |
|---|---|---|---|
| site(обязательный) | path | string |
The site's id.
|
| format | query | string |
json (default) a JSON document; csv one row per element (UTF-8 with BOM); github Markdown with one GitHub issue per rule; jira a CSV for Jira's importer with one issue per rule; pdf the PDF report for the site's owner. GitHub and Jira files list problems (WCAG A/AA) only; CSV and JSON add the recommendations.
Значения: json, csv, github, jira, pdf По умолчанию: json |
| lang | query | string |
Report language; the site's report language when left out.
Значения: en, uk, de, pl, cs, ro, ru |
Ответ
200 The file as an attachment: Content-Disposition carries the file name, Pandus-Scan the check it is from. JSON, CSV (UTF-8 with BOM), Markdown or PDF by format.
application/json
{
"format": "accessibility-issues/1",
"disclaimer": "Automated check, not a full audit. Not legal advice.",
"generated_by": "Nova Studio",
"site": {
"name": "Example Shop",
"url": "https://shop.example/"
},
"checked_at": "2026-10-14T06:03:41+00:00",
"pages_checked": 37,
"standard": "WCAG 2.2 A/AA, EN 301 549",
"engine": {
"axe": "4.10.0"
},
"language": "en",
"rules": [
{
"rule": "button-name",
"type": "problem",
"severity": "critical",
"title": "Some buttons have no name",
"summary": "Some buttons have no readable label, for example an icon-only \"add to cart\", search or menu button.",
"how_to_fix": "Give every button a name: visible text, aria-label, or aria-labelledby pointing to visible text.",
"platform_tip": "Usually icon buttons in the theme or buttons injected by apps.",
"wcag": [
"4.1.2 Name, Role, Value (A)"
],
"en301549": [
"9.4.1.2"
],
"count": 1,
"pages": 1,
"issues": [
{
"page": "https://shop.example/cart",
"selector": ".cart-icon",
"html": "<button class=\"cart-icon\"></button>",
"failure_summary": "Fix any of the following: Element does not have inner text that is visible to screen readers",
"is_new": false
}
]
}
]
}
text/csv
rule,type,severity,wcag,title,page_url,selector,html,failure_summary,new_since_previous_check
button-name,problem,critical,"4.1.2 Name, Role, Value (A)","Some buttons have no name",https://shop.example/cart,.cart-icon,"<button class=""cart-icon""></button>","Fix any of the following",no
text/markdown
# Accessibility issues of Example Shop
> Automated check, not a full audit. Not legal advice.
## [critical] Some buttons have no name (button-name)
- [ ] https://shop.example/cart — `.cart-icon`
Labels: `accessibility`, `a11y:critical`, `axe:button-name`
application/pdf
Файл; пример не показан.
| Статус | Когда | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | Without the ability read, without the API in the plan, or without exports in the plan (CSV, JSON, GitHub and Jira files; the PDF is in every plan with the API). |
Issue exports are not included in the current plan of this team. / Invalid ability provided. |
| 404 | The site is not in this team, or it has no completed check yet. | The site has no completed check yet. / Not found. |
| 422 | The request is not valid or a plan limit stops it. | The url field is required. (and 1 more error) |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
| 503 | The PDF could not be prepared right now; try again after Retry-After seconds. |
The PDF could not be prepared right now. Please try again in a minute. |
Примеры кода
curl
curl "https://pandus.online/api/v1/sites/01j9z3m2c5v7b9n1m3k5j7h9g1/export?format=pdf&lang=de" \
-H "Authorization: Bearer $PANDUS_TOKEN" \
--fail -o report.pdf
JavaScript
import { writeFile } from 'node:fs/promises';
const response = await fetch('https://pandus.online/api/v1/sites/01j9z3m2c5v7b9n1m3k5j7h9g1/export?format=pdf&lang=de', {
headers: {
Authorization: `Bearer ${process.env.PANDUS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Pandus API: HTTP ${response.status}`);
await writeFile('report.pdf', Buffer.from(await response.arrayBuffer()));
PHP
use Illuminate\Support\Facades\Http;
$response = Http::withToken(env('PANDUS_TOKEN'))
->get('https://pandus.online/api/v1/sites/01j9z3m2c5v7b9n1m3k5j7h9g1/export', [
'format' => 'pdf',
'lang' => 'de',
])
->throw();
file_put_contents('report.pdf', $response->body());
Python
import os
import requests
response = requests.get(
"https://pandus.online/api/v1/sites/01j9z3m2c5v7b9n1m3k5j7h9g1/export",
headers={"Authorization": f"Bearer {os.environ['PANDUS_TOKEN']}"},
params={
"format": "pdf",
"lang": "de",
},
timeout=30,
)
response.raise_for_status()
with open("report.pdf", "wb") as file:
file.write(response.content)
Отчёт в JSON
GET
/scans/{scan}/report
Право токена: read
The owner report of a completed check, the same content as in the cabinet: problem groups with plain-language explanations and fixing tips, examples, affected pages, totals by severity, the comparison with the previous check and the trend. Recommendations (AAA, best practice) are under advisory. Screenshot URLs need the same token.
Параметры
| Имя | Где | Тип | Описание |
|---|---|---|---|
| scan(обязательный) | path | string |
The check's id.
|
| lang | query | string |
Report language; the site's report language when left out.
Значения: en, uk, de, pl, cs, ro, ru |
Ответ
200 The report.
application/json
{
"data": {
"scan_id": "01j9z4a7d2f4g6h8j0k2l4z6x8",
"locale": "en",
"url": "https://shop.example/",
"title": "Example Shop",
"platform": "shopify",
"platform_name": "Shopify",
"checked_at": "2026-10-14T06:03:41.000000Z",
"screen": null,
"totals": {
"critical": 1,
"serious": 0,
"moderate": 0,
"minor": 0
},
"total": 1,
"groups": [
{
"rule": "button-name",
"severity": "critical",
"rank": 0,
"count": 1,
"new_count": 0,
"title": "Some buttons have no name",
"summary": "Some buttons have no readable label, for example an icon-only \"add to cart\", search or menu button.",
"impact": "Screen reader and voice control users cannot tell which button adds to cart, opens the menu or submits the search.",
"who": [
"blind customers",
"screen reader users",
"voice control users"
],
"how_to_fix": "Give every button a name: visible text, aria-label, or aria-labelledby pointing to visible text.",
"platform_tip": "Usually icon buttons in the theme or buttons injected by apps. Add labels in the theme's Liquid snippets.",
"machine": false,
"has_card": true,
"help": "Buttons must have discernible text",
"refs": [
"WCAG 2.2 4.1.2 (A) Name, Role, Value",
"EN 301 549 9.4.1.2"
],
"pages": [
{
"url": "https://shop.example/cart",
"count": 1
}
],
"examples": [
{
"id": 4182,
"page": "https://shop.example/cart",
"target": ".cart-icon",
"html": "<button class=\"cart-icon\"></button>",
"details": null,
"is_new": false,
"shot": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/evidence/issue/4182",
"shot_width": 1280,
"shot_height": 720
}
],
"boxes": [],
"n": 1
}
],
"advisory": [],
"pages": [
{
"url": "https://shop.example/cart",
"title": "Cart",
"issues": 1,
"error": null
}
],
"diff": {
"previous_id": "01j9yq3r5t7y9u1i3o5p7a9s1d",
"previous_date": "2026-10-07T06:02:57+00:00",
"previous_total": 2,
"new": 0,
"fixed": 1,
"remaining": 1,
"fixed_rules": [
{
"title": "Images have no text description",
"count": 1
}
]
},
"trend": [
{
"id": "01j9yq3r5t7y9u1i3o5p7a9s1d",
"date": "2026-10-07T06:02:57+00:00",
"total": 2
},
{
"id": "01j9z4a7d2f4g6h8j0k2l4z6x8",
"date": "2026-10-14T06:03:41+00:00",
"total": 1
}
]
}
}
| Статус | Когда | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | The token lacks the ability for this request, the team's plan does not include the API, or the team is blocked. | Invalid ability provided. / The API is not included in the current plan of this team. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 409 | The check is still queued or running, or it failed. | The scan is not completed yet. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
Примеры кода
curl
curl "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/report" \
-H "Authorization: Bearer $PANDUS_TOKEN" \
-H "Accept: application/json"
JavaScript
const response = await fetch('https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/report', {
headers: {
Authorization: `Bearer ${process.env.PANDUS_TOKEN}`,
Accept: 'application/json',
},
});
if (!response.ok) throw new Error(`Pandus API: HTTP ${response.status}`);
const { data } = await response.json();
PHP
use Illuminate\Support\Facades\Http;
$response = Http::withToken(env('PANDUS_TOKEN'))
->acceptJson()
->get('https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/report')
->throw();
$data = $response->json('data');
Python
import os
import requests
response = requests.get(
"https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/report",
headers={"Authorization": f"Bearer {os.environ['PANDUS_TOKEN']}", "Accept": "application/json"},
timeout=30,
)
response.raise_for_status()
data = response.json()["data"]
Отчёт в PDF
GET
/scans/{scan}/pdf
Право токена: read
The PDF report of a completed check, under the team's brand on plans with white-label. Synchronous: the first request for a language prepares the file (up to a minute; keep the read timeout of your client at 120 seconds) and the next ones get the stored file. If the renderer is busy or down the answer is 503 with Retry-After. The same file as GET /scans/{scan}/export?format=pdf. 20 requests a minute.
Параметры
| Имя | Где | Тип | Описание |
|---|---|---|---|
| scan(обязательный) | path | string |
The check's id.
|
| lang | query | string |
Report language; the site's report language when left out.
Значения: en, uk, de, pl, cs, ro, ru |
Ответ
200 The PDF file (Content-Disposition attachment).
application/pdf
Файл; пример не показан.
| Статус | Когда | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | The token lacks the ability for this request, the team's plan does not include the API, or the team is blocked. | Invalid ability provided. / The API is not included in the current plan of this team. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 409 | The check is still queued or running, or it failed. | The scan is not completed yet. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
| 503 | The PDF could not be prepared right now; try again after Retry-After seconds. |
The PDF could not be prepared right now. Please try again in a minute. |
Примеры кода
curl
curl "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/pdf?lang=de" \
-H "Authorization: Bearer $PANDUS_TOKEN" \
--fail -o report.pdf
JavaScript
import { writeFile } from 'node:fs/promises';
const response = await fetch('https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/pdf?lang=de', {
headers: {
Authorization: `Bearer ${process.env.PANDUS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Pandus API: HTTP ${response.status}`);
await writeFile('report.pdf', Buffer.from(await response.arrayBuffer()));
PHP
use Illuminate\Support\Facades\Http;
$response = Http::withToken(env('PANDUS_TOKEN'))
->get('https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/pdf', [
'lang' => 'de',
])
->throw();
file_put_contents('report.pdf', $response->body());
Python
import os
import requests
response = requests.get(
"https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/pdf",
headers={"Authorization": f"Bearer {os.environ['PANDUS_TOKEN']}"},
params={
"lang": "de",
},
timeout=30,
)
response.raise_for_status()
with open("report.pdf", "wb") as file:
file.write(response.content)
Экспорт проблем для разработчиков
GET
/scans/{scan}/export
Право токена: read
The issues of a completed check as a file for developers and ticket systems, the same files as the export menus in the cabinet. Included in paid plans (and for the check an express report was bought for). format=pdf returns the PDF report instead, as "The report as PDF" (every plan with the API). 20 requests a minute.
Параметры
| Имя | Где | Тип | Описание |
|---|---|---|---|
| scan(обязательный) | path | string |
The check's id.
|
| format | query | string |
json (default) a JSON document; csv one row per element (UTF-8 with BOM); github Markdown with one GitHub issue per rule; jira a CSV for Jira's importer with one issue per rule; pdf the PDF report for the site's owner. GitHub and Jira files list problems (WCAG A/AA) only; CSV and JSON add the recommendations.
Значения: json, csv, github, jira, pdf По умолчанию: json |
| lang | query | string |
Report language; the site's report language when left out.
Значения: en, uk, de, pl, cs, ro, ru |
Ответ
200 The file as an attachment: Content-Disposition carries the file name, Pandus-Scan the check it is from. JSON, CSV (UTF-8 with BOM), Markdown or PDF by format.
application/json
{
"format": "accessibility-issues/1",
"disclaimer": "Automated check, not a full audit. Not legal advice.",
"generated_by": "Nova Studio",
"site": {
"name": "Example Shop",
"url": "https://shop.example/"
},
"checked_at": "2026-10-14T06:03:41+00:00",
"pages_checked": 37,
"standard": "WCAG 2.2 A/AA, EN 301 549",
"engine": {
"axe": "4.10.0"
},
"language": "en",
"rules": [
{
"rule": "button-name",
"type": "problem",
"severity": "critical",
"title": "Some buttons have no name",
"summary": "Some buttons have no readable label, for example an icon-only \"add to cart\", search or menu button.",
"how_to_fix": "Give every button a name: visible text, aria-label, or aria-labelledby pointing to visible text.",
"platform_tip": "Usually icon buttons in the theme or buttons injected by apps.",
"wcag": [
"4.1.2 Name, Role, Value (A)"
],
"en301549": [
"9.4.1.2"
],
"count": 1,
"pages": 1,
"issues": [
{
"page": "https://shop.example/cart",
"selector": ".cart-icon",
"html": "<button class=\"cart-icon\"></button>",
"failure_summary": "Fix any of the following: Element does not have inner text that is visible to screen readers",
"is_new": false
}
]
}
]
}
text/csv
rule,type,severity,wcag,title,page_url,selector,html,failure_summary,new_since_previous_check
button-name,problem,critical,"4.1.2 Name, Role, Value (A)","Some buttons have no name",https://shop.example/cart,.cart-icon,"<button class=""cart-icon""></button>","Fix any of the following",no
text/markdown
# Accessibility issues of Example Shop
> Automated check, not a full audit. Not legal advice.
## [critical] Some buttons have no name (button-name)
- [ ] https://shop.example/cart — `.cart-icon`
Labels: `accessibility`, `a11y:critical`, `axe:button-name`
application/pdf
Файл; пример не показан.
| Статус | Когда | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | Without the ability read, without the API in the plan, or without exports in the plan (CSV, JSON, GitHub and Jira files; the PDF is in every plan with the API). |
Issue exports are not included in the current plan of this team. / Invalid ability provided. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 409 | The check is still queued or running, or it failed. | The scan is not completed yet. |
| 422 | The request is not valid or a plan limit stops it. | The url field is required. (and 1 more error) |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
| 503 | The PDF could not be prepared right now; try again after Retry-After seconds. |
The PDF could not be prepared right now. Please try again in a minute. |
Примеры кода
curl
curl "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/export?format=csv" \
-H "Authorization: Bearer $PANDUS_TOKEN" \
--fail -o issues.csv
JavaScript
import { writeFile } from 'node:fs/promises';
const response = await fetch('https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/export?format=csv', {
headers: {
Authorization: `Bearer ${process.env.PANDUS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Pandus API: HTTP ${response.status}`);
await writeFile('issues.csv', Buffer.from(await response.arrayBuffer()));
PHP
use Illuminate\Support\Facades\Http;
$response = Http::withToken(env('PANDUS_TOKEN'))
->get('https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/export', [
'format' => 'csv',
])
->throw();
file_put_contents('issues.csv', $response->body());
Python
import os
import requests
response = requests.get(
"https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/export",
headers={"Authorization": f"Bearer {os.environ['PANDUS_TOKEN']}"},
params={
"format": "csv",
},
timeout=30,
)
response.raise_for_status()
with open("issues.csv", "wb") as file:
file.write(response.content)
Сравнить две проверки
GET
/scans/{scan}/compare/{other}
Право токена: read
Before and after between two completed checks of one site, per rule: which problems (WCAG A/AA) were fixed, which are new and which are still open. Problems are matched by rule, page and element. Up to 25 elements are listed per rule and state; count has the full number.
Параметры
| Имя | Где | Тип | Описание |
|---|---|---|---|
| scan(обязательный) | path | string |
The check's id.
|
| other(обязательный) | path | string | ID of another completed check of the same site. The older of the two is "before". |
| lang | query | string |
Report language; the site's report language when left out.
Значения: en, uk, de, pl, cs, ro, ru |
Ответ
200 The comparison.
application/json
{
"data": {
"site_id": "01j9z3m2c5v7b9n1m3k5j7h9g1",
"locale": "en",
"before": {
"id": "01j9yq3r5t7y9u1i3o5p7a9s1d",
"date": "2026-10-07T06:02:57+00:00",
"issues": 3
},
"after": {
"id": "01j9z4a7d2f4g6h8j0k2l4z6x8",
"date": "2026-10-14T06:03:41+00:00",
"issues": 2
},
"totals": {
"fixed": 2,
"new": 1,
"open": 1,
"before": 3,
"after": 2
},
"rules": [
{
"rule": "button-name",
"title": "Some buttons have no name",
"severity": "critical",
"fixed": {
"count": 1,
"items": [
{
"page": "https://shop.example/cart",
"selector": ".wishlist"
}
]
},
"new": {
"count": 0,
"items": []
},
"open": {
"count": 1,
"items": [
{
"page": "https://shop.example/cart",
"selector": ".cart-icon"
}
]
}
},
{
"rule": "color-contrast",
"title": "Text is hard to read because of low contrast",
"severity": "serious",
"fixed": {
"count": 0,
"items": []
},
"new": {
"count": 1,
"items": [
{
"page": "https://shop.example/cart",
"selector": ".price"
}
]
},
"open": {
"count": 0,
"items": []
}
}
]
}
}
| Статус | Когда | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | The token lacks the ability for this request, the team's plan does not include the API, or the team is blocked. | Invalid ability provided. / The API is not included in the current plan of this team. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 409 | The check is still queued or running, or it failed. | The scan is not completed yet. |
| 422 | The checks belong to different sites, or it is the same check twice. | Both checks must belong to the same site. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
Примеры кода
curl
curl "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/compare/01j9yq3r5t7y9u1i3o5p7a9s1d" \
-H "Authorization: Bearer $PANDUS_TOKEN" \
-H "Accept: application/json"
JavaScript
const response = await fetch('https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/compare/01j9yq3r5t7y9u1i3o5p7a9s1d', {
headers: {
Authorization: `Bearer ${process.env.PANDUS_TOKEN}`,
Accept: 'application/json',
},
});
if (!response.ok) throw new Error(`Pandus API: HTTP ${response.status}`);
const { data } = await response.json();
PHP
use Illuminate\Support\Facades\Http;
$response = Http::withToken(env('PANDUS_TOKEN'))
->acceptJson()
->get('https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/compare/01j9yq3r5t7y9u1i3o5p7a9s1d')
->throw();
$data = $response->json('data');
Python
import os
import requests
response = requests.get(
"https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/compare/01j9yq3r5t7y9u1i3o5p7a9s1d",
headers={"Authorization": f"Bearer {os.environ['PANDUS_TOKEN']}", "Accept": "application/json"},
timeout=30,
)
response.raise_for_status()
data = response.json()["data"]
Скриншот из отчёта
GET
/scans/{scan}/evidence/{kind}/{id}
Право токена: read
The JPEG behind a shot or screen.url link of the JSON report.
Параметры
| Имя | Где | Тип | Описание |
|---|---|---|---|
| scan(обязательный) | path | string |
The check's id.
|
| kind(обязательный) | path | string |
page for a page screenshot, issue for the screenshot of one element.
Значения: page, issue |
| id(обязательный) | path | integer |
The number from the report (examples[].id for an element).
|
Ответ
200 The screenshot.
image/jpeg
Файл; пример не показан.
| Статус | Когда | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | The token lacks the ability for this request, the team's plan does not include the API, or the team is blocked. | Invalid ability provided. / The API is not included in the current plan of this team. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
Примеры кода
curl
curl "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/evidence/issue/4182" \
-H "Authorization: Bearer $PANDUS_TOKEN" \
--fail -o screenshot.jpg
JavaScript
import { writeFile } from 'node:fs/promises';
const response = await fetch('https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/evidence/issue/4182', {
headers: {
Authorization: `Bearer ${process.env.PANDUS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Pandus API: HTTP ${response.status}`);
await writeFile('screenshot.jpg', Buffer.from(await response.arrayBuffer()));
PHP
use Illuminate\Support\Facades\Http;
$response = Http::withToken(env('PANDUS_TOKEN'))
->get('https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/evidence/issue/4182')
->throw();
file_put_contents('screenshot.jpg', $response->body());
Python
import os
import requests
response = requests.get(
"https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/evidence/issue/4182",
headers={"Authorization": f"Bearer {os.environ['PANDUS_TOKEN']}"},
timeout=30,
)
response.raise_for_status()
with open("screenshot.jpg", "wb") as file:
file.write(response.content)
Черновик ACR в формате JSON
GET
/scans/{scan}/acr
Право токена: read
Draft of an Accessibility Conformance Report (VPAT 2.x terms) from a completed check: one entry per WCAG 2.2 A/AA criterion with the automated result and what a person entered. Agency plans. Only automated comes from the check (fails, no_failures, not_testable); conformance and remarks are only ever what a person sent, and "no automated failures" never becomes "supports". It is a draft, not a VPAT/ACR, until a person has evaluated every criterion (notice).
Параметры
| Имя | Где | Тип | Описание |
|---|---|---|---|
| scan(обязательный) | path | string |
The check's id.
|
| lang | query | string |
Report language; the site's report language when left out.
Значения: en, uk, de, pl, cs, ro, ru |
Ответ
200 The draft.
application/json
{
"data": {
"scan_id": "01j9z4a7d2f4g6h8j0k2l4z6x8",
"site_id": "01j9z3m2c5v7b9n1m3k5j7h9g1",
"locale": "en",
"notice": "Draft based on an automated check. Not a VPAT/ACR until a person has evaluated every criterion. Not legal advice.",
"generated_by": "Nova Studio",
"site": {
"name": "Example Shop",
"url": "https://shop.example/"
},
"checked_at": "2026-10-14T06:03:41+00:00",
"pages_checked": 37,
"standard": "WCAG 2.2 A/AA",
"engine": {
"axe": "4.10.0"
},
"counts": {
"fails": 6,
"no_failures": 17,
"not_testable": 32
},
"filled": 1,
"levels": [
"supports",
"partially_supports",
"does_not_support",
"not_applicable",
"not_evaluated"
],
"criteria": [
{
"criterion": "1.1.1",
"name": "Non-text Content",
"level": "A",
"url": "https://www.w3.org/WAI/WCAG22/Understanding/non-text-content",
"automated": "fails",
"failures": [
{
"rule": "image-alt",
"title": "Images without a text alternative",
"count": 4,
"pages": 2
}
],
"conformance": "partially_supports",
"remarks": "Product photos in the gallery have no alt text; fixed in the next release."
},
{
"criterion": "1.2.1",
"name": "Audio-only and Video-only (Prerecorded)",
"level": "A",
"url": "https://www.w3.org/WAI/WCAG22/Understanding/audio-only-and-video-only-prerecorded",
"automated": "not_testable",
"failures": [],
"conformance": null,
"remarks": null
}
],
"unmapped": [],
"links": {
"html": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/acr/export?format=html&lang=en",
"pdf": "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/acr/export?format=pdf&lang=en"
}
}
}
| Статус | Когда | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | Without the ability the request needs, without the API in the plan, or without ACR drafts in the plan (Agency plans). | ACR drafts are not included in the current plan of this team. / Invalid ability provided. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 409 | The check is still queued or running, or it failed. | The scan is not completed yet. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
Примеры кода
curl
curl "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/acr" \
-H "Authorization: Bearer $PANDUS_TOKEN" \
-H "Accept: application/json"
JavaScript
const response = await fetch('https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/acr', {
headers: {
Authorization: `Bearer ${process.env.PANDUS_TOKEN}`,
Accept: 'application/json',
},
});
if (!response.ok) throw new Error(`Pandus API: HTTP ${response.status}`);
const { data } = await response.json();
PHP
use Illuminate\Support\Facades\Http;
$response = Http::withToken(env('PANDUS_TOKEN'))
->acceptJson()
->get('https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/acr')
->throw();
$data = $response->json('data');
Python
import os
import requests
response = requests.get(
"https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/acr",
headers={"Authorization": f"Bearer {os.environ['PANDUS_TOKEN']}", "Accept": "application/json"},
timeout=30,
)
response.raise_for_status()
data = response.json()["data"]
Заполнить один критерий ACR
PUT
/scans/{scan}/acr/{criterion}
Право токена: write
Saves what a person decided for one criterion: the conformance level (VPAT 2.x terms, lower case as in levels) and remarks. A field left out keeps its value, null clears it; both empty removes the entry. Agency plans, ability write. Values are never set by Pandus itself: only what you send is stored. 60 requests a minute.
Параметры
| Имя | Где | Тип | Описание |
|---|---|---|---|
| scan(обязательный) | path | string |
The check's id.
|
| criterion(обязательный) | path | string | Number of a WCAG 2.2 A/AA success criterion. 4.1.1 Parsing is not part of WCAG 2.2. |
| lang | query | string |
Report language; the site's report language when left out.
Значения: en, uk, de, pl, cs, ro, ru |
Тело запроса application/json
| Поле | Тип | Описание |
|---|---|---|
| conformance | string | null |
Значения: supports, partially_supports, does_not_support, not_applicable, not_evaluated |
| remarks | string | null (max. 5000) |
Пример
{
"conformance": "partially_supports",
"remarks": "Product photos in the gallery have no alt text; fixed in the next release."
}
Ответ
200 The criterion as saved.
application/json
{
"data": {
"criterion": "1.1.1",
"name": "Non-text Content",
"level": "A",
"url": "https://www.w3.org/WAI/WCAG22/Understanding/non-text-content",
"automated": "fails",
"failures": [
{
"rule": "image-alt",
"title": "Images without a text alternative",
"count": 4,
"pages": 2
}
],
"conformance": "partially_supports",
"remarks": "Product photos in the gallery have no alt text; fixed in the next release."
}
}
| Статус | Когда | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | Without the ability the request needs, without the API in the plan, or without ACR drafts in the plan (Agency plans). | ACR drafts are not included in the current plan of this team. / Invalid ability provided. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 409 | The check is still queued or running, or it failed. | The scan is not completed yet. |
| 422 | An unknown criterion, a level that is not one of levels, remarks over 5000 characters, or an empty body. |
The selected conformance is invalid. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
Примеры кода
curl
curl -X PUT "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/acr/1.1.1" \
-H "Authorization: Bearer $PANDUS_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"conformance": "partially_supports",
"remarks": "Product photos in the gallery have no alt text; fixed in the next release."
}'
JavaScript
const response = await fetch('https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/acr/1.1.1', {
method: 'PUT',
headers: {
Authorization: `Bearer ${process.env.PANDUS_TOKEN}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
conformance: 'partially_supports',
remarks: 'Product photos in the gallery have no alt text; fixed in the next release.',
}),
});
if (!response.ok) throw new Error(`Pandus API: HTTP ${response.status}`);
const { data } = await response.json();
PHP
use Illuminate\Support\Facades\Http;
$response = Http::withToken(env('PANDUS_TOKEN'))
->acceptJson()
->put('https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/acr/1.1.1', [
'conformance' => 'partially_supports',
'remarks' => 'Product photos in the gallery have no alt text; fixed in the next release.',
])
->throw();
$data = $response->json('data');
Python
import os
import requests
response = requests.put(
"https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/acr/1.1.1",
headers={"Authorization": f"Bearer {os.environ['PANDUS_TOKEN']}", "Accept": "application/json"},
json={
"conformance": "partially_supports",
"remarks": "Product photos in the gallery have no alt text; fixed in the next release.",
},
timeout=30,
)
response.raise_for_status()
data = response.json()["data"]
Скачать черновик ACR
GET
/scans/{scan}/acr/export
Право токена: read
The ACR draft as a PDF or an HTML file, under the team's brand on plans with white-label, the same file as in the cabinet. The draft wording is at the start, at the end and, in the PDF, in the footer of every page. Made on every request (remarks change), synchronously: keep the read timeout of your client at 120 seconds. Agency plans. 20 requests a minute.
Параметры
| Имя | Где | Тип | Описание |
|---|---|---|---|
| scan(обязательный) | path | string |
The check's id.
|
| format | query | string |
pdf (default) or html, a single file that opens in any browser.
Значения: pdf, html По умолчанию: pdf |
| lang | query | string |
Report language; the site's report language when left out.
Значения: en, uk, de, pl, cs, ro, ru |
Ответ
200 The file (Content-Disposition attachment, e.g. acr-draft-shop.example-2026-10-14.pdf).
application/pdf
Файл; пример не показан.
text/html
Файл; пример не показан.
| Статус | Когда | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | Without the ability the request needs, without the API in the plan, or without ACR drafts in the plan (Agency plans). | ACR drafts are not included in the current plan of this team. / Invalid ability provided. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 409 | The check is still queued or running, or it failed. | The scan is not completed yet. |
| 422 | The request is not valid or a plan limit stops it. | The url field is required. (and 1 more error) |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
| 503 | The PDF could not be prepared right now; try again after Retry-After seconds. |
The PDF could not be prepared right now. Please try again in a minute. |
Примеры кода
curl
curl "https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/acr/export?format=pdf&lang=en" \
-H "Authorization: Bearer $PANDUS_TOKEN" \
--fail -o acr-draft.pdf
JavaScript
import { writeFile } from 'node:fs/promises';
const response = await fetch('https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/acr/export?format=pdf&lang=en', {
headers: {
Authorization: `Bearer ${process.env.PANDUS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Pandus API: HTTP ${response.status}`);
await writeFile('acr-draft.pdf', Buffer.from(await response.arrayBuffer()));
PHP
use Illuminate\Support\Facades\Http;
$response = Http::withToken(env('PANDUS_TOKEN'))
->get('https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/acr/export', [
'format' => 'pdf',
'lang' => 'en',
])
->throw();
file_put_contents('acr-draft.pdf', $response->body());
Python
import os
import requests
response = requests.get(
"https://pandus.online/api/v1/scans/01j9z4a7d2f4g6h8j0k2l4z6x8/acr/export",
headers={"Authorization": f"Bearer {os.environ['PANDUS_TOKEN']}"},
params={
"format": "pdf",
"lang": "en",
},
timeout=30,
)
response.raise_for_status()
with open("acr-draft.pdf", "wb") as file:
file.write(response.content)