{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://api.youraitools.co/stats/v1/schema.json",
  "title": "YourAITools public per-tool stats",
  "description": "Schema version 1.0. GET https://api.youraitools.co/stats/v1/tools returns the list document; GET https://api.youraitools.co/stats/v1/tools/<slug> returns the same envelope with \"tool\" instead of \"tools\". No IPs, payer addresses or request contents are collected. Counting started at data_since; windows that start earlier only contain data from data_since on.",
  "type": "object",
  "required": [
    "schema_version",
    "schema",
    "generated_at",
    "data_since",
    "probe",
    "notes"
  ],
  "properties": {
    "schema_version": {
      "const": "1.0"
    },
    "schema": {
      "type": "string",
      "format": "uri"
    },
    "generated_at": {
      "type": "string",
      "format": "date-time"
    },
    "data_since": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time"
    },
    "probe": {
      "type": "object",
      "properties": {
        "path": {
          "const": "/health"
        },
        "interval_s": {
          "const": 600
        },
        "timeout_s": {
          "const": 10
        },
        "last_round": {
          "type": "object",
          "description": "The latest probe round: at (time), tools probed, failed, prober_ok (false = nearly every tool failed at once, treated as a prober fault), counted (whether it counted toward uptime and status).",
          "properties": {
            "at": {
              "type": "string",
              "format": "date-time"
            },
            "tools": {
              "type": "integer",
              "minimum": 0
            },
            "failed": {
              "type": "integer",
              "minimum": 0
            },
            "prober_ok": {
              "type": "boolean"
            },
            "counted": {
              "type": "boolean"
            }
          }
        }
      }
    },
    "last_checked_at": {
      "type": "string",
      "format": "date-time",
      "description": "When the latest health probe round ran (same as probe.last_round.at)."
    },
    "notes": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "tools": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/tool"
      }
    },
    "tool": {
      "$ref": "#/$defs/tool"
    }
  },
  "oneOf": [
    {
      "required": [
        "tools"
      ]
    },
    {
      "required": [
        "tool"
      ]
    }
  ],
  "$defs": {
    "tool": {
      "type": "object",
      "required": [
        "slug",
        "name",
        "kind",
        "price",
        "url",
        "stats_url",
        "status",
        "last_probe",
        "last_call_at",
        "windows"
      ],
      "properties": {
        "slug": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "kind": {
          "enum": [
            "tool",
            "service",
            "hub",
            "gateway"
          ],
          "description": "hub = the MCP Hub (api.youraitools.co/mcp/hub); its calls are also counted under the underlying tool. gateway = the Sell-your-API gateway (api.youraitools.co/g/): all proxied third-party listing calls combined; per-listing stats are in the gateway directory."
        },
        "price": {
          "type": "string",
          "description": "Human-readable price list (per call or one-time)."
        },
        "url": {
          "type": "string",
          "format": "uri"
        },
        "stats_url": {
          "type": "string",
          "format": "uri"
        },
        "status": {
          "enum": [
            "up",
            "down",
            "unknown"
          ],
          "description": "up unless the last 2 probe rounds in a row failed (each round retries a failed probe once); unknown if no probe in the last 30 minutes."
        },
        "last_probe": {
          "type": [
            "object",
            "null"
          ],
          "properties": {
            "at": {
              "type": "string",
              "format": "date-time"
            },
            "ok": {
              "type": "boolean"
            },
            "ms": {
              "type": "integer",
              "minimum": 0
            }
          }
        },
        "last_call_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time",
          "description": "Last counted (non-self) call, hour resolution of storage applies to windows only."
        },
        "windows": {
          "type": "object",
          "required": [
            "24h",
            "7d",
            "30d"
          ],
          "description": "24h = last 24 hourly buckets (UTC, current hour included); 7d / 30d = last 7 / 30 UTC days (today included).",
          "properties": {
            "24h": {
              "$ref": "#/$defs/window"
            },
            "7d": {
              "$ref": "#/$defs/window"
            },
            "30d": {
              "$ref": "#/$defs/window"
            }
          }
        }
      }
    },
    "window": {
      "type": "object",
      "required": [
        "from",
        "to",
        "calls",
        "success_rate",
        "latency_ms",
        "uptime",
        "self_calls_excluded"
      ],
      "properties": {
        "from": {
          "type": "string",
          "format": "date-time"
        },
        "to": {
          "type": "string",
          "format": "date-time"
        },
        "calls": {
          "type": "object",
          "description": "API requests through api.youraitools.co (paid routes, free API routes under /v1/, remote MCP) and MCP Hub tools/call requests. Landing pages, docs, openapi, .well-known and health checks are not counted. Self/test traffic is excluded.",
          "required": [
            "total",
            "paid",
            "free_ok",
            "payment_required",
            "client_error",
            "server_error"
          ],
          "properties": {
            "total": {
              "type": "integer",
              "minimum": 0,
              "description": "All counted requests."
            },
            "paid": {
              "type": "integer",
              "minimum": 0,
              "description": "2xx/3xx responses that settled a payment (x402 PAYMENT-RESPONSE / X-PAYMENT-RESPONSE) or debited prepaid credits (X-Credits-Charged)."
            },
            "free_ok": {
              "type": "integer",
              "minimum": 0,
              "description": "2xx/3xx responses without a payment (free tier, previews, MCP free calls, status routes)."
            },
            "payment_required": {
              "type": "integer",
              "minimum": 0,
              "description": "HTTP 402 responses (payment terms; not an error)."
            },
            "client_error": {
              "type": "integer",
              "minimum": 0,
              "description": "Other 4xx (bad input, rate limits, not found)."
            },
            "server_error": {
              "type": "integer",
              "minimum": 0,
              "description": "5xx, or no response from the tool (exception/timeout)."
            }
          }
        },
        "success_rate": {
          "type": [
            "number",
            "null"
          ],
          "minimum": 0,
          "maximum": 1,
          "description": "(total - server_error) / total, 4 decimals; null when total = 0."
        },
        "latency_ms": {
          "type": "object",
          "description": "Time from the proxy forwarding the request to the tool's response headers, for 2xx/3xx responses only. Percentiles are the upper bound of a histogram bucket (10, 25, 50, 75, 100, 150, 200, 300, 500, 750, 1000, 1500, 2000, 3000, 5000, 10000, 20000 ms; 20001 means slower than 20000 ms). null when samples = 0.",
          "required": [
            "p50",
            "p95",
            "p99",
            "avg",
            "samples"
          ],
          "properties": {
            "p50": {
              "type": [
                "number",
                "null"
              ]
            },
            "p95": {
              "type": [
                "number",
                "null"
              ]
            },
            "p99": {
              "type": [
                "number",
                "null"
              ]
            },
            "avg": {
              "type": [
                "number",
                "null"
              ]
            },
            "samples": {
              "type": "integer",
              "minimum": 0
            }
          }
        },
        "uptime": {
          "type": "object",
          "description": "Share of health probes that succeeded. A cron probes each tool's free GET /health every 10 minutes (never a paid route); ok = HTTP 2xx/3xx within 10 s.",
          "required": [
            "pct",
            "probes",
            "failed",
            "avg_probe_ms"
          ],
          "properties": {
            "pct": {
              "type": [
                "number",
                "null"
              ],
              "minimum": 0,
              "maximum": 100,
              "description": "Percent, 2 decimals; null when probes = 0."
            },
            "probes": {
              "type": "integer",
              "minimum": 0
            },
            "failed": {
              "type": "integer",
              "minimum": 0
            },
            "avg_probe_ms": {
              "type": [
                "number",
                "null"
              ]
            }
          }
        },
        "self_calls_excluded": {
          "type": "integer",
          "minimum": 0,
          "description": "Requests identified as our own monitoring/test traffic (x-yat-self header, yat-self / yat-stats-probe user agent, subrequests from our own Cloudflare Workers, known swarm payer wallets) and left out of every other number."
        },
        "featured_purchases": {
          "type": "integer",
          "minimum": 0,
          "description": "Gateway only: paid Featured slot purchases on /g/ (count only)."
        },
        "self_calls_by_source": {
          "type": "object",
          "description": "How the excluded self calls were recognised (sums to self_calls_excluded).",
          "properties": {
            "header": {
              "type": "integer",
              "minimum": 0
            },
            "user_agent": {
              "type": "integer",
              "minimum": 0
            },
            "our_worker": {
              "type": "integer",
              "minimum": 0
            },
            "our_payer": {
              "type": "integer",
              "minimum": 0
            },
            "before_source_tagging": {
              "type": "integer",
              "minimum": 0,
              "description": "Self calls recorded before the source was tagged (2026-10-24)."
            }
          }
        }
      }
    }
  }
}