The algo trading API.

A trading algorithm is a condition and what you do when it fires. Here the condition is one SQL clause over 421+ precomputed columns, and the same clause runs in all three tenses: as a scan of the whole market right now, as of any past day to validate it with no look-ahead and no survivorship bias, and as a webhook that fires when a ticker starts or stops matching — your entries and exits from one delivery. Your handler decides; your broker executes.

Free plan. Every ticker, every signal, real-time data, all-time history.

what you get

The capability, itemized.

One condition, both sidesA ticker starting to match is your entry, a held ticker no longer matching is your exit — no second exit rule to author
Whole-market evaluationOne scan evaluates the clause across every US equity — 421+ indicators, flags and fundamentals as columns
Validate on the pastThe same q with ?asof= returns the candidate set as it stood that day, delisted tickers included, unlimited depth
Price the tradesAligned series return entry and exit prices for the whole candidate set on one time grid
Run it liveSubscribe the q as a webhook: signed, retried deliveries when membership changes — the market becomes the scheduler
Execution stays yoursRead-only market state; orders go through your broker, under your risk rules, every action in your logs

Every read runs in three tenses: live, as of any past moment (add ?asof=; no look-ahead, no survivorship bias), or on push — the same query as a webhook that fires when the answer changes. Under it sits the computed table: every US equity plus rates, FX and crypto, every signal precomputed and refreshed continuously, all-time history. Data included — there’s no feed to bring.

one call

The condition, asked of the whole market.

The clause that will be the entry and exit rule, evaluated once across every ticker — right now:

POST /v2/scan
{ "q": "rsi_14 < 30 AND above_sma_200 AND market_cap > 1e9" }

// → { "as_of": "2026-08-23T15:18:49Z", "count": 3,
//     "results": [ { "ticker": "LION", "price": 11.67, … },
//                  { "ticker": "KRG",  "price": 26.15, … },
//                  { "ticker": "DVA",  "price": 173.79, … } ] }

Add "asof": "2024-01-05" and the same call answers as that day would have; POST the same q to /v2/scan/subscribe and it fires when the answer changes. Three tenses of one rule is what lets an algorithm go from backtest to live without translation.

Ready to wire it into something? Build a trading algorithm below walks it end to end. Reference: POST /v2/scan · as-of queries · scan subscriptions · the SQL surface.

build it

Build a trading algorithm.

A trading algorithm is a condition and what you do when it fires. Write the condition in SQL, validate it against the past, run it live as a webhook — your handler decides, your broker executes.

1. Define the entry and exit conditionA SQL WHERE clause over 421+ precomputed columns: the whole market evaluated in one call, right now.

curl -X POST "https://api.tickerbot.io/v2/scan" \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "q": "rsi_14 < 30 AND above_sma_200 AND market_cap > 1e9" }'
{
  "as_of": "2026-08-21T12:56:11Z",
  "count": 2,
  "results": [
    { "ticker": "LION", "name": "Lionsgate Studios Corp.",
      "price": 11.50, "day_change_pct": 0.0031, "market_cap": 3445741094, … },
    { "ticker": "DVA", "name": "DaVita Inc.",
      "price": 175.65, "day_change_pct": 0.0023, "market_cap": 11309826000, … }
  ]
}

One q carries both sides: a ticker starting to match is your entry, a held ticker no longer matching is your exit. There is no separate exit condition to author.

2. Validate it historicallyThe same q with asof reruns it against the market as it was — delisted tickers included, unlimited depth. This walk validates the exact rule the live loop trades: enter when a ticker matches (its scan row carries the entry price), exit when it no longer does — and since an exited ticker is absent from the scan, one series call prices the exits.

const KEY = process.env.TICKERBOT_API_KEY
const Q = 'rsi_14 < 30 AND above_sma_200 AND market_cap > 1e9'
const dates = ['2024-01-05', '2024-02-05', '2024-03-05', '2024-04-05', '2024-05-06']

const positions = new Map()   // ticker → entry price

