Developers

AuditZap API

Start audits from your own scripts and pull the results into spreadsheets, dashboards and client reports.

Overview

The API is part of the Agency plan ($79/month). Requests go to https://auditzap.io/api/v1, bodies and responses are JSON, and every response is either { "data": ... } or { "error": "..." }.

Audits you start through the API are ordinary audits: they appear in your dashboard, and they count toward your plan's daily audit allowance, shared with the dashboard.

Authentication

Create a key in Settings, API keys. You can hold up to 5 active keys. We show a key once, when you create it, so store it somewhere safe. Send it on every request:

Authorization: Bearer az_live_your_key_here

Revoking a key stops it immediately. Keys also stop working if your account leaves the Agency plan, and start working again if it comes back.

Create an audit: POST /api/v1/audits

Body: url (required) and maxPages (optional, a whole number of at least 1). Without maxPages the audit crawls up to 250 pages; a larger value is lowered to that cap.

curl -s -X POST https://auditzap.io/api/v1/audits \
  -H "Authorization: Bearer $AUDITZAP_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com", "maxPages": 100}'

The response is 202 Accepted:

{
  "data": {
    "id": "b7c1e0d2-6a8f-4f0e-9a51-2f0c3d9e7a11",
    "status": "queued",
    "url": "https://example.com/",
    "maxPages": 100
  }
}

Poll an audit: GET /api/v1/audits/{id}

status moves from queued to running to complete or failed. Poll every 10 seconds until it is complete or failed. Results, the summary and fix verifications appear once it is complete.

while true; do
  body=$(curl -s https://auditzap.io/api/v1/audits/$AUDIT_ID \
    -H "Authorization: Bearer $AUDITZAP_KEY")
  status=$(echo "$body" | jq -r '.data.status // empty')
  if [ "$status" != "queued" ] && [ "$status" != "running" ]; then echo "$body"; break; fi
  sleep 10
done

Each result carries a stable category (critical, performance, seo, quick_wins, ai_visibility) and a readable categoryLabel. projectedScore is the score with every check you have verified as fixed counted as passing, or null when none are.

{
  "data": {
    "id": "b7c1e0d2-6a8f-4f0e-9a51-2f0c3d9e7a11",
    "url": "https://example.com/",
    "status": "complete",
    "score": 72,
    "pagesAudited": 48,
    "maxPages": 100,
    "crawlScope": "complete",
    "errorMessage": null,
    "source": "api",
    "createdAt": "2026-10-14T09:30:00.000Z",
    "completedAt": "2026-10-14T09:33:12.000Z",
    "summary": "Strong technical base. Two critical issues block indexing.",
    "results": [
      {
        "category": "critical",
        "categoryLabel": "Critical",
        "checkName": "Broken internal links (4xx)",
        "status": "fail",
        "details": "3 internal links return 404.",
        "failingUrls": [
          "https://example.com/old-page"
        ],
        "affectedCount": 3,
        "fixInstruction": "Update or remove the three links that point at deleted pages.",
        "impactScore": 8,
        "unscored": false
      },
      {
        "category": "ai_visibility",
        "categoryLabel": "AI Visibility",
        "checkName": "llms.txt file",
        "status": "warn",
        "details": "No llms.txt file found.",
        "failingUrls": [],
        "affectedCount": null,
        "fixInstruction": "Publish an llms.txt file at the site root.",
        "impactScore": 3,
        "unscored": false
      }
    ],
    "verifications": [],
    "projectedScore": null
  }
}

List audits: GET /api/v1/audits

Newest first. Optional query parameters: url (an exact site URL, written any way you like), status, limit (1 to 50, default 20) and cursor. When more audits exist the response includes nextCursor; pass it back to get the next page. Items have the same fields as a single audit without results, plus issueCount.

curl -s "https://auditzap.io/api/v1/audits?limit=20&status=complete" \
  -H "Authorization: Bearer $AUDITZAP_KEY"

Exports: GET /api/v1/audits/{id}/export?format=csv and GET /api/v1/audits/{id}/pdf

Download a complete audit as CSV (format=csv), plain text (format=txt) or the PDF report with your white-label branding. Exports of an audit that is not complete return 409.

curl -s -o audit.csv "https://auditzap.io/api/v1/audits/$AUDIT_ID/export?format=csv" \
  -H "Authorization: Bearer $AUDITZAP_KEY"
