StockUp Quan API Developer Hub
Build finance-native applications with server-verified stock context, SEC filing workflows, citations, portfolio risk analysis, streaming responses, and explicit unavailable-data handling.
Read API Docs
Learn endpoints, connection details, headers, model variations, and response schemas to get started.
Enterprise Setup Guide
Run Quan from an Enterprise workspace, use Enterprise model aliases, manage employee quotas, and prepare production rollouts.
Enterprise Financial AI
Explore company, team, fintech, bank, and financial-institution use cases for Quan API Enterprise.
Model Context Protocol
Hook up StockUp Quan tools to Claude Desktop, Cursor, and other agent systems using our public MCP server.
Developer Credentials
Generate secure API keys, check limits, test connection queries in real-time, and manage account credits.
Developer Notes
StockUp API Integration Guide
Integrate and use the StockUp Quan API in your own client and backend systems.
Step 1: Obtain your API Key
- Navigate to the developer console (or log in on production).
- Click New Key under API Credentials.
- Provide a description label (e.g. "Slack Bot Integration") and generate the key.
- Copy the key immediately and save it securely. It will not be shown again.
Step 2: Connection Details
- Production Endpoint Base:
https://stockup.cc/v1/query - Local Testing Endpoint:
http://127.0.0.1:5001/stockup-4ab83/us-central1/quanPublicApi - HTTP Request Method:
POST - Required Headers:
Content-Type: application/json
x-api-key: YOUR_API_KEY
Step 3: Choose a Model Variant
The StockUp API supports several model variations tailored for specific financial tasks:
quan-nova(Beta): The replacement forquan-3.0, built from the ground up for faster, higher-quality responses at the same price ($0.50/M input, $3.00/M output).quan-3.0is discontinued October 1st — see the Nova integration and migration guide for details.quan-3.0(Lite): Optimized for raw speed and minimal latencies. Best for quick summaries, basic definitions, and rapid stock stats. Being replaced byquan-nova.quan-3.4-l: Lightweight Quan 3.4 for faster, lower-cost grounded market analysis. It uses the Quan 3.4 reasoning contract and costs $0.75/M input and $4.50/M output tokens.quan-3.4(Standard Flagship): Brand new flagship model for high-precision reasoning, numerical calculations, and next-generation LBO and DCF estimates. Powered by StockUp's latest flagship reasoning architecture.quan-3.5: Available at $5.50/M input and $28.00/M output tokens. Same request shape asquan-3.4; returns JSON unless"stream": trueis sent. See Quan 3.5 below.quan-3.4-deep-research(Deep Thinking): Advanced deep reasoning model for massive audits, competitor catalysts, and deep portfolio modeling. Powered by StockUp's deep reasoning architecture.
Step 4: Request Payload Structure
All payloads are structured as standard JSON. We support standard OpenAI-compatible message matrices:
Response budgets: API keys have configurable defaultMaxOutputTokens and maxOutputTokensCap settings. New keys default to 8,000 and 65,536; either can be set up to 65,536. Set maxOutputTokens per request to use a different budget; requests above the key cap or provider maximum are rejected, and requests that cannot be funded are rejected before generation rather than silently truncated. Thinking tokens count toward usage and billing.
Calculations the model must do itself: when a request asks for a calculation (a valuation such as a DCF, a backtest, or a portfolio risk figure such as VaR) and no server-side calculation is attached — for example with Decision Intelligence turned off for the key — the model does the arithmetic itself and needs room to reason. If maxOutputTokens is too small for that, the request is refused before generation with 400 INSUFFICIENT_OUTPUT_BUDGET and is not billed. Questions that ask for a view rather than a figure (“Should I buy NVDA?”, a signal or macro question) are never refused for this reason; on a small budget they are answered at a lower reasoning level. The error body gives requiredMaxOutputTokens (for example 14,512 for a quan-3.4 valuation) and fundedMaxOutputTokens; resend with maxOutputTokens at or above the required value:
{"error": "This valuation request requires the model to perform the calculation itself, which needs at least 14512 maxOutputTokens. ...", "code": "INSUFFICIENT_OUTPUT_BUDGET", "requiredMaxOutputTokens": 14512, "fundedMaxOutputTokens": 8000, "taskType": "valuation"}
Response headers about the budget: X-Quan-Thinking-Level is the reasoning level used (HIGH, MEDIUM or LOW); X-Quan-Arithmetic-Grounded is true when a server-side calculation was attached; X-Quan-Output-Budget-Warning means the budget may be too small to finish the answer; X-Quan-Response-Truncated: true means the answer was cut at the budget (the safety disclaimer is still added). X-Quan-Model-Redirected appears when a retired model name was served by its replacement, for example quan-3.0 -> quan-nova.
{
"model": "quan-3.4",
"messages": [
{ "role": "user", "content": "Compare Apple and Microsoft valuation." }
],
"googleSearch": true,
"stream": true,
"temperature": 0.2
}
Request Parameters Table
| Parameter | Type | Description |
|---|---|---|
model |
string |
Identifies the model variant (quan-3.0, quan-3.4-l, quan-3.4, quan-3.5, or quan-3.4-deep-research). Defaults to quan-3.4. |
messages |
array |
An array of conversation objects: [{"role": "user" | "assistant", "content": "string"}]. |
stream |
boolean |
Enable Server-Sent Events (SSE) streaming for real-time text delivery. Defaults to true, except on quan-3.5, which streams only when stream is true. |
googleSearch |
boolean |
Enable live web grounding for up-to-date context. Defaults to true. |
temperature |
number |
Model temperature value between 0.0 and 2.0. Lower values are recommended for math. |
Step 5: Code Integration Examples
curl -X POST "https://stockup.cc/v1/query" \
-H "Content-Type: application/json" \
-H "x-api-key: sk_quan_your_secret_key" \
-d '{
"model": "quan-3.4",
"messages": [
{ "role": "user", "content": "How does compound interest work?" }
],
"stream": false
}'
const apiKey = "sk_quan_your_secret_key";
const url = "https://stockup.cc/v1/query";
async function streamResponse() {
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": apiKey
},
body: JSON.stringify({
model: "quan-3.4",
messages: [{ role: "user", content: "Analyze Apple stock setup." }],
stream: true
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\n");
buffer = lines.pop(); // Keep partial line in buffer
for (const line of lines) {
if (line.startsWith("data: ")) {
const payload = line.slice(6).trim();
if (payload === "[DONE]") {
console.log("\n--- Stream Finished ---");
break;
}
try {
const chunk = JSON.parse(payload);
const text = chunk.candidates?.[0]?.content?.parts?.[0]?.text || "";
process.stdout.write(text);
} catch (err) {
// Skip non-JSON chunks or log metadata
}
}
}
}
}
streamResponse();
import requests
url = "https://stockup.cc/v1/query"
headers = {
"Content-Type": "application/json",
"x-api-key": "sk_quan_your_secret_key"
}
payload = {
"model": "quan-3.4",
"messages": [
{"role": "user", "content": "Explain the difference between P/E and forward P/E."}
],
"stream": False
}
response = requests.post(url, headers=headers, json=payload)
if response.status_code == 200:
data = response.json()
text = data['candidates'][0]['content']['parts'][0]['text']
print(text)
else:
print(f"Error {response.status_code}: {response.text}")
Note on CORS: When calling this endpoint from web browsers, standard CORS preflight requests apply. Be sure to configure safe headers, or run calls through your own server to avoid exposing your secret API key on client-side browsers.
Quan 3.5
Quan 3.5 is available on the Developer API. It uses the same endpoint, authentication headers, request body and response shape as quan-3.4. Select it with "model": "quan-3.5"; the hyphenated alias quan-3-5 is also accepted.
Two differences to handle when you switch from quan-3.4: Quan 3.5 returns one JSON response unless the request sends "stream": true (quan-3.4 streams unless the request sends "stream": false), and an out-of-range temperature, topP or topK is rejected with 400 INVALID_PARAMETER before any model call.
Pricing
Prices are per 1M tokens. As with every model, output tokens include the thinking tokens the model provider reports. Executed web searches cost $0.007 each on every model. Quan 3.5 has no free allowance: it needs a prepaid balance.
| Model | Input | Output | Notes |
|---|---|---|---|
quan-3.5 |
$5.50 | $28.00 | From November 1, 2026. Prepaid balance required. |
quan-3.4 |
$2.50, then $3.00 | $15.00, then $18.00 | The new price applies from December 15, 2026. |
quan-3.4-l |
$0.75 | $4.50 | Unchanged. Includes 100,000 free tokens for verified developers. |
Rates only, for comparison: a request that uses 2,000 input tokens and 6,000 output tokens costs $0.179 on quan-3.5, $0.095 on quan-3.4 ($0.114 from December 15) and $0.0285 on quan-3.4-l before any free allowance. The same question does not use the same number of tokens on every model, so measure your own traffic before comparing costs.
Choosing quan-3.5, quan-3.4 or quan-3.4-l
| Model | Use it when |
|---|---|
quan-3.5 |
You want the behaviour listed under What changes from quan-3.4: context carried across conversation turns, the latest reported results attached from SEC filings, multi-part questions answered section by section, answer styles, and checked short answers to everyday questions. It has the highest per-token price of the three. |
quan-3.4 |
You have an integration built on its current behaviour, including streaming by default. Its behaviour is not changing with this release; only its price changes on December 15, 2026. |
quan-3.4-l |
Cost matters most. It runs at a fixed low reasoning level, has the lowest price of the three, and includes a free allowance. |
What changes from quan-3.4
| Area | Quan 3.5 behaviour |
|---|---|
| Streaming | Streams only when the request sends "stream": true. Omitting stream returns one JSON response. |
| Parameter validation | temperature must be 0 to 2, topP / top_p 0 to 1, and topK / top_k an integer from 1 to 1000. Anything else returns 400 INVALID_PARAMETER before any model call, with parameter and received in the body. |
| Response body | thoughtSignature is removed from response parts and responseId from the payload. Read the answer from candidates[0].content.parts[0].text as before. |
| Reasoning depth | Everyday questions (a price, key figures, news, earnings, dividends, analyst views, what a company does, comparisons, funds, upcoming events, the economy and the Fed, the market, a sector, explaining a concept) and “why is this stock moving” questions are answered at a lower reasoning level in a short format. The answer is checked against the data the server retrieved before it is returned, and rewritten if it fails the check. Opinion, forecast, portfolio and multi-part questions keep the full reasoning path. X-Quan-Thinking-Level reports the level used. |
| Latest reported results | For analytical questions about a single company, the most recently reported period is read from its SEC filings and given to the model. X-Quan-Latest-Earnings: attached shows when this happened. If the lookup fails, the request continues without it. |
| Conversation history | A ticker named in an earlier user message is used when the current message refers back to it. Limits stated in earlier user messages (for example a maximum position size) are carried forward; they are taken from user turns only, and a value in the request's constraints field or in the current message always wins. X-Quan-Carried-Constraints lists any limits carried forward. |
| Portfolios in the question | Holdings written in the question text (for example “40% NVDA, 30% MSFT, 30% TLT”) are used as the portfolio when the request has no portfolio field. Each symbol is still checked against price history. |
| Multi-part questions | With Decision Intelligence enabled for the key, a question with several parts is split and each part is grounded and answered separately. X-Quan-Multi-Intent-Sections gives the number of sections. |
| Server-side calculations | With Decision Intelligence enabled for the key: reported allocation weights never sum past 100%. Risk figures use the standard Sortino denominator, Wilder’s RSI and compounded portfolio returns, and the risk-free rate used is reported and marked when it is an assumption. DCF inputs stated plainly in the question are accepted, including the horizon and cash-flow timing, and a single-stage model is allowed. For tickers in the question, moving averages, the 52-week range and average volume are computed from the verified daily price series. |
| Unknown balances | A balance the user describes as unknown is never treated as cash. A request to treat it as cash is refused before any search or model call, with X-Quan-Truth-Guard and a quanTruthGuard object in the response. When cash is unknown, a sale counts in full against a one-day turnover limit. |
| Answer style | Optional answerStyle: balanced (default), simple or advanced. balanced and simple lead with the overall view in everyday words and explain finance terms; advanced keeps the institutional write-up. Figures and checks are the same in every style. X-Quan-Answer-Style reports the style used. |
| Response cache | X-Quan-Cache is hit when the answer came from Quan’s response cache and miss otherwise. |
Request examples
These use the same endpoint and headers as the examples above. Only model changes; answerStyle is optional.
curl -X POST "https://stockup.cc/v1/query" \
-H "Content-Type: application/json" \
-H "x-api-key: sk_quan_your_secret_key" \
-d '{
"model": "quan-3.5",
"messages": [
{ "role": "user", "content": "Explain the difference between P/E and forward P/E." }
],
"stream": false,
"answerStyle": "balanced"
}'
const apiKey = "sk_quan_your_secret_key";
const url = "https://stockup.cc/v1/query";
async function askQuan35() {
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": apiKey
},
body: JSON.stringify({
model: "quan-3.5",
messages: [{ role: "user", content: "Summarize the most recent reported quarter for Microsoft." }],
stream: false
})
});
if (!response.ok) {
const error = await response.json().catch(() => null);
throw new Error(`${response.status} ${error?.code || ""} ${error?.error || ""}`);
}
const data = await response.json();
console.log(data.candidates?.[0]?.content?.parts?.[0]?.text || "");
console.log("version:", response.headers.get("X-Quan-Model-Version")); // "3.5"
console.log("charge (USD):", response.headers.get("X-StockUp-Charge"));
console.log("balance (USD):", response.headers.get("X-StockUp-Balance"));
}
askQuan35();
import requests
url = "https://stockup.cc/v1/query"
headers = {
"Content-Type": "application/json",
"x-api-key": "sk_quan_your_secret_key"
}
payload = {
"model": "quan-3.5",
"messages": [
{"role": "user", "content": "Explain the difference between P/E and forward P/E."}
],
"stream": False
}
response = requests.post(url, headers=headers, json=payload)
if response.status_code == 200:
data = response.json()
text = data['candidates'][0]['content']['parts'][0]['text']
print(text)
print("charge (USD):", response.headers.get("X-StockUp-Charge"))
else:
print(f"Error {response.status_code}: {response.text}")
Streaming
Send "stream": true to receive Server-Sent Events in the same format as other models: data: lines carrying JSON chunks, then data: [DONE]. Two points are specific to how billing and checking work on a stream:
- The
X-StockUp-billing headers are not sent on a stream, because headers go out before the charge is known. Read theevent: billingframe instead; it arrives beforedata: [DONE]and carriescharge,searchCharge,searchQueries,remainingBalance,billingModeandrequestId. If settlement fails, the stream sendsevent: errorwithBILLING_SETTLEMENT_FAILEDand therequestId. - Answers that go through the answer check (everyday and “why is this stock moving” questions) are held back until the check finishes, so their text can arrive in a single chunk near the end of the stream.
const response = await fetch("https://stockup.cc/v1/query", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": process.env.STOCKUP_API_KEY
},
body: JSON.stringify({
model: "quan-3.5",
messages: [{ role: "user", content: "What is moving the semiconductor sector today?" }],
stream: true
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
let eventName = "message";
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\n");
buffer = lines.pop();
for (const line of lines) {
if (line.startsWith("event: ")) { eventName = line.slice(7).trim(); continue; }
if (!line.startsWith("data: ")) { if (line === "") eventName = "message"; continue; }
const payload = line.slice(6).trim();
if (payload === "[DONE]") continue;
const data = JSON.parse(payload);
if (eventName === "billing") {
console.log("\ncharge (USD):", data.charge, "balance:", data.remainingBalance);
} else if (eventName === "message") {
process.stdout.write(data.candidates?.[0]?.content?.parts?.[0]?.text || "");
}
}
}
Quan 3.5 promo billing fields
X-StockUp-Promo-Charge and X-StockUp-Promo-Balance show the promo-funded charge and remaining promo balance in dollars. X-StockUp-Cash-Charge shows the cash-funded portion. The streamed event: billing uses promoCharge, promoBalance, and cashCharge. Total charge includes both. Launch credit applies only to Quan 3.5, is spent first, and expires at the time shown in your account.
Response and billing headers
| Header | Meaning |
|---|---|
X-Quan-Model-Version |
3.5 when the request was served by Quan 3.5. |
X-StockUp-Model |
The model id as sent, lowercased: quan-3.5 or quan-3-5. |
X-Quan-Thinking-Level |
The reasoning level used: HIGH, MEDIUM or LOW. |
X-StockUp-Charge |
Total charged for the request in USD, tokens plus searches, to six decimal places. |
X-StockUp-Search-Charge / X-StockUp-Search-Queries |
The search part of the charge and the number of executed searches charged. |
X-StockUp-Balance |
Prepaid balance in USD after this request. |
X-StockUp-Billing-Mode |
The billing path used for the request. |
X-Request-Id |
Quote this when you contact support about a request. |
The budget headers described in Step 4 (X-Quan-Arithmetic-Grounded, X-Quan-Output-Budget-Warning, X-Quan-Response-Truncated) work the same way on Quan 3.5.
Errors
| Response | Cause | Fix |
|---|---|---|
400 INVALID_PARAMETER |
temperature, topP or topK is not a number or is outside its range. The body names the parameter and what was received. |
Send a value inside the range, or omit the parameter. |
400 INSUFFICIENT_OUTPUT_BUDGET |
The request needs the model to do a calculation itself and maxOutputTokens is too small. Not billed. |
Resend with maxOutputTokens at or above requiredMaxOutputTokens. |
400 Unknown model... |
Typo in the model field. |
Use exactly quan-3.5 (or quan-3-5), lowercase. |
402 This model requires a prepaid API balance. (PREPAID_BALANCE_REQUIRED) |
The account has no prepaid balance. Quan 3.5 has no free allowance. | Add a prepaid balance in the developer console. |
402 INSUFFICIENT_RESPONSE_BUDGET |
The balance cannot cover maxOutputTokens at Quan 3.5 rates, with the safety margin reserved for reasoning tokens. The message says how many tokens the balance covers. Checked before the model is called. |
Lower maxOutputTokens, or add balance. |
402 INSUFFICIENT_BALANCE |
The balance cannot fund the request input at Quan 3.5 rates. Checked before the model is called. | Add balance. |
An error body that is not JSON is a server error from the hosting front end; see When an error body is not JSON.
A verified response-cache hit returns the stored answer without reserving or charging tokens or searches.
Launch credit
The first 100 developers to redeem code QUAN35 in the developer console receive $5.00 of API credit that can be used only on quan-3.5.
- One redemption per account. The account needs a verified email and must have accepted the current Developer API Terms.
- The credit is spent before your cash balance, and only on
quan-3.5requests. Other models draw from your cash balance as usual. - Requests paid with the credit are charged at the normal Quan 3.5 rates.
- The code stops working after 100 redemptions, or earlier if the offer is withdrawn.
Integrating Quan Visual Elements
Quan Visual Elements are frontend components for turning trusted financial values into app-ready visual cards. They are useful for DCA projections, forensic audit summaries, risk radar panels, and analyst split panels.
Important: public API responses are plain text/JSON response streams today. Do not expect raw [QUAN_VISUALS] or [CHART_DATA] blocks from the public API. Use Quan for analysis, use your own backend/provider data for verified values, then map those values into the visual data object below.
1. Add the visual renderer files
Copy the shared visual files into your public assets folder, then load the CSS and JavaScript on the page where the visual should appear.
<link rel="stylesheet" href="/assets/quan-visual-demo.css"> <script src="/assets/quan-visual-demo.js" defer></script>
2. Add the visual shell
The renderer initializes every .quan-visual-demo section on the page. The stock buttons, visual buttons, and view hooks are required. The explanation hook is optional.
<section class="quan-visual-demo"
data-default-stock="VOO"
data-default-visual="dca"
data-demo-context="api"
data-demo-guide-href="/docs#visual-elements"
data-demo-stocks="VOO,NVDA,AMZN,MSFT">
<div class="qvd-showcase">
<div class="qvd-visual-column">
<div class="qvd-shell">
<div class="qvd-controls">
<div class="qvd-control-group">
<div class="qvd-control-label">Stock</div>
<div class="qvd-button-row" data-demo-stock-buttons></div>
</div>
<div class="qvd-control-group">
<div class="qvd-control-label">Visual element</div>
<div class="qvd-button-row" data-demo-visual-buttons></div>
</div>
</div>
<div class="qvd-view" data-demo-view aria-live="polite"></div>
</div>
</div>
<div class="qvd-copy">
<span class="qvd-kicker">Interactive visual elements</span>
<h2>Quan visual elements</h2>
<p>Use trusted backend values to render app-ready financial visuals.</p>
<div class="qvd-explain-card" data-demo-explanation aria-live="polite"></div>
</div>
</div>
</section>
3. Supply your own ticker data
Define window.QuanVisualDemoData before loading or re-initializing the renderer. The data shape below is intentionally plain JavaScript so it can be generated from any backend.
window.QuanVisualDemoData = {
VOO: {
name: "Vanguard S&P 500 ETF",
dca: {
monthly: 1000,
years: 10,
annualReturn: 7.4,
cagr: 3.73
},
audit: {
verdict: "Clean broad-market exposure with macro sensitivity",
confidence: "High",
checks: [
["Source coverage", "Broad ETF data is easy to cross-check", "good"],
["Narrative risk", "Mostly index-level claims", "good"],
["Key watch", "Multiple compression if rates stay elevated", "warn"]
]
},
risk: {
score: 42,
summary: "Balanced",
factors: [
["Valuation", 48, "Market multiple is the main sensitivity."],
["Concentration", 36, "Diversified, with mega-cap weight."],
["Volatility", 40, "Lower than most single-name equities."],
["Earnings sensitivity", 44, "Index EPS revisions matter."],
["Macro exposure", 52, "Rate and growth expectations matter."]
]
},
analyst: {
consensus: "Broad market proxy",
asOf: "Provider snapshot",
buy: 65,
hold: 30,
sell: 5,
low: 455,
average: 520,
high: 585
}
}
};
// Use this when you update data after the page has already loaded.
window.initQuanVisualDemos?.();
4. Call Quan through a safe backend proxy
Keep your StockUp API key server-side. Your backend can call Quan for a written analysis, then combine that analysis with verified values from your market-data provider before sending visual data to your frontend.
import express from "express";
const app = express();
app.use(express.json());
app.post("/api/quan-visual", async (req, res) => {
const ticker = String(req.body.ticker || "VOO").toUpperCase();
const quanResponse = await fetch("https://stockup.cc/v1/query", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": process.env.STOCKUP_API_KEY
},
body: JSON.stringify({
model: "quan-3.4",
stream: false,
googleSearch: true,
temperature: 0.2,
messages: [{
role: "user",
content: `Summarize ${ticker} risk, DCA assumptions, audit concerns, and analyst context. Separate confirmed facts from judgment.`
}]
})
});
const quanJson = await quanResponse.json();
const analysisText =
quanJson?.candidates?.[0]?.content?.parts?.[0]?.text || "";
// Replace these placeholders with verified values from your own provider.
const providerValues = await loadVerifiedMarketValues(ticker);
res.json({
ticker,
analysisText,
visualData: mapToQuanVisualData(ticker, providerValues)
});
});
function mapToQuanVisualData(ticker, values) {
return {
[ticker]: {
name: values.name,
dca: {
monthly: values.monthlyContribution,
years: values.projectionYears,
annualReturn: values.assumedAnnualReturn,
cagr: values.estimatedCagr
},
audit: {
verdict: values.auditVerdict,
confidence: values.auditConfidence,
checks: values.auditChecks
},
risk: {
score: values.riskScore,
summary: values.riskSummary,
factors: values.riskFactors
},
analyst: {
consensus: values.consensus,
asOf: values.asOf,
buy: values.buyCount,
hold: values.holdCount,
sell: values.sellCount,
low: values.targetLow,
average: values.targetAverage,
high: values.targetHigh
}
}
};
}
5. Render backend data in your frontend
Fetch your proxy output, assign the returned visualData, and re-run the renderer.
async function loadQuanVisual(ticker) {
const response = await fetch("/api/quan-visual", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ ticker })
});
const payload = await response.json();
window.QuanVisualDemoData = payload.visualData;
window.initQuanVisualDemos?.();
// Optional: display Quan's written analysis beside the visual.
document.querySelector("#analysisText").textContent = payload.analysisText;
}
loadQuanVisual("VOO");
Production checklist
- Never expose
x-api-keyin browser JavaScript. - Use verified provider data for exact prices, analyst counts, targets, and financial metrics.
- Label projections as assumptions and include a disclaimer near DCA outputs.
- Sanitize or validate any values generated by an LLM before rendering them into a visual.
- Prefer concise labels on mobile so buttons, stat cards, and tables do not overflow.