AFR.IM · API v1

API documentation

Every read endpoint on AFR.IM is public: no API key, no authentication, and Access-Control-Allow-Origin: * so browsers can call it from any origin. Responses are JSON snapshots of the same data this site renders; ratings are computed from public, reproducible evidence — see the AFR standard.

Base URL

https://afr.im

Endpoints

GET /api/v1/sites

Paginated directory listing. Natural results never include Pending (unratable) sites.

category · grade · q · sort (score | rank | new) · page

{
  "items": [ { "domain": "github.com", "grade": "A", "score_total": 91,
               "strategy_tag": "AI-Open", "verified": true, "scanned_at": "2026-11-10T11:40:00.000Z" } ],
  "page": 1, "per_page": 24, "total": 8, "sort": "score"
}
GET /api/v1/sites/:domain

One site with its latest scan and the full evidence chain of that scan.

{
  "site":  { "domain": "github.com", "grade": "A", "score_total": 91 },
  "scan":  { "engine_version": "afr-1.1.0", "score_l1": 33, "score_l2": 38, "score_l3": 20 },
  "evidence": [ { "check_id": "l1.robots.ai-policy", "status": "pass",
                  "points": 25, "max_points": 25, "evidence_ref": "GET /robots.txt" } ]
}
GET /api/v1/sites/:domain/history

All scans for a domain, newest first — feeds the score history chart.

GET /api/v1/stats

Totals, verified count, Pending count, average score, and grade distribution.

{
  "total": 8,
  "verified": 4,
  "pending": 0,
  "avg_score": 58,
  "grades": { "A": 1, "B": 2, "C": 2, "D": 2, "F": 1 }
}
GET /api/v1/categories

The category tree with per-category counts of rated sites.

GET /api/v1/leaderboard

Top sites sorted by pure AFR score descending. Sponsored placements are never mixed in.

category · limit (≤ 50)

{
  "category": null,
  "items": [
    { "rank": 1, "domain": "github.com", "title": "GitHub · Where software is built",
      "grade": "A", "score": 91, "category": "dev-tools", "strategy_tag": "AI-Open" }
  ]
}
GET /api/v1/method

The machine-readable AFR standard: layers, every sub-check with its points, and grade thresholds.

GET /badge/:domain.svg

Rating badge image (SVG) for a domain — ring, grade letter, score. Cached one hour; unknown domains get a neutral gray badge.

POST /api/v1/submissions

Request a scan of a site. Body: { "domain": "example.com", "note": "optional" }. Limited to 5 per IP per day.

// POST {"domain": "example.com", "note": "optional"}
{ "ok": true, "id": 1, "domain": "example.com", "status": "queued" }

Errors

Errors are JSON objects with an error code. 400 for invalid parameters, 404 for unknown domains, 429 for rate-limited submissions. When the network is down, the service worker answers /api/* requests with 503 { "error": "offline" } and a friendly message.

{ "error": "not_found", "domain": "unknown.example" }

Rate limits

GET endpoints are free of keys and limits beyond normal politeness — identify yourself, keep to about one request per second, and cache responses (see /agents.md). POST /api/v1/submissions allows 5 requests per IP per day. Badge images are cached upstream for one hour.

Machine-readable formats

Read the AFR standard → What our own scanner fetches →