Pricing Data: API
The price feed is also available as an HTTP API. You give us an ISIN — or a CUSIP, or a list of either — and we hand back the latest price we have, the raw bid/mid/ask sides behind it, and enough reference and lifecycle context to know what you're looking at. It spans everything in our security master: structured notes, municipal and corporate bonds, treasuries, CDs, ETFs/ETNs, UITs, and more. We route the lookup to the right product's price table for you; you don't have to know which one a given ISIN lives in.
The API lives at https://api.sqxnotes.com/v1/. Authentication is by API key in the x-api-key header. We'll get you a key.
Unlike the reference-data API, pricing is synchronous. There's no analysis pipeline to wait on — every call returns its answer on the connection. No jobs, no polling.
The five-second version
If you have an ISIN, ask for it:
curl -H "x-api-key: $API_KEY" \
"https://api.sqxnotes.com/v1/price/byIsin?isin=US06370EDU91"
You get back 200 with the price right away, or 404 if we've never heard of the ISIN. That's the whole flow.
What you get back on success
Every response — success or error — comes in the same envelope: a data object, an errors array (empty on success), and a meta block with a request id and the server's as_of timestamp. On a 200, data carries three things: the pricing record, a reference block identifying the security, and a lifecycle_status.
{
"data": {
"reference": {
"isin": "US06370EDU91",
"cusip": "06370EDU9",
"description": "Bank of Montreal Senior Medium-Term Notes",
"type": "structured_note",
"issuer": "Bank of Montreal",
"currency": "USD",
"ticker": null,
"maturity_date": "2029-05-30"
},
"pricing": {
"pricing_date": "2026-06-03",
"price": 98.42,
"bid_price": 98.17,
"mid_price": 98.42,
"ask_price": 98.67,
"currency": null,
"source": "icevpdt"
},
"lifecycle_status": "active"
},
"errors": [],
"meta": {
"request_id": "3f1c8a2e-9b04-4e77-bd1a-0c2f5e6a7d10",
"as_of": "2026-06-04T20:00:00+00:00",
"page": null
}
}
The pricing object always has the same seven keys, whatever the product. Anything we don't have for a given security comes back as JSON null — never an empty string or an "N/A" sentinel.
price— the single representative price. Where a product only quotes sides, this is coalesced from bid→mid→ask.bid_price,mid_price,ask_price— the raw quote sides.nullon products that carry only a single price.pricing_date— the date the price is as of,YYYY-MM-DD.currency— the currency *on the price record*. Only some product tables carry one, so this is oftennull; the security's own currency always rides inreference.currency.source— the vendor or broker token the price came from.
Alongside the price we return lifecycle_status — active, inactive, or null. It's computed from the security master: a security past maturity or flagged matured/called/redeemed reads inactive; one that's still live and recently priced reads active; anything we can't place stays null.
> The authoritative, field-by-field contract — every key, enum, and status code, including the response envelope — is the OpenAPI 3.1 spec (openapi.yaml). This page is the narrative guide; the spec is the source of truth.
A CUSIP works just as well
If you hold CUSIPs rather than ISINs, use byCusip. We resolve it to the ISIN through the security master and answer with the identical envelope — your CUSIP echoes back in reference.cusip.
curl -H "x-api-key: $API_KEY" \
"https://api.sqxnotes.com/v1/price/byCusip?cusip=06370EDU9"
A price on a particular date
For a historical look-up, add a date:
curl -H "x-api-key: $API_KEY" \
"https://api.sqxnotes.com/v1/price/byIsinHistorical?isin=US06370EDU91&date=2025-09-15"
Markets don't price every security every day, so we don't make you guess whether a given date has a row. The pricing object grows three extra fields that tell you exactly what we returned relative to what you asked for:
{
"requested_date": "2025-09-15",
"effective_date": "2025-09-12",
"match": "prior",
"price": 97.10,
"bid_price": 96.85,
"mid_price": 97.10,
"ask_price": 97.35,
"currency": null,
"source": "icevpdt",
"pricing_date": "2025-09-12"
}
match is one of exact (we had a row on your date), prior (we fell back to the most recent earlier date), next (nothing on or before your date, so we reached forward to the first later one), or none (we have no price for this security at all). When match is none you still get 200 — effective_date and the price fields are simply null. It isn't an error, so there's nothing in errors; the enum tells the whole story.
Looking up a lot of them at once
To price a list, POST it to the bulk endpoint instead of firing one request per ISIN:
curl -H "x-api-key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{"isin_list": ["US06370EDU91", "US912828YL83", "NOTANISIN"]}' \
https://api.sqxnotes.com/v1/price/bulk/byIsin
The response sorts every ISIN you sent into one of three buckets, all three always present even when empty:
{
"data": {
"success": [
{ "reference": { "...": "..." }, "pricing": { "...": "..." }, "lifecycle_status": "active" }
],
"unprocessed": [
{ "isin": "NOTANISIN", "reason": "invalid_isin: ISIN must be 12 characters, got 9" }
],
"not_found": ["US912828YL83"]
},
"errors": [],
"meta": { "...": "..." }
}
success— one full record per priced ISIN, each shaped exactly like a singlebyIsinpayload.unprocessed— ISINs we couldn't even try: a failed format check, or a security whose product type has no price table. Each carries areason.not_found— well-formed ISINs we have no price for (unknown to the security master, or known but with no price on file).
Duplicates in your input are de-duped, and the cap is 1000 ISINs per request. Send more and you get 429 with a QUOTA_LIMIT_EXCEEDED error telling you the limit and what you sent.
When we have the security but no price
A 404 means we don't recognize the identifier at all. It does not mean "no price" — those are different answers. If the security is in our master but we simply don't carry a price for it (its product type has no price table, or nothing's been captured yet), you still get 200 with the full reference block and lifecycle_status, and the pricing fields come back null. You can tell an unpriced-but-known security from a stale one by reading lifecycle_status alongside the null price.
When you get input wrong
Bad input returns 400 with a structured error in the errors array. Each one has a numeric messageCode, a category, a machine-readable type, and a human message:
{
"data": null,
"errors": [
{
"category": "VALIDATION_ERROR",
"type": "INVALID_INPUT",
"messageCode": 1010,
"message": "Invalid ISIN: checksum invalid",
"messageValues": { "isin": "US06370EDU90" }
}
],
"meta": { "...": "..." }
}
The codes you'll see on these endpoints:
| messageCode | When |
|---|---|
1010 | ISIN is malformed or fails its check digit. |
1011 | CUSIP is malformed or fails its check digit. |
1012 | date isn't a valid YYYY-MM-DD. |
1040 | The identifier is well-formed but we have no such security (404). |
1050 | Bulk request exceeded the 1000-ISIN cap (429). |
Lowercase identifiers are silently uppercased — copy-paste from a spreadsheet just works.
Endpoint reference
| Method | Path | Purpose |
|---|---|---|
| GET | /price/byIsin?isin={ISIN} | Latest price by ISIN. 200 with the record, 404 if the ISIN is unknown. |
| GET | /price/byCusip?cusip={CUSIP} | Latest price by CUSIP. Resolves to ISIN first; same payload as byIsin. |
| GET | /price/byIsinHistorical?isin={ISIN}&date={YYYY-MM-DD} | Price as of a date, with a match of exact, prior, next, or none. |
| POST | /v1/price/bulk/byIsin | Price up to 1000 ISINs in one call. Body {"isin_list": [...]}; returns success / unprocessed / not_found. |
| GET | /version | Report API version. No auth required. |
Authentication & versioning
Every request except /version needs a valid API key in x-api-key. Keys are scoped per client — pricing access and historical-pricing access are separate grants — and a key can carry an expiry or a call quota. A key that has lapsed answers 403 (messageCode 1033); one that's over its quota answers 429 (messageCode 1050).
The v1 base path is stable. Field additions are non-breaking and ship in-place. Breaking changes — renames, removals, semantic shifts — land at a new major version on a new stage. Minor and patch updates are reported at /version.