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.

託管 MCP 伺服器

npx add-mcp 'https://api.quova.io/mcp'

可安裝到 Claude Code、Codex、Cursor 等客戶端

文件

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&currency=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.

KindExample
Listed securityNASDAQ:AAPL, LSE:VOD, XETRA:SAP or ASX:VAS. The MIC works too, as in XASX:VAS
Open identifiersisin:US0378331005 or figi:BBG000B9XRY4
Licensed identifierscusip:037833100 and sedol:... are accepted as input but never returned
Broker contract IDconid:265598, the ID Interactive Brokers gives a contract. Accepted as input but never returned
Unlisted fundapir:BTA0419AU
Listed optionOCC:AAPL250117C00150000 for a US option, with or without the OSI spaces, or ASX:BHPS49
FX pairFX:EURUSD
CryptoCRYPTO:BTC for the most liquid match, or SOLANA:<contract> for an exact token
NFTETHEREUM:<contract>/<token id> for one NFT, or the bare contract for its collection. SOLANA:<mint> on Solana
Index, rate, futureINDEX:AXJO, RATE:AU10Y, FUT:CL1
Quova IDins_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"
    }
  ]
}
StatusCodeMeaning
400invalid_requestA parameter is missing or malformed. errors says which one and why
400invalid_cursorThe cursor isn't valid. Start again without one
401missing_api_keyNo Authorization header was sent
401invalid_api_keyThe key doesn't exist, was revoked or isn't sent as a bearer token
401key_expiredThe key is past its expiry date
402trial_credits_exhaustedThe trial's credits ran out on this request, so the trial has ended. Choose a plan in billing to carry on
402trial_endedThe trial has ended and the API is paused until you choose a paid plan or the free plan in billing
402credits_exhaustedYour plan's credits for this period are used up. Retry-After says when they reset
402spend_cap_reachedYour organisation hit its spend cap for this billing period
402payment_failedStripe couldn't collect a payment after retrying. Update your card in billing
402no_active_planThis organisation needs a paid plan, since its creator's one free organisation is another one
403plan_upgrade_requiredYour plan doesn't include this endpoint, or dates that far back
403endpoint_not_allowedThe key isn't allowed to call this endpoint
403ip_not_allowedThe request came from an IP outside the key's allowlist
403org_suspendedThe organisation is suspended
404not_foundThe path or the item you asked for doesn't exist
429key_rate_limitedThe key went over its own rate limit
429org_rate_limitedYour organisation went over its plan's rate limit
429credit_budget_exhaustedThe key used up its credit budget for the day, week or month
500internal_errorSomething went wrong on our side
503data_pendingWe'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.

CodeMeaning
invalid_symbolThe identifier isn't in a format we recognise
instrument_not_foundNothing matches the identifier
not_supportedThe dataset doesn't apply to this instrument, like dividends on a currency pair
data_unavailableNone of our sources have this data
data_pendingWe're fetching it now. Ask again shortly
data_failedEvery 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.

PlanRequests a minute
Trial60
Free10
Starter300
Growth1,000
Scale3,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.