Přejít na obsah
Pandus
Obsah

Dokumentace API

Referenční příručka REST API v1 Pandus: ověření, limity, chyby, stránkování, každý endpoint s parametry a ukázkami v curl, JavaScriptu, PHP a Pythonu a podepsané webhooky.

Verze:
1.1
Základní URL:
https://pandus.online/api/v1
Aktualizováno:

Specifikace OpenAPI (YAML) K čemu API slouží

API vrací výsledky automatické kontroly, ne úplného auditu. Nejde o právní poradenství.

Začínáme

API Pandus přináší automatické kontroly přístupnosti z účtu do vašich vlastních nástrojů: do skriptu nasazení, přehledu agentury, systému úkolů. Každá odpověď je automatická kontrola, ne úplný audit, a není právní poradenství.

  1. API je součástí tarifů Pro a Agency, viz ceník.
  2. V účtu otevřete API a webhooky a vytvořte token. Hned si ho zkopírujte: zobrazí se jen jednou.
  3. Token uchovávejte na svém serveru, například v proměnné prostředí PANDUS_TOKEN, a pošlete první požadavek:
curl https://pandus.online/api/v1/account \
  -H "Authorization: Bearer $PANDUS_TOKEN" \
  -H "Accept: application/json"

Odpověď ukazuje váš tarif, jeho limity a kolik z nich je využito. Typická integrace pak přidá web (POST /sites), počká na kontrolu (GET /scans/{scan} nebo webhook scan.completed) a přečte zprávu jako JSON nebo PDF.

Každý soubor kontroly je i v API: PDF zpráva, exporty problémů (CSV, JSON, úkoly GitHub, import do Jiry) a v tarifech Agency koncept ACR jako JSON, HTML nebo PDF. GET /sites/{site}/export vrátí soubor poslední dokončené kontroly webu, aniž byste kontrolu nejdřív hledali. PDF vznikají synchronně, poprvé až minutu: nastavte časový limit čtení klienta na 120 sekund.

Všechny adresy níže jsou relativní k základní URL https://pandus.online/api/v1. Požadavky a odpovědi jsou JSON v UTF-8 (PDF, snímky obrazovky a exporty jsou soubory); data jsou ve formátu ISO 8601 s časovým pásmem. Strojově čitelný popis všeho na této stránce je specifikace OpenAPI.

Ověření

Token posílejte v hlavičce Authorization každého požadavku:

Authorization: Bearer 12|pNd4sY…
Accept: application/json
  • Token patří týmu, ne osobě, takže funguje dál, i když někdo tým opustí.
  • Oprávnění. read pokrývá každý požadavek GET. write pokrývá přidávání a mazání webů a spouštění kontrol. Pro přehled stačí token jen pro čtení.
  • Zrušení. Smažte token v účtu a okamžitě přestane fungovat. Cookie relace účtu API nikdy nepřijme.
  • Tarif. Pokud tým přejde na tarif bez API, jeho tokeny odpovídají 403, dokud tarif API opět nezahrnuje; smazány nejsou.
  • Držte ho v tajnosti. Nedávejte token do kódu, který běží v prohlížeči, ani do veřejného repozitáře. Používejte jeden token na integraci, abyste mohli jeden zrušit bez dopadu na ostatní.

Limity

  • Požadavky: 60 za minutu na token. PDF zpráva, exporty problémů a soubory ACR: 20 za minutu.
  • Každá odpověď obsahuje X-RateLimit-Limit a X-RateLimit-Remaining. Nad limitem dostanete 429 s Retry-After (kolik sekund čekat) a X-RateLimit-Reset (čas Unix).
  • Limity tarifu platí jako v účtu: počet webů, stránek na kontrolu, jak dlouho se kontroly uchovávají (historie) a jak často lze kontrolu spustit ručně: jednou za hodinu na web a nikdy, dokud kontrola webu běží. Požadavek nad limit vrátí 422 se zprávou.
  • GET /account vrací limity a využití, včetně manual_checks_available: počtu webů, na kterých může ruční kontrola začít hned teď.

Chyby

Chyby jsou JSON s polem message v angličtině. Chyby validace a limity tarifu (422) přidávají errors s problémy podle polí:

{
    "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."]
    }
}
Stav Význam
401 Chybí token, nebo je token neznámý či smazaný.
403 Token nemá oprávnění, tarif nezahrnuje API nebo nástroj, nebo je tým zablokován.
404 Nenalezeno ve vašem týmu, nebo starší než historie tarifu. Tělo je vždy {"message": "Not found."}.
409 Kontrola ještě není dokončena.
422 Požadavek není platný, nebo ho zastavuje limit tarifu.
429 Příliš mnoho požadavků: počkejte Retry-After sekund.
503 PDF se teď nepodařilo připravit: zkuste to za minutu.

