Salt la conținut
Pandus
Cuprins

Documentația API

Referința REST API v1 Pandus: autentificare, limite, erori, paginare, fiecare endpoint cu parametri și exemple în curl, JavaScript, PHP și Python, plus webhookuri semnate.

Versiune:
1.1
URL de bază:
https://pandus.online/api/v1
Actualizat:

Specificația OpenAPI (YAML) La ce folosește API-ul

API-ul întoarce rezultatele unei verificări automate, nu ale unui audit complet. Nu este consultanță juridică.

Primii pași

API-ul Pandus aduce verificările automate de accesibilitate din cont în propriile dumneavoastră unelte: un script de deploy, un panou de agenție, un sistem de tichete. Fiecare răspuns este o verificare automată, nu un audit complet, și nu este consultanță juridică.

  1. API-ul este inclus în planurile Pro și Agency, vedeți prețurile.
  2. În cont, deschideți API și webhookuri și creați un token. Copiați-l imediat: este afișat o singură dată.
  3. Păstrați tokenul pe serverul dumneavoastră, de exemplu în variabila de mediu PANDUS_TOKEN, și trimiteți prima cerere:
curl https://pandus.online/api/v1/account \
  -H "Authorization: Bearer $PANDUS_TOKEN" \
  -H "Accept: application/json"

Răspunsul arată planul, limitele lui și cât din ele este folosit. O integrare tipică adaugă apoi un site (POST /sites), așteaptă verificarea (GET /scans/{scan} sau webhookul scan.completed) și citește raportul ca JSON sau PDF.

Fiecare fișier al unei verificări este și în API: raportul PDF, exporturile problemelor (CSV, JSON, issues GitHub, import Jira) și, în planurile Agency, ciorna ACR ca JSON, HTML sau PDF. GET /sites/{site}/export întoarce fișierul ultimei verificări finalizate a site-ului, fără să căutați întâi verificarea. PDF-urile se fac sincron, prima dată până la un minut: setați timpul de citire al clientului la 120 de secunde.

Toate adresele de mai jos sunt relative la URL-ul de bază https://pandus.online/api/v1. Cererile și răspunsurile sunt JSON în UTF-8 (PDF-ul, capturile de ecran și exporturile sunt fișiere); datele sunt ISO 8601 cu fus orar. Descrierea care poate fi citită de mașini a tot ce este pe această pagină este specificația OpenAPI.

Autentificare

Trimiteți tokenul în antetul Authorization al fiecărei cereri:

Authorization: Bearer 12|pNd4sY…
Accept: application/json
  • Un token aparține echipei, nu unei persoane, așa că funcționează și după ce cineva pleacă din echipă.
  • Drepturi. read acoperă orice cerere GET. write acoperă adăugarea și ștergerea site-urilor și pornirea verificărilor. Pentru un panou ajunge un token doar pentru citire.
  • Revocare. Ștergeți un token în cont și nu mai funcționează imediat. API-ul nu acceptă niciodată cookie-ul de sesiune al contului.
  • Plan. Dacă echipa trece la un plan fără API, tokenurile ei răspund 403 până când planul include din nou API-ul; nu sunt șterse.
  • Păstrați-l secret. Nu puneți tokenul în cod care rulează în browser și nici într-un depozit public. Folosiți câte un token pentru fiecare integrare, ca să puteți revoca unul fără să le atingeți pe celelalte.

Limite

  • Cereri: 60 pe minut pentru fiecare token. Raportul PDF, exporturile problemelor și fișierele ACR: 20 pe minut.
  • Fiecare răspuns conține X-RateLimit-Limit și X-RateLimit-Remaining. Peste limită primiți 429 cu Retry-After (câte secunde să așteptați) și X-RateLimit-Reset (timp Unix).
  • Limitele planului se aplică la fel ca în cont: numărul de site-uri, paginile pe verificare, cât timp se păstrează verificările (istoric) și cât de des poate fi pornită manual o verificare: o dată pe oră pentru fiecare site și niciodată în timp ce rulează o verificare a site-ului. O cerere peste limită întoarce 422 cu un mesaj.
  • GET /account întoarce limitele și utilizarea, inclusiv manual_checks_available: numărul de site-uri pe care o verificare manuală poate porni chiar acum.

Erori

Erorile sunt JSON cu un message în engleză. Erorile de validare și limitele planului (422) adaugă errors cu problemele pe câmpuri:

{
    "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."]
    }
}
Stare Înțeles
401 Lipsește tokenul sau tokenul este necunoscut ori șters.
403 Tokenului îi lipsește dreptul, planul nu include API-ul sau unealta, ori echipa este blocată.
404 Nu a fost găsit în echipa dumneavoastră sau este mai vechi decât istoricul planului. Conținutul este mereu {"message": "Not found."}.
409 Verificarea nu este încă finalizată.
422 Cererea nu este validă sau o limită a planului o oprește.
429 Prea multe cereri: așteptați Retry-After secunde.
503 PDF-ul nu a putut fi pregătit acum: încercați din nou peste un minut.

