Outrings
AI agents and automation

How do I audit hundreds of websites at once?

Running a portfolio, a client list or a research sample through a measurement API without abusing anyone — including the sites you are measuring.

4 min read
Short answer

Loop over the list, request the smallest response that answers your question, pace the requests, and store the raw JSON rather than a summary. The constraint that matters is not the API rate limit — it is that each audit sends dozens of requests to the target site, so an unpaced loop is a load test somebody did not ask for.

Ask for the smallest thing that answers the question

Scope first. Across hundreds of sites the difference between endpoints is the difference between a script that finishes and one that does not.

If you needCallPer site
A single number per site/api/v1/score~1.7 KB
One area in depth/api/v1/security~2–8 KB
Scores plus what to fix/api/v1/summary~8.5 KB
Everything, for later analysis/api/v1/full~124 KB

Five hundred sites at full is around sixty megabytes and a great deal of work for both ends. The same five hundred at score is under a megabyte.

A workable script

#!/usr/bin/env bash
set -uo pipefail

mkdir -p out

while read -r site; do
  [ -z "$site" ] && continue
  safe="${site//\//_}"

  # Skip anything already fetched, so a rerun resumes instead of starting over.
  [ -s "out/$safe.json" ] && continue

  if curl -sf --max-time 45 \
       "https://outrings.com/api/v1/summary?url=$site" \
       -o "out/$safe.json"; then
    printf '%-30s %s\n' "$site" "$(jq -r '.score' "out/$safe.json")"
  else
    printf '%-30s FAILED\n' "$site" >&2
    rm -f "out/$safe.json"
  fi

  sleep 5
done < sites.txt

The two details that matter most are the resume check and the sleep. Long runs get interrupted, and restarting from zero wastes everyone's bandwidth.

Pacing and limits

  • Rate limit: 40 audits per hour, 200 per day per client. Exceeding it returns 429 with Retry-After, which your script should honour rather than retry through.
  • Cached results are free. Repeating a URL within ten minutes does not consume quota, so a resumed run costs nothing for what it already fetched.
  • A large portfolio takes days, by design. Two hundred a day is the ceiling. Plan for a rolling schedule rather than a single sweep.
  • Pace for the targets, not for the API. Each audit issues dozens of requests to the site being measured. A five-second gap is courteous; no gap at all is not.
If you are measuring sites you do not own, pacing is an ethical point rather than a technical one. Your research sample is somebody's production infrastructure, and they did not opt in to being surveyed.

Store the raw JSON

Write the complete response to disk, one file per site, before extracting anything. Analysis questions change; refetching hundreds of sites because you decided you also wanted the certificate expiry is avoidable and slow.

Once collected, aggregate locally:

# Score distribution across the sample
jq -s '[.[] | .score] | {
  n: length,
  mean: (add / length),
  min: min,
  max: max
}' out/*.json

# The twenty most common failing checks
jq -r '.problems[]?.id' out/*.json | sort | uniq -c | sort -rn | head -20

Interpreting a portfolio

Two habits keep large-sample conclusions honest. First, check how many sites returned undetermined checks — a cohort behind aggressive bot protection will look systematically different for reasons that have nothing to do with quality. Second, confirm the ruleset version is identical across the run; a sweep spanning a ruleset update is comparing two different measurements.

What our audit reports about this

Every item below is measured directly, not inferred. Run it against your own site and the result names the exact rule or header responsible.

  • Scoped endpoints so a bulk run can request kilobytes per site instead of the full report.
  • Clear 429 responses with Retry-After, so a script can pace itself correctly rather than guessing.
  • Ten-minute result caching, so resumed runs do not re-consume quota for work already done.
  • Version stamps on every response, so a sweep spanning a ruleset change is detectable rather than silently inconsistent.

For agents and scripts, the same measurement is at /api/v1/summary?url=yoursite.com — see the API documentation.

Related questions

Can I request a higher rate limit?

The published limits are what shared infrastructure can sustain while staying available to everyone. For a large ongoing portfolio, a rolling daily schedule within the limit is the practical approach.

Should I run requests in parallel?

Modestly at most. Parallelism multiplies the load on the sites being measured, not just on the API, and the rate limit caps throughput regardless — so the main effect is a burst of pressure on other people's origins.

How do I handle sites that fail?

Log them and continue. Common causes are unreachable hosts, aggressive bot protection returning challenges, and DNS that no longer resolves — all worth recording as findings in their own right for a portfolio review.

Is there a bulk endpoint?

No. One URL per request keeps rate limiting, caching and error attribution honest — a partial failure inside a batch is much harder to report accurately than a failure of a single call.

Read next

All 100 guides · How every check works · API for agents