curl -s -o audit.pdf "https://auditzap.io/api/v1/audits/$AUDIT_ID/pdf" \
  -H "Authorization: Bearer $AUDITZAP_KEY"

Usage: GET /api/v1/usage

How much of today's allowance is left, and your crawl cap. If our rate-limit store is briefly unavailable, usedToday reads 0 until it is back.

{
  "data": {
    "plan": "agency",
    "dailyLimit": 250,
    "usedToday": 12,
    "remainingToday": 238,
    "maxPagesPerAudit": 250
  }
}

What an API audit does not do

An API audit does not send the completion email: your script is already polling for the result. It also does not join weekly monitoring, so a bulk run can never push out the sites you watch from the dashboard. To monitor a site weekly, run it once from the dashboard.

Auditing a list of sites

Loop over your list and create one audit at a time. For research across many sites, set maxPages to 100 or fewer: smaller crawls finish sooner and run alongside your other audits. This loop waits out a rate limit using Retry-After and stops when today's allowance is used up.

while read -r url; do
  while true; do
    code=$(curl -s -o response.json -D headers.txt -w '%{http_code}' \
      -X POST https://auditzap.io/api/v1/audits \
      -H "Authorization: Bearer $AUDITZAP_KEY" \
      -H "Content-Type: application/json" \
      -d "{\"url\": \"$url\", \"maxPages\": 100}")
    if [ "$code" != "429" ]; then
      if [ "$code" = "202" ]; then jq -r .data.id response.json >> ids.txt; fi
      cat response.json; echo; break
    fi
    wait=$(grep -i '^retry-after:' headers.txt | tr -dc '0-9')
    if [ -z "$wait" ]; then echo "Daily allowance used up"; exit 0; fi
    sleep "$wait"
  done
done < sites.txt

The loop above saves the id of every audit it creates to ids.txt. To collect the results, page through the list endpoint rather than polling each audit on its own: 50 is the page size, not the size of your run, so the loop follows nextCursor to the last page on every pass, which keeps a large run inside the read limit of 120 requests a minute. The list also holds audits started from the dashboard, so the loop only waits on, and collects, the audits in ids.txt. Once each of those is complete or failed, it fetches one CSV per completed audit.

while true; do
  : > statuses.txt
  cursor=""
  while true; do
    curl -s -G https://auditzap.io/api/v1/audits \
      --data-urlencode "limit=50" \
      ${cursor:+--data-urlencode "cursor=$cursor"} \
      -H "Authorization: Bearer $AUDITZAP_KEY" > page.json
    jq -r '.data.audits[] | "\(.id) \(.status)"' page.json >> statuses.txt
    cursor=$(jq -r '.data.nextCursor // empty' page.json)
    [ -z "$cursor" ] && break
  done
  pending=0
  while read -r id; do
    status=$(awk -v id="$id" '$1 == id { print $2 }' statuses.txt)
    if [ "$status" != "complete" ] && [ "$status" != "failed" ]; then pending=1; fi
  done < ids.txt
  [ "$pending" = "0" ] && break
  sleep 10
done
# then one CSV per completed audit this script created
while read -r id; do
  if awk -v id="$id" '$1 == id && $2 == "complete" { found = 1 } END { exit !found }' statuses.txt; then
    curl -s "https://auditzap.io/api/v1/audits/$id/export?format=csv" \
      -H "Authorization: Bearer $AUDITZAP_KEY" -o "audit-$id.csv"
  fi
done < ids.txt

Rate limits

Each key can create up to 10 audits a minute and make up to 120 read requests a minute. PDF downloads have a lower limit of their own because they take longer to build. Above the per-key limits, every audit also counts toward your plan's daily audit allowance, shared with the dashboard.

Every rate-limited 429 carries Retry-After (seconds). Responses from the per-key limits also send X-RateLimit-Limit and X-RateLimit-Remaining.

Errors

StatusMeaning
400The body or query is invalid, or the URL is not a public website we can audit. The error says which.
401The key is missing, malformed, unknown or revoked. Sent with WWW-Authenticate: Bearer.
403The account is not on the Agency plan, or cannot use the API.
404No audit with that id belongs to this key’s account.
409The audit is not complete yet, so there is nothing to export.
413The request body is larger than 10 KB.
429With Retry-After: a rate limit, wait that many seconds. Without it: today’s audit allowance is used up.
500Something failed on our side. Any audit allowance the request used has been given back.

Managing keys

Create, name and revoke keys in Settings, API keys. The list shows when each key was created and last used. Not on Agency yet? See the plans.