Перейти до вмісту
Pandus
Зміст

Документація API

Довідник REST API v1 Pandus: автентифікація, ліміти, помилки, пагінація, кожен ендпоінт із параметрами та прикладами на curl, JavaScript, PHP і Python, підписані вебхуки.

Версія:
1.1
Базова URL:
https://pandus.online/api/v1
Оновлено:

Специфікація OpenAPI (YAML) Для чого потрібен API

API повертає результати автоматичної перевірки, а не повного аудиту. Це не юридична консультація.

Початок роботи

API Pandus дає ті самі автоматичні перевірки доступності, що й кабінет, у ваших власних інструментах: у скрипті деплою, панелі агенції, системі задач. Кожна відповідь — це автоматична перевірка, а не повний аудит, і не юридична консультація.

  1. API входить у тарифи Pro та Agency, дивіться ціни.
  2. У кабінеті відкрийте API і webhooks та створіть токен. Скопіюйте його одразу: його показують лише один раз.
  3. Зберігайте токен на своєму сервері, наприклад у змінній середовища 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-EventThe event type, the same as type in the body.
Pandus-DeliveryID of this delivery; the same on every retry, so you can ignore repeats.
Pandus-Signaturet=<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-EventThe event type, the same as type in the body.
Pandus-DeliveryID of this delivery; the same on every retry, so you can ignore repeats.
Pandus-Signaturet=<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-EventThe event type, the same as type in the body.
Pandus-DeliveryID of this delivery; the same on every retry, so you can ignore repeats.
Pandus-Signaturet=<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": "&laquo; Previous",
                "page": null,
                "active": false
            },
            {
                "url": "https://pandus.online/api/v1/sites?page=1",
                "label": "1",
                "page": 1,
                "active": true
            },
            {
                "url": null,
                "label": "Next &raquo;",
                "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)