{
  "openapi": "3.1.0",
  "info": {
    "title": "dhawal.org Markets API",
    "version": "1.0.0",
    "description": "Public, key-less JSON endpoints powering the markets surfaces on dhawal.org. Quote (Yahoo + Finnhub), macro (FRED), Commitments of Traders (CFTC), SEC filings (EDGAR), and earnings calendar (Finnhub). No auth required; rate-limited at the edge.",
    "license": {
      "name": "MIT",
      "url": "https://dhawal.org"
    },
    "contact": {
      "name": "Dhawal Ranka",
      "url": "https://dhawal.org",
      "email": "hello@dhawal.org"
    }
  },
  "servers": [
    {
      "url": "https://dhawal.org/api",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "quote",
      "description": "Live and last-session prices."
    },
    {
      "name": "macro",
      "description": "FRED macro series accessor."
    },
    {
      "name": "cot",
      "description": "CFTC weekly Commitments of Traders."
    },
    {
      "name": "filings",
      "description": "SEC EDGAR submissions."
    },
    {
      "name": "earnings",
      "description": "Earnings calendar window."
    }
  ],
  "paths": {
    "/quote": {
      "get": {
        "tags": [
          "quote"
        ],
        "summary": "Live quote for a curated symbol.",
        "description": "Yahoo primary, Finnhub equity fallback. Returns a normalised flat envelope. Edge cache TTL: 30s.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "ES=F",
            "description": "Yahoo-style symbol. Must be in the curated 64-symbol universe; returns 404 otherwise."
          }
        ],
        "responses": {
          "200": {
            "description": "Normalised quote envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailed"
          }
        }
      }
    },
    "/macro": {
      "get": {
        "tags": [
          "macro"
        ],
        "summary": "FRED macro series snapshots.",
        "description": "Pulls the most-recent observation + week-over-week delta for each requested series. Up to 10 series per call. Edge cache TTL: 6h.",
        "parameters": [
          {
            "name": "series",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "VIXCLS,T10Y2Y,DTWEXBGS",
            "description": "Comma-separated FRED series ids (alphanumeric + underscore)."
          }
        ],
        "responses": {
          "200": {
            "description": "Series readings keyed by id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MacroEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "503": {
            "$ref": "#/components/responses/NotConfigured"
          }
        }
      }
    },
    "/cot": {
      "get": {
        "tags": [
          "cot"
        ],
        "summary": "Commitments of Traders for one futures product.",
        "description": "CFTC legacy Futures-Only report (dataset kh3c-gbw2). Returns the latest week plus 52-week history. Edge cache TTL: 24h.",
        "parameters": [
          {
            "name": "product",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "ES",
                "NQ",
                "YM",
                "RTY",
                "GC",
                "SI",
                "HG",
                "CL",
                "NG",
                "ZB",
                "ZN",
                "ZF",
                "EURUSD",
                "GBPUSD",
                "USDJPY",
                "BTC"
              ]
            },
            "example": "ES"
          }
        ],
        "responses": {
          "200": {
            "description": "COT envelope with history + percentile.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CotEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailed"
          }
        }
      }
    },
    "/filings": {
      "get": {
        "tags": [
          "filings"
        ],
        "summary": "Recent SEC EDGAR filings for a US-listed symbol.",
        "description": "Resolves ticker → CIK via SEC company_tickers.json, then fetches submissions. Material forms only by default. Edge cache TTL: 1h.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "AAPL"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 25,
              "default": 5
            }
          },
          {
            "name": "all",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "0",
                "1"
              ]
            },
            "description": "Pass `1` to include every form type, not just material."
          },
          {
            "name": "insider",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "0",
                "1"
              ]
            },
            "description": "Pass `1` to return ONLY insider forms (4, 144, 3, 5)."
          }
        ],
        "responses": {
          "200": {
            "description": "Filings envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FilingsEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailed"
          },
          "503": {
            "$ref": "#/components/responses/NotConfigured"
          }
        }
      }
    },
    "/earnings": {
      "get": {
        "tags": [
          "earnings"
        ],
        "summary": "Earnings calendar for a date window.",
        "description": "Sources from Finnhub /calendar/earnings. Edge cache TTL: 6h.",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-05-11"
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-05-15"
          }
        ],
        "responses": {
          "200": {
            "description": "Earnings calendar window.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EarningsEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailed"
          },
          "503": {
            "$ref": "#/components/responses/NotConfigured"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "QuoteEnvelope": {
        "type": "object",
        "required": [
          "symbol",
          "last",
          "change",
          "changePct",
          "prevClose",
          "currency",
          "source",
          "fetchedAt",
          "ttlSeconds"
        ],
        "properties": {
          "symbol": {
            "type": "string",
            "example": "ES=F"
          },
          "display": {
            "type": "string",
            "example": "E-mini S&P 500"
          },
          "kind": {
            "type": "string",
            "enum": [
              "index",
              "future",
              "fx",
              "crypto",
              "equity",
              "etf"
            ]
          },
          "last": {
            "type": "number"
          },
          "change": {
            "type": "number"
          },
          "changePct": {
            "type": "number"
          },
          "prevClose": {
            "type": "number"
          },
          "open": {
            "type": [
              "number",
              "null"
            ]
          },
          "dayHigh": {
            "type": [
              "number",
              "null"
            ]
          },
          "dayLow": {
            "type": [
              "number",
              "null"
            ]
          },
          "fiftyTwoWeekHigh": {
            "type": [
              "number",
              "null"
            ]
          },
          "fiftyTwoWeekLow": {
            "type": [
              "number",
              "null"
            ]
          },
          "volume": {
            "type": [
              "number",
              "null"
            ]
          },
          "currency": {
            "type": "string",
            "example": "USD"
          },
          "source": {
            "type": "string",
            "enum": [
              "yahoo",
              "finnhub"
            ]
          },
          "fetchedAt": {
            "type": "integer",
            "description": "Unix seconds."
          },
          "ttlSeconds": {
            "type": "integer",
            "example": 30
          }
        }
      },
      "MacroEnvelope": {
        "type": "object",
        "required": [
          "series",
          "source",
          "fetchedAt",
          "ttlSeconds"
        ],
        "properties": {
          "series": {
            "type": "object",
            "additionalProperties": {
              "oneOf": [
                {
                  "type": "null"
                },
                {
                  "type": "object",
                  "required": [
                    "last",
                    "date"
                  ],
                  "properties": {
                    "last": {
                      "type": "number"
                    },
                    "date": {
                      "type": "string",
                      "format": "date"
                    },
                    "change": {
                      "type": [
                        "number",
                        "null"
                      ]
                    }
                  }
                }
              ]
            }
          },
          "source": {
            "type": "string",
            "enum": [
              "fred"
            ]
          },
          "fetchedAt": {
            "type": "integer"
          },
          "ttlSeconds": {
            "type": "integer"
          }
        }
      },
      "CotEnvelope": {
        "type": "object",
        "required": [
          "product",
          "weekEnding",
          "commercialNet",
          "largeSpecNet",
          "source",
          "fetchedAt"
        ],
        "properties": {
          "product": {
            "type": "string",
            "example": "ES"
          },
          "contractCode": {
            "type": "string",
            "example": "13874A"
          },
          "name": {
            "type": "string"
          },
          "weekEnding": {
            "type": "string",
            "format": "date"
          },
          "commercialLong": {
            "type": "number"
          },
          "commercialShort": {
            "type": "number"
          },
          "commercialNet": {
            "type": "number"
          },
          "largeSpecLong": {
            "type": "number"
          },
          "largeSpecShort": {
            "type": "number"
          },
          "largeSpecNet": {
            "type": "number"
          },
          "nonReportableLong": {
            "type": "number"
          },
          "nonReportableShort": {
            "type": "number"
          },
          "nonReportableNet": {
            "type": "number"
          },
          "openInterest": {
            "type": "number"
          },
          "history": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "weekEnding": {
                  "type": "string",
                  "format": "date"
                },
                "commercialNet": {
                  "type": "number"
                },
                "largeSpecNet": {
                  "type": "number"
                },
                "nonReportableNet": {
                  "type": "number"
                },
                "openInterest": {
                  "type": "number"
                }
              }
            }
          },
          "wowChange": {
            "type": "object",
            "properties": {
              "commercial": {
                "type": "number"
              },
              "largeSpec": {
                "type": "number"
              }
            }
          },
          "percentile52w": {
            "type": "object",
            "properties": {
              "commercial": {
                "type": "number",
                "minimum": 0,
                "maximum": 100
              },
              "largeSpec": {
                "type": "number",
                "minimum": 0,
                "maximum": 100
              }
            }
          },
          "source": {
            "type": "string",
            "enum": [
              "cftc"
            ]
          },
          "fetchedAt": {
            "type": "integer"
          },
          "ttlSeconds": {
            "type": "integer"
          }
        }
      },
      "FilingsEnvelope": {
        "type": "object",
        "required": [
          "symbol",
          "cik",
          "items",
          "source",
          "fetchedAt"
        ],
        "properties": {
          "symbol": {
            "type": "string"
          },
          "cik": {
            "type": "string",
            "example": "0000320193"
          },
          "name": {
            "type": "string"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FilingEntry"
            }
          },
          "source": {
            "type": "string",
            "enum": [
              "sec"
            ]
          },
          "fetchedAt": {
            "type": "integer"
          },
          "ttlSeconds": {
            "type": "integer"
          }
        }
      },
      "FilingEntry": {
        "type": "object",
        "required": [
          "form",
          "filedAt",
          "accessionNumber",
          "url"
        ],
        "properties": {
          "form": {
            "type": "string",
            "example": "10-Q"
          },
          "filedAt": {
            "type": "string",
            "format": "date"
          },
          "periodOfReport": {
            "type": "string"
          },
          "accessionNumber": {
            "type": "string"
          },
          "primaryDocument": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "size": {
            "type": "integer"
          }
        }
      },
      "EarningsEnvelope": {
        "type": "object",
        "required": [
          "from",
          "to",
          "items",
          "source",
          "fetchedAt"
        ],
        "properties": {
          "from": {
            "type": "string",
            "format": "date"
          },
          "to": {
            "type": "string",
            "format": "date"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EarningsRow"
            }
          },
          "source": {
            "type": "string",
            "enum": [
              "finnhub"
            ]
          },
          "fetchedAt": {
            "type": "integer"
          },
          "ttlSeconds": {
            "type": "integer"
          }
        }
      },
      "EarningsRow": {
        "type": "object",
        "required": [
          "symbol",
          "date"
        ],
        "properties": {
          "symbol": {
            "type": "string"
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "hour": {
            "type": "string",
            "enum": [
              "bmo",
              "amc",
              "dmh",
              ""
            ]
          },
          "quarter": {
            "type": [
              "integer",
              "null"
            ]
          },
          "year": {
            "type": [
              "integer",
              "null"
            ]
          },
          "epsEstimate": {
            "type": [
              "number",
              "null"
            ]
          },
          "epsActual": {
            "type": [
              "number",
              "null"
            ]
          },
          "revenueEstimate": {
            "type": [
              "number",
              "null"
            ]
          },
          "revenueActual": {
            "type": [
              "number",
              "null"
            ]
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Required parameter missing or malformed.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "NotFound": {
        "description": "The requested resource (symbol / product) is outside the curated coverage.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "UpstreamFailed": {
        "description": "Upstream provider returned a non-2xx status, was unreachable, or sent malformed data.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "NotConfigured": {
        "description": "Required server-side credentials / env binding are not present in this deployment.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      }
    }
  }
}