Stock scores your code — and your agent — can call

One REST API for our AI Scores, the graded evidence behind them, the fundamentals they are built from, and the daily bars they are measured on. Plus a published MCP server, so an assistant in Claude, Cursor, Goose, OpenCode or Antigravity can use the same data with no glue code of yours.

Create an account to get a keyOpenAPI specMCP setupSEC filingsWorked examples

Keys belong to an account, so that you can revoke one, see what it is using, and move it between plans. Signing up takes a minute and needs no card — the free tier is 500 requests a day and stays free. Already registered? Sign in.

Read this before you build a strategy on it

The AI Score ranks covered companies against each other at a point in time. It is not a forecast, a price target or a probability. Its measured forward ranking power is small and concentrated in short horizons — /v1/track-record returns the real figures, including the horizons where it has shown none, and /proof explains them in plain language. Nothing here is investment advice, and no endpoint places a trade.

Quickstart

Create a key at /account/api-keys. It is shown once — we store only a hash, so we cannot show it to you again.

bash
export BMI_KEY=bmi_live_your_key

curl -H "Authorization: Bearer $BMI_KEY" \
  https://brimindinvest.com/api/v1/scores/NVDA
PowerShell
$env:BMI_KEY = "bmi_live_your_key"

Invoke-RestMethod -Uri "https://brimindinvest.com/api/v1/scores/NVDA" `
  -Headers @{ Authorization = "Bearer $env:BMI_KEY" } | ConvertTo-Json -Depth 5

Authentication

Send the key as Authorization: Bearer bmi_live_… or X-API-Key: bmi_live_…. Bearer wins if both are present. Keys never expire unless you set an expiry, and revoking one takes effect immediately for new verifications — see the FAQ on the 60-second cache. Keep keys server-side: the API sends Access-Control-Allow-Origin: * so you can call it from a browser, but a key in front-end code is a key you have published.

Endpoints

Every success is { data, meta }. Every failure is { error: { type, message, docs } }. Branch on error.type, never on the message text. All ratios are fractional: 0.184 means 18.4%.

GET/v1/scores/{symbol}

AI Score, verdict band, universe rank and the six factor scores for one stock.

curl -H "Authorization: Bearer $BMI_KEY" \
  https://brimindinvest.com/api/v1/scores/NVDA
Response
{
  "data": {
    "symbol": "NVDA",
    "score": 81,
    "rank": 62,
    "band": { "key": "strong-buy", "label": "Strong Buy" },
    "factors": {
      "value": 23, "growth": 94, "profitability": 88,
      "health": 76, "momentum": 71, "risk": 44
    },
    "asOf": "2026-09-25T00:00:00.000Z"
  },
  "meta": { "ratiosAreFractional": true }
}
GET/v1/scores?symbols=

Batch lookup. Results come back in the order you asked for; anything outside the scored universe is listed in notFound rather than dropped.

curl -H "Authorization: Bearer $BMI_KEY" \
  "https://brimindinvest.com/api/v1/scores?symbols=NVDA,AMD,INTC"
Response
{
  "data": [ { "symbol": "NVDA", "score": 81, ... }, ... ],
  "notFound": ["XYZQ"],
  "meta": { "requested": 4, "returned": 3, "truncatedToPlanLimit": null }
}
GET/v1/scores?rank=top

Screen the ranking. rank=top|bottom, with optional limit, minScore and maxScore.

curl -H "Authorization: Bearer $BMI_KEY" \
  "https://brimindinvest.com/api/v1/scores?rank=top&limit=25&minScore=80"
GET/v1/scores/{symbol}/history

Every score we have published for a symbol, oldest first. Never revised after publication, so it is an audit trail rather than a backtest.

curl -H "Authorization: Bearer $BMI_KEY" \
  "https://brimindinvest.com/api/v1/scores/NVDA/history?days=365"
GET/v1/track-record

The graded forward performance of the score against SPY: hit rate, excess return, within-day rank correlation and per-band results at 5, 21 and 63 trading days.

curl -H "Authorization: Bearer $BMI_KEY" \
  https://brimindinvest.com/api/v1/track-record
GET/v1/fundamentals/{symbol}

The valuation, growth, margin, balance-sheet, risk and dividend metrics the score is computed from, grouped by theme.

curl -H "Authorization: Bearer $BMI_KEY" \
  https://brimindinvest.com/api/v1/fundamentals/NVDA
GET/v1/prices/{symbol}

Daily closes and volume, one bar per trading day. The same bars /v1/track-record is graded on, so a backtest built here reproduces our published figures.

curl -H "Authorization: Bearer $BMI_KEY" \
  "https://brimindinvest.com/api/v1/prices/NVDA?days=730"
GET/v1/symbols?q=

Resolve a company name or partial ticker. Searches our own universe, so every result is answerable by the rest of the API.

curl -H "Authorization: Bearer $BMI_KEY" \
  "https://brimindinvest.com/api/v1/symbols?q=nvidia"
GET/v1/me

What this key is, which plan it is on, and how much of today's quota is left.

curl -H "Authorization: Bearer $BMI_KEY" \
  https://brimindinvest.com/api/v1/me

SEC filings

The same account and the same key read SEC filings: annual and quarterly reports, 8-K events, Form 4 insider transactions, Form 13F holdings and the segment figures a filer tagged in XBRL, straight from EDGAR. Nothing is vendored and nothing is summarised — you get the filer's own words, normalized and cut into pieces a model can hold.

Three things make that different from downloading the document yourself. Every chunk carries a citation: an accession number, a paragraph index and a character range in the normalized text, so a quote can be traced back and checked rather than trusted. You set the token budget, and a section comes back in pages that respect it instead of a megabyte that blows the context window. Offsets travel with a parser version, and each version's parse is kept, so a citation issued today still names the same words after the parser changes.

Want to see it before you write any code? The filing explorer walks the first three calls in the browser, needs no key, and shows the citation and the token count for every chunk it renders. For the whole exchange written out — the calls, the responses and what each answer cost in tokens — five worked examples record real runs against this service.

GET/v1/filings?ticker=

A filer's filings, newest first. The entry point: everything else needs an accession number. Filter by form and date; asking for 10-K also returns 10-K/A, because an amendment is the current truth for that period.

curl -H "Authorization: Bearer $BMI_KEY"   "https://brimindinvest.com/api/v1/filings?ticker=AAPL&form=10-K&limit=3"
Response
{
  "data": [
    {
      "accession": "0000320193-25-000079",
      "form": "10-K",
      "filed_at": "2025-11-01",
      "period": "2025-09-27",
      "items": [],
      "primary_document": "aapl-20250927.htm",
      "source_url": "https://www.sec.gov/Archives/edgar/data/320193/000032019325000079/"
    }
  ],
  "meta": { "company": "Apple Inc.", "returned": 3, "truncated": false }
}
GET/v1/filings/{accession}/sections

The section outline of one filing: what is in it, how long each part is, and where each one starts and ends in the normalized text. This is the call to make before asking for text, so a budget can be spent on the section that matters. `is_stub` marks a section under 500 characters, which is usually the filer saying "none" or pointing at the proxy statement.

curl -H "Authorization: Bearer $BMI_KEY"   https://brimindinvest.com/api/v1/filings/0000320193-25-000079/sections
Response
{
  "data": [
    {
      "section_name": "item_1a_risk_factors",
      "label": "Item 1A. Risk Factors",
      "part": "I",
      "item": "1A",
      "char_start": 14982,
      "char_end": 73551,
      "char_length": 58569,
      "paragraph_count": 118,
      "is_stub": false
    }
  ],
  "meta": {
    "form": "10-K",
    "parser_version": 2,
    "normalized_char_length": 205471,
    "source_url": "https://www.sec.gov/Archives/..."
  }
}
GET/v1/filings/{accession}/sections/{section}

One section as chunks that fit a token budget you set. Paragraphs are never split unless one alone exceeds the budget, and then the pieces say so. Follow `next_page_token` until it is null. Accepts the full id, "Item 1A" or "item_1a".

curl -H "Authorization: Bearer $BMI_KEY"   "https://brimindinvest.com/api/v1/filings/0000320193-25-000079/sections/item_1a_risk_factors?max_tokens=1500"
Response
{
  "data": [
    {
      "text": "The Company's business, reputation, results of operations...",
      "para_idx": 0,
      "char_start": 15042,
      "char_end": 16380,
      "token_count": 271,
      "citation": "AAPL 10-K 2025-11-01 (0000320193-25-000079) Item 1A, para 0, chars 15042-16380"
    }
  ],
  "meta": {
    "section_name": "item_1a_risk_factors",
    "parser_version": 2,
    "tokenizer": "o200k_base",
    "paragraph_count": 118,
    "section_token_count": 14238,
    "max_tokens": 1500,
    "returned": 6,
    "returned_tokens": 1487,
    "next_page_token": "eyJhIjoiMDAwMDMyMDE5My0yNS0wMDAwNzki..."
  }
}
GET/v1/filings/diff

What changed in one section between two filings, paragraph by paragraph: added, removed and rewritten, each with a citation into the filing it came from. The structural answer to "what is new in this year's risk factors", without sending both documents to a model and asking it to spot the difference.

curl -H "Authorization: Bearer $BMI_KEY"   "https://brimindinvest.com/api/v1/filings/diff?accession_a=0000320193-24-000123&accession_b=0000320193-25-000079&section=item_1a_risk_factors"
GET/v1/filings/cross-section

The same section across a filer's recent filings, in one call. Three years of MD&A without three round trips and without the caller having to resolve three accession numbers first.

curl -H "Authorization: Bearer $BMI_KEY"   "https://brimindinvest.com/api/v1/filings/cross-section?ticker=AAPL&section=item_7_mda&lookback=3"
GET/v1/filings/events

Recent 8-K events for one filer, tagged by the item number that was reported, so "did they change auditors" is a filter rather than a reading exercise. Item 5.02 is officer changes, 2.02 is results, and so on.

curl -H "Authorization: Bearer $BMI_KEY"   "https://brimindinvest.com/api/v1/filings/events?ticker=AAPL&lookback_days=90"
GET/v1/filings/insider

Insider transactions read out of Form 4 filings: who, what, how much, at what price, and whether it was an open-market trade or an award. Cluster buys are computed with a definition you can move, because one person's cluster is another's coincidence.

curl -H "Authorization: Bearer $BMI_KEY"   "https://brimindinvest.com/api/v1/filings/insider?ticker=AAPL&lookback_days=90&transaction_codes=open_market"
GET/v1/filings/holdings

Institutional holdings from Form 13F, either by manager or by stock: who holds it, how much, and how that changed from the quarter before. Ask by manager for a portfolio, by ticker for a shareholder list.

curl -H "Authorization: Bearer $BMI_KEY"   "https://brimindinvest.com/api/v1/filings/holdings?ticker=NVDA&quarter=latest"
GET/v1/filings/segments

Segment revenue, operating income and store counts as the filer tagged them, grouped by breakdown: by reportable segment, by product, by geography. Answers which part of the business grew, which a consolidated income statement cannot. Store and location counts come from the filing's own concept index, under topic=counts.

curl -H "Authorization: Bearer $BMI_KEY"   "https://brimindinvest.com/api/v1/filings/segments?ticker=WMT&lookback=3"
GET/v1/filings/sector

A company's revenue growth beside its industry's, period by period, with the difference in percentage points. The denominator is the US Census survey covering the filer's SIC code, summed over the exact fiscal period the company reported. Needs no API keys of its own: Census is read directly when CENSUS_API_KEY is set and through FRED's keyless mirror otherwise.

curl -H "Authorization: Bearer $BMI_KEY"   "https://brimindinvest.com/api/v1/filings/sector?ticker=WMT&periods=5"

Section names are stable ids like item_1a_risk_factors and item_7_mda, and /v1/filings/{accession}/sections lists the ones a given filing actually has. Large filers routinely incorporate Part III into a proxy statement, so a missing section is usually the filer's choice rather than a parser failure — which is why that endpoint reports what is present instead of promising a fixed list.

Plans and rate limits

The daily quota is counted exactly, per key, and resets at 00:00 UTC. The per-minute limit is an abuse brake applied per server process, so treat it as approximate. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset.

A request is one call on REST. On /api/mcp a JSON-RPC batch is charged one call for every tools/call it carries, with a floor of one, so a batch cannot buy ten tool calls for the price of one. The response states the charge in X-RateLimit-Cost whenever it is more than one, and protocol messages such as initialize and tools/list are covered by the floor rather than charged on top.

PlanRequests / dayRequests / minSymbols per batchScore & price history
Free500301090 days
Pro25,0003001001,825 days
Business250,0001,200500full history

The filings endpoints are capped on reach as well as on rate, and these are a different set of numbers from the score and price history above. A listing is narrowed to what the plan reaches and names the floor it used in meta.capped_by_plan; reading a single filing older than the plan reaches returns 403 plan_history_exceeded instead, because a section cannot be served partly and an empty body would read as a parse failure.

PlanFiling historyFilings per cross-filing callRedistribution
Free90 days2Not permitted
Pro1,825 days5Not permitted
Businessfull archive10Permitted

Redistribution is the line the paid plans are actually sold on: whether filing text this API returns may be republished inside another product. The filings are public domain and anyone may fetch them from EDGAR; what is licensed here is this parse of them, the citations and the offsets.

Free needs nothing but an account. Pro comes with a BriMindInvest subscription — create a key while subscribed and it is issued on the Pro plan. For Business volume or a commercial redistribution arrangement, get in touch.

Errors

error.typeHTTPMeansRetry?
missing_api_key401No Authorization or X-API-Key header was sent.No — add the header.
malformed_api_key401The value does not have the shape of one of our keys. Usually a truncated paste.No.
invalid_api_key401No key matches.No.
revoked_api_key401The key was revoked in the dashboard.No — create a new key.
expired_api_key401The key passed its expiry date.No — rotate it in the dashboard.
burst_limit_exceeded429Too many requests in one minute.Yes — after retryAfterSeconds.
quota_exceeded429The daily allowance is spent.Not until the 00:00 UTC reset.
invalid_request400A parameter is missing or out of range.No — fix the request.
symbol_not_scored404The symbol exists but has no published score. We never compute one on the fly.No.
not_found404No data stored for that symbol.No.
plan_history_exceeded403The filing, or the quarter, is older than your plan reaches.No — upgrade, or ask for a filing inside the window.
service_unavailable503We could not reach our own datastore.Yes.
internal_error500A bug on our side.Yes, once.

MCP server

@brimindinvest/mcp is a published MCP server: a thin client over the endpoints above, with no local computation and no cache, so it cannot disagree with this API or with the site about what a score is. What it adds is output formatted for a model to read, and tool descriptions that send the model to the track record instead of letting it dress a ranking up as a prediction.

No install step — npx fetches it on first run. Replace bmi_live_your_key with your key in each snippet.

Claude Desktop

Settings → Developer → Edit Config. Restart the app afterwards.

{
  "mcpServers": {
    "brimindinvest": {
      "command": "npx",
      "args": ["-y", "@brimindinvest/mcp"],
      "env": { "BRIMINDINVEST_API_KEY": "bmi_live_your_key" }
    }
  }
}
Claude Code

One command. Add --scope project to share it with your team via .mcp.json.

claude mcp add brimindinvest \
  --env BRIMINDINVEST_API_KEY=bmi_live_your_key \
  -- npx -y @brimindinvest/mcp
Cursor

~/.cursor/mcp.json for every project, or .cursor/mcp.json inside one.

{
  "mcpServers": {
    "brimindinvest": {
      "command": "npx",
      "args": ["-y", "@brimindinvest/mcp"],
      "env": { "BRIMINDINVEST_API_KEY": "bmi_live_your_key" }
    }
  }
}
Goose

goose configure → Add Extension → Command-line Extension, or edit ~/.config/goose/config.yaml.

extensions:
  brimindinvest:
    enabled: true
    type: stdio
    cmd: npx
    args: ["-y", "@brimindinvest/mcp"]
    envs:
      BRIMINDINVEST_API_KEY: bmi_live_your_key
    timeout: 300
OpenCode

opencode.json in the project, or ~/.config/opencode/opencode.json globally.

{
  "mcp": {
    "brimindinvest": {
      "type": "local",
      "command": ["npx", "-y", "@brimindinvest/mcp"],
      "enabled": true,
      "environment": { "BRIMINDINVEST_API_KEY": "bmi_live_your_key" }
    }
  }
}
Antigravity

Open the MCP configuration from the Agent panel's tools menu, then refresh the server list.

{
  "mcpServers": {
    "brimindinvest": {
      "command": "npx",
      "args": ["-y", "@brimindinvest/mcp"],
      "env": { "BRIMINDINVEST_API_KEY": "bmi_live_your_key" }
    }
  }
}
ToolWhat it does
get_stock_scoreScore, band, rank and factor breakdown for one stock.
compare_stock_scoresUp to 100 symbols in one request, ranked.
screen_stocksThe top or bottom of the ranking, optionally within a score range.
get_score_historyEvery score ever published for a stock.
get_track_recordThe graded evidence. Every tool description points the model here.
get_fundamentalsThe numbers behind the score.
get_price_historyDaily bars, the ones the track record is graded on.
search_symbolsCompany name to ticker, without guessing.
get_api_usagePlan and remaining quota.
get_composite_signalScore, momentum and insider activity as one directional read.
get_paper_portfolioThe paper account: positions, cash and realized result.
execute_paper_tradeA simulated trade. No real order ever leaves this API.
list_filingsA filer's filings, newest first. Where every filing question starts.
list_sectionsWhat is inside one filing, and how long each part is.
get_filing_sectionOne section, in chunks that fit a token budget, each one cited.
diff_sectionsWhat changed in a section between two filings, paragraph by paragraph.
cross_filing_sectionThe same section across several years, in one call.
recent_8k_eventsRecent 8-K events, tagged by the item reported.
insider_transactionsForm 4 activity, with a cluster-buy definition you can move.
institutional_holdingsForm 13F holdings, by manager or by stock.
get_segment_revenueRevenue, operating income and store counts per segment, product and country.
compare_to_sectorRevenue growth against the Census industry total for the filer's SIC code.

A tool list says what exists, not what using it looks like. Five worked examples take a question each and show the calls, the real responses and the token cost — including the two cases where the honest answer is that the filer disclosed nothing.

FAQ

What exactly is the AI Score?

A cross-sectional ranking. On each scoring run we rate every covered company on value, growth, profitability, financial health, momentum and risk, and the 0–100 score is where that company lands relative to the others in that run. It is not a price target, a probability, or a forecast of return.

Does it work?

Partly, weakly, and we publish the measurement rather than the claim. /v1/track-record returns hit rate against SPY, mean excess return and within-day rank correlation at 5, 21 and 63 trading days, along with the number of independent entry days behind each figure. The short horizon shows a small positive ranking signal; the longer ones are near zero. Read the numbers before you build on them.

Will a score I fetch today still be there tomorrow?

Yes. The API serves stored, published values only — it never computes a score on request. A symbol without a published score returns 404 symbol_not_scored instead of a number that would not reproduce. That is why the API is reproducible and why it is fast.

How are rate limits counted?

The daily quota is counted exactly, per key, and resets at 00:00 UTC. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. The per-minute limit is an abuse brake applied per server process, so treat it as approximate and do not build request pacing on it. On REST one request is one call. On the MCP endpoint a JSON-RPC batch is charged one call per tools/call it carries, with a floor of one, so a batch of ten tools/call costs ten; the response states the charge in X-RateLimit-Cost whenever it is more than one. Protocol messages — initialize, tools/list and ping — are covered by the floor and are not charged on top of the calls beside them.

Are percentages fractional or already multiplied?

Fractional, everywhere, in every field. 0.184 means 18.4%. Responses carry meta.ratiosAreFractional so a client can assert on it.

What happens when I revoke a key?

It stops working immediately for new verifications, but verified keys are cached for up to 60 seconds, so a request already in flight can succeed briefly. If you are rotating a leaked key, deploy the replacement and consider that minute.

How do I change a key without downtime?

Use Rotate in the dashboard rather than revoking. You get the new key straight away and the old one keeps working for 24 hours, so the swap can ride a normal deploy instead of a scramble. If the key has actually leaked, rotate and then revoke the old one immediately — the grace window is for convenience, not a safe place to leave a published credential.

Do keys expire?

Only if you ask them to. A key lives until you revoke it by default; you can set an expiry of 30, 90 or 365 days when you create one, or add, change and remove an expiry later. An expired key returns 401 expired_api_key and we email the account before it happens.

Can an API key create another API key?

No. Key management is session-authenticated only, from the dashboard. One leaked key can read data until you revoke it; it can never mint a second credential that survives the revocation.

Is the MCP server different from the REST API?

It is a thin client over the same endpoints — no local computation, no cache. It cannot disagree with this API or with the website about what a score is. What it adds is formatting for a model to read and tool descriptions that push the model towards the track record instead of towards a confident-sounding number.

Can I redistribute the data?

Caching for your own application is fine and encouraged. Republishing our scores as a dataset, or building a product whose primary value is reselling them, is not. If you want to do something in between, ask us first.

What about corporate actions in the price series?

Closes are as-reported and unadjusted for dividends. Splits are reflected. Because these are the same bars our own grading runs on, a backtest built on them reproduces our published figures instead of quietly diverging from them.

Terms

Use the data in your own applications, cache it as aggressively as you like, and show it to your users. Do not republish our scores as a dataset, resell them as your own product, or strip the attribution when you display them. Do not present a score as a recommendation or as advice — it is a ranking, and the track record says how much of a ranking. We may change or withdraw a key that is being used to redistribute the data wholesale, and we version the API so a breaking change arrives as /v2 rather than as a surprise inside /v1. Data is provided as-is, with no warranty of accuracy, completeness or fitness for any purpose.