Rozhodujte se podle stavového kódu, ne podle znění message: znění se může zlepšovat.

Stránkování

Seznamy (GET /sites a GET /sites/{site}/scans) přicházejí po stránkách. per_page určuje velikost od 1 do 100 (výchozí 50 webů nebo 20 kontrol), page číslo stránky. Každý seznam má tři klíče:

  • data: položky této stránky;
  • links: first, last, prev a next, úplné URL nebo null;
  • meta: current_page, last_page, per_page, total, from, to a path.

Chcete-li přečíst vše, sledujte links.next, dokud nebude 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;
}

Webhooky

Webhooky dají vašemu serveru vědět, když se něco stane, takže se nemusíte stále dotazovat. Přidejte endpoint v účtu v části API a webhooky: veřejnou adresu https a události, které poslouchá. Tým může mít až 5 endpointů. Tajný klíč (whsec_…) se zobrazí jednou, při vytvoření endpointu.

  • Doručení. POST s tělem JSON a hlavičkami Pandus-Event, Pandus-Delivery a Pandus-Signature. Přesměrování se nesledují; soukromé a místní adresy jsou odmítnuty.
  • Odpovězte libovolným 2xx do 10 sekund. Pomalou práci dělejte až po odpovědi, například ve frontě.
  • Opakování. Jiné odpovědi a vypršení času se opakují po 1 minutě, 5 minutách, 30 minutách, 2 hodinách a 6 hodinách. Pandus-Delivery je při každém opakování stejné: použijte ho k ignorování duplicit. Po 20 doručeních, která selhala při každém pokusu, se endpoint vypne; v účtu ho znovu zapněte.
  • Podpis. Pandus-Signature: t=<čas unix>,v1=<hex>. v1 je HMAC-SHA256 řetězce "<t>.<surové tělo>" s tajným klíčem endpointu. Počítejte ho ze surového těla před jakýmkoli parsováním JSON, porovnávejte v konstantním čase a odmítejte časová razítka starší než 5 minut.

Následují události, jejich data a ověření podpisu ve čtyřech jazycích.

scan.completed · Kontrola je dokončena

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.

Hlavičky

JménoPopis
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.

Obsah

{
    "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 našla nové problémy

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

Hlavičky

JménoPopis
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.

Obsah

{
    "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 · Testovací událost

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

Hlavičky

JménoPopis
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.

Obsah

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

Ověření podpisu

Spočítejte HMAC-SHA256 z časového razítka, tečky a surového těla s tajným klíčem endpointu, porovnejte výsledek s v1 v konstantním čase a odmítejte časová razítka starší než pět 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

Účet

Tým, kterému token patří, jeho tarif, limity a využití.

Tarif, limity a využití týmu

GET /account Oprávnění 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.

Odpověď

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
        }
    }
}
Chyby
StavKdymessage
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.

Ukázky kódu

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

Weby

Weby týmu. Přidání webu spustí jeho první kontrolu.

Seznam webů týmu

GET /sites Oprávnění tokenu: read

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

Parametry

JménoKdeTypPopis
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. Výchozí: 50
page query integer Page number, from 1. Výchozí: 1

Odpověď

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
    }
}
Chyby
StavKdymessage
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.

Ukázky kódu

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

Přidat web

POST /sites Oprávnění 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.

Tělo požadavku application/json

PoleTypPopis
url(povinný) string (max. 500) The address; https:// is added when left out.
name string | null (max. 120)
report_locale(povinný) string Hodnoty: 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. Výchozí: true

Příklad

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

Odpověď

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"
    }
}
Chyby
StavKdymessage
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.

Ukázky kódu

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

Jeden web

GET /sites/{site} Oprávnění tokenu: read

Parametry

JménoKdeTypPopis
site(povinný) path string The site's id.

Odpověď

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"
    }
}
Chyby
StavKdymessage
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.

Ukázky kódu

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

Smazat web

DELETE /sites/{site} Oprávnění tokenu: write

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

Parametry

JménoKdeTypPopis
site(povinný) path string The site's id.

Odpověď

204 Deleted; no body.

Chyby
StavKdymessage
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.

Ukázky kódu

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

Kontroly

Spusťte kontrolu a sledujte její stav.

Kontroly webu

GET /sites/{site}/scans Oprávnění 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

JménoKdeTypPopis
site(povinný) path string The site's id.
per_page query integer (1–100) Items per page, 1 to 100. Výchozí: 20
page query integer Page number, from 1. Výchozí: 1

Odpověď

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
    }
}
Chyby
StavKdymessage
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.

Ukázky kódu

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

Spustit kontrolu nyní

POST /sites/{site}/scans Oprávnění 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

JménoKdeTypPopis
site(povinný) path string The site's id.

