Signals examples library
Copy-paste recipes for the /v1/signals endpoint in three flavours: curl, Python (requests), and Node / TypeScript.
1. Fetch the latest signals for a ticker
A plain GET returns a JSON array of signal records, newest first. With no date filter you get the most recent 30 (default limit). Each row carries the 0-1 Piotroski score plus the five component families.
curl https://api.vectorfinancials.com/v1/signals/AAPL \
-H "X-API-Key: vf_sk_your_key_here" \
-G -d "limit=5"
# Response (newest first):
[
{
"ticker": "AAPL",
"date": "2026-06-16",
"score": 0.778,
"components": {
"piotroski": {
"positive_ni": true, "positive_ocf": true, "roa_increasing": true,
"ocf_gt_ni": true, "leverage_decreasing": false,
"current_ratio_increasing": true, "no_dilution": true,
"gross_margin_increasing": true, "asset_turnover_increasing": false
},
"altman_z": {
"score": 6.12, "zone": "safe",
"components": { "x1": 0.28, "x2": 0.19, "x3": 0.22, "x4": 4.85, "x5": 0.88 }
},
"beneish_m": { "score": -2.61, "flag": false },
"sloan_accrual": { "ratio": -0.021, "quality": "high" },
"regime": { "current": "bull", "confidence": 0.82 }
},
"effective_ts": "2026-06-16T00:00:00Z",
"knowledge_ts": "2026-06-17T06:30:00Z"
}
]import os, requests
H = {"X-API-Key": os.environ["VECTORFIN_API_KEY"]}
rows = requests.get(
"https://api.vectorfinancials.com/v1/signals/AAPL",
headers=H,
params={"limit": 5},
).json()
latest = rows[0] # rows are newest-first
print(latest["date"], latest["score"])const H = { "X-API-Key": process.env.VECTORFIN_API_KEY! };
const res = await fetch(
"https://api.vectorfinancials.com/v1/signals/AAPL?limit=5",
{ headers: H },
);
const rows: SignalRecord[] = await res.json(); // newest first
const latest = rows[0];
console.log(latest.date, latest.score);2. Filter by date range and limit
Use date_from and date_to (ISO YYYY-MM-DD, both inclusive) to bound the window, and limit (1 to 365, default 30) to cap the number of rows returned. Free-tier keys are clamped to a 90-day look-back and a 30-row cap.
curl https://api.vectorfinancials.com/v1/signals/AAPL \
-H "X-API-Key: vf_sk_your_key_here" \
-G \
-d "date_from=2026-01-01" \
-d "date_to=2026-03-31" \
-d "limit=90"import os, requests
H = {"X-API-Key": os.environ["VECTORFIN_API_KEY"]}
rows = requests.get(
"https://api.vectorfinancials.com/v1/signals/AAPL",
headers=H,
params={
"date_from": "2026-01-01",
"date_to": "2026-03-31",
"limit": 90,
},
).json()
print(len(rows), "rows in Q1 2026")const H = { "X-API-Key": process.env.VECTORFIN_API_KEY! };
const qs = new URLSearchParams({
date_from: "2026-01-01",
date_to: "2026-03-31",
limit: "90",
});
const res = await fetch(
`https://api.vectorfinancials.com/v1/signals/AAPL?${qs}`,
{ headers: H },
);
const rows: SignalRecord[] = await res.json();
console.log(rows.length, "rows in Q1 2026");3. Interpreting the components object
The top-level score already equals the Piotroski F-Score divided by 9, but you often want the raw diagnostics: count the 9 piotroski booleans yourself, read the Altman Z distress zone, check the Beneish M manipulation flag, read Sloan accrual quality (which may be null), and read the market regime (current may be null) with its confidence.
# jq derives the five diagnostics from a single record
curl -s https://api.vectorfinancials.com/v1/signals/AAPL \
-H "X-API-Key: vf_sk_your_key_here" -G -d "limit=1" |
jq '.[0].components | {
f_score: ([.piotroski[] | select(. == true)] | length),
z_zone: .altman_z.zone,
m_flag: .beneish_m.flag,
accruals: (.sloan_accrual.quality // "n/a"),
regime: (.regime.current // "n/a"),
regime_conf: .regime.confidence
}'import os, requests
H = {"X-API-Key": os.environ["VECTORFIN_API_KEY"]}
rec = requests.get(
"https://api.vectorfinancials.com/v1/signals/AAPL",
headers=H, params={"limit": 1},
).json()[0]
c = rec["components"]
f_score = sum(1 for v in c["piotroski"].values() if v) # 0..9
z_zone = c["altman_z"]["zone"] # safe|grey|distress
m_flag = c["beneish_m"]["flag"] # True => risk
sloan = c.get("sloan_accrual")
accruals = sloan["quality"] if sloan else None # may be null
regime = (c.get("regime") or {}).get("current") # bull|bear|sideways|None
reg_conf = (c.get("regime") or {}).get("confidence")
print(f"F-Score {f_score}/9 | Z={z_zone} | M-flag={m_flag} | accruals={accruals} | regime={regime} ({reg_conf})")const H = { "X-API-Key": process.env.VECTORFIN_API_KEY! };
const res = await fetch(
"https://api.vectorfinancials.com/v1/signals/AAPL?limit=1",
{ headers: H },
);
const rec: SignalRecord = (await res.json())[0];
const c = rec.components;
const fScore = Object.values(c.piotroski).filter(Boolean).length; // 0..9
const zZone = c.altman_z.zone; // safe|grey|distress
const mFlag = c.beneish_m.flag; // true => risk
const accr = c.sloan_accrual?.quality ?? null; // may be null
const regime = c.regime?.current ?? null; // bull|bear|sideways|null
console.log({ fScore, zZone, mFlag, accr, regime });4. Bitemporal / point-in-time views
Signals are append-only and bitemporal: there can be several rows for the same date, distinguished by knowledge_ts (when we computed that version). For the current view, take the row with the latest knowledge_ts per date. For a look-ahead-free backtest, instead keep only rows whose knowledge_ts is on or before your as-of timestamp before reducing, so you never use data you could not have known yet.
# Pull a window, then reduce client-side to the latest knowledge_ts per date
curl -s https://api.vectorfinancials.com/v1/signals/AAPL \
-H "X-API-Key: vf_sk_your_key_here" \
-G -d "date_from=2026-06-01" -d "date_to=2026-06-16" -d "limit=365" |
jq 'group_by(.date) | map(max_by(.knowledge_ts))'import os, requests
from collections import defaultdict
H = {"X-API-Key": os.environ["VECTORFIN_API_KEY"]}
rows = requests.get(
"https://api.vectorfinancials.com/v1/signals/AAPL",
headers=H,
params={"date_from": "2026-06-01", "date_to": "2026-06-16", "limit": 365},
).json()
# Current view: latest knowledge_ts wins per date.
latest = {}
for r in rows:
d = r["date"]
if d not in latest or r["knowledge_ts"] > latest[d]["knowledge_ts"]:
latest[d] = r
# Look-ahead-free backtest: filter BEFORE reducing.
as_of = "2026-06-10T00:00:00Z"
pit = {}
for r in rows:
if r["knowledge_ts"] <= as_of:
d = r["date"]
if d not in pit or r["knowledge_ts"] > pit[d]["knowledge_ts"]:
pit[d] = rconst H = { "X-API-Key": process.env.VECTORFIN_API_KEY! };
const res = await fetch(
"https://api.vectorfinancials.com/v1/signals/AAPL?date_from=2026-06-01&date_to=2026-06-16&limit=365",
{ headers: H },
);
const rows: SignalRecord[] = await res.json();
// Current view: latest knowledge_ts per date.
const latest = new Map<string, SignalRecord>();
for (const r of rows) {
const prev = latest.get(r.date);
if (!prev || r.knowledge_ts > prev.knowledge_ts) latest.set(r.date, r);
}
// Look-ahead-free backtest: filter on knowledge_ts before reducing.
const asOf = "2026-06-10T00:00:00Z";
const pit = new Map<string, SignalRecord>();
for (const r of rows) {
if (r.knowledge_ts > asOf) continue;
const prev = pit.get(r.date);
if (!prev || r.knowledge_ts > prev.knowledge_ts) pit.set(r.date, r);
}5. Multi-ticker screening
There is no batch endpoint yet, so you discover the universe with GET /v1/tickers and then loop, calling GET /v1/signals/{ticker}?limit=1 for each to collect the latest score. Mind your plan rate limit (a 429 returns a Retry-After header); add a small delay or backoff for large universes.
# Discover the ticker universe...
curl -s https://api.vectorfinancials.com/v1/tickers \
-H "X-API-Key: vf_sk_your_key_here" | jq '.total'
# ...then loop one ticker at a time (no batch endpoint):
for T in AAPL MSFT NVDA; do
curl -s "https://api.vectorfinancials.com/v1/signals/$T?limit=1" \
-H "X-API-Key: vf_sk_your_key_here" |
jq -r '.[0] | "\(.ticker) \(.score)"'
doneimport os, requests
VF = "https://api.vectorfinancials.com"
H = {"X-API-Key": os.environ["VECTORFIN_API_KEY"]}
universe = requests.get(f"{VF}/v1/tickers", headers=H).json()["tickers"]
# No batch endpoint yet — loop one request per ticker.
scores = {}
for t in universe[:50]:
rows = requests.get(f"{VF}/v1/signals/{t}", headers=H,
params={"limit": 1}).json()
if rows:
scores[t] = rows[0]["score"]
top = sorted(scores.items(), key=lambda kv: kv[1], reverse=True)[:10]
print(top)const VF = "https://api.vectorfinancials.com";
const H = { "X-API-Key": process.env.VECTORFIN_API_KEY! };
const { tickers } = await (await fetch(`${VF}/v1/tickers`, { headers: H })).json();
// No batch endpoint yet — loop one request per ticker.
const scores: Record<string, number> = {};
for (const t of tickers.slice(0, 50)) {
const rows: SignalRecord[] = await (
await fetch(`${VF}/v1/signals/${t}?limit=1`, { headers: H })
).json();
if (rows.length) scores[t] = rows[0].score;
}
const top = Object.entries(scores).sort((a, b) => b[1] - a[1]).slice(0, 10);
console.log(top);