Quova MCP
Quova is a financial data API. You get daily and intraday prices, quotes, technical indicators, company profiles, financial statements, earnings, dividends, corporate actions, insider trades, institutional and substantial holders, short interest, and fund profiles and holdings.
Server MCP Terhosting
npx add-mcp 'https://api.quova.io/mcp'Terpasang ke Claude Code, Codex, Cursor, dan lainnya
Dokumentasi
Your first request
Create a key in the dashboard, then ask for a week of prices for Apple and BHP. Every request goes to https://api.quova.io.
curl "https://api.quova.io/v1/prices/daily?symbols=NASDAQ:AAPL,ASX:BHP&from=2026-06-01&to=2026-06-05¤cy=USD" \
-H "Authorization: Bearer $QUOVA_API_KEY"
You get back the bars for each symbol along with what it resolved to and what the call cost. A symbol we can't answer for gets an entry in errors instead of failing the whole call.
{
"data": [
{
"symbol": "NASDAQ:AAPL",
"instrument": {
"id": "ins_34VInjBb4l9Qz9gdXnDar",
"symbol": "NASDAQ:AAPL",
"name": "Apple Inc.",
"type": "equity",
"mic": "XNAS",
"currency": "USD",
"country": "US"
},
"asOf": "2026-06-05T00:00:00Z",
"currency": "USD",
"adjust": "splits",
"bars": [
{
"date": "2026-06-01",
"open": 309.625,
"high": 310.94,
"low": 305.02,
"close": 306.31,
"volume": 48849933
},
{
"date": "2026-06-02",
"open": 307.46,
"high": 315.45,
"low": 306.685,
"close": 315.2,
"volume": 44534716
},
{
"date": "2026-06-03",
"open": 314.175,
"high": 316.94,
"low": 308.85,
"close": 310.26,
"volume": 50836705
},
{
"date": "2026-06-04",
"open": 313.23,
"high": 313.54,
"low": 309.65,
"close": 311.23,
"volume": 44869134
},
{
"date": "2026-06-05",
"open": 312.86,
"high": 315.17,
"low": 307.15,
"close": 307.34,
"volume": 65310502
}
]
},
{
"symbol": "ASX:BHP",
"instrument": {
"id": "ins_34VMvft6kZEXThtzFGcAL",
"symbol": "ASX:BHP",
"name": "BHP Group Ltd",
"type": "equity",
"mic": "XASX",
"currency": "AUD",
"country": "AU"
},
"asOf": "2026-06-05T00:00:00Z",
"currency": "USD",
"adjust": "splits",
"bars": [
{
"date": "2026-06-01",
"open": 44.582805,
"high": 45.040725,
"low": 44.4575925,
"close": 44.70444,
"volume": 6338357
},
{
"date": "2026-06-02",
"open": 45.50852,
"high": 45.637724,
"low": 44.89839,
"close": 45.486986,
"volume": 7621289
},
{
"date": "2026-06-03",
"open": 46.01124,
"high": 46.360512,
"low": 45.897192,
"close": 46.267848,
"volume": 8405596
},
{
"date": "2026-06-04",
"open": 45.57987,
"high": 45.6512,
"low": 44.488521,
"close": 44.79524,
"volume": 7871447
},
{
"date": "2026-06-05",
"open": 43.8199,
"high": 43.967845,
"low": 42.953365,
"close": 43.14358,
"volume": 7385673
}
]
}
],
"errors": [],
"meta": {
"credits": 2,
"requestId": "req_34VoxwgsJXbOecNLR74B7"
}
}
Authentication
Send your key in the Authorization header as a bearer token. Keys start with qv_ and are shown once, when you create them, because we only store a hash.
Authorization: Bearer qv_...
Keys never go in the query string, since URLs end up in logs. A key can call every endpoint or a fixed list of operation IDs, and it can have its own credit budget, rate limit, IP allowlist and expiry date.
Identifiers
Every instrument is referenced as namespace:value, and any of these work wherever a symbol is expected.
| Kind | Example |
|---|---|
| Listed security | NASDAQ:AAPL, LSE:VOD, XETRA:SAP or ASX:VAS. The MIC works too, as in XASX:VAS |
| Open identifiers | isin:US0378331005 or figi:BBG000B9XRY4 |
| Licensed identifiers | cusip:037833100 and sedol:... are accepted as input but never returned |
| Broker contract ID | conid:265598, the ID Interactive Brokers gives a contract. Accepted as input but never returned |
| Unlisted fund | apir:BTA0419AU |
| Listed option | OCC:AAPL250117C00150000 for a US option, with or without the OSI spaces, or ASX:BHPS49 |
| FX pair | FX:EURUSD |
| Crypto | CRYPTO:BTC for the most liquid match, or SOLANA:<contract> for an exact token |
| NFT | ETHEREUM:<contract>/<token id> for one NFT, or the bare contract for its collection. SOLANA:<mint> on Solana |
| Index, rate, future | INDEX:AXJO, RATE:AU10Y, FUT:CL1 |
| Quova ID | ins_7Hq2kP3xY9vLmN4bR8sT1u. Store this one, it survives renames and ticker changes |
A bare ticker like BHP resolves to its home listing. Each entry in the response includes the instrument your symbol resolved to, so you can always check.
Tickers get reused, so a listing that has ended comes back with its last trading day, like ASX:CCL@2021-05-11, and sending that symbol always gets you the same instrument. To look a ticker up as it was on a given day, put the day in the symbol, as in ASX:CCL@2021-01-04, or send listedOn=2021-01-04 to date every symbol in the call.
Errors
When a whole request fails you get an error status and an RFC 9457 application/problem+json body. Branch on code, which never changes, rather than on the title or detail, which we may reword.
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "The request has invalid parameters",
"code": "invalid_request",
"requestId": "req_34Voy9DCjEWXnyooRZsd8",
"errors": [
{
"location": "query.from",
"message": "must be a date like 2026-09-01"
}
]
}
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | A parameter is missing or malformed. errors says which one and why |
| 400 | invalid_cursor | The cursor isn't valid. Start again without one |
| 401 | missing_api_key | No Authorization header was sent |
| 401 | invalid_api_key | The key doesn't exist, was revoked or isn't sent as a bearer token |
| 401 | key_expired | The key is past its expiry date |
| 402 | trial_credits_exhausted | The trial's credits ran out on this request, so the trial has ended. Choose a plan in billing to carry on |
| 402 | trial_ended | The trial has ended and the API is paused until you choose a paid plan or the free plan in billing |
| 402 | credits_exhausted | Your plan's credits for this period are used up. Retry-After says when they reset |
| 402 | spend_cap_reached | Your organisation hit its spend cap for this billing period |
| 402 | payment_failed | Stripe couldn't collect a payment after retrying. Update your card in billing |
| 402 | no_active_plan | This organisation needs a paid plan, since its creator's one free organisation is another one |
| 403 | plan_upgrade_required | Your plan doesn't include this endpoint, or dates that far back |
| 403 | endpoint_not_allowed | The key isn't allowed to call this endpoint |
| 403 | ip_not_allowed | The request came from an IP outside the key's allowlist |
| 403 | org_suspended | The organisation is suspended |
| 404 | not_found | The path or the item you asked for doesn't exist |
| 429 | key_rate_limited | The key went over its own rate limit |
| 429 | org_rate_limited | Your organisation went over its plan's rate limit |
| 429 | credit_budget_exhausted | The key used up its credit budget for the day, week or month |
| 500 | internal_error | Something went wrong on our side |
| 503 | data_pending | We're still fetching this data. Retry after the Retry-After header |
Inside a batch, a symbol that fails gets its own entry in errors with one of these codes, and you aren't billed for it.
| Code | Meaning |
|---|---|
invalid_symbol | The identifier isn't in a format we recognise |
instrument_not_found | Nothing matches the identifier |
not_supported | The dataset doesn't apply to this instrument, like dividends on a currency pair |
data_unavailable | None of our sources have this data |
data_pending | We're fetching it now. Ask again shortly |
data_failed | Every source failed for this item. Try again later |
Credits
Every endpoint has a weight in credits, shown in the API reference and in the x-credits field of the OpenAPI spec. Most endpoints charge the weight for each instrument that resolves, and the rest charge per call, page or series. History length doesn't change the price.
meta.credits is what the call cost. The Credits-Remaining header is what's left under the tightest limit on your key. That's the key's own budget, the trial cap, your plan's monthly pool or your spend cap, whichever runs out first, and it reads 0 once you're into overage.
HTTP/2 200
Content-Type: application/json
Credits-Remaining: 4,998
The pricing page lists the cost of every endpoint.
Rate limits
Each plan has a requests-per-minute limit that every key in the organisation shares. You can give a key a lower limit of its own.
| Plan | Requests a minute |
|---|---|
| Trial | 60 |
| Free | 10 |
| Starter | 300 |
| Growth | 1,000 |
| Scale | 3,000 |
Going over returns a 429 with key_rate_limited or org_rate_limited and a Retry-After header in seconds. Wait that long before you retry.
The Free plan also keeps history to the last 5 years. Its responses carry a History-From header with the first date it serves, leaving out from starts there, and asking for an earlier date returns a 403 with plan_upgrade_required.
Batch requests
Endpoints that take symbols accept up to 100 comma-separated identifiers, and one symbol or fifty use the same call. Results come back in the order you sent them.
If a symbol fails it goes into errors and the rest of the call still succeeds. Only items that return data are billed, and a symbol sent twice in one call, like ASX:VAS,XASX:VAS, is billed once.
Fresh data
We keep everything we fetch and refresh it when someone reads it after it has gone stale, so most reads come from our cache. The first request for an instrument we've never seen waits while we fetch it, which makes it slower than the ones after it.
Add fresh=true to skip the cache and get the latest data during your call. Items refreshed this way are billed at 2x. If we can't refresh an item, it falls back to our cache and costs the normal price.
CSV
Add format=csv to any data endpoint to get text/csv instead of JSON. The body only has data rows, so the Batch-Errors header tells you how many inputs failed.
Pagination
List endpoints return a page at a time. Set the page size with limit, then send meta.nextCursor back as cursor to get the next page. On the last page nextCursor is null. There's no offset pagination.