{
  "name": "xmr.club directory API",
  "license": "CC-BY-4.0 — attribute \"xmr.club\"",
  "cors": "open on read endpoints",
  "endpoints": {
    "GET /data.json": {
      "description": "Full provider dump, every active listing. Cached 5 min.",
      "sample": "/data.json"
    },
    "GET /tree.txt": {
      "description": "Plain-text tree (category → subcategory → provider). curl-friendly.",
      "sample": "/tree.txt"
    },
    "GET /feed.xml": {
      "description": "Atom feed of the 30 most-recently-verified entries.",
      "sample": "/feed.xml"
    },
    "GET /feed.json": {
      "description": "JSON Feed v1.1 — same 30 entries as /feed.xml but easier to parse.",
      "sample": "/feed.json"
    },
    "GET /feed/tag/:tag.json": {
      "description": "JSON Feed v1.1 filtered to one kyc / feature / highlight tag. Tag slug is the underscore-lowercase form (e.g. `anonymous_signup`, `tor_supported`).",
      "sample": "/feed/tag/anonymous_signup.json"
    },
    "GET /sitemap.xml": {
      "description": "Sitemap of every category + provider + static page.",
      "sample": "/sitemap.xml"
    },
    "GET /api/content/:category": {
      "description": "Single category as JSON (title + intro + providers[]).",
      "sample": "/api/content/vpns"
    },
    "GET /api/v1/providers": {
      "description": "Filterable, paginated provider listing. Params: category, grade, tag, limit (≤200), offset. CORS, cached 5min.",
      "sample": "/api/v1/providers?category=vpns&grade=A&limit=10"
    },
    "GET /api/v1/categories": {
      "description": "All categories with active-provider counts + intro. CORS, cached 10min.",
      "sample": "/api/v1/categories"
    },
    "GET /api/v1/tags": {
      "description": "Tag → count map across kyc/feature/highlight tags. CORS, cached 10min.",
      "sample": "/api/v1/tags"
    },
    "GET /api/v1/search": {
      "description": "Substring search across name/tagline/review/id/subcategory. Params: q (≥2 chars), limit (≤50). Scored: name match > tagline > review.",
      "sample": "/api/v1/search?q=monero"
    },
    "GET /api/v1/home": {
      "description": "Home page data — { stats, catCounts }. Same shape SSR injects into __INITIAL_DATA__ so SPA navigations match.",
      "sample": "/api/v1/home"
    },
    "GET /api/v1/picks": {
      "description": "Editor's picks — providers with editors_pick:true. CORS, cached 10min.",
      "sample": "/api/v1/picks"
    },
    "GET /api/v1/onion": {
      "description": "Tor mirrors — every provider in the directory with a .onion service set. CORS, cached 10min.",
      "sample": "/api/v1/onion"
    },
    "GET /api/v1/onion-audit": {
      "description": "Drift report between listed `tor` field and the operator's current Onion-Location header. Buckets: verified, mismatch, missing, unverified. Refreshed by the daily uptime cron.",
      "sample": "/api/v1/onion-audit"
    },
    "GET /api/v1/freshness": {
      "description": "Curator-facing staleness report — providers with `last_verified` older than ?days=N (default 30) or no value at all.",
      "sample": "/api/v1/freshness?days=30"
    },
    "GET /api/v1/changes": {
      "description": "Recent provider-level audit entries grouped by action (created/updated/deleted). Default window ?days=14.",
      "sample": "/api/v1/changes?days=14"
    },
    "GET /api/v1/audit": {
      "description": "Public read-only audit log — last <=200 curator actions (create / update / sponsorship / review). Actor + raw details hidden, summary only.",
      "sample": "/api/v1/audit?limit=20"
    },
    "GET /api/v1/stats": {
      "description": "Everything an external dashboard / social-cron needs: totals, by-grade, by-category, onions, picks, probe + sponsor counts.",
      "sample": "/api/v1/stats"
    },
    "GET /api/v1/guides": {
      "description": "List of every curator-written explainer guide. JSON includes slug, title, description, canonical URL, markdown-twin URL, and picks (with provider links). Cached 1h.",
      "sample": "/api/v1/guides"
    },
    "GET /api/v1/guides/:slug": {
      "description": "Single guide as JSON: intro, plain-text body, HTML body, picks, canonical URL, markdown-twin URL. Use for partner reposts / embedded readers. Cached 1h.",
      "sample": "/api/v1/guides/tor-for-crypto-safely"
    },
    "GET /api/v1/glossary": {
      "description": "Canonical taxonomy as JSON: grade definitions, KYC postures, feature tags. The terms behind every chip on the site. Backs Schema.org DefinedTermSet at /glossary.",
      "sample": "/api/v1/glossary"
    },
    "GET /badge/<provider-id>.svg": {
      "description": "Embeddable \"Listed on xmr.club\" SVG badge providers can put on their own site. Grade-colored, includes ★ PICK marker. CORS, cached 1h.",
      "sample": "/badge/mullvad.svg"
    },
    "GET /embed/<category>": {
      "description": "Iframe-friendly category widget. Inline CSS, no script bundle. Drop on partner sites via <iframe src=\"/embed/vpns\" width=\"400\" height=\"600\">. Cached 10min.",
      "sample": "/embed/vpns"
    },
    "GET /api/probe/state": {
      "description": "Latest uptime probe row per provider. Cron runs daily 06:00 UTC.",
      "sample": "/api/probe/state"
    },
    "GET /api/sponsor/state": {
      "description": "Current sponsor slot availability + waitlist + recent sales.",
      "sample": "/api/sponsor/state"
    },
    "POST /api/sponsor/waitlist": {
      "description": "Join the sponsor waitlist. JSON body: {tier, category, display_name, website, contact}."
    },
    "POST /api/submit": {
      "description": "Submit a candidate listing. Accepts JSON or form-urlencoded body: {name, url, category, proposed_kyc, body, submitter}. Form-encoded posts 303-redirect to /thanks."
    },
    "POST /api/review": {
      "description": "Submit a public review for a provider. Accepts JSON or form-urlencoded body: {provider_id, category, body, display?, rating?}. Moderated before publication."
    },
    "GET /go/:slug": {
      "description": "Outbound redirect with affiliate-ref injected. Click-logged. 302.",
      "sample": "/go/mullvad"
    }
  }
}