Overview
UX, IA, A11y, ConversionVertaaUX gives you programmatic audits across usability, information architecture, accessibility, and conversion signals. The API is fully asynchronous: start an audit, receive a job_id, then poll or subscribe to a webhook for completion. All examples below use the public base URL https://vertaaux.ai/api/v1.
What you get
- Scores per dimension (usability, clarity, IA, a11y, performance).
- Issue list with severity, selector, and recommendations.
- Heatmap prediction and optional HTML snapshot (if enabled).
Typical flow
- Send
POST /auditwith your target URL. - Poll
GET /audit/{job_id}until completed. - Store scores/issues in your system or wait for webhook delivery.
Authentication
Authenticate every request with an API key using the X-API-Key header. Keep the key in server-side environments or CI secrets; do not expose it to browsers or client-side code.
Test / Staging
Use staging host https://staging-api.vertaaux.ai/v1 and test keys where available. Expect lower quotas and sandboxed results.
See docs/TEST-MODE.md for SDK/CLI overrides, fixtures, and webhook tips.
Get your API key
Create and manage your API keys in the Dashboard → API Keys section. You'll need to be signed in to access this page.
Header format
X-API-Key: your_api_key_here
Requests without a valid key return 401 Unauthorized.
SDKs & Clients
Official SDK scaffolds are available for JS/TS and Python. They include retries, idempotency helpers, webhook verification, and typed responses. Base URL defaults to production; override for staging/test mode.
Copy-paste starter (JS/TS)
import { createVertaauxClient } from "@vertaaux/sdk-js";
const client = createVertaauxClient({ apiKey: process.env.VERTAAUX_API_KEY! });
const audit = await client.createAudit({ url: "https://example.com", mode: "basic" });
const status = await client.getAudit(audit.job_id);JS/TS
pnpm add ./sdk/jsimport { createVertaauxClient, generateIdempotencyKey } from "@vertaaux/sdk-js";
const client = createVertaauxClient({
apiKey: process.env.VERTAAUX_API_KEY!,
baseUrl: "https://api.vertaaux.ai/v1", // or staging host
idempotencyKey: generateIdempotencyKey(),
});
const audit = await client.createAudit({ url: "https://example.com", mode: "basic" });
const status = await client.getAudit(audit.job_id);See docs/SDK-QUICKSTART.md and sdk/js/README.md for publish steps.
Python
pip install -e ./sdk/pythonfrom vertaaux_api_client import AuthenticatedClient
from vertaaux_api_client.api.audits import create_audit, get_audit
from vertaaux_api_client.models import AuditRequest
client = AuthenticatedClient(
base_url="https://api.vertaaux.ai/v1",
token="YOUR_API_KEY",
)
resp = create_audit.sync(client=client, json_body=AuditRequest(url="https://example.com", mode="basic"))
status = get_audit.sync(client=client, job_id=resp.job_id)Generated from OpenAPI; update sdk/python/pyproject.toml before publishing.
Making Your First Request
- Set your API key in a secure env variable.
- POST to
/auditwith a fully qualified URL. - Save the returned
job_id. - Poll for status or subscribe via webhook.
curl -X POST https://vertaaux.ai/api/v1/audit \
-H "X-API-Key: $VERTAA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"mode": "standard"
}'const response = await fetch("https://vertaaux.ai/api/v1/audit", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": process.env.VERTAA_API_KEY!,
},
body: JSON.stringify({ url: "https://example.com", mode: "standard" }),
});
const { job_id } = await response.json();import os, requests
api_key = os.environ["VERTAA_API_KEY"]
payload = {"url": "https://example.com", "mode": "standard"}
res = requests.post(
"https://vertaaux.ai/api/v1/audit",
headers={"X-API-Key": api_key, "Content-Type": "application/json"},
json=payload,
)
print(res.json()["job_id"])Poll until complete
curl -X GET https://vertaaux.ai/api/v1/audit/{job_id} \
-H "X-API-Key: $VERTAA_API_KEY"const res = await fetch(`https://vertaaux.ai/api/v1/audit/${jobId}`, {
headers: { "X-API-Key": process.env.VERTAA_API_KEY! },
});
const audit = await res.json();
if (audit.status === "completed") {
console.log(audit.scores, audit.issues?.length);
}Audit Flows
Use the same endpoint for different targets. Choose mode to control depth: basic,standard, or deep.
Web (single-page)
- Pass the page URL in the body.
- Use
mode: basicfor faster screening ordeepfor thorough coverage. - Store the
job_idto retrieve scores and issues later.
curl -X POST https://vertaaux.ai/api/v1/audit \
-H "X-API-Key: $VERTAA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://shop.example.com/checkout",
"mode": "deep"
}'Multi-page runs
- Send one
POST /auditper URL and aggregate results on your side. - Throttle concurrency to respect rate limits and avoid 429 responses.
- Use webhooks to reduce polling load when running larger batches.
const urls = [
"https://example.com/",
"https://example.com/pricing",
"https://example.com/checkout",
];
const results = [];
for (const url of urls) {
const res = await fetch("https://vertaaux.ai/api/v1/audit", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": process.env.VERTAA_API_KEY!,
},
body: JSON.stringify({ url, mode: "standard" }),
});
results.push(await res.json());
}
console.log(results.map((r) => r.job_id));Figma designs
- Provide a Figma file or prototype share link as the
url. - Ensure the link is accessible to the audit service (set to “can view” or use a secure shared link).
- Results return the same structure: scores, issues, and optional heatmap predictions.
curl -X POST https://vertaaux.ai/api/v1/audit \
-H "X-API-Key: $VERTAA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://www.figma.com/proto/your-shared-link",
"mode": "standard"
}'Responses & Error Codes
The audit status endpoint returns the full payload once processing is complete. Common HTTP responses are listed below.
Sample completed audit
{
"job_id": "audit_01HF2YAJDQZ98X2M2YB1T5R9SJ",
"status": "completed",
"progress": 100,
"scores": {
"usability": 92,
"clarity": 88,
"ia": 86,
"accessibility": 90,
"performance": 82
},
"issues": [
{
"id": "iss_001",
"severity": "high",
"category": "accessibility",
"description": "Button lacks accessible label",
"recommendation": "Add aria-label or text content.",
"selector": "button.primary",
"wcag_reference": "2.5.3"
}
],
"heatmap_prediction": "https://cdn.example.com/heatmaps/audit_01HF2YA.png",
"version": "1.0.0"
}CI/CD gating: send fail_on_score or fail_on_drop with POST /audit. Responses include gating.failed and gating.reason when thresholds are crossed.
Success codes
202 Accepted— audit queued; returnsjob_id.200 OK— current audit status or completed result.204 No Content— webhook deleted.
Error codes
400 Bad Request— malformed body (e.g., missing URL).401 Unauthorized— missing or invalid API key.404 Not Found— unknownjob_id.429 Too Many Requests— rate limit exceeded.500— transient error; retry with backoff.
Error format
{
"error": "Validation error",
"message": "Quota exceeded",
"details": [...]
}On 429, use Retry-After and backoff; SDK retries handle this by default. Responses include X-API-Version and ruleset_version for traceability.
- Reduce concurrency or move to webhooks to avoid heavy polling.
- Check
GET /quotabefore large batches.
Pagination
Issue payloads support server-side filtering and pagination to avoid oversized responses. Use page and per_page on GET /audit/{job_id} to page through issues. Filters:severity=error|warning|info and fields=severity,selector,category to trim the payload. Responses include total and total_pages.
Streaming is available for large sets: GET /audit/{job_id}/issues/stream returns NDJSON so you can process issues incrementally.
Webhooks
Receive a POST callback when an audit finishes. Register a target URL and optional signing secret using POST /webhooks.
Delivery logs & replay
- List deliveries:
GET /api/webhooks/logs(filter bywebhook_id). - Replay a delivery:
POST /api/webhooks/logswithdelivery_id.
Signature verification
// Node/TS
import { verifyWebhookSignature } from "@vertaaux/sdk-js";
const isValid = await verifyWebhookSignature({
secret: process.env.VERTAAUX_WEBHOOK_SECRET!,
payload: rawBodyString,
signature: req.headers["x-vertaaux-signature"] as string,
timestamp: req.headers["x-vertaaux-signature-timestamp"] as string,
toleranceSeconds: 300,
});# Python
import hmac, hashlib, time
def verify(secret: str, payload: str, signature: str, timestamp: str, tolerance_seconds: int = 300) -> bool:
try:
ts_int = int(timestamp)
except (TypeError, ValueError):
return False
if abs(time.time() - ts_int / 1000) > tolerance_seconds:
return False
digest = hmac.new(secret.encode(), f"{timestamp}.{payload}".encode(), hashlib.sha256).hexdigest()
if len(digest) != len(signature):
return False
return hmac.compare_digest(digest, signature)Webhooks include X-Vertaaux-Signature and X-Vertaaux-Signature-Timestamp. Signatures are HMAC-SHA256 overtimestamp.payload (millisecond timestamp). Reject stale requests (>5 minutes).
curl -X POST https://vertaaux.ai/api/v1/webhooks \
-H "X-API-Key: $VERTAA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/vertaa",
"secret": "your-shared-secret"
}'import crypto from "crypto";
import { NextRequest, NextResponse } from "next/server";
const secret = process.env.VERTAAUX_WEBHOOK_SECRET!;
export async function POST(req: NextRequest) {
const rawBody = await req.text();
const signature = req.headers.get("x-vertaaux-signature") ?? "";
const timestamp = req.headers.get("x-vertaaux-signature-timestamp") ?? "";
if (!timestamp || Math.abs(Date.now() - Number(timestamp)) > 5 * 60 * 1000) {
return NextResponse.json({ ok: false, reason: "stale" }, { status: 400 });
}
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
if (signature !== expected) return NextResponse.json({ ok: false }, { status: 401 });
const payload = JSON.parse(rawBody);
// payload.job_id, payload.status, payload.completed_at, payload.scores
return NextResponse.json({ received: true });
}Webhook expectations
- Payload includes
job_id,status, timestamps, scores, and issues if completed. - Return any
2xxstatus to acknowledge delivery. - Use your shared secret to verify authenticity before processing.
- Delivery attempts: 6 retries over ~24h with exponential backoff; signature headers include
X-Vertaaux-SignatureandX-Vertaaux-Signature-Timestamp. Reject stale requests (>5 minutes).
Rate Limiting
The API enforces per-account limits to protect stability. Typical caps: Free 5 audits/day, Pro ~33/day (1000/month), Business 5000/month. Exceeding limits returns 429 Too Many Requests with X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, and Retry-After headers.
- Distribute batch runs over time or use webhooks instead of rapid polling.
- Retry after the reset timestamp provided in the response headers.
- For sustained throughput needs, contact support for higher limits.
See docs/RATE-LIMITS.md for bucket behavior and Postman examples.
SDK Examples
Use your preferred HTTP client; the API is language-agnostic. These snippets show minimal wrappers for common tasks.
import axios from "axios";
const client = axios.create({
baseURL: "https://vertaaux.ai/api/v1",
headers: { "X-API-Key": process.env.VERTAA_API_KEY! },
});
export async function runAudit(url: string) {
const { data } = await client.post("/audit", { url, mode: "basic" });
return data.job_id;
}import requests, os
BASE_URL = "https://vertaaux.ai/api/v1"
HEADERS = {"X-API-Key": os.environ["VERTAA_API_KEY"]}
def get_quota():
res = requests.get(f"{BASE_URL}/quota", headers=HEADERS, timeout=10)
res.raise_for_status()
return res.json()
def delete_webhook(webhook_id: str):
res = requests.delete(f"{BASE_URL}/webhooks/{webhook_id}", headers=HEADERS)
return res.status_code == 204Troubleshooting
Common issues
- 401 Unauthorized: confirm
X-API-Keyheader is present and correct. - 404 Not Found: verify the
job_idexists and belongs to your account. - 429 Too Many Requests: reduce concurrency and retry after the reset time.
- Timeouts: use
mode: basicfor faster responses or increase your client timeout.
Support checklist
- Include
job_idand timestamp when contacting support. - Share the HTTP status code and response body you received.
- Redact secrets before sending logs or examples.