Changelog
Dated changes to the API, the MCP server and the Python client, newest first.
This page lists changes that affect callers of the REST API, the MCP server and the Python client, newest first.
Each change is tagged Added, Changed, Fixed, Removed, Deprecated or Security. Breaking marks a change that requires callers to update their code.
Install the current client with pip install -U xfinlink. The current version is 0.13.1.
2026-10-11: Reporting currency, scale labels and request counting
- Added Fundamentals rows carry
currency, the three-letter code of the currency the row's money values are stated in, andfield_currency, which names any requested value in another currency than its row's. Values are not converted. See Fundamentals fields. - Changed On
/v1/fundamentals,meta.unitsreadsmillions_usdwhen every row on the page saysUSDand none carriesfield_currency, andmillions_reporting_currencyotherwise. A computed field isnullwhere its inputs are in two currencies. - Added
labelsonfilingrows carrymoney_scale_suspect, the money fields served as filed that thearchiverecord carries about 1,000 or 1,000,000 times larger or smaller, andmoney_scale_withheld, the money fields left empty because another filing of the period and thearchiverecord carry them at such a multiple. In the default view, a period with a field left empty is served by itsarchiverow where that row reportsrevenueornet_incomethat thefilingrow lacks, and otherwise the fieldsfilled_from_historicalcovers are filled from thearchiverow. - Changed
eps_scale_suspectis set onfilingandarchiverows, where an EPS figure exceeds 100,000 per share in absolute value or is about 1,000 or 1,000,000 times another measure of the same figure: on afilingrow, larger or smaller than thearchiverecord's figure, or larger than the company's other filings of the period or the row's ownnet_incomeper weighted share; on anarchiverow, larger than the row's own income per basic share.share_scale_suspectcompares a share count with thearchiverecord's count for the period as well as with the count implied bynet_incomeand EPS. - Added On
/v1/metrics, a metric that would set statement figures in two currencies against each other, or a figure in a currency other than USD against a USD close or market cap, or that is a per-share figure or money amount computed from such a figure, isnull, andmissing_inputsnames each such input as<input>:currency. See Metrics. - Changed A refused request does not count against the request quota and carries no quota headers: every
400, including too many tickers, an invalid parameter and a page past the plan's page limit, the402and the row-allowance429. A200,404or409counts one request per ticker sent. See Rate limits.
2026-10-10: Request checks, error bodies and share classes
- Added Every error body from the data endpoints and
/v1/statuscarriescode, the error code to branch on, witherror,statusanddetail.errorkeeps its value: a short sentence on the request-limit andTemporarily blocked429responses, on the page-limit400and on the403, and the code on every other error. The401,403, page-limit400and those429bodies also carrymessage, and every409carrieshint. See Errors. - Changed Dates are real calendar dates from
0001-01-01to9999-12-31. Fundamentals takesstartandendfrom0002-01-01to9999-11-30, and metrics takesstartandendfrom0005-01-01to9999-11-30andas_offrom0005-01-01. A date outside these ranges, anendbeforestart, alimitthat is not a whole number from 1 to 999,999,999, a negativeoffset, aninclude_amendments,include_derivativesorinclude_uncodedvalue other thantrueorfalse, a holdingsquarterthat is not a calendar quarter end, a fundamentalsperiod_endthat is not a real date, and a metricsvieworversionother thanlatestandrestatedreturn400with adetailthat names the parameter. These checks skip an empty value, such aslimit=, and on the paid endpoints a Free key gets their400before the402. - Added
warningsnames a parameter the endpoint does not read, with the parameter meant where the name is a common variant, and a Free request that reaches before the one-year floor. - Changed On
/v1/metrics, a ticker that names more than one security on the date returns409, as on/v1/pricesand/v1/resolve./v1/fundamentals,/v1/insidersand/v1/holdingsserve the company when a ticker names several share classes of one company, with a warning, and return409when it names more than one company. - Changed On
/v1/fundamentals,/v1/metricsand/v1/insiders, rows requested by an issuer'sentity_idcarry thetickerandentity_nameof its primary listed class, chosen as described on Tickers and entities. Metrics requested by issuer id are priced on that class. On/v1/holdings, rows requested by issuer id or served at the company level carry each class's own ticker and the company's name. - Changed
/v1/statusanswers methods other thanGETandOPTIONSwith405, and every405carries anAllow: GETheader. - Changed
%and_in searchq,insider_name,insider_role, holdingsmanagerand manager searchsearchmatch those characters. - Changed
/v1/holdingsserves a request that carriessecurity_class, which is not a filter, with a warning. - Added A comma-separated ticker list in the path may be percent-encoded, as in
AAPL%2CMSFT. - Added MCP:
get_pricestakesinterval,get_fundamentalstakesviewandas_of,get_metricstakesas_of,get_insiderstakesinclude_derivativesandinclude_uncoded, andget_holdingstakessource.resolve_tickertakesdateand returns the same body as/v1/resolvewith your key. A tool error carries the REST error body,codeincluded. See MCP server.
2026-10-10: Institutional holdings amendments
- Changed In the default view of
/v1/holdings,/v1/managers/{manager_id}/holdingsand the MCPget_holdingstool, a 13F amendment marked as adding new holdings entries adds them to the report it amends: a position stated in both is their sum, andlinescounts the lines of both filings. Such an amendment replaces the positions it states instead when it repeats an entry already in force with the same quantity, has more than a quarter as many lines as the holdings in force, states more than a quarter of the positions in force, or states more than a tenth of the positions it shares with them within 5% of the quantity already held. An amendment marked as a restatement replaces the report, and any other amendment replaces the positions it states. Withinclude_amendments=true, each filing's figures are their own row; withinclude_amendments=false, the report's figures. See Holdings.
2026-10-10: Fundamentals column definitions
- Changed On
filingrows,revenueis the filing's top line; a total smaller than one of its own parts in the same filing is not used. For a bank, it is total net revenue: the bank's own total where it reports one, otherwise net interest income plus noninterest income, andnullwhere that total is negative. - Changed
interest_expenseis total interest expense as the filer reports it, for a bank including interest on deposits. Where the filer reports only a net interest figure, it is the net interest cost as a positive number; net interest income is not reported as interest expense. - Changed
pretax_incomeis the sum of domestic and foreign pretax income where the filer reports only those two. - Changed
cash_and_equivalentsis the balance sheet's cash and cash equivalents or, where the balance sheet has no such line, the cash-flow statement's total of cash, cash equivalents and restricted cash, or the balance sheet's cash or cash and due from banks line. Any of these three above the filing's total assets is not used, and for a blank-check company whose balance sheet shows its trust account separately, cash held in the trust is not included. - Changed On
filingrows,property_plant_equipment_netis net utility plant for a utility and net investment property for a real-estate company; either line above the filing's total assets is not used. - Changed
pension_liabilitiesis one line the filing reports, usually the noncurrent amount: the noncurrent line for pension and other post-retirement benefits together where the filing reports that line; otherwise the pension line alone; otherwise another benefit line, such as other post-retirement benefits, post-employment benefits, or a combined line that also includes post-employment benefits or the current amount. Asset-retirement obligations, accrued payroll and deferred compensation are not included. - Changed
dividends_paid_commonis the cash dividends paid to common shareholders or, where the filing reports no payment of common dividends, the common dividends it declares.dividend_yieldand the other metrics built on it follow. See Fundamentals fields.
2026-10-07: Cursor paging
- Changed A
next_cursorvalue works only with the request that returned it: the same path and the same parameters, withlimitfree to change. A cursor the API did not issue, an altered cursor, or a cursor from another request or endpoint returns HTTP 400 with adetailthat beginsInvalid cursor. An emptycursor=returns the first page. - Added
meta.has_moreandmeta.next_cursoron every cursor endpoint:/v1/prices,/v1/fundamentals,/v1/metrics,/v1/insiders,/v1/holdings,/v1/managers/{manager_id}/holdingsand/v1/managers. Endpoints that also return a top-levelnext_cursorkeep it, with the same value./v1/searchreturnsmeta.has_more. - Changed The plan's cap on pages per query applies to
/v1/metricsand/v1/insidersas well as the other cursor endpoints. - Changed Row order on every paged endpoint ends in a unique key, so a row never repeats across pages.
2026-10-07: Per-share metrics on the price date's share basis
- Added
meta.per_share_basison/v1/metrics, with the valuesplit_adjusted_to_price_date. - Changed Earnings per share enters per-share metrics on the share basis of the price date where the filing's own weighted share count confirms the basis it reported. Where it does not, the row's EPS enters as filed and the metrics computed from it follow.
- Changed Growth metrics compare with the period ending a year earlier (
yoy), a quarter earlier (qoq) or the trailing twelve months ending a year earlier (ttm_yoy), selected by date. A derived fourth quarter is compared like any other quarter. Where no such period exists the metric isnullandmissing_inputsnamesprior:<field>. - Added
missing_inputsreasonseps_diluted:scale_suspect,eps_basic:scale_suspect,shares_outstanding:depositary_countandmarket_cap:<entity_id>:depositary_count. - Removed
missing_inputsreasonsderived_q4:no_prior_comparisonandderived_q4:window_contains_derived.
2026-10-07: Full docs in one file
- Added
https://xfinlink.com/llms-full.txtholds every page of these docs as Markdown, in sidebar order. It is served at the same path onhttps://api.xfinlink.com. See llms.txt.
2026-10-07: API keys in the Authorization header
- Added The REST API and the MCP server accept the API key in the
Authorizationheader asBearer YOUR_API_KEY, as well as in theX-API-Keyheader and theapi_keyquery parameter. When a request carries a key in more than one place,X-API-Keyis read first, thenapi_key, thenAuthorization. See Authentication. - Changed A request that carries a valid API key is served even when its IP address is temporarily blocked. Requests from that address without a valid key are still refused with
Temporarily blocked. See Errors.
2026-10-04: Derived fourth quarters and metric reasons
- Added On Fundamentals, a quarterly row whose
labelscarryderived_q4: trueis a fourth quarter derived as the fiscal year reported on the 10-K less the first nine months reported on the same fiscal year's third-quarter 10-Q. It is present only where no fourth-quarter row from a filing or the archive is on record for that fiscal year. Its income-statement and cash-flow lines are those differences, listed inderived_from_ytd; its balance-sheet lines are the 10-K's year-end figures;eps_basic,eps_diluted,shares_outstandingandweighted_avg_shares_basicarenull. The row readsfiscal_periodQ4,sourcefilingandversionoriginal, with the 10-K'sfiling_date. - Added On Metrics, every row carries
ttm_includes_derived_q4:truewhere the four quarters behind its trailing-twelve-month figures include a derived fourth quarter. - Added Quarterly,
ttmanddailymetrics rows carryttm_eps_basiswhere a trailing EPS was not the four quarters summed: per EPS-based metric, one entry per window read,quarterly_sumorannual_as_filed:<period_end>(the EPS filed on that fiscal year's annual report). - Changed Where a quarter's EPS in a trailing window is
null, the trailing EPS is the EPS filed on the annual report when the four quarters span exactly that fiscal year, or when the window's only missing EPS is a derived fourth quarter's.pe_ratioand the other EPS-based metrics read it. - Added New reasons in
missing_inputs:close:not_common_equity(pe_ratioandpeg_ratioarenullwhere the priced security is not common stock, a depositary receipt or a fund share),derived_q4:no_prior_comparison,derived_q4:window_contains_derived,eps_diluted:zero,eps_diluted:negative,shares_outstanding:zero,prior:<field>:zeroandeps_diluted_growth:zero. See Metrics. - Changed
grahams_numberisnull, not0, at zero EPS.
2026-10-01: Financial statements before 1970
- Added
/v1/fundamentalsand/v1/metricsreturn annual periods from 1950-06-30 and quarterly periods from 1962-01-31. The added rows carrysource: "archive"and fill periods that had no row. See Data coverage and boundaries. - Changed On
/v1/metrics, growth figures such asrevenue_growthon the first periods after the added rows take those rows as the prior period. - Added An
archiverow carriesknowledge_time_basis:observedorbulk_loadwhere the row has a publication date on record,nonewhere it has none.bulk_loadmarks a figure that was public before 1986-12-14, the day the point-in-time record opens. Afilingrow does not carry the key; its clock isfiling_date. - Changed A row whose
knowledge_time_basisisnonecarriesfiling_date: nulland is returned by the default view alone. On/v1/fundamentals,as_of,view=as_reportedandversion=originalreturn nothing for those periods; on/v1/metrics,as_ofreturns nothing for them. Point-in-time versions run from 1970-06-30 for annual periods, with a few annual periods from 1968-06-30, and from 1973-09-30 for quarterly periods. See Point-in-time and as-of. - Added Every
/v1/fundamentalsand/v1/metricsresponse carries afundamentals_historicalentry inmeta.freshness, alongsidefundamentalsandfundamentals_point_in_time./v1/statuslists it, for nineteen datasets in all.
2026-10-01: Python client 0.13.1
- Changed On HTTP
401and403,XfinlinkErrorcarries the API's message for the cause. - Changed A call that needs a key raises
XfinlinkErrorwithout sending a request when no key is set, or when the key contains characters outside ASCII, such as the masked key text on the dashboard.search()andresolve()run without a key.
2026-09-30: API key errors and MCP index snapshots
- Changed Every
401names its cause inmessage: no key, a key under another header or parameter name, an incomplete key, a revoked key (with the date it was revoked) or an unrecognised key. The body keeps"error": "unauthorized". See Authentication. - Changed
/v1/resolveand/v1/searchreturn401for a key sent inX-API-Keyorapi_keythat is incomplete, revoked or unrecognised. A request with no key is still answered as a keyless request. - Fixed MCP key errors repeat the request's JSON-RPC
id, or leave it out when the request has none, and carry the same messages as the REST API. - Fixed The MCP
get_indextool applies the plan'sas_ofallowance, as the REST API does: 50 distinct dates per UTC day on Free and no limit on paid plans.
{"error":"unauthorized","message":"No API key was sent. Pass your key in the X-API-Key header or the ?api_key= query parameter. Get one free at https://xfinlink.com/signup"}2026-09-19: API and client 0.13.0
Response metadata
Added Every data response carries meta.knowledge_time and meta.freshness, and carries meta.as_of when the request fixes a date. freshness holds one {dataset, through, promoted_at} entry for each dataset behind the response. Read coverage from freshness rather than assuming a fixed end date. /v1/status returns the entries for every dataset. See Response metadata and Status and freshness.
"knowledge_time": "2026-09-26T12:48:18Z",
"as_of": "2026-09-25",
"freshness": [
{"dataset": "prices", "through": "2026-09-25", "promoted_at": "2026-09-21T14:19:25Z"},
{"dataset": "price_adjustments", "through": "2026-09-25", "promoted_at": "2026-09-18T19:07:02Z"},
{"dataset": "shares_outstanding", "through": "2026-09-23", "promoted_at": "2026-09-24T08:30:33Z"}
]New parameters
- Added
as_ofon/v1/fundamentalsand/v1/metricsreturns the figures knowable on that date. - Added
viewon/v1/fundamentalstakeslatest(one row per period) oras_reported(every filed version of each period). - Added
dateon/v1/resolveresolves a ticker as it stood on that date. - Added
sourceon/v1/holdingstakeshistoricalorfilingand asks that era for its own figures for a quarter. - Changed Breaking
include_amendmentson/v1/holdingsand/v1/insiderstakes three states. Omitted returns the version in force,trueadds the superseded versions, andfalsereturns originals only.meta.amendment_viewreadscurrent,allororiginals. - Added
include_derivatives=trueon/v1/insidersadds derivative-security lines.meta.derivative_linesreadsincluded. - Added
include_uncoded=trueon/v1/insidersadds archive-era rows that carry no SEC transaction code (see "Insider transactions in two eras").
view=as_reported returns one row per filed version. Microsoft's fiscal 2022 revenue, in millions of USD, from GET /v1/fundamentals/MSFT?period=annual&start=2022-06-30&end=2022-06-30&view=as_reported&fields=revenue:
{
"data": [
{"entity_id": 8611, "ticker": "MSFT", "entity_name": "MICROSOFT CORP", "gics_sector": "Information Technology", "period_end": "2022-06-30", "period_type": "annual", "fiscal_year": 2022, "fiscal_period": "FY", "filing_date": "2022-07-28", "version": "original", "source": "filing", "revenue": 198270},
{"entity_id": 8611, "ticker": "MSFT", "entity_name": "MICROSOFT CORP", "gics_sector": "Information Technology", "period_end": "2022-06-30", "period_type": "annual", "fiscal_year": 2022, "fiscal_period": "FY", "filing_date": "2023-07-27", "version": "restated", "source": "filing", "revenue": 198270},
...
],
"meta": {
...
"count": 4,
"units": "millions_usd",
"entity_id_basis": "issuer",
"view": "as_reported",
...
}
}Share classes are separate securities
Changed Breaking entity_id names one share class on prices, resolve, search, index and holdings, and the company (the issuer) on fundamentals, metrics and insiders. Every response states which in meta. See Tickers and entities.
Changed Breaking A ticker that names two or more listed share classes on the resolution date, with nothing else in the request to serve, returns 409 ambiguous_ticker with a candidates list. Pass one of those IDs as entity_id. Dot and hyphen class forms both resolve: BRK.B and BRK-B return the same security.
{
"error": "ambiguous_ticker",
"status": 409,
"detail": "BRK names more than one security on 2026-09-25. Request by entity_id to choose one.",
"candidates": {
"BRK": [
{"entity_id": 10006133, "name": "BERKSHIRE HATHAWAY INC DEL", "exchange": "NYSE", "share_class": "A", "last_bar_date": "2026-09-25", "valid_from": null, "valid_to": null},
{"entity_id": 10025221, "name": "BERKSHIRE HATHAWAY INC DEL", "exchange": "NYSE", "share_class": "B", "last_bar_date": "2026-09-25", "valid_from": null, "valid_to": null}
]
}
}New fields
- Added Prices:
price_basis,ca_source,dividend_type,split_ratio_suspect,shares_as_of,shares_source,shares_stale, andmeta.delistingon a series that ends at a recorded delisting. - Added Resolve and search:
entity_grain,issuer_entity_id,share_class,instrument_type,exchange,classification_as_of(insideclassifications) andticker_valid_to. - Added Index constituents and events:
member_grain,ticker_basis, andmeta.coverage_end. - Added Holdings:
manager_id_space,manager_number,filer_cik,issuer_entity_id,link_method,title_of_class(the filer's own class text),issuer_name_as_filed,accession_number,vintage_date,unit_scale_suspect,is_currentandlines. - Added Insiders:
officer_title_as_filed,is_derivative,is_current,security_title,nature_of_ownership,reporting_owners,is_weekend_dated,is_dated_after_own_filingandrule_10b5_1_affirmed. - Added Fundamentals:
derived_from_ytd, andfilled_from_historicalas a per-row list of field names.
Indexes
Changed Breaking /v1/index/{index_name} and /v1/index/{index_name}/events serve sp500, ndx100 and djia. Any other index returns 400 with the list of available ones. For djia and ndx100, a date after meta.coverage_end returns 400 naming that date.
{
"error": "bad_request",
"status": 400,
"detail": "Unknown index: dax. Available: djia, ndx100, sp500"
}{
"error": "bad_request",
"status": 400,
"detail": "djia membership is recorded through 2026-09-26. Request a date on or before 2026-09-26."
}Cost of revenue
Changed cost_of_goods_sold is the cost the filing tags for goods and services sold. cost_of_revenue is the total cost of revenue. Where a filing tags a product-only cost of goods sold together with its own gross profit, cost_of_revenue is revenue less that gross profit, and the row lists derived_from_filed: ["cost_of_revenue"]. cost_of_goods_sold stays the tagged figure.
Added Three per-row lists now name where a value came from: filled_from_historical, derived_from_ytd and derived_from_filed. Microsoft's fiscal 2016 row from GET /v1/fundamentals/MSFT?period=annual&start=2016-06-30&end=2016-06-30, in millions of USD:
{
"entity_id": 8611,
"ticker": "MSFT",
...
"period_end": "2016-06-30",
...
"fiscal_year": 2016,
...
"revenue": 85320,
"cost_of_revenue": 32780,
"cost_of_goods_sold": 17880,
"gross_profit": 52540,
...
"derived_from_filed": ["cost_of_revenue"]
}Insider transactions in two eras
Added /v1/insiders serves an archive era and a filing era in one series. Each row's source reads archive up to meta.archive_era.through and filing from meta.archive_era.seam. Archive rows carry source_quality_code.
Added transaction_code uses the SEC Form 4 transaction codes. Archive rows without such a code are left out by default and counted in meta.uncoded_rows_excluded. include_uncoded=true returns them with transaction_code set to null. /v1/status lists insider_transactions_historical alongside the other datasets.
Requested names that are not returned
Changed Breaking meta.fields_not_available on /v1/fundamentals and meta.metrics_not_available on /v1/metrics name requested fields and metrics that the response does not carry. fields= accepts those names, and rows omit them.
Changed Manager search by name (/v1/managers) covers managers that have filed Form 13F from 2013 onward. A manager that appears only in earlier quarters is reached by ID at /v1/managers/{manager_id}/holdings.
Errors
Fixed 409 ambiguous_ticker, 504 query_timeout and 504 request_timeout return JSON bodies with error, status and detail, like other errors. 500 internal_error returns error and status. Errors shows each body.
Python client 0.13.0
New keyword arguments:
- Added
fundamentals(view=, as_of=) - Added
metrics(as_of=) - Added
resolve(date_=) - Added
holdings(source=) - Added
insiders(include_derivatives=, include_uncoded=)
No function or argument was removed, and no Python default changed.
Changed llm_context() returns the current llms.txt.
Changed Breaking What a default call sends for include_amendments did change. In 0.13.0, insiders() sends include_amendments=false (originals only) and holdings() sends include_amendments=true (every filed version). Client 0.12.1 left the parameter out of both, which returns the version in force. Pass include_amendments=None to get the version in force from 0.13.0.
import logging
import xfinlink as xfl
logging.basicConfig(level=logging.INFO, format="%(message)s")
xfl.set_api_key("YOUR_API_KEY")
xfl.insiders("AAPL", start="2026-09-01", max_rows=1)
xfl.holdings("AAPL", quarter="2026-06-30", max_rows=1)
xfl.insiders("AAPL", start="2026-09-01", include_amendments=None, max_rows=1)
xfl.holdings("AAPL", quarter="2026-06-30", include_amendments=None, max_rows=1)HTTP Request: GET https://api.xfinlink.com/v1/insiders/AAPL?limit=1&start=2026-09-01&include_amendments=false "HTTP/1.1 200 OK"
HTTP Request: GET https://api.xfinlink.com/v1/holdings/AAPL?limit=1&quarter=2026-06-30&include_amendments=true "HTTP/1.1 200 OK"
HTTP Request: GET https://api.xfinlink.com/v1/insiders/AAPL?limit=1&start=2026-09-01 "HTTP/1.1 200 OK"
HTTP Request: GET https://api.xfinlink.com/v1/holdings/AAPL?limit=1&quarter=2026-06-30 "HTTP/1.1 200 OK"2026-07-30: Date-aware ticker resolution
Changed /v1/prices, /v1/fundamentals, /v1/metrics, /v1/insiders and /v1/holdings resolve a ticker on the dates of the request window. A window that ends before the ticker's current holder acquired it returns the company that held the ticker then, and historical_resolution names the entity served and the ticker's current holder. Pass entity_id to choose an entity outright. The change applies to every client version. See Tickers and entities.
2026-07-30: Python client 0.12.1
Fixed A call that passes end without start or period gets a default window of one year ending on that end date. prices(), fundamentals(), metrics(), insiders(), holdings() and manager_holdings() all follow this rule. A call with start, with period, with an end after today, or with no dates is unaffected.
2026-07-30: Python client 0.12.0
Added prices(), fundamentals(), metrics(), insiders() and holdings() take an entity_id argument in place of a ticker. It accepts an integer, a list or tuple of integers, or a comma-separated string such as "3165,4". Passing both entity_id and a ticker raises XfinlinkError before any request is sent. Each id counts as one ticker against the plan's per-request limit. Find ids with resolve() or search(). See Tickers and entities.
Was this page helpful?
Thank you for helping improve xfinlink documentation.
- Need help? Get in touch.
- Building with an LLM? Read llms.txt.
- Using Claude or ChatGPT? Connect the MCP server.