Przejdź do treści
Pandus
Spis treści

Dokumentacja API

Dokumentacja REST API v1 Pandus: uwierzytelnianie, limity, błędy, paginacja, każdy endpoint z parametrami i przykładami w curl, JavaScript, PHP i Python oraz podpisane webhooki.

Wersja:
1.1
Bazowy URL:
https://pandus.online/api/v1
Zaktualizowano:

Specyfikacja OpenAPI (YAML) Do czego służy API

API zwraca wyniki automatycznej kontroli, a nie pełnego audytu. To nie jest porada prawna.

Pierwsze kroki

API Pandus daje automatyczne kontrole dostępności z panelu we własnych narzędziach: w skrypcie wdrożeniowym, panelu agencji, systemie zgłoszeń. Każda odpowiedź to automatyczna kontrola, a nie pełny audyt, i nie porada prawna.

  1. API jest dostępne w planach Pro i Agency, zobacz cennik.
  2. W panelu otwórz API i webhooki i utwórz token. Skopiuj go od razu: jest pokazywany tylko raz.
  3. Przechowuj token na swoim serwerze, na przykład w zmiennej środowiskowej PANDUS_TOKEN, i wyślij pierwsze zapytanie:
curl https://pandus.online/api/v1/account \
  -H "Authorization: Bearer $PANDUS_TOKEN" \
  -H "Accept: application/json"

Odpowiedź pokazuje Twój plan, jego limity i ich wykorzystanie. Typowa integracja dodaje potem stronę (POST /sites), czeka na kontrolę (GET /scans/{scan} albo webhook scan.completed) i czyta raport jako JSON lub PDF.

Każdy plik kontroli jest też w API: raport PDF, eksporty problemów (CSV, JSON, zgłoszenia GitHub, import do Jira) oraz w planach Agency wersja robocza ACR jako JSON, HTML lub PDF. GET /sites/{site}/export zwraca plik ostatniej zakończonej kontroli strony bez wcześniejszego szukania kontroli. Pliki PDF powstają synchronicznie, za pierwszym razem do minuty: ustaw limit czasu odczytu klienta na 120 sekund.

Wszystkie adresy poniżej są względne wobec bazowego URL https://pandus.online/api/v1. Zapytania i odpowiedzi to JSON w UTF-8 (PDF, zrzuty ekranu i eksporty to pliki); daty w formacie ISO 8601 ze strefą czasową. Maszynowo czytelny opis wszystkiego z tej strony to specyfikacja OpenAPI.

Uwierzytelnianie

Wysyłaj token w nagłówku Authorization każdego zapytania:

Authorization: Bearer 12|pNd4sY…
Accept: application/json
  • Token należy do zespołu, nie do osoby, więc działa dalej, gdy ktoś odchodzi z zespołu.
  • Uprawnienia. read obejmuje każde zapytanie GET. write obejmuje dodawanie i usuwanie stron oraz uruchamianie kontroli. Do panelu wystarczy token tylko do odczytu.
  • Unieważnianie. Usuń token w panelu, a od razu przestanie działać. API nigdy nie przyjmuje ciasteczka sesji panelu.
  • Plan. Gdy zespół przejdzie na plan bez API, jego tokeny odpowiadają 403, dopóki plan znów nie obejmie API; nie są usuwane.
  • Trzymaj go w tajemnicy. Nie umieszczaj tokenu w kodzie działającym w przeglądarce ani w publicznym repozytorium. Używaj jednego tokenu na integrację, aby móc unieważnić jeden bez ruszania pozostałych.

Limity

  • Zapytania: 60 na minutę na token. Raport PDF, eksporty problemów i pliki ACR: 20 na minutę.
  • Każda odpowiedź zawiera X-RateLimit-Limit i X-RateLimit-Remaining. Powyżej limitu dostajesz 429 z Retry-After (ile sekund czekać) i X-RateLimit-Reset (czas Unix).
  • Limity planu działają jak w panelu: liczba stron, podstron na kontrolę, jak długo przechowywane są kontrole (historia) i jak często można ręcznie uruchomić kontrolę: raz na godzinę dla strony i nigdy w trakcie trwającej kontroli tej strony. Zapytanie ponad limit zwraca 422 z komunikatem.
  • GET /account zwraca limity i wykorzystanie, w tym manual_checks_available: liczbę stron, na których ręczna kontrola może ruszyć od razu.

