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í.
- API je součástí tarifů Pro a Agency, viz ceník.
- V účtu otevřete API a webhooky a vytvořte token. Hned si ho zkopírujte: zobrazí se jen jednou.
- 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í.
readpokrývá každý požadavekGET.writepokrý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-LimitaX-RateLimit-Remaining. Nad limitem dostanete429sRetry-After(kolik sekund čekat) aX-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í
422se zprávou. GET /accountvrací 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,prevanext, úplné URL nebonull;meta:current_page,last_page,per_page,total,from,toapath.
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í.
POSTs tělem JSON a hlavičkamiPandus-Event,Pandus-DeliveryaPandus-Signature. Přesměrování se nesledují; soukromé a místní adresy jsou odmítnuty. - Odpovězte libovolným
2xxdo 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-Deliveryje 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>.v1je 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éno | Popis |
|---|---|
| Pandus-Event | The event type, the same as type in the body. |
| Pandus-Delivery | ID of this delivery; the same on every retry, so you can ignore repeats. |
| Pandus-Signature | t=<unix time>,v1=<hex>. v1 is HMAC-SHA256 of "<t>.<raw body>" with the endpoint's secret (whsec_…). Compare it in constant time and refuse old timestamps (for example older than 5 minutes) against replays. |
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éno | Popis |
|---|---|
| Pandus-Event | The event type, the same as type in the body. |
| Pandus-Delivery | ID of this delivery; the same on every retry, so you can ignore repeats. |
| Pandus-Signature | t=<unix time>,v1=<hex>. v1 is HMAC-SHA256 of "<t>.<raw body>" with the endpoint's secret (whsec_…). Compare it in constant time and refuse old timestamps (for example older than 5 minutes) against replays. |
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éno | Popis |
|---|---|
| Pandus-Event | The event type, the same as type in the body. |
| Pandus-Delivery | ID of this delivery; the same on every retry, so you can ignore repeats. |
| Pandus-Signature | t=<unix time>,v1=<hex>. v1 is HMAC-SHA256 of "<t>.<raw body>" with the endpoint's secret (whsec_…). Compare it in constant time and refuse old timestamps (for example older than 5 minutes) against replays. |
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
}
}
}
| Stav | Kdy | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | The token lacks the ability for this request, the team's plan does not include the API, or the team is blocked. | Invalid ability provided. / The API is not included in the current plan of this team. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
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éno | Kde | Typ | Popis |
|---|---|---|---|
| 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": "« Previous",
"page": null,
"active": false
},
{
"url": "https://pandus.online/api/v1/sites?page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": null,
"label": "Next »",
"page": null,
"active": false
}
],
"path": "https://pandus.online/api/v1/sites",
"per_page": 50,
"to": 1,
"total": 1
}
}
| Stav | Kdy | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | The token lacks the ability for this request, the team's plan does not include the API, or the team is blocked. | Invalid ability provided. / The API is not included in the current plan of this team. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
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
| Pole | Typ | Popis |
|---|---|---|
| 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"
}
}
| Stav | Kdy | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | The token lacks the ability for this request, the team's plan does not include the API, or the team is blocked. | Invalid ability provided. / The API is not included in the current plan of this team. |
| 422 | The request is not valid or a plan limit stops it. | The url field is required. (and 1 more error) |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
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éno | Kde | Typ | Popis |
|---|---|---|---|
| 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"
}
}
| Stav | Kdy | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | The token lacks the ability for this request, the team's plan does not include the API, or the team is blocked. | Invalid ability provided. / The API is not included in the current plan of this team. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
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éno | Kde | Typ | Popis |
|---|---|---|---|
| site(povinný) | path | string |
The site's id.
|
Odpověď
204 Deleted; no body.
| Stav | Kdy | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | The token lacks the ability for this request, the team's plan does not include the API, or the team is blocked. | Invalid ability provided. / The API is not included in the current plan of this team. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
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éno | Kde | Typ | Popis |
|---|---|---|---|
| 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
}
}
| Stav | Kdy | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | The token lacks the ability for this request, the team's plan does not include the API, or the team is blocked. | Invalid ability provided. / The API is not included in the current plan of this team. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
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éno | Kde | Typ | Popis |
|---|---|---|---|
| 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
}
}
}
| Stav | Kdy | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | The token lacks the ability for this request, the team's plan does not include the API, or the team is blocked. | Invalid ability provided. / The API is not included in the current plan of this team. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 422 | A check of the site is running, or one was started by hand too recently. | You started a check of this site less than an hour ago. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
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éno | Kde | Typ | Popis |
|---|---|---|---|
| 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"
}
}
}
| Stav | Kdy | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | The token lacks the ability for this request, the team's plan does not include the API, or the team is blocked. | Invalid ability provided. / The API is not included in the current plan of this team. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
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éno | Kde | Typ | Popis |
|---|---|---|---|
| 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.
| Stav | Kdy | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | Without the ability read, without the API in the plan, or without exports in the plan (CSV, JSON, GitHub and Jira files; the PDF is in every plan with the API). |
Issue exports are not included in the current plan of this team. / Invalid ability provided. |
| 404 | The site is not in this team, or it has no completed check yet. | The site has no completed check yet. / Not found. |
| 422 | The request is not valid or a plan limit stops it. | The url field is required. (and 1 more error) |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
| 503 | The PDF could not be prepared right now; try again after Retry-After seconds. |
The PDF could not be prepared right now. Please try again in a minute. |
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éno | Kde | Typ | Popis |
|---|---|---|---|
| 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
}
]
}
}
| Stav | Kdy | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | The token lacks the ability for this request, the team's plan does not include the API, or the team is blocked. | Invalid ability provided. / The API is not included in the current plan of this team. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 409 | The check is still queued or running, or it failed. | The scan is not completed yet. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
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éno | Kde | Typ | Popis |
|---|---|---|---|
| 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.
| Stav | Kdy | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | The token lacks the ability for this request, the team's plan does not include the API, or the team is blocked. | Invalid ability provided. / The API is not included in the current plan of this team. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 409 | The check is still queued or running, or it failed. | The scan is not completed yet. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
| 503 | The PDF could not be prepared right now; try again after Retry-After seconds. |
The PDF could not be prepared right now. Please try again in a minute. |
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éno | Kde | Typ | Popis |
|---|---|---|---|
| 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.
| Stav | Kdy | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | Without the ability read, without the API in the plan, or without exports in the plan (CSV, JSON, GitHub and Jira files; the PDF is in every plan with the API). |
Issue exports are not included in the current plan of this team. / Invalid ability provided. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 409 | The check is still queued or running, or it failed. | The scan is not completed yet. |
| 422 | The request is not valid or a plan limit stops it. | The url field is required. (and 1 more error) |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
| 503 | The PDF could not be prepared right now; try again after Retry-After seconds. |
The PDF could not be prepared right now. Please try again in a minute. |
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éno | Kde | Typ | Popis |
|---|---|---|---|
| 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": []
}
}
]
}
}
| Stav | Kdy | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | The token lacks the ability for this request, the team's plan does not include the API, or the team is blocked. | Invalid ability provided. / The API is not included in the current plan of this team. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 409 | The check is still queued or running, or it failed. | The scan is not completed yet. |
| 422 | The checks belong to different sites, or it is the same check twice. | Both checks must belong to the same site. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
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éno | Kde | Typ | Popis |
|---|---|---|---|
| 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.
| Stav | Kdy | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | The token lacks the ability for this request, the team's plan does not include the API, or the team is blocked. | Invalid ability provided. / The API is not included in the current plan of this team. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
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éno | Kde | Typ | Popis |
|---|---|---|---|
| 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"
}
}
}
| Stav | Kdy | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | Without the ability the request needs, without the API in the plan, or without ACR drafts in the plan (Agency plans). | ACR drafts are not included in the current plan of this team. / Invalid ability provided. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 409 | The check is still queued or running, or it failed. | The scan is not completed yet. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
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éno | Kde | Typ | Popis |
|---|---|---|---|
| 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
| Pole | Typ | Popis |
|---|---|---|
| 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."
}
}
| Stav | Kdy | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | Without the ability the request needs, without the API in the plan, or without ACR drafts in the plan (Agency plans). | ACR drafts are not included in the current plan of this team. / Invalid ability provided. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 409 | The check is still queued or running, or it failed. | The scan is not completed yet. |
| 422 | An unknown criterion, a level that is not one of levels, remarks over 5000 characters, or an empty body. |
The selected conformance is invalid. |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
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éno | Kde | Typ | Popis |
|---|---|---|---|
| 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.
| Stav | Kdy | message |
|---|---|---|
| 401 | No token, an unknown token or a deleted one. | Unauthenticated. |
| 403 | Without the ability the request needs, without the API in the plan, or without ACR drafts in the plan (Agency plans). | ACR drafts are not included in the current plan of this team. / Invalid ability provided. |
| 404 | Not found in this team, or older than the plan's history. | Not found. |
| 409 | The check is still queued or running, or it failed. | The scan is not completed yet. |
| 422 | The request is not valid or a plan limit stops it. | The url field is required. (and 1 more error) |
| 429 | Too many requests in a minute. Wait Retry-After seconds. |
Too Many Attempts. |
| 503 | The PDF could not be prepared right now; try again after Retry-After seconds. |
The PDF could not be prepared right now. Please try again in a minute. |
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)