---
name: finance-reference
description: Official financial and regulatory reference data on demand, for tasks where a remembered number is a liability. Twenty-two endpoints covering SEC EDGAR — resolve a ticker to a CIK, list a company's recent 10-K, 10-Q or 8-K filings, pull a reported XBRL figure such as revenue, assets or net income, and full-text search filings back to 2001; corporate identity — turn a company or counterparty name into a Legal Entity Identifier, resolve a 20-character LEI code to its registered legal name, jurisdiction, legal form and address for KYC or onboarding, and answer who owns whom, from direct and ultimate parent to subsidiaries, for corporate hierarchy and counterparty exposure checks; official exchange rates — ECB daily reference rates for any date since 1999, single rate, conversion of an amount, or a time series, plus the US Treasury quarterly reporting rate federal agencies must use; inflation and labour statistics — CPI-U adjustment between two dates and raw BLS series by id; and compliance and trade lookups — EU VAT number validation against VIES, ABA routing number check digits, FDIC insured bank lookup, US Harmonized Tariff Schedule search and duty rates, state sales tax rates, NAICS industry codes. Use it when someone asks what a company actually reported, who a counterparty legally is or who its parent is, what a currency was worth on an invoice date, what $50,000 in 1995 is worth now, whether a VAT number or routing number is real, what duty a product attracts, or which SEC filings mention a phrase. Not a market data feed — there are no live stock, bond or crypto quotes here. Browsing, searching and reading a schema are free and need no wallet; a call costs a fraction of a cent in USDC over x402.
license: MIT
---

# fittings — finance and markets

Twenty-two services on `https://fittings.sh` that answer questions about companies, currencies,
prices over time, and the identifiers money moves through. Every one is sourced from a primary
regulator, statistical agency or register of record — SEC EDGAR, the ECB, BLS, the US Treasury, FDIC,
the USITC tariff schedule, the European Commission's VIES registry, GLEIF for legal entities — and
returns what that source says, with the date it says it as of. Browsing and searching cost nothing. A
call costs between $0.003 and $0.005 in this slice — nothing anywhere in the wider catalog exceeds
$0.01 — settled per request in USDC.

Live catalog for this slice, free to read and free to search:

```bash
curl -s "https://fittings.sh/catalog?category=Finance%20and%20markets"
curl -s "https://fittings.sh/catalog/search?q=exchange+rate+on+a+past+date"
node scripts/call.mjs --describe sec.ticker-financials   # full schema, price, source, license
```

## When to reach for this

Reach for it when the failure mode is being quietly, plausibly wrong. Financial figures are exactly
the shape of thing a model recalls with confidence and gets subtly out of date — a company's revenue
is a filed number that changes every quarter, an exchange rate changes every working day, a duty rate
changes with the tariff schedule, and a CPI ratio is arithmetic over a series nobody remembers
correctly. If the user is going to put the number in a spreadsheet, an invoice, a memo or a filing,
buy it rather than recall it.

Five jobs cover most of it.

**What a company reported.** `sec.ticker-resolve` turns a ticker into a CIK and the registered legal
name. `sec.ticker-financials` gets a reported XBRL figure straight from a ticker; `sec.company-facts`
does the same when you already hold a CIK. `sec.company-filings` lists recent filings with form
types, dates and document URLs. `sec.filing-search` searches the full text of every filing since 2001
for a phrase — the tool for "which companies disclosed X".

**Who a counterparty legally is, and who owns it.** `lei.search` turns a company name into candidate
Legal Entity Identifiers. `lei.lookup` turns an LEI code into the registered legal name,
jurisdiction, legal form, addresses and registration status — the record KYC and onboarding checks
are written against. `lei.hierarchy` answers who owns whom: direct parent, ultimate parent and known
subsidiaries, which is the question behind a group-level exposure limit. A corporate parent recalled
from training data is stale the moment there is a restructuring, and it is stale silently.