Błędy

Błędy to JSON z polem message po angielsku. Błędy walidacji i limity planu (422) dodają errors z problemami dla każdego pola:

{
    "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."]
    }
}
Status Znaczenie
401 Brak tokenu albo token jest nieznany lub usunięty.
403 Token nie ma uprawnienia, plan nie obejmuje API lub narzędzia albo zespół jest zablokowany.
404 Nie znaleziono w Twoim zespole albo starsze niż historia planu. Treść to zawsze {"message": "Not found."}.
409 Kontrola nie jest jeszcze zakończona.
422 Zapytanie jest niepoprawne albo blokuje je limit planu.
429 Za dużo zapytań: odczekaj Retry-After sekund.
503 Nie udało się teraz przygotować PDF: spróbuj za minutę.

Decyduj na podstawie kodu statusu, a nie treści message: sformułowania mogą się poprawiać.

Paginacja

Listy (GET /sites i GET /sites/{site}/scans) przychodzą stronami. per_page ustawia rozmiar od 1 do 100 (domyślnie 50 stron lub 20 kontroli), page numer strony. Każda lista ma trzy klucze:

  • data: elementy tej strony;
  • links: first, last, prev i next, pełne adresy URL albo null;
  • meta: current_page, last_page, per_page, total, from, to i path.

Aby przeczytać wszystko, przechodź za links.next, aż będzie 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;
}

Webhooki

Webhooki informują Twój serwer, gdy coś się dzieje, więc nie musisz stale odpytywać API. Dodaj endpoint w panelu w sekcji API i webhooki: publiczny adres https i zdarzenia, których słucha. Zespół może mieć do 5 endpointów. Sekret (whsec_…) jest pokazywany raz, przy tworzeniu endpointu.

  • Dostarczenie. POST z treścią JSON i nagłówkami Pandus-Event, Pandus-Delivery oraz Pandus-Signature. Przekierowania nie są wykonywane; adresy prywatne i lokalne są odrzucane.
  • Odpowiedz dowolnym 2xx w ciągu 10 sekund. Wolną pracę wykonuj po odpowiedzi, na przykład w kolejce.
  • Ponowienia. Inne odpowiedzi i przekroczenia czasu są ponawiane po 1 minucie, 5 minutach, 30 minutach, 2 godzinach i 6 godzinach. Pandus-Delivery jest taki sam przy każdym ponowieniu: używaj go do pomijania powtórzeń. Po 20 dostarczeniach nieudanych przy każdej próbie endpoint zostaje wyłączony; włącz go ponownie w panelu.
  • Podpis. Pandus-Signature: t=<czas unix>,v1=<hex>. v1 to HMAC-SHA256 ciągu "<t>.<surowa treść>" z sekretem endpointu. Licz go z surowej treści, przed parsowaniem JSON, porównuj w stałym czasie i odrzucaj znaczniki czasu starsze niż 5 minut.

Poniżej zdarzenia, ich dane i sprawdzanie podpisu w czterech językach.

scan.completed · Kontrola zakończona

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.

Nagłówki

Imię i nazwiskoOpis
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.

Treść

{
    "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 · Kontrola znalazła nowe problemy

Sent after scan.completed when the check found problems that the previous check did not have (new_issues > 0). Same payload.

Nagłówki

Imię i nazwiskoOpis
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.

Treść

{
    "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 · Zdarzenie testowe

Sent when you press "Send a test event" next to an endpoint in the cabinet. Every endpoint receives it, whatever events it listens to.

Nagłówki

Imię i nazwiskoOpis
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.

Treść

{
    "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.

Sprawdzanie podpisu

Oblicz HMAC-SHA256 ze znacznika czasu, kropki i surowej treści z sekretem endpointu, porównaj wynik z v1 w stałym czasie i odrzucaj znaczniki czasu starsze niż pięć minut.

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

Konto

Zespół, do którego należy token, jego plan, limity i wykorzystanie.

Plan, limity i wykorzystanie zespołu

GET /account Uprawnienie tokenu: 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.

Odpowiedź

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
        }
    }
}
Błędy
StatusKiedymessage
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.

