Auditaar

API reference

Order audits, poll status, receive webhooks, and pull findings and links to the report files as JSON. REST over HTTPS, JSON in and out. Available on the partner plans; create keys in your console.

Base URL and authentication

Every endpoint lives under https://auditaar.com/api/v1. Send your key as a bearer token. A key is shown once at creation and stored only as a hash, so if you lose it, revoke it and make another.

curl https://auditaar.com/api/v1/audits \
  -H "Authorization: Bearer ak_live_8f2c9d1a.xxxxxxxx"

Sandbox keys

A sandbox key returns a finished audit immediately. It creates no row, crawls nothing and charges nothing, so you can build and test the whole integration before spending anything. Sandbox responses carry "sandbox": true and the fixed id aud_sandbox. They return no PPTX or CSV: there is no file behind them, and returning a fake one would let a bug reach production undetected. A read of any other id returns 404, and no webhook fires in sandbox.

Errors

Every error has the same shape. Switch on code, not on the message.

{ "error": {
    "code": "insufficient_credit",
    "message": "This order costs $499.00 and the account has $0.00. Top up in the portal, then retry.",
    "docs": "https://auditaar.com/docs/api" } }

401 unauthorized missing, wrong or revoked key. 402 insufficient_credit the wallet cannot cover the order. 404 not_found no such audit on this account (ids from other accounts are 404, never 403). 409 not_ready the audit has not finished. 429 rate_limited too many requests for the key (see Rate limits).

Rate limits

Up to 120 requests per minute per API key, a fixed window. Over it, a call returns 429 rate_limited with a Retry-After header in seconds and X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (unix seconds). The limit is per key, so one busy key never starves another on the same account.

Paying for audits

The API spends from your wallet balance rather than opening a payment page, so an integration never has to put a checkout in front of your own client. Top up in the portal; the API debits per order and returns 402 naming the shortfall when it cannot.

Endpoints

POST/audits

Order an audit. Returns the created audit with status queued.

POST /audits
{ "url": "https://client-site.com",
  "tier": "audit",
  "competitors": ["rival.com"],
  "theme_ref": "thm_8f2c",
  "theme_hex": "#D62828",
  "share": true,
  "webhook_url": "https://youragency.com/hooks/auditaar" }
  • tier: one of audit.
  • competitors: up to 3 domains, for the head-to-head grid.
  • theme_ref: a brand kit id, or use theme_hex.
  • theme_hex: exact brand color, beats detection.
  • share: mint a shareable link. Defaults to false; a share link opens the report to anyone who signs in to a free account, so turning it on is your call.
  • webhook_url: must be https.

POST/audits/bulk

Order up to 50 audits in one call. All-or-nothing: if any order is invalid the whole request is a 400 that creates nothing and lists the offending indexes, and the batch total is charged against the wallet atomically, so a partial charge is never left for you to reconcile. If the wallet cannot cover the batch total, the whole call returns a 402 and creates nothing. Each order takes the same fields as a single POST /audits.

POST /audits/bulk
{ "orders": [
  { "url": "https://client-one.com", "tier": "audit" },
  { "url": "https://client-two.com", "tier": "audit",
    "webhook_url": "https://youragency.com/hooks/auditaar" }
] }

{ "object": "list", "count": 2,
  "data": [ { "id": "aud_...", "status": "queued", ... }, ... ] }

GET/audits/{id}

Live status, stage and ETA, the same data the portal shows. eta_seconds is null until Auditaar can estimate the finish, and score is null until the audit completes. This is always authoritative; webhooks are a convenience on top of it.

{ "id": "aud_8f2c9d1a4b6e",
  "object": "audit",
  "status": "running",
  "stage": "ai_surface_sampling",
  "stage_label": "Sampling AI surfaces",
  "percent": 62,
  "eta_seconds": 540,
  "score": null }

GET/audits

Your audits, newest first. ?limit= up to 100.

GET/audits/{id}/artifacts

Findings inline, files as links. 409 until the audit is completed. Findings are read from the same stored facts the report is built from, so the two cannot disagree. deck_url and csv_url take the same bearer key; viewer_url opens the hosted report to anyone who signs in to a free account, and the links do not expire.

{ "audit_id": "aud_8f2c9d1a4b6e",
  "deck_url": "https://auditaar.com/api/v1/audits/aud_8f2c9d1a4b6e/deck",
  "csv_url":  "https://auditaar.com/api/v1/audits/aud_8f2c9d1a4b6e/deck?format=csv",
  "viewer_url": "https://auditaar.com/d/c8aedacbaf1542f19734",
  "score": 62,
  "findings": [ { "id": "missing-canonical", "module": "Technical",
                  "severity": "critical", "count": 1,
                  "title": "1 page without a canonical tag",
                  "detail": "...", "fix": "...",
                  "effort": "low", "priority": "critical" } ],
  "quick_wins": [ ... ] }

GET/audits/{id}/deck

The PowerPoint. ?format=csv for the findings spreadsheet. Same bearer key as everything else.

POST/themes

Register a client’s brand color once and pass the returned id as theme_ref on every audit for that client. GET /themes lists them. A saved color sets the report’s accent and leads the cover with the kit name; upload a template (POST /themes with a .pptx) to also apply its fonts and logo. The Auditaar footer credit stays; removing it is a manual edit.

POST /themes
{ "name": "Northwind Ltd", "hex": "#D62828" }

{ "id": "thm_8f2c9d1a", "object": "brand_kit",
  "name": "Northwind Ltd", "hex": "D62828" }

Webhooks

Pass webhook_url on an order and Auditaar POSTs to it on every state change: audit.running, audit.completed and audit.failed. The first attempt is immediate; if your endpoint is down, Auditaar retries up to twelve more times on a backoff that starts at 60 seconds and doubles to a six-hour cap, about a day in all, from a durable outbox queue before giving up, and every attempt is recorded on its side. The event id is stable across retries, so you can treat it as an idempotency key.

POST https://youragency.com/hooks/auditaar
auditaar-timestamp: 1780000000
auditaar-signature: 9f86d081884c7d65...

{ "id": "evt_4b6e8f2c9d1a",
  "type": "audit.completed",
  "created": 1780000000,
  "data": { ...the same object as GET /audits/{id} } }

The signature is HMAC-SHA256(webhook_secret, timestamp + "." + rawBody). Your webhook secret is in the console next to the key. Verify it against the raw body before parsing, and reject anything with a timestamp older than five minutes.

import { createHmac, timingSafeEqual } from "node:crypto";

const expected = createHmac("sha256", process.env.AUDITAAR_WEBHOOK_SECRET)
  .update(req.headers["auditaar-timestamp"] + "." + rawBody)
  .digest("hex");

const sig = req.headers["auditaar-signature"];
if (sig.length !== expected.length ||
    !timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
  return res.status(400).end();
}

Good to know

The rate limit is counted in memory on a single instance, so it resets on a deploy; design against the published number rather than probing for it. Auditaar publishes no latency SLA on completion: a full audit is minutes, not seconds, and depends on the site, so a webhook or a poll of GET /audits/{id}, which is always authoritative, is the way to know it is done. If anything here blocks you, email Auditaar at info@auditaar.com and Auditaar will look into it. Do not include your API key in the message.