{
  "openapi": "3.1.0",
  "info": {
    "title": "VestWealthy Public Market Data API",
    "version": "1.0.0",
    "summary": "Read-only US equity quotes, calendars, sentiment and news.",
    "description": "VestWealthy aggregates third-party market data. This API returns it; it does not generate it.\n\n**Attribution requirements.** Market data belongs to the upstream provider.\nNews is republished wire copy — link the original publisher URL, not a\nVestWealthy URL. A field returned as `null` means the provider had nothing;\nit does not mean zero.\n\n**For agents:** the [MCP server](/mcp) exposes the same data as typed tools\nand is the preferred integration path.\n\nNot investment advice. See the [full disclaimer](/disclaimer).",
    "contact": {
      "name": "VestWealthy",
      "url": "https://vestwealthy.com/contact"
    },
    "license": {
      "name": "All rights reserved"
    }
  },
  "servers": [
    {
      "url": "https://vestwealthy.com",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "quotes",
      "description": "Real-time and delayed equity quotes."
    },
    {
      "name": "news",
      "description": "Aggregated financial news wire."
    },
    {
      "name": "calendar",
      "description": "Earnings and macroeconomic events."
    },
    {
      "name": "sentiment",
      "description": "Social sentiment and market regime."
    }
  ],
  "paths": {
    "/api/market-quotes": {
      "get": {
        "tags": [
          "quotes"
        ],
        "summary": "Live quotes for an allow-listed set of index, futures, commodity and crypto symbols.",
        "parameters": [
          {
            "name": "symbols",
            "in": "query",
            "required": false,
            "description": "Comma-separated symbols. Restricted to a server-side allow-list.",
            "schema": {
              "type": "string"
            },
            "example": "^GSPC,^VIX,BTC-USD"
          }
        ],
        "responses": {
          "200": {
            "description": "Array of quote objects.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/LiveQuote"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/stock/{ticker}": {
      "get": {
        "tags": [
          "quotes"
        ],
        "summary": "Consolidated per-ticker payload: snapshot, daily bars, news, analyst consensus, price target, insider filings, social sentiment and options positioning.",
        "parameters": [
          {
            "name": "ticker",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "NVDA"
          }
        ],
        "responses": {
          "200": {
            "description": "Consolidated ticker payload. Every field except `symbol` is nullable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConsolidatedStock"
                }
              }
            }
          },
          "404": {
            "description": "Symbol not recognised."
          }
        }
      }
    },
    "/api/options/{ticker}": {
      "get": {
        "tags": [
          "quotes"
        ],
        "summary": "Options positioning: max pain, gamma flip, call/put walls and net GEX.",
        "parameters": [
          {
            "name": "ticker",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Options positioning metrics."
          },
          "404": {
            "description": "No option chain available for this symbol."
          }
        }
      }
    },
    "/api/news": {
      "get": {
        "tags": [
          "news"
        ],
        "summary": "Aggregated financial news from six upstream wire feeds, deduped and sorted newest-first.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Substring filter over headline, summary, author and source."
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 5,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "source",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Exact publisher match."
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated news items.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "pageSize": {
                      "type": "integer"
                    },
                    "count": {
                      "type": "integer"
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/NewsItem"
                      }
                    },
                    "sourceCounts": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "integer"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/calendar": {
      "get": {
        "tags": [
          "calendar"
        ],
        "summary": "Economic events, trending earnings and trending symbols for the current window.",
        "responses": {
          "200": {
            "description": "Calendar payload."
          }
        }
      }
    },
    "/api/calendar/earnings": {
      "get": {
        "tags": [
          "calendar"
        ],
        "summary": "Earnings reports within a date range, with consensus EPS and revenue estimates.",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Earnings events."
          }
        }
      }
    },
    "/api/calendar/economic": {
      "get": {
        "tags": [
          "calendar"
        ],
        "summary": "Macroeconomic releases within a date range, with actuals, estimates and impact ratings.",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Economic events."
          }
        }
      }
    },
    "/api/momentum": {
      "get": {
        "tags": [
          "sentiment"
        ],
        "summary": "Symbols trending on Stocktwits, ranked by message volume.",
        "responses": {
          "200": {
            "description": "Trending symbols with message and watchlist counts."
          }
        }
      }
    },
    "/api/momentum/returns": {
      "get": {
        "tags": [
          "sentiment"
        ],
        "summary": "One-day and one-month returns for up to 40 symbols.",
        "parameters": [
          {
            "name": "symbols",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated, max 40."
          }
        ],
        "responses": {
          "200": {
            "description": "Return map keyed by symbol."
          }
        }
      }
    },
    "/api/market-regime": {
      "get": {
        "tags": [
          "sentiment"
        ],
        "summary": "Deterministic market regime reading: stance, 0-100 score, component metrics and allocation.",
        "responses": {
          "200": {
            "description": "Regime snapshot."
          }
        }
      }
    },
    "/api/fear-greed": {
      "get": {
        "tags": [
          "sentiment"
        ],
        "summary": "CNN Fear & Greed composite index with its historical series.",
        "responses": {
          "200": {
            "description": "Composite value, label, series and as-of date."
          },
          "502": {
            "description": "Upstream provider unavailable."
          }
        }
      }
    },
    "/api/search": {
      "get": {
        "tags": [
          "quotes"
        ],
        "summary": "Symbol search by company name or partial ticker.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching symbols with display name and type."
          }
        }
      }
    },
    "/api/webview": {
      "get": {
        "tags": [
          "search"
        ],
        "summary": "Fetches a DuckDuckGo results page with its frame-blocking headers removed, so /webview can embed it. Allowlisted to DuckDuckGo hosts; the response is sandboxed into an opaque origin so its scripts cannot reach this app's cookies or storage.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "The search term. Preferred input: the target URL is constructed server-side, so the common path never carries a caller-supplied URL."
          },
          {
            "name": "url",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Absolute https URL on an allowlisted DuckDuckGo host. Re-checked on every redirect hop."
          }
        ],
        "responses": {
          "200": {
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Target off the allowlist, or a redirect left it."
          },
          "502": {
            "description": "Search provider unreachable, errored or too large."
          }
        }
      }
    },
    "/feed.xml": {
      "get": {
        "tags": [
          "news"
        ],
        "summary": "RSS 2.0 feed of the aggregated news stream. Each item links to the ORIGINAL publisher.",
        "responses": {
          "200": {
            "content": {
              "application/rss+xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "LiveQuote": {
        "type": "object",
        "properties": {
          "symbol": {
            "type": "string"
          },
          "price": {
            "type": [
              "number",
              "null"
            ]
          },
          "change": {
            "type": [
              "number",
              "null"
            ]
          },
          "changePercent": {
            "type": [
              "number",
              "null"
            ]
          },
          "previousClose": {
            "type": [
              "number",
              "null"
            ]
          },
          "currency": {
            "type": [
              "string",
              "null"
            ]
          },
          "asOf": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "StockSnapshot": {
        "type": "object",
        "description": "A quote. Null fields mean the provider returned nothing.",
        "properties": {
          "price": {
            "type": [
              "number",
              "null"
            ]
          },
          "change": {
            "type": [
              "number",
              "null"
            ]
          },
          "changePercent": {
            "type": [
              "number",
              "null"
            ]
          },
          "prevClose": {
            "type": [
              "number",
              "null"
            ]
          },
          "open": {
            "type": [
              "number",
              "null"
            ]
          },
          "high": {
            "type": [
              "number",
              "null"
            ]
          },
          "low": {
            "type": [
              "number",
              "null"
            ]
          },
          "volume": {
            "type": [
              "number",
              "null"
            ]
          },
          "currency": {
            "type": [
              "string",
              "null"
            ]
          },
          "timestamp": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "extendedHours": {
            "oneOf": [
              {
                "type": "object",
                "description": "A pre-market or after-hours print, measured against the most recent regular-session close. Distinct from `change`, which is measured against the previous close.",
                "properties": {
                  "session": {
                    "type": "string",
                    "enum": [
                      "pre",
                      "post"
                    ]
                  },
                  "price": {
                    "type": "number"
                  },
                  "change": {
                    "type": "number"
                  },
                  "changePercent": {
                    "type": "number"
                  },
                  "timestamp": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time"
                  }
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "Null during the regular session and when no provider returned a print. Never a synthesised zero."
          },
          "isRealData": {
            "type": "boolean",
            "description": "False means the quote is delayed or partially reconstructed."
          }
        }
      },
      "ConsolidatedStock": {
        "type": "object",
        "properties": {
          "symbol": {
            "type": "string"
          },
          "companyName": {
            "type": "string"
          },
          "exchange": {
            "type": "string"
          },
          "snapshot": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/StockSnapshot"
              },
              {
                "type": "null"
              }
            ]
          },
          "bars": {
            "type": "array",
            "description": "Daily OHLCV bars. Empty when history is unavailable; never synthesised.",
            "items": {
              "type": "object",
              "properties": {
                "t": {
                  "type": "string",
                  "description": "Trading date, YYYY-MM-DD"
                },
                "o": {
                  "type": "number"
                },
                "h": {
                  "type": "number"
                },
                "l": {
                  "type": "number"
                },
                "c": {
                  "type": "number"
                },
                "v": {
                  "type": "number"
                }
              }
            }
          },
          "news": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NewsItem"
            }
          },
          "recommendations": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "priceTarget": {
            "type": [
              "object",
              "null"
            ]
          },
          "insiderTransactions": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "stocktwits": {
            "type": "object"
          },
          "options": {
            "type": [
              "object",
              "null"
            ]
          },
          "fetchedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "NewsItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "headline": {
            "type": "string"
          },
          "summary": {
            "type": "string"
          },
          "author": {
            "type": "string",
            "description": "Original publisher."
          },
          "url": {
            "type": "string",
            "description": "Permalink on the ORIGINAL publisher, not on this site."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "source": {
            "type": "string"
          }
        }
      }
    }
  },
  "externalDocs": {
    "description": "Data provenance and provider list",
    "url": "https://vestwealthy.com/api"
  }
}