Przykłady kodu

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"]

Strony

Strony zespołu. Dodanie strony uruchamia jej pierwszą kontrolę.

Lista stron zespołu

GET /sites Uprawnienie tokenu: read

Sites in alphabetical order of their domain, 50 a page by default.

Parametry

Imię i nazwiskoGdzieTypOpis
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. Domyślnie: 50
page query integer Page number, from 1. Domyślnie: 1

Odpowiedź

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
    }
}
Błędy
StatusKiedymessage
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.

Przykłady kodu

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"]

Dodaj stronę

POST /sites Uprawnienie tokenu: 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.

Treść zapytania application/json

PoleTypOpis
url(wymagany) string (max. 500) The address; https:// is added when left out.
name string | null (max. 120)
report_locale(wymagany) string Wartości: 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. Domyślnie: true

Przykład

{
    "url": "shop.example",
    "name": "Example Shop",
    "report_locale": "de",
    "stack": [
        "shopify"
    ],
    "tags": [
        "client-a"
    ],
    "scan": true
}

Odpowiedź

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"
    }
}
Błędy
StatusKiedymessage
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.

Przykłady kodu

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"]

Jedna strona

GET /sites/{site} Uprawnienie tokenu: read

Parametry

Imię i nazwiskoGdzieTypOpis
site(wymagany) path string The site's id.

Odpowiedź

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"
    }
}
Błędy
StatusKiedymessage
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.

Przykłady kodu

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"]

Usuń stronę

DELETE /sites/{site} Uprawnienie tokenu: write

Deletes the site with all its checks, reports and screenshots. This cannot be undone.

Parametry

Imię i nazwiskoGdzieTypOpis
site(wymagany) path string The site's id.

Odpowiedź

204 Deleted; no body.

Błędy
StatusKiedymessage
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.

Przykłady kodu

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

Sprawdzenia

Uruchom kontrolę i śledź jej status.

Kontrole strony

GET /sites/{site}/scans Uprawnienie tokenu: 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.

Parametry

Imię i nazwiskoGdzieTypOpis
site(wymagany) path string The site's id.
per_page query integer (1–100) Items per page, 1 to 100. Domyślnie: 20
page query integer Page number, from 1. Domyślnie: 1

Odpowiedź

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
    }
}
Błędy
StatusKiedymessage
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.

Przykłady kodu

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"]

Uruchom kontrolę teraz

POST /sites/{site}/scans Uprawnienie tokenu: 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.

Parametry

Imię i nazwiskoGdzieTypOpis
site(wymagany) path string The site's id.

Odpowiedź

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
        }
    }
}
Błędy
StatusKiedymessage
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.

Przykłady kodu

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"]

Status kontroli

GET /scans/{scan} Uprawnienie tokenu: read

The status (queued, running, completed, failed), the number of pages and problems.

Parametry

Imię i nazwiskoGdzieTypOpis
scan(wymagany) path string The check's id.

Odpowiedź

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"
        }
    }
}
Błędy
StatusKiedymessage
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.

Przykłady kodu

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"]

Raporty

Wyniki zakończonej kontroli: JSON, PDF, pliki dla programistów lub porównanie.

Eksport ostatniej kontroli witryny

GET /sites/{site}/export Uprawnienie tokenu: 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.

Parametry

Imię i nazwiskoGdzieTypOpis
site(wymagany) 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. Wartości: json, csv, github, jira, pdf Domyślnie: json
lang query string Report language; the site's report language when left out. Wartości: en, uk, de, pl, cs, ro, ru

Odpowiedź

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

Plik; bez przykładu.

Błędy
StatusKiedymessage
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.

Przykłady kodu

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)

Raport jako JSON

GET /scans/{scan}/report Uprawnienie tokenu: 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.

Parametry

Imię i nazwiskoGdzieTypOpis
scan(wymagany) path string The check's id.
lang query string Report language; the site's report language when left out. Wartości: en, uk, de, pl, cs, ro, ru