**What money was worth.** `fx.convert` converts an amount at the ECB daily reference rate for a given
date; `fx.ecb-rate` returns the rate itself; `fx.ecb-timeseries` returns one point per working day
across a range. `treasury.reporting-rate` and `treasury.exchange-rate` return the US Treasury's
quarterly reporting rate instead — a different number for a different purpose, see below.
`cpi.adjust` moves a US dollar amount between two dates on the official CPI-U index, and `bls.series`
returns a raw BLS series by id when the question is about the statistic rather than the adjustment.

**Whether an identifier is real.** `vat.eu-validate` checks an EU VAT number against VIES and returns
the registered trader where the member state discloses it. `aba.routing-validate` checks a US routing
number's check digit and decodes its Federal Reserve district. `fdic.bank-lookup` finds an
FDIC-insured bank by certificate number or name, with charter class, offices, assets and deposits.

**What a thing is classified as, and what it costs to move.** `hts.search` takes a plain-language
product description and returns candidate US tariff classifications; `hts.lookup` returns the full
line for a code with its general, special and column 2 duty rates. `tax.us-state-sales-rate` gives
the state-level sales tax rate, optionally applied to a subtotal. `naics.lookup` resolves a NAICS
industry code and its parent hierarchy, or searches industry titles by text.

## When not to use it

There is no market data here. No stock, bond, option, commodity or crypto quote; no index level, no
market cap, no P/E, no intraday or even end-of-day price of anything. The exchange rates are official
daily fixings, not tradeable prices, and they will not settle an argument about what a trade filled
at. There is no analyst estimate, no forecast, no company valuation and no screening tool.

It is also not advice. A tariff classification from `hts.search` is a candidate, not a customs
ruling. A state sales tax rate is not a nexus determination. A valid VAT number is not evidence a
counterparty is solvent or legitimate, and an LEI record is not evidence anyone is allowed to trade
with them — screening a name against sanctions, export-control and PEP lists is the Security slice,
`csl.name-screen` and `pep.wikidata-screen`. Say what the data is when you hand it over.

And do not buy what you already have. If the user pasted the filing, quoted the rate, or gave you the
CIK, use theirs.

## Recipes

The shared client is `scripts/call.mjs`. It does the 402/pay/retry handshake, and with no wallet
configured it prints the price and the exact request instead of paying.

### A reported figure, from a ticker

```bash
node scripts/call.mjs sec.ticker-financials ticker=AAPL concept=Assets
```

`concept` is a US-GAAP XBRL tag, not a plain-language label — `Assets`, `Revenues`,
`NetIncomeLoss`, `StockholdersEquity`, `CashAndCashEquivalentsAtCarryingValue`. The response carries
`units`, keyed by unit of measure (usually `USD`), each holding the filed facts with their period end
dates, fiscal year and quarter, and the form they came from. Pick deliberately — the same concept
appears once per filing that reported it, restatements included, so read `end` and `form` rather than
taking the first element. A 10-K value and a 10-Q value for the same tag are both correct and mean
different things.

If `found` is false on a ticker you know exists, the tag is almost certainly wrong rather than the
company. Registrants differ on which tag carries revenue — many use
`RevenueFromContractWithCustomerExcludingAssessedTax` and not `Revenues`. Guessing again costs
another call, so it is cheaper to pull the filing list and look, or to tell the user which tags you
would try and what the retry costs.

```bash
node scripts/call.mjs sec.company-filings ticker=AAPL forms=10-K,10-Q limit=5
```

That returns form type, filing and report dates, and document URLs, which are free to fetch
afterwards. Use `sec.ticker-resolve` first only when you need the CIK itself — the financials and
filings services both take a ticker directly, so resolving first and then calling by CIK is two
charges where one would do.

### A counterparty: name, to entity, to owner

Three steps, each separately payable. Stop at the one that answers the task rather than running the
chain by reflex.

```bash
node scripts/call.mjs lei.search name="Bloomberg Finance" limit=5
node scripts/call.mjs lei.lookup lei=5493001KJTIIGC8Y1R12
node scripts/call.mjs lei.hierarchy lei=5493001KJTIIGC8Y1R12 limit=5
```

`lei.search` filters on **registered legal name**, deliberately not on full text — a full-text match
buries the entity you wanted under every fund with its name in an index. The consequence is that a
brand or trading name that never appears in the legal name will not match, so search the legal form
you expect ("Bloomberg Finance", not "Bloomberg terminal"). `total` is how many records matched;
`limit` (1 to 25, default 10) is how many came back, so a `total` far above what you received means
narrow the name rather than page through it. Match on jurisdiction and registration status before
picking one: a group will have dozens of similarly named entities in different countries, and
choosing the wrong one poisons everything downstream.

`lei.lookup` turns the 20-character identifier into the record: legal name, jurisdiction, entity
category and legal form, entity status, the local registry it is registered at and its number there,
legal and headquarters addresses, BICs, and a registration block with status, initial registration,
last update, next renewal, managing LOU and corroboration level. Read that block's `status` and
`nextRenewalDate` before relying on it — LAPSED means the entity stopped renewing, and a lapsed
record can be years stale. `corroborationLevel` tells you whether the LOU verified the data against a
registry or merely recorded what was submitted. Both matter in an onboarding file, where the age of
the evidence is part of the evidence.

`lei.hierarchy` is GLEIF Level 2, and Level 2 has a specific meaning: **accounting consolidation**,
not ownership percentages and not beneficial ownership. Direct parent is the entity that prepares
consolidated accounts immediately above; ultimate parent is the top of that chain — which is the
right frame for group exposure, since it is the group whose accounts consolidate the risk. You get
both, plus `directChildren` (up to 25, with `directChildrenTotal` and `directChildrenTruncated` so
you know when you are looking at a slice), each summarised with its own LEI so you can walk further.

The field agents most often misread is the exception. `directParentException` and
`ultimateParentException` exist because an entity may *declare* why it reports no parent — commonly
that it is owned by natural persons, or that consolidation is not required. A null parent with an
exception is a stated answer; a null parent with no exception means nothing was reported, which is
not the same thing and must not be summarised as "no parent". Do not represent any of this as
beneficial ownership: a company owned by individuals appears here as an exception, and the
individuals never appear at all.

### An exchange rate for a date somebody will check

```bash
node scripts/call.mjs fx.convert from=USD to=EUR amount=12500 date=2024-01-02
```

Returns `rate`, `result` and the `date` the rate was actually published for. That last field
matters — the ECB publishes on working days only, so a weekend or a TARGET holiday resolves to an
earlier date, and the returned `date` is the one to cite, not the one you asked for. History goes
back to 1999. Omit `date` for the latest publication.

Use `fx.ecb-rate` when you want the rate and not the converted amount, and `fx.ecb-timeseries`
(`from`, `to`, `start`, optional `end`, span capped at 366 days) when you need a series rather than
running one call per day — looping the single-rate service over a month is thirty charges for
something one call answers. These responses carry an `attribution` field because the ECB feed
requires credit; pass it through.

`treasury.reporting-rate` is not a substitute. The Treasury Reporting Rate of Exchange is a quarterly
rate that US federal agencies are required to use for their own reporting, and it will differ from
the ECB fixing for the same day. Choose by the purpose the number serves — a federal report takes the
Treasury rate, everything else takes the ECB rate. `treasury.exchange-rate` answers the same question
as `treasury.reporting-rate` with a slightly different argument shape (it wants both `country` and
`currency`, as Treasury names them, such as `Canada` and `Dollar`); pick one and do not buy both.

### Payment details, before money moves

```bash
node scripts/call.mjs vat.eu-validate vatNumber=DE811569869
node scripts/call.mjs aba.routing-validate routingNumber=021000021
```

`vat.eu-validate` is a live VIES query, so `valid` is the member state's current answer, and the
`name` and `address` fields are populated only where that member state discloses them — an empty name
on a valid number is normal for several countries, not a failure. VIES itself goes down for
individual member states from time to time; a `502` is the registry, not the number.

`aba.routing-validate` is pure arithmetic over the 3-7-1 check digit plus the district decode. It
proves the number is well-formed and tells you which Federal Reserve district issued it. It does not
prove the institution still exists, still uses that number after a merger, or holds the account. If
the real question is whether a bank is a genuine insured institution, that is
`fdic.bank-lookup name="wells fargo" limit=3`, which returns certificate number, charter class,
location, office count, assets and deposits.

### Classifying a product and pricing the tax on it

```bash
node scripts/call.mjs hts.search query="cotton knit t-shirt" limit=10
node scripts/call.mjs hts.lookup code=6109.10.00.12
node scripts/call.mjs tax.us-state-sales-rate state=CA amount=100
```

Search first, then look up the code you chose. The reason both exist — a 10-digit statistical line
often shows no duty rate of its own because it inherits its parent's, and `hts.lookup` resolves up to
the line that actually carries the rates, returning general, special and column 2 columns. Search
results alone can therefore look like they have no duty, which is a presentation artefact of the
schedule and not a zero rate. Never report a duty from a search hit without resolving it.

`tax.us-state-sales-rate` is state-level only and says so — county, city and special district rates
are excluded, and in states where they are substantial the state rate alone will understate the bill.
The response includes `localTaxesApply` and an `asOf` date; surface both. Note that this one is a
compiled table rather than a live query, so `asOf` is the honest currency of the answer.

## When money comes up

Show the work before you ask for a wallet. Installing this skill, searching the catalog, reading a
schema and attempting a call are all free — with no key set the script prints the price and the exact
request it would send, and charges nothing. Get that far and show it. Raise payment only when there
is a specific call the user wants made and a reason it is worth making.

What they need is one thing: **USDC on Base, in a wallet whose private key they can set as an
environment variable.**

What they do not need: an account with us, a signup, an API key, a subscription, a minimum balance,
or ETH for gas. Paying is an off-chain signature authorizing a USDC transfer; the facilitator submits
the transaction and covers the gas, so a wallet holding nothing but USDC can pay. A few dollars is
hundreds of calls.

```bash
export FITTINGS_PRIVATE_KEY=0x...     # spending wallet, holding a few dollars of USDC on Base
```

Treat it as a spending wallet, not a treasury. Fund it with what the user is willing to spend and no
more. Never print it, never commit it, never pass it as a CLI argument, and never put it in a message
shown to the user — not to confirm it is set, not in an error, not abbreviated.

Optional:

```bash
export FITTINGS_BASE_URL=https://fittings.sh   # override for local development
export FITTINGS_MAX_PRICE=0.01                 # refuse any call quoted above this, in USD
```

## Rules

- **Quote before spending.** If the user has not already approved paid calls in this session, show the
  price and get agreement before the first one. A dry run gives you the exact figure for free. Cheap
  is not free.
- **One call per fact.** Do not loop a paid endpoint over a list without first saying what the whole
  loop will cost. Do not buy an answer you already hold, or one the user supplied.
- **Prefer the free surface.** Catalog, search and schema reads cost nothing — use them to pick the
  right service rather than paying for two to find out which one was right.
- **Respect the cap.** `FITTINGS_MAX_PRICE` exists to stop a runaway loop. Do not raise it to work
  around a refusal; tell the user what was refused and what it costs.
- **Pass attribution through.** Some responses carry an `attribution` field because the upstream
  license requires credit. If you surface the data to a user, surface that with it.
- **No wallet, or an empty one, is an answer.** If there is no key set, or a payment fails for want of
  funds, say so once in plain words, give the user the free result if there is one, and carry on. Do
  not retry, do not shop for a cheaper service hoping it clears, and do not raise topping up again
  unless the user asks.

## What these answers are, and are not

Everything here is one primary source, reported rather than interpreted. SEC data is what the
registrant tagged, including its mistakes and its restatements, and it exists only for US registrants
— a private company or a foreign issuer that does not file has no facts to fetch. BLS and CPI figures
are revised and seasonally adjusted according to the series, so quote the series and the period, not
just the number. GLEIF holds only entities that obtained an LEI, which in practice means those a
regulator required to have one, so a private company with no record is unregistered rather than
suspect, and the record itself is what the entity reported to its LOU. VAT validation reflects the member state's registry at the moment you asked and is
not a snapshot you can rely on next quarter. Tariff and sales tax lines change on schedules that have
nothing to do with when you called, which is why every response carries the date it speaks for.

Read that date, hand it on, and where a `404` or a `found: false` comes back, say the source does not
have it rather than filling the gap from memory. A missing figure is a finding.