Decideți după codul de stare, nu după textul din message: formularea se poate îmbunătăți.

Paginare

Listele (GET /sites și GET /sites/{site}/scans) vin pe pagini. per_page stabilește mărimea, de la 1 la 100 (implicit 50 de site-uri sau 20 de verificări), page numărul paginii. Fiecare listă are trei chei:

  • data: elementele acestei pagini;
  • links: first, last, prev și next, URL-uri complete sau null;
  • meta: current_page, last_page, per_page, total, from, to și path.

Ca să citiți tot, urmați links.next până devine 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;
}

Webhookuri

Webhookurile anunță serverul dumneavoastră când se întâmplă ceva, ca să nu trebuiască să interogați mereu. Adăugați un endpoint în cont, la API și webhookuri: o adresă publică https și evenimentele pe care le ascultă. O echipă poate avea până la 5 endpointuri. Secretul (whsec_…) este afișat o singură dată, la crearea endpointului.

  • Livrare. Un POST cu conținut JSON și antetele Pandus-Event, Pandus-Delivery și Pandus-Signature. Redirecționările nu sunt urmate; adresele private și locale sunt refuzate.
  • Răspundeți cu orice 2xx în 10 secunde. Faceți munca lentă după răspuns, de exemplu într-o coadă.
  • Reîncercări. Alte răspunsuri și expirările sunt reîncercate după 1 minut, 5 minute, 30 de minute, 2 ore și 6 ore. Pandus-Delivery rămâne același la fiecare reîncercare: folosiți-l ca să ignorați repetările. După 20 de livrări care au eșuat la fiecare încercare, endpointul este oprit; porniți-l din nou din cont.
  • Semnătură. Pandus-Signature: t=<timp unix>,v1=<hex>. v1 este HMAC-SHA256 al șirului "<t>.<conținut brut>" cu secretul endpointului. Calculați-l pe conținutul brut, înainte de orice parsare JSON, comparați în timp constant și refuzați marcajele de timp mai vechi de 5 minute.

Urmează evenimentele, conținutul lor și verificarea semnăturii în patru limbaje.

scan.completed · O verificare s-a încheiat

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.

Antete

NumeDescriere
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.

Conținut

{
    "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 · O verificare a găsit probleme noi

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

Antete

NumeDescriere
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.

Conținut

{
    "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 · Un eveniment de test

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

Antete

NumeDescriere
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.

Conținut

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

Verificarea semnăturii

Calculați HMAC-SHA256 din marcajul de timp, un punct și conținutul brut cu secretul endpointului, comparați rezultatul cu v1 în timp constant și refuzați marcajele de timp mai vechi de cinci minute.

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

Cont

Echipa căreia îi aparține tokenul, planul, limitele și utilizarea ei.

Planul, limitele și utilizarea echipei

GET /account Dreptul tokenului: 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.

Răspuns

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
        }
    }
}
Erori
StareCândmessage
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.

Exemple de cod

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

Site-uri

Site-urile echipei. Adăugarea unui site pornește prima lui verificare.

Lista site-urilor echipei

GET /sites Dreptul tokenului: read

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

Parametri

NumeUndeTipDescriere
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. Implicit: 50
page query integer Page number, from 1. Implicit: 1

Răspuns

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
    }
}
Erori
StareCândmessage
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.

Exemple de cod

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

Adăugați un site

POST /sites Dreptul tokenului: 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.

Conținutul cererii application/json

CâmpTipDescriere
url(obligatoriu) string (max. 500) The address; https:// is added when left out.
name string | null (max. 120)
report_locale(obligatoriu) string Valori: 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. Implicit: true

Exemplu

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

Răspuns

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"
    }
}
Erori
StareCândmessage
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.

Exemple de cod

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

Un site

GET /sites/{site} Dreptul tokenului: read

Parametri

NumeUndeTipDescriere
site(obligatoriu) path string The site's id.

Răspuns

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"
    }
}
Erori
StareCândmessage
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.

Exemple de cod

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

Ștergeți un site

DELETE /sites/{site} Dreptul tokenului: write

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

Parametri

NumeUndeTipDescriere
site(obligatoriu) path string The site's id.

Răspuns

204 Deleted; no body.

Erori
StareCândmessage
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.

Exemple de cod

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

Verificări

Porniți o verificare și urmăriți-i starea.

Verificările unui site

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

Parametri