Odpowiedź

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
            }
        ]
    }
}
Błędy
StatusKiedymessage
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.

Przykłady kodu

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"]

Raport jako PDF

GET /scans/{scan}/pdf Uprawnienie tokenu: 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.

Parametry

Imię i nazwiskoGdzieTypOpis
scan(wymagany) path string The check's id.
lang query string Report language; the site's report language when left out. Wartości: en, uk, de, pl, cs, ro, ru

Odpowiedź

200 The PDF file (Content-Disposition attachment).

application/pdf

Plik; bez przykładu.

Błędy
StatusKiedymessage
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.

Przykłady kodu

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)

Eksport problemów dla programistów

GET /scans/{scan}/export Uprawnienie tokenu: 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.

Parametry

Imię i nazwiskoGdzieTypOpis
scan(wymagany) 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. Wartości: json, csv, github, jira, pdf Domyślnie: json
lang query string Report language; the site's report language when left out. Wartości: en, uk, de, pl, cs, ro, ru

Odpowiedź

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

Plik; bez przykładu.

Błędy
StatusKiedymessage
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.

Przykłady kodu

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)

Porównaj dwie kontrole

GET /scans/{scan}/compare/{other} Uprawnienie tokenu: 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.

Parametry

Imię i nazwiskoGdzieTypOpis
scan(wymagany) path string The check's id.
other(wymagany) 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. Wartości: en, uk, de, pl, cs, ro, ru

Odpowiedź

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": []
                }
            }
        ]
    }
}
Błędy
StatusKiedymessage
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.

Przykłady kodu

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"]

Zrzut ekranu z raportu

GET /scans/{scan}/evidence/{kind}/{id} Uprawnienie tokenu: read

The JPEG behind a shot or screen.url link of the JSON report.

Parametry

Imię i nazwiskoGdzieTypOpis
scan(wymagany) path string The check's id.
kind(wymagany) path string page for a page screenshot, issue for the screenshot of one element. Wartości: page, issue
id(wymagany) path integer The number from the report (examples[].id for an element).

Odpowiedź

200 The screenshot.

image/jpeg

Plik; bez przykładu.

Błędy
StatusKiedymessage
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.

Przykłady kodu

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)

Wersja robocza ACR jako JSON

GET /scans/{scan}/acr Uprawnienie tokenu: 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).

Parametry

Imię i nazwiskoGdzieTypOpis
scan(wymagany) path string The check's id.
lang query string Report language; the site's report language when left out. Wartości: en, uk, de, pl, cs, ro, ru

Odpowiedź

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"
        }
    }
}
Błędy
StatusKiedymessage
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.

Przykłady kodu

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"]

Wypełnij jedno kryterium ACR

PUT /scans/{scan}/acr/{criterion} Uprawnienie tokenu: 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.

Parametry

Imię i nazwiskoGdzieTypOpis
scan(wymagany) path string The check's id.
criterion(wymagany) 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. Wartości: en, uk, de, pl, cs, ro, ru

Treść zapytania application/json

PoleTypOpis
conformance string | null Wartości: supports, partially_supports, does_not_support, not_applicable, not_evaluated
remarks string | null (max. 5000)

Przykład

{
    "conformance": "partially_supports",
    "remarks": "Product photos in the gallery have no alt text; fixed in the next release."
}

Odpowiedź

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."
    }
}
Błędy
StatusKiedymessage
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.

Przykłady kodu

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"]

Pobierz wersję roboczą ACR

GET /scans/{scan}/acr/export Uprawnienie tokenu: 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.

Parametry

Imię i nazwiskoGdzieTypOpis
scan(wymagany) path string The check's id.
format query string pdf (default) or html, a single file that opens in any browser. Wartości: pdf, html Domyślnie: pdf
lang query string Report language; the site's report language when left out. Wartości: en, uk, de, pl, cs, ro, ru

Odpowiedź

200 The file (Content-Disposition attachment, e.g. acr-draft-shop.example-2026-10-14.pdf).

application/pdf

Plik; bez przykładu.

text/html

Plik; bez przykładu.

Błędy
StatusKiedymessage
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.

Przykłady kodu

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)