{
  "openapi": "3.1.0",
  "info": {
    "title": "GPU Rental Prices free API",
    "version": "1.0.0",
    "summary": "Daily-verified cloud GPU rental prices ($/GPU-hr) across cloud providers.",
    "description": "Free, no-auth, read-only endpoints for today's verified GPU rental price snapshot. Every price is fetched from a provider source (source_url + fetched_at per row); nothing is typed in by hand. Data updates once per day, so please cache responses rather than polling. Today's snapshot is licensed CC BY 4.0 with a visible link to gpurentalprices.com; the full historical ledger is available under a commercial license (see https://gpurentalprices.com/data).",
    "termsOfService": "https://gpurentalprices.com/data",
    "contact": {
      "name": "GPU Rental Prices data team",
      "email": "data@gpurentalprices.com",
      "url": "https://gpurentalprices.com/data"
    },
    "license": {
      "name": "CC BY 4.0",
      "identifier": "CC-BY-4.0"
    }
  },
  "servers": [
    { "url": "https://gpurentalprices.com" }
  ],
  "paths": {
    "/api/latest.json": {
      "get": {
        "operationId": "getLatestSnapshot",
        "summary": "Today's full price snapshot",
        "description": "The latest daily snapshot: every verified GPU rental offer plus per-provider verification status. A provider marked stale is serving carry-forward data from its last successful fetch (last_verified), never silently. Prerendered daily at build time; no auth required.",
        "responses": {
          "200": {
            "description": "The latest daily snapshot.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Snapshot" }
              }
            }
          }
        }
      }
    },
    "/api/health.json": {
      "get": {
        "operationId": "getHealth",
        "summary": "Dataset freshness and staleness signal",
        "description": "Machine-readable health of the price pipeline: snapshot age, how many providers verified today vs serving stale carry-forward rows, and any GPU-looking SKUs the pipeline saw but has not mapped. status is \"ok\" when every provider verified, \"stale\" otherwise.",
        "responses": {
          "200": {
            "description": "Pipeline health computed from the latest snapshot.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Health" }
              }
            }
          }
        }
      }
    },
    "/gpu-prices.csv": {
      "get": {
        "operationId": "getPricesCsv",
        "summary": "Today's price table as CSV",
        "description": "Today's full offer table as CSV with header row: date,provider,gpu,vram_gb,usd_per_hr,kind,source_url,fetched_at. One row per offer; each row carries its own source URL and fetch timestamp, so per-row freshness is disclosed by construction.",
        "responses": {
          "200": {
            "description": "CSV with columns date, provider, gpu, vram_gb, usd_per_hr, kind, source_url, fetched_at.",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string",
                  "examples": ["date,provider,gpu,vram_gb,usd_per_hr,kind,source_url,fetched_at\n2026-07-18,runpod,h100-sxm,80,2.39,secure,https://www.runpod.io/pricing,2026-07-18T05:21:04.000Z"]
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Offer": {
        "type": "object",
        "description": "One verified GPU rental offer, normalized to price per single GPU per hour.",
        "required": ["provider", "gpu", "vram_gb", "usd_hr", "kind", "source_url", "fetched_at"],
        "properties": {
          "provider": { "type": "string", "description": "Provider slug, e.g. \"runpod\", \"lambda\", \"aws\"." },
          "gpu": { "type": "string", "description": "Canonical GPU id from the site registry, e.g. \"h100-sxm\", \"rtx-4090\"." },
          "vram_gb": { "type": "number", "description": "VRAM of the GPU model in GB." },
          "usd_hr": { "type": "number", "description": "USD per GPU per hour." },
          "kind": {
            "type": "string",
            "enum": ["on-demand", "secure", "community", "spot", "serverless"],
            "description": "Pricing tier. on-demand/secure are firm (fixed-price, non-interruptible); spot/community are interruptible; serverless is per-request capacity with no VM access."
          },
          "source_url": { "type": "string", "format": "uri", "description": "The provider page or API this price was fetched from." },
          "fetched_at": { "type": "string", "format": "date-time", "description": "When this price was fetched from the source (ISO 8601)." },
          "min_gpus": { "type": "integer", "minimum": 1, "description": "Smallest rentable node size for this per-GPU price. Absent means 1 (single GPU rentable)." }
        }
      },
      "ProviderStatus": {
        "type": "object",
        "description": "Per-provider verification status for the snapshot.",
        "required": ["ok", "last_verified", "stale"],
        "properties": {
          "ok": { "type": "boolean", "description": "True when the provider's live fetch succeeded for this snapshot." },
          "last_verified": { "type": "string", "format": "date-time", "description": "ISO timestamp of the last successful fetch." },
          "stale": { "type": "boolean", "description": "True when the provider's rows are carry-forward data from a previous successful fetch, not verified today." },
          "source_url": { "type": "string", "format": "uri", "description": "The provider source attempted for this snapshot." },
          "error": { "type": "string", "description": "Fetch error message, present when the last fetch failed." }
        }
      },
      "ProviderFailure": {
        "type": "object",
        "description": "Actionable details for a provider currently serving carried-forward rows.",
        "required": ["provider", "error", "source_url", "last_verified", "carried_rows"],
        "properties": {
          "provider": { "type": "string" },
          "error": { "type": "string" },
          "source_url": { "type": ["string", "null"], "format": "uri", "description": "The source attempted by the failed adapter, or null for legacy snapshots without source metadata or carried rows." },
          "last_verified": { "type": "string", "description": "ISO timestamp of the last successful fetch, or never when no successful row exists." },
          "carried_rows": { "type": "integer", "minimum": 0 }
        }
      },
      "UnknownSku": {
        "type": "object",
        "description": "A GPU-looking SKU seen at a provider that is not yet mapped to a canonical GPU id (surfaced instead of silently dropped).",
        "required": ["provider", "name"],
        "properties": {
          "provider": { "type": "string" },
          "name": { "type": "string", "description": "The raw SKU name as listed by the provider." }
        }
      },
      "SnapshotMeta": {
        "type": "object",
        "description": "Pipeline summary counts. Optional: snapshots written before 2026-07-08 omit it.",
        "properties": {
          "provider_count": { "type": "integer" },
          "ok_count": { "type": "integer" },
          "stale_count": { "type": "integer" },
          "stale_providers": { "type": "array", "items": { "type": "string" } },
          "unknown_skus": { "type": "array", "items": { "$ref": "#/components/schemas/UnknownSku" } }
        }
      },
      "Snapshot": {
        "type": "object",
        "description": "One daily snapshot of the whole index.",
        "required": ["date", "generated_at", "offers", "providers"],
        "properties": {
          "date": { "type": "string", "format": "date", "description": "Snapshot date (YYYY-MM-DD)." },
          "generated_at": { "type": "string", "format": "date-time", "description": "When the snapshot was generated (ISO 8601)." },
          "offers": { "type": "array", "items": { "$ref": "#/components/schemas/Offer" } },
          "providers": {
            "type": "object",
            "description": "Verification status keyed by provider slug.",
            "additionalProperties": { "$ref": "#/components/schemas/ProviderStatus" }
          },
          "meta": { "$ref": "#/components/schemas/SnapshotMeta" }
        }
      },
      "Health": {
        "type": "object",
        "description": "Pipeline health computed from the latest snapshot.",
        "required": ["status", "providers_ok", "provider_coverage_slo", "fresh_ratio", "snapshot_date", "generated_at", "provider_count", "ok_count", "stale_count", "stale_providers", "provider_failures", "unknown_sku_count", "unknown_skus", "new_unknown_sku_count", "new_unknown_skus", "offer_count"],
        "properties": {
          "status": { "type": "string", "enum": ["ok", "degraded", "unhealthy"], "description": "ok when every provider is fresh, degraded when stale providers remain inside the coverage SLO, and unhealthy when coverage breaches it." },
          "providers_ok": { "type": "boolean", "description": "True when fresh provider coverage meets provider_coverage_slo." },
          "provider_coverage_slo": { "type": "number", "minimum": 0, "maximum": 1 },
          "fresh_ratio": { "type": "number", "minimum": 0, "maximum": 1 },
          "snapshot_date": { "type": "string", "format": "date" },
          "generated_at": { "type": "string", "format": "date-time" },
          "provider_count": { "type": "integer" },
          "ok_count": { "type": "integer" },
          "stale_count": { "type": "integer" },
          "stale_providers": { "type": "array", "items": { "type": "string" } },
          "provider_failures": { "type": "array", "items": { "$ref": "#/components/schemas/ProviderFailure" } },
          "unknown_sku_count": { "type": "integer" },
          "unknown_skus": { "type": "array", "items": { "$ref": "#/components/schemas/UnknownSku" }, "description": "All GPU-looking SKUs currently unmapped (full transparency set)." },
          "new_unknown_sku_count": { "type": "integer" },
          "new_unknown_skus": { "type": "array", "items": { "$ref": "#/components/schemas/UnknownSku" }, "description": "Unmapped SKUs not yet triaged (the actionable subset of unknown_skus)." },
          "offer_count": { "type": "integer" }
        }
      }
    }
  }
}