NumeUndeTipDescriere
site(obligatoriu) path string The site's id.
per_page query integer (1–100) Items per page, 1 to 100. Implicit: 20
page query integer Page number, from 1. Implicit: 1

Răspuns

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
    }
}
Erori
StareCândmessage
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.

Exemple de cod

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

Porniți o verificare acum

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

Parametri

NumeUndeTipDescriere
site(obligatoriu) path string The site's id.

Răspuns

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
        }
    }
}
Erori
StareCândmessage
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.

Exemple de cod

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

Starea unei verificări

GET /scans/{scan} Dreptul tokenului: read

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

Parametri

NumeUndeTipDescriere
scan(obligatoriu) path string The check's id.

Răspuns

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"
        }
    }
}
Erori
StareCândmessage
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.

Exemple de cod

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

Rapoarte

Rezultatele unei verificări finalizate, ca JSON, PDF, fișiere pentru dezvoltatori sau o comparație.

Exportul ultimei verificări a unui site

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

Parametri

NumeUndeTipDescriere
site(obligatoriu) 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. Valori: json, csv, github, jira, pdf Implicit: json
lang query string Report language; the site's report language when left out. Valori: en, uk, de, pl, cs, ro, ru

Răspuns

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

Un fișier; fără exemplu afișat.

Erori
StareCândmessage
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.

Exemple de cod

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)

Raportul ca JSON

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

Parametri

NumeUndeTipDescriere
scan(obligatoriu) path string The check's id.
lang query string Report language; the site's report language when left out. Valori: en, uk, de, pl, cs, ro, ru

Răspuns

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
            }
        ]
    }
}
Erori
StareCândmessage
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.

Exemple de cod

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

Raportul ca PDF

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

Parametri

NumeUndeTipDescriere
scan(obligatoriu) path string The check's id.
lang query string Report language; the site's report language when left out. Valori: en, uk, de, pl, cs, ro, ru

Răspuns

200 The PDF file (Content-Disposition attachment).

application/pdf

Un fișier; fără exemplu afișat.

Erori
StareCândmessage
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.

Exemple de cod

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)

Exportul problemelor pentru dezvoltatori

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

Parametri

NumeUndeTipDescriere
scan(obligatoriu) 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. Valori: json, csv, github, jira, pdf Implicit: json
lang query string Report language; the site's report language when left out. Valori: en, uk, de, pl, cs, ro, ru

Răspuns

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

Un fișier; fără exemplu afișat.

Erori
StareCândmessage
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.

Exemple de cod

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)

Comparați două verificări

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

Parametri

NumeUndeTipDescriere
scan(obligatoriu) path string The check's id.
other(obligatoriu) 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. Valori: en, uk, de, pl, cs, ro, ru

Răspuns

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": []
                }
            }
        ]
    }
}
Erori
StareCândmessage
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.

Exemple de cod

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

O captură de ecran din raport

GET /scans/{scan}/evidence/{kind}/{id} Dreptul tokenului: read

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

Parametri

NumeUndeTipDescriere
scan(obligatoriu) path string The check's id.
kind(obligatoriu) path string page for a page screenshot, issue for the screenshot of one element. Valori: page, issue
id(obligatoriu) path integer The number from the report (examples[].id for an element).

Răspuns

200 The screenshot.

image/jpeg

Un fișier; fără exemplu afișat.

Erori
StareCândmessage
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.

Exemple de cod

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)

Ciorna ACR ca JSON

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

Parametri

NumeUndeTipDescriere
scan(obligatoriu) path string The check's id.
lang query string Report language; the site's report language when left out. Valori: en, uk, de, pl, cs, ro, ru

Răspuns

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"
        }
    }
}
Erori
StareCândmessage
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.

Exemple de cod

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

Completați un criteriu ACR

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

Parametri

NumeUndeTipDescriere
scan(obligatoriu) path string The check's id.
criterion(obligatoriu) 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. Valori: en, uk, de, pl, cs, ro, ru

Conținutul cererii application/json

CâmpTipDescriere
conformance string | null Valori: supports, partially_supports, does_not_support, not_applicable, not_evaluated
remarks string | null (max. 5000)

Exemplu

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

Răspuns

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."
    }
}
Erori
StareCândmessage
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.

Exemple de cod

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

Descărcați ciorna ACR

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

Parametri

NumeUndeTipDescriere
scan(obligatoriu) path string The check's id.
format query string pdf (default) or html, a single file that opens in any browser. Valori: pdf, html Implicit: pdf
lang query string Report language; the site's report language when left out. Valori: en, uk, de, pl, cs, ro, ru

Răspuns

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

application/pdf

Un fișier; fără exemplu afișat.

text/html

Un fișier; fără exemplu afișat.

Erori
StareCândmessage
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.

Exemple de cod

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)