{
  "openapi": "3.1.0",
  "info": {
    "title": "Promethean Markets access API",
    "version": "1.1.0",
    "description": "Certified prediction-market historical data. Four authenticated GET endpoints over the released lane, plus an unauthenticated public mirror of the release index. Every file has a SHA256 manifest; only released (certified, gapless) days are servable. Only /v1/download consumes the per-key daily budget — catalog, availability, and manifests are free (verification never costs budget)."
  },
  "servers": [{ "url": "https://api.promethean.markets" }],
  "security": [{ "bearer": [] }],
  "paths": {
    "/v1/datasets": {
      "get": {
        "summary": "Catalog of released sources and joining candidates",
        "responses": {
          "200": {
            "description": "released/_index/catalog.json, filtered to the key's scopes",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Catalog" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/BudgetExceeded" }
        }
      }
    },
    "/v1/availability": {
      "get": {
        "summary": "Released-day availability index (gapless chain, per-day bytes/objects)",
        "parameters": [{ "name": "source", "in": "query", "required": false, "schema": { "type": "string" }, "description": "Optional filter; omit for every source." }],
        "responses": {
          "200": {
            "description": "released/_index/availability.json (optionally source-filtered)",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Availability" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/BudgetExceeded" }
        }
      }
    },
    "/v1/download": {
      "get": {
        "summary": "Stream one released day file (Parquet) — the only budget-metered route",
        "parameters": [
          { "name": "source", "in": "query", "required": true, "schema": { "type": "string", "example": "binance_spot" } },
          { "name": "channel", "in": "query", "required": true, "schema": { "type": "string", "example": "crypto_trades" } },
          { "name": "scope", "in": "query", "required": true, "schema": { "type": "string", "example": "BTC" } },
          { "name": "day", "in": "query", "required": true, "schema": { "type": "string", "format": "date" } },
          { "name": "Range", "in": "header", "schema": { "type": "string", "example": "bytes=0-1048575" }, "description": "Resumable transfers. A malformed or unsatisfiable Range returns 416 rather than silently sending the whole object." }
        ],
        "responses": {
          "200": {
            "description": "The Parquet file (X-Promethean-SHA256 header carries the manifest hash)",
            "content": { "application/octet-stream": { "schema": { "type": "string", "format": "binary" } } }
          },
          "206": {
            "description": "Partial content for a valid Range",
            "content": { "application/octet-stream": { "schema": { "type": "string", "format": "binary" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/ScopeForbidden" },
          "404": { "$ref": "#/components/responses/NotReleased" },
          "416": {
            "description": "Malformed or unsatisfiable Range",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
          },
          "429": { "$ref": "#/components/responses/BudgetExceeded" }
        }
      }
    },
    "/v1/manifest": {
      "get": {
        "summary": "The file's manifest: sha256, row_count, schema_fingerprint, timestamp bounds (free — never metered)",
        "parameters": [
          { "name": "source", "in": "query", "required": true, "schema": { "type": "string" } },
          { "name": "channel", "in": "query", "required": true, "schema": { "type": "string" } },
          { "name": "scope", "in": "query", "required": true, "schema": { "type": "string" } },
          { "name": "day", "in": "query", "required": true, "schema": { "type": "string", "format": "date" } }
        ],
        "responses": {
          "200": {
            "description": "manifest.json — re-hash your download and compare sha256",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Manifest" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/ScopeForbidden" },
          "404": { "$ref": "#/components/responses/NotReleased" },
          "429": { "$ref": "#/components/responses/BudgetExceeded" }
        }
      }
    },
    "/v1/public/datasets": {
      "get": {
        "summary": "Unauthenticated catalog mirror (cacheable, CORS *)",
        "security": [],
        "responses": {
          "200": {
            "description": "Same bytes the released lane publishes",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Catalog" } } }
          }
        }
      }
    },
    "/v1/public/availability": {
      "get": {
        "summary": "Unauthenticated availability mirror (cacheable, CORS *)",
        "security": [],
        "responses": {
          "200": {
            "description": "The live released-lane index the website builds against",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Availability" } } }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearer": { "type": "http", "scheme": "bearer", "description": "API keys are prefixed pmk_. Only a SHA256 of the key is stored server-side." }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": { "error": { "type": "string" } },
        "additionalProperties": true
      },
      "Availability": {
        "type": "object",
        "description": "An EMPTY chain (days:{}, null inception_day/latest_day) is a valid pre-launch state, never an error.",
        "required": ["index_schema_version", "lane", "gapless", "days", "corrections", "generated_at_utc"],
        "properties": {
          "index_schema_version": { "type": "integer", "const": 1 },
          "lane": { "type": "string", "const": "released" },
          "gapless": { "type": "boolean" },
          "inception_day": { "type": ["string", "null"], "format": "date" },
          "latest_day": { "type": ["string", "null"], "format": "date" },
          "generated_at_utc": { "type": "string", "format": "date-time" },
          "release_sources": { "type": "array", "items": { "type": "string" } },
          "corrections": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "action": { "type": "string" },
                "day": { "type": "string", "format": "date" },
                "reason": { "type": "string" },
                "recorded_at_utc": { "type": "string", "format": "date-time" },
                "source": { "type": ["string", "null"] }
              }
            }
          },
          "days": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "required": ["complete", "bytes", "objects", "sources"],
              "properties": {
                "complete": { "type": "boolean" },
                "bytes": { "type": "integer" },
                "objects": { "type": "integer", "description": "Downloadable pair files (parquet + manifests)" },
                "sources": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "object",
                    "properties": {
                      "bytes": { "type": "integer" },
                      "parquet_files": { "type": "integer" },
                      "manifest_files": { "type": "integer" },
                      "rows": { "type": "integer", "description": "Sum of manifest row_count for the source/day; may be absent on older indexes" }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "Catalog": {
        "type": "object",
        "required": ["release_sources", "datasets"],
        "properties": {
          "release_contract_version": { "type": "integer" },
          "release_sources": { "type": "array", "items": { "type": "string" } },
          "source_channels": { "type": "object", "additionalProperties": { "type": "object" } },
          "datasets": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "schema_fingerprint": { "type": "string" },
                "fields": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": { "name": { "type": "string" }, "type": { "type": "string" } }
                  }
                }
              }
            }
          }
        }
      },
      "Manifest": {
        "type": "object",
        "required": ["sha256", "row_count"],
        "properties": {
          "sha256": { "type": "string" },
          "row_count": { "type": "integer" },
          "schema_fingerprint": { "type": "string" },
          "min_event_ts_ms": { "type": "integer" },
          "max_event_ts_ms": { "type": "integer" },
          "file_size_bytes": { "type": "integer" }
        },
        "additionalProperties": true
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Failed auth never consumes budget",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "missing_or_invalid_api_key" } } }
      },
      "ScopeForbidden": {
        "description": "The key's scopes do not include this source",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "source_not_in_key_scope" } } }
      },
      "NotReleased": {
        "description": "Not a released day/object (withdrawn or never released)",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "day_not_committed" } } }
      },
      "BudgetExceeded": {
        "description": "Key over its daily download budget — resets 00:00 UTC, never silently charges",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "daily_budget_exceeded", "daily_request_budget": 20000 } } }
      }
    }
  }
}
