Structured Note Reference Data: API
The reference data feed is also available as an HTTP API. You give us an ISIN or a PDF, we give you the same nested record that ships in the daily file feed — every field on the term sheet, every underlier, every payoff breakpoint.
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.
The five-second version
If you have an ISIN, ask for it:
curl -H "x-api-key: $API_KEY" \
"https://api.sqxnotes.com/v1/reference/byIsin?isin=US78017UPK06"
If you have a PDF, send it:
curl -H "x-api-key: $API_KEY" \
-H "Content-Type: application/pdf" \
--data-binary @./pricing_supplement.pdf \
https://api.sqxnotes.com/v1/reference/byPdf
Either way, when we already have the analyzed record, you get back 200 with the full payload right away. Skip the next section.
When we don't have it yet
Sometimes you'll ask about an ISIN we know exists — say, one that just hit our SEC ingest queue — but our extraction pipeline hasn't run on it yet. Or you'll send us a PDF whose ISIN we've never seen. In both cases we can't answer immediately. The note has to be analyzed end-to-end: text extracted, ISIN resolved, underliers identified, payoff structure decoded. That takes a few minutes per filing.
So the API uses the standard asynchronous request-reply pattern. Instead of making you wait on the connection, we accept the request, kick off the analysis on our side, and hand you back a job handle to poll.
You get a 202 Accepted response with three things in it:
{
"job_id": "df8414cbddf37d265ecc6e5961b8d9847d9cb9ddeeb06656848346ce6fe2e61d",
"status": "queued",
"status_url": "https://api.sqxnotes.com/v1/reference/byPdf/status?job_id=df8414cbddf37d265ecc6e5961b8d9847d9cb9ddeeb06656848346ce6fe2e61d"
}
A Location header on the same response repeats the status_url, in case your HTTP client prefers to read it from there.
You then GET the status_url on a polling loop. While we're working, it returns 202 with {"status": "queued"} (the upload is in our queue but hasn't started analysis yet) or {"status": "processing"} (extraction is running). When the analysis completes, the same URL flips to 200 with the same nested record you would have gotten from byIsin directly.
A reasonable poll loop:
while :; do
code=$(curl -s -w '%{http_code}' -o /tmp/r.json \
-H "x-api-key: $API_KEY" "$status_url")
case "$code" in
200) cat /tmp/r.json; break ;; # done — payload is in /tmp/r.json
202) sleep 15 ;; # still working
4*|5*) echo "error: $code"; cat /tmp/r.json; break ;;
esac
done
15 seconds between polls is plenty — analysis runs on a separate pipeline and won't go faster if you ask more often. Total wait is usually under five minutes; complex multi-tranche supplements can run longer.
The same status_url works for both flows — the only difference is the job_id shape (a 64-character SHA for PDF uploads, a numeric filing ID for ISIN lookups). You don't have to handle them differently.
What you get back on success
The 200 payload is the full nested reference-data record — same structure as the file feed. Top-level keys, in the order they appear:
call_leg issuer-call / autocall provisions
classification payoff archetype, link type, tax + legal
coupon_leg coupon mechanics, conditionality, memory, payout
fee_economics public-offering price, fees, estimated value
isin the 12-char identifier
mechanics issue date, denomination, listing, settlement
observation_leg observation schedule and reference levels
payoff_legs piecewise-linear payoff breakpoints at maturity
r_observation_schedule per-date observation calendar
reference cross-references (filing_id, parent prospectus, etc.)
security_master identifying fields (CUSIP, issuer, currency, maturity)
underliers one entry per underlying asset (single-stock or basket)
Here is an excerpt of the response for a real autocallable note (US05617VCX10):
[
{
"isin": "US05617VCX10",
"classification": {
"instrument_id": 3937870,
"legal_wrapper": "NOTE",
"payoff_link_type": "EQUITY",
"payoff_profile": "AUTOCALLABLE",
"tax_classification": "CPDI",
"income_accrual_method": "CONSTANT_YIELD",
"has_phantom_income": "TRUE"
},
"call_leg": {
"instrument_id": 3937870,
"call_exercise_style": "AUTOCALL",
"call_payment_type": "PRINCIPAL_PLUS_COUPON",
"first_call_date": "2026-08-21",
"last_call_date": "2028-11-21"
},
"coupon_leg": {
"instrument_id": 3937870,
"coupon_rate_type": "FIXED",
"coupon_conditionality": "CONTINGENT",
"frequency": "QUARTERLY",
"has_memory": "TRUE",
"observation_scope": "WORST_OF",
"barrier": { "barrier_type": "PCT_OF_INITIAL" },
"payout": { "coupon_rate_pct": 7.35 },
"coupon_terms": "7.350% per annum (1.8375% per quarter); paid if Closing Level of each Underlying Asset >= 70% of Initial Level..."
},
"fee_economics": {
"estimated_value_pct": 96.424,
"public_offering_price_pct": 100.0,
"principal_amount_per_unit": 1000.0,
"total_offering_amount": 5140000,
"units_issued": 5140
},
"underliers": [
{ "underlier_id": "...", "weight": "...", "ticker": "..." },
{ "underlier_id": "...", "weight": "...", "ticker": "..." }
],
"...": "remaining sections: mechanics, observation_leg, payoff_legs, r_observation_schedule, reference, security_master"
}
]
The full record for this ISIN is browsable at https://sqxnotes.com/api/reference/US05617VCX10.json — that's the same JSON shape the live API returns to a 200. Field-by-field documentation lives in the Data Dictionary.
The response is a JSON array. For most ISINs there's exactly one element; the array form is preserved because a single filing can cover multiple note tranches that share a parent prospectus.
When the ISIN isn't a structured note
If you call /reference/byIsin with an ISIN we have on file as something other than a structured note — an ETF, an ADR, a corporate bond — we'll still answer. You get 200 with whatever we have in our security master:
{
"isin": "US0010122028",
"cusip": "001012202",
"description": "AECI LTD",
"type": "ADR",
"issuer": "AECI Ltd",
"message": "ISIN is type=ADR; only security_master fields are available for this type."
}
If we've never heard of the ISIN at all, you get 404.
When the PDF isn't what you thought
PDFs without a parseable ISIN are still accepted — our pipeline mints a synthetic identifier and runs the same extraction. If the document turns out to be an SN filing whose ISIN was on a page our OCR couldn't read, you'll still get a result. If it isn't an SN at all, the analysis pipeline detects that and skips the record cleanly; the polling response will eventually surface a 404 or a status indicating no record was produced.
PDFs containing active content — embedded JavaScript, launch actions, embedded files, XFA forms — are refused with 400 at the upload boundary. We don't try to strip the dangerous bits and continue; we reject the upload outright. Extracted text is also normalized and prompt-injection markers neutralized before any downstream LLM step.
The hard size cap is 10 MB per request. Above that you'll get a 413, returned as plain text by the AWS platform layer (this is the only response that isn't JSON).
When you get input wrong
ISIN format errors return 400 with a specific message:
{"message": "ISIN must be 12 characters, got 5"}
{"message": "ISIN checksum invalid; did you mean US78017UPK06?"}
{"message": "ISIN format invalid (must be 2 letters + 9 alphanumerics + 1 digit)"}
Lowercase input is silently uppercased — copy-paste from spreadsheets just works without any rejection.
PDF upload errors are similar:
{"message": "empty body — POST PDF bytes"}
{"message": "not a PDF (missing %PDF- header)"}
{"message": "pdf rejected: contains active content marker /JavaScript"}
Status URL errors:
{"message": "missing job_id"}
{"message": "job_id must be a numeric filing_id or 64-char SHA"}
{"message": "unknown job_id 999999999"}
All API responses are JSON, with a single platform-level exception: the 413 for oversize bodies, which is plain text from the AWS gateway.
Endpoint reference
| Method | Path | Purpose |
|---|---|---|
| GET | /reference/byIsin?isin={ISIN} | Look up by ISIN. 200 if analyzed, 202 if known but pending, 404 if unknown. |
| POST | /reference/byPdf | Upload a PDF. 200 if its ISIN is already analyzed, 202 with a job_id otherwise, 400 on bad input. |
| GET | /reference/byPdf/status?job_id={ID} | Poll a 202 from either of the above. 200 with payload when ready, 202 with status while pending. |
| 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 to specific reference fields per client.
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.