for (const date of dates) {
  // who matches, knowing only what this date knew — rows carry the price
  const { results } = await fetch('https://api.tickerbot.io/v2/scan', {
    method: 'POST',
    headers: { Authorization: `Bearer ${KEY}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({ q: Q, asof: date }),
  }).then((r) => r.json())
  const matching = new Map(results.map((r) => [r.ticker, r.price]))

  // EXITS — held positions no longer matching. An exited ticker is absent
  // from the scan, so one series call prices the exits.
  const exiting = [...positions.keys()].filter((t) => !matching.has(t))
  if (exiting.length) {
    const { series } = await fetch(
      `https://api.tickerbot.io/v2/series?tickers=${exiting.join(',')}` +
        `&columns=close&interval=1d&from=${date}&to=${date}`,
      { headers: { Authorization: `Bearer ${KEY}` } },
    ).then((r) => r.json())
    for (const t of exiting) {
      const close = (series[t] ?? [])[0]?.close
      if (close) console.log(`${date}  exit  ${t}  ${((close / positions.get(t) - 1) * 100).toFixed(1)}%`)
      positions.delete(t)
    }
  }

  // ENTRIES — matching tickers not yet held, priced by their own scan row
  for (const [t, price] of matching) {
    if (!positions.has(t) && price) positions.set(t, price)
  }
}

// Real output for this q (condensed):
// 2024-02-05  exit  AAPL  3.6%     2024-03-05  exit  DUOL  18.4%
// 2024-02-05  exit  MANH  25.0%    2024-03-05  exit  AFL   5.9%
// 2024-02-05  exit  KFRC  8.7%     2024-04-05  exit  RAMP  8.4%
// 2024-02-05  exit  LOPE  5.1%     2024-05-06  exit  PFGC  -1.1%
// 2024-02-05  exit  WINA  -2.1%    2024-05-06  exit  NXT   -6.3%

Entries and exits here follow the identical membership rule the webhook fires on. The full treatment — equity curve, position sizing — is the backtester guide.

3. Run it liveSubscribe the same query as a webhook: a ticker entering your condition is your signal to decide; your handler routes any order to your broker.

curl -X POST "https://api.tickerbot.io/v2/scan/subscribe" \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "q": "rsi_14 < 30 AND above_sma_200 AND market_cap > 1e9",
        "target_url": "https://your-bot.example.com/hook" }'
{
  "id": "wh_SVrlI0OowiE",
  "q": "rsi_14 < 30 AND above_sma_200 AND market_cap > 1e9",
  "channel": "webhook",
  "target_url": "https://your-bot.example.com/hook",
  "signing_secret": "whsec_…",
  "status": "active",
  …
}

4. Write the algorithmThe delivery's matches are tickers that just entered the condition; current_matches is everything matching now — entries and exits from one delivery. (Payload lists cap at 100 rows; confirm any single position with GET /v2/tickers/{ticker}.)

import express from 'express'
const app = express()
app.use(express.json())

const positions = new Map()                     // ticker → qty: your book
const buy  = async (ticker, qty) => { /* your broker's order API */ }
const sell = async (ticker, qty) => { /* your broker's order API */ }

app.post('/hook', async (req, res) => {
  const { matches, current_matches } = req.body
  const matching = new Set(current_matches.map((r) => r.ticker))

  // ENTRIES — new matches you don't already hold
  for (const row of matches) {
    if (!positions.has(row.ticker)) {
      await buy(row.ticker, 100)
      positions.set(row.ticker, 100)
    }
  }

  // EXITS — held positions whose entry condition no longer holds
  for (const [ticker, qty] of positions) {
    if (!matching.has(ticker)) {
      await sell(ticker, qty)
      positions.delete(ticker)
    }
  }

  res.sendStatus(200)
})

app.listen(3000)   // the server behind your target_url

Condition-exit is one exit style — thesis invalidation. Stops, targets, and time exits are your strategy logic, layered on the same book.

The calls behind it

POST /v2/scanThe condition, evaluated across the whole market: live or ?asof=
GET /v2/seriesThe validation’s pricing side: forward closes on one aligned grid
POST /v2/scan/subscribeThe same condition as a push: fires on match-set changes

questions

FAQ

Does this API place trades?

No, and deliberately. It is the market-state layer of a trading system: conditions as computed data, history to validate them, pushes to run them. Execution belongs to your broker’s API, behind your own risk rules — that separation is what keeps the system auditable and keeps the decision yours.

Can I backtest the exact rule I trade live?

Yes — it is the same string. A scan with asof reruns the clause against the market as it stood, with no look-ahead by construction and delisted tickers still in the answer; the live loop subscribes the identical clause as a webhook. Nothing is re-implemented between validation and production.

Why does a trading system need computed state?

Because every action it takes — an alert, a screen result, an order — is a condition over derived values: RSI below 30, price above the 200-day average, volume three times normal. Raw data doesn’t contain those. Something has to compute and refresh them for every symbol, continuously, and keep the history so past answers are reproducible. That’s a data pipeline, not a feature — you either build and operate it, or query one that already runs.

How does Tickerbot work with AI agents?

Every call here is also a native tool call: install the MCP server and Claude, ChatGPT, Cursor, or any MCP runtime queries the market directly. Computed state is what makes that work well: hand a model raw data and its context window fills with math to do; hand it computed answers and the context goes to decisions. A scan returns the tickers matching your condition: a list, not a workload.

How does Tickerbot pricing work?

The Free plan needs no card and carries the full data side: every ticker, every signal, real-time data, all-time history and as-of queries, at 10,000 calls a month and 60 a minute. Paid plans start at $29/mo, remove the monthly cap, and raise the rate limit; webhooks and streaming come with them. Data depth is never a tier lever: every plan sees the same table.

in the wild

900K+ calls served, and counting.

What people are saying.

“dude. whoah.”
President, ShopifyHarley Finkelstein
“Tickerbot is insane. It turns Claude into a quant.”
Quantitative Finance MScLounes Vennema
“Best value for hobbyists and advanced traders alike.”
AI Engineer, ImergeRon Reid

get started

Get a key. Run a scan.

Free plan. Every ticker, every signal, real-time data, all-time history.