Odpověď

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
        }
    }
}
Chyby
StavKdymessage
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.

Ukázky kódu

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

Stav kontroly

GET /scans/{scan} Oprávnění tokenu: read

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

Parametry

JménoKdeTypPopis
scan(povinný) path string The check's id.

Odpověď

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"
        }
    }
}
Chyby
StavKdymessage
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.

Ukázky kódu

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

Zprávy

Výsledky dokončené kontroly jako JSON, PDF, soubory pro vývojáře nebo porovnání.

Export poslední kontroly webu

GET /sites/{site}/export Oprávnění 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

JménoKdeTypPopis
site(povinný) 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. Hodnoty: json, csv, github, jira, pdf Výchozí: json
lang query string Report language; the site's report language when left out. Hodnoty: en, uk, de, pl, cs, ro, ru

Odpověď

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

Soubor; příklad se nezobrazuje.

Chyby
StavKdymessage
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.

Ukázky kódu

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)

Zpráva jako JSON

GET /scans/{scan}/report Oprávnění 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

JménoKdeTypPopis
scan(povinný) path string The check's id.
lang query string Report language; the site's report language when left out. Hodnoty: en, uk, de, pl, cs, ro, ru

Odpověď

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
            }
        ]
    }
}
Chyby
StavKdymessage
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.

Ukázky kódu

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

Zpráva jako PDF

GET /scans/{scan}/pdf Oprávnění 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

JménoKdeTypPopis
scan(povinný) path string The check's id.
lang query string Report language; the site's report language when left out. Hodnoty: en, uk, de, pl, cs, ro, ru

Odpověď

200 The PDF file (Content-Disposition attachment).

application/pdf

Soubor; příklad se nezobrazuje.

Chyby
StavKdymessage
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.

Ukázky kódu

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)

Export problémů pro vývojáře

GET /scans/{scan}/export Oprávnění 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

JménoKdeTypPopis
scan(povinný) 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. Hodnoty: json, csv, github, jira, pdf Výchozí: json
lang query string Report language; the site's report language when left out. Hodnoty: en, uk, de, pl, cs, ro, ru

Odpověď

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

Soubor; příklad se nezobrazuje.

Chyby
StavKdymessage
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.

Ukázky kódu

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)

Porovnat dvě kontroly

GET /scans/{scan}/compare/{other} Oprávnění 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

JménoKdeTypPopis
scan(povinný) path string The check's id.
other(povinný) 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. Hodnoty: en, uk, de, pl, cs, ro, ru

Odpověď

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": []
                }
            }
        ]
    }
}
Chyby
StavKdymessage
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.

Ukázky kódu

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

Snímek obrazovky ze zprávy

GET /scans/{scan}/evidence/{kind}/{id} Oprávnění tokenu: read

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

Parametry

JménoKdeTypPopis
scan(povinný) path string The check's id.
kind(povinný) path string page for a page screenshot, issue for the screenshot of one element. Hodnoty: page, issue
id(povinný) path integer The number from the report (examples[].id for an element).

Odpověď

200 The screenshot.

image/jpeg

Soubor; příklad se nezobrazuje.

Chyby
StavKdymessage
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.

Ukázky kódu

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)

Koncept ACR jako JSON

GET /scans/{scan}/acr Oprávnění 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

JménoKdeTypPopis
scan(povinný) path string The check's id.
lang query string Report language; the site's report language when left out. Hodnoty: en, uk, de, pl, cs, ro, ru

Odpověď

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"
        }
    }
}
Chyby
StavKdymessage
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.

Ukázky kódu

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

Vyplnit jedno kritérium ACR

PUT /scans/{scan}/acr/{criterion} Oprávnění 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

JménoKdeTypPopis
scan(povinný) path string The check's id.
criterion(povinný) 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. Hodnoty: en, uk, de, pl, cs, ro, ru

Tělo požadavku application/json

PoleTypPopis
conformance string | null Hodnoty: supports, partially_supports, does_not_support, not_applicable, not_evaluated
remarks string | null (max. 5000)

Příklad

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

Odpověď

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."
    }
}
Chyby
StavKdymessage
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.

Ukázky kódu

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

Stáhnout koncept ACR

GET /scans/{scan}/acr/export Oprávnění 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

JménoKdeTypPopis
scan(povinný) path string The check's id.
format query string pdf (default) or html, a single file that opens in any browser. Hodnoty: pdf, html Výchozí: pdf
lang query string Report language; the site's report language when left out. Hodnoty: en, uk, de, pl, cs, ro, ru

Odpověď

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

application/pdf

Soubor; příklad se nezobrazuje.

text/html

Soubor; příklad se nezobrazuje.

Chyby
StavKdymessage
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.

Ukázky kódu

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)