Products
Reference Data
An identifier crosswalk for listed securities: translate between tickers, CUSIPs, ISINs, SEDOLs, estimate-vendor tickers, gvkeys and CIKs, as of today or any past date, with retired identifiers still resolving and every match stating how it was made.
What's inside
Every other dataset you own is keyed on one of these identifiers, and joining two of them on a bare ticker is how a portfolio ends up holding the wrong company. The crosswalk answers four questions:
- What does this symbol carry? Every current identifier of the security and its issuer, the issuer's other listed securities, and the dated history of every identifier the security has held.
- What is this identifier? A reverse lookup from any one identifier back to the security, or, for an issuer key, to every security under that issuer. Retired identifiers resolve, with the date they were replaced and what replaced them.
- What was it called then? The identifiers in force on a past date, and the dated events where the issuer changed its name, ticker or CUSIP.
- How much is covered? The corpus measured live and stated as numbers, including the identifiers it deliberately does not serve.
Security grain and issuer grain
Identifiers come in two grains, and the responses keep them apart:
| Grain | Identifiers | What it names |
|---|---|---|
| Security | CUSIP9, CUSIP8, ISIN, SEDOL, ticker, estimate-vendor ticker (IBTIC) | One share line on one venue |
| Issuer | gvkey, CUSIP6, CIK, the API's entity_id | A company, which can list several securities (common lines, other share classes, ADRs) |
Security-grain identifiers are returned in an identifiers block and issuer keys in a separate issuer block. Join price and holdings data on security-grain identifiers; join filings and fundamentals on gvkey or CIK. A lookup by an issuer key returns every security under that issuer and does not nominate one.
Coverage
Measured live on 2026-08-02: 77,313 securities (25,512 active), 58,179 issuers, and 969,333 identifier bindings, of which 209,715 are current and 759,618 retired. 38,850 securities carry at least one prior ticker. CUSIP9 and CUSIP8 cover 100% of securities, ISIN 79.4%, SEDOL 74.2%, CIK 67.8% and the estimate-vendor ticker 37.2%. 26,189 issuers (45.0%) are linked to the private-company graph. Call /api/v3/reference/coverage for the current numbers.
Not served: PERMNO, FIGI and LEI. The coverage response names each one with the reason. Use the CIK to reach regulatory filings.
Staleness is stated, not implied. Every response lists its sources with as_of, age_days, expected_refresh_days and is_stale. The issuer change log is a periodic vintage that lags the security master; where the two disagree, the response says so and the security master is authoritative.
Access
3 credits/call | Pro and up (alt_data)
A batch call of up to 100 symbols is one call. Failed requests are not billed: when the reference store cannot be reached, the routes return 503 rather than an empty answer.
Endpoints
| Method | Path | Plan / credits | MCP tool | Description |
|---|---|---|---|---|
GET | /api/ | Pro+ / 3 | reference_crosswalk | Every identifier one symbol's security and issuer carry, which identifier matched and with what confidence, the issuer's other securities, the dated identifier timeline, and the link into the private-company graph with its confidence grade. as_of (YYYY-MM-DD, optional): the identifiers in force on that date instead of today's. prefer_country (string, optional, such as USA): choose between current securities that share the symbol; without it a shared symbol is refused with its candidates. include_history (boolean, default true). include_share_classes (boolean, default true). include_private_graph (boolean, default true). |
GET | /api/v3/reference/lookup | Pro+ / 3 | reference_lookup | Reverse lookup from exactly one identifier: cusip (9 or 8 characters for a security; 6 characters is an issuer key), isin, sedol, ticker, ibtic (estimate-vendor ticker), gvkey (issuer), cik (issuer), entity_id (issuer), or private_company_id (returns the listed issuer it bridges to, if any). include_retired (boolean, default true): also match identifiers that have been retired. Passing none or more than one identifier returns 422. |
GET | /api/ | Pro+ / 3 | reference_history | Every identifier the security has been bound to, with the dates each binding started and ended, grouped by identifier type, plus the issuer's dated name, ticker and CUSIP change events. as_of (YYYY-MM-DD, optional): also return the identifiers in force on that date. prefer_country (string, optional). include_change_log (boolean, default true). |
GET | /api/v3/reference/batch | Pro+ / 3 | reference_batch | Up to 100 symbols in one call, each with its current identifiers, issuer keys, matched identifier and status. symbols (string, required): comma-separated. prefer_country (string, optional). |
GET | /api/v3/reference/coverage | Pro+ / 3 | reference_coverage | What the crosswalk contains, measured live: securities and issuers (and how many are active), bindings current and retired, the share of securities carrying each identifier, private-graph links by confidence grade, change events on file, and the identifiers not served, with each source's age. No parameters. |
How answers are shaped
Every symbol gets an explicit status. resolved is a clean match. historical means the symbol is retired: the security is returned with what it trades as now (now_trades_as). ambiguous means more than one current security shares the symbol: the candidates are listed and no identity is guessed, so pass prefer_country. unknown means the symbol is not carried. ambiguous and unknown are answers, returned as 200. In a batch, symbols past the 100 limit come back as over_limit and malformed ones as invalid, never silently dropped. A batch entry can also be unavailable: the resolver could not answer for that symbol, which is not the same as unknown, so retry it. coverage.request.degraded counts these entries.
Which identifier matched. matched_on names the identifier type (rung), its grain, its confidence, and whether that binding is still current (binding_is_current). TICKER_NORM is the punctuation bridge (BRK-A to BRKA) and the weakest match the crosswalk reports.
Dated history. identifier_ lists each binding with value, valid_from, valid_thru and is_current, and reports n_current and n_retired. With as_of, point_ holds the set in force on that date. Identifier types with no validity window cannot be asserted for a past date and are listed under point_ instead.
A retired symbol is not the answer. FB resolves today to an ETF. Meta Platforms held the string from 2012-02-01 to 2022-06-08 and comes back under prior_, dated, never as the answer.
Private-company links carry a grade. A link made through a regulator filing number or through matching ticker and name is an identity. A link where only the ticker matches is served as a candidate with is_identity: false; confirm it before joining anything to it.
Coverage is in every response. coverage.request counts what this answer contains, and coverage.dataset is the corpus census.
Examples
Map a symbol to every identifier (3 credits)
curl -H "Authorization: Bearer $WEALTHNOW_API_KEY" \ "https://firm.wealthnow.io/api/v3/reference/crosswalk/AAPL?include_private_graph=false"Check whether an identifier in your data is stale (3 credits)
Exxon Mobil's CUSIP changed from 30231G102 to 30233Q108 on 2026-07-02. The old CUSIP still resolves, and the binding block says it is retired and what replaced it:
curl -H "Authorization: Bearer $WEALTHNOW_API_KEY" \ "https://firm.wealthnow.io/api/v3/reference/lookup?cusip=30231G102"Abbreviated response structure:
{ "ok": true, "query": { "rung": "CUSIP9", "value": "30231G102", "grain": "security", "include_retired": true }, "answered": true, "matched": true, "binding": { "rung": "CUSIP9", "value": "30231G102", "is_current": false, "valid_thru": "2026-07-01", "replaced_by": [ { "value": "30233Q108", "valid_from": "2026-07-02" } ], "note": "This CUSIP9 is RETIRED: ... If it appears in your own data, that record is stale." }, "identifiers": { "CUSIP9": "30233Q108", "...": "..." }, "issuer": { "GVKEY": "...", "CIK": "...", "grain": "issuer" }}The identifiers block always holds the security's current identifiers, so the same call tells you what to replace the stale value with.
Map a whole portfolio in one call (3 credits)
import os, requests symbols = ["AAPL", "MSFT", "SQ", "FB", "BRK.B"]r = requests.get( "https://firm.wealthnow.io/api/v3/reference/batch", params={"symbols": ",".join(symbols), "prefer_country": "USA"}, headers={"Authorization": f"Bearer {os.environ['WEALTHNOW_API_KEY']}"}, timeout=60,)r.raise_for_status()body = r.json()for entry in body["results"]: ids = entry.get("identifiers") or {} print(f"{entry['symbol']:6} {entry['status']:12} CUSIP9={ids.get('CUSIP9')} ISIN={ids.get('ISIN')} now={entry.get('now_trades_as')}")req = body["coverage"]["request"]print(f"Resolved {req['resolved']} of {req['symbols_looked_up']} ({req['resolved_pct']}%)")Identifiers in force on a past date (3 credits)
curl -H "Authorization: Bearer $WEALTHNOW_API_KEY" \ "https://firm.wealthnow.io/api/v3/reference/history/XOM?as_of=2015-06-30"Read point_ for the set in force on 2015-06-30, identifier_ for the full history, and issuer_change_log with its source age for the name, ticker and CUSIP events.