Screener API · One series, every symbol

Ask the whole market one question.

GET /v1/screener reads one indicator series across every symbol in the live cache, keeps the ones inside your min/max band, sorts them by value and pages the result. One call instead of one per symbol.

42 screenable series 7 timeframes 2× request weight
tickatlas.com / v1 / screener 200 OK
GET /v1/screener?indicator=RSI_14&timeframe=H1&max_val=30&sort=asc
RSI_14 · H1 · max_val 30
3 matches
sort asc
AUDUSD22.4
NZDUSD25.1
EURJPY28.9
total_matches3
limit50
offset0
has_morefalse
{
  "success": true,
  "data": {
    "indicator": "RSI_14",
    "timeframe": "H1",
    "filter": { "min": null, "max": 30.0 },
    "results": [
      { "symbol": "AUDUSD", "value": 22.4, "bid": 0.6421 },
      { "symbol": "NZDUSD", "value": 25.1, "bid": 0.57832 },
      { "symbol": "EURJPY", "value": 28.9, "bid": 161.452 }
    ],
    "total_matches": 3,
    "pagination": { "offset": 0, "limit": 50, "total": 3, "has_more": false },
    "updated_at": 1774620183
  }
}
42Screenable series
7Timeframes
200Max results per page
2×Request weight
What it is, exactly

One series. One band. Every symbol.

This is a deliberately small endpoint, and knowing its edges is what makes it useful. It answers "which symbols have this indicator in this range right now" in one request — and nothing more than that.

IT DOES

Scan, band, sort, page

Walks every symbol in the live indicator cache, reads one series on one timeframe, drops anything outside min_val/max_val, sorts the survivors by value and returns a page of {symbol, value, bid} with the pre-pagination total beside it.

  • Any of the 42 series in the catalogue below
  • Both band ends optional, and recorded back in filter
  • offset/limit paging up to 200 a page
IT DOES NOT

Combine conditions, or pick fields

There is no second indicator, no AND across indicators, no field-selectable sort, no symbol allow-list and no derived column such as a cross, a band position or a bias. Composition happens in your code, or on the endpoints built for it.

  • Two conditions → two screens, then intersect
  • Fixed symbol list → /v1/multi
  • Aggregate verdict → /v1/summary

Unknown query parameters are ignored, not rejected. That is FastAPI's default, and it is why a wrong parameter here is worse than an error: a request carrying invented filters returns HTTP 200 with the band unapplied, which looks like a working call and is not one. Only the seven parameters in the next section have any effect.

Parameter reference

Seven parameters. One of them required.

The full query surface, with the defaults the route declares. Invalid values are rejected with a named error code and the acceptable set in the detail, rather than being coerced into something that silently changes your results.

Parameter Type Required Default Behaviour
indicator string required — Exactly one series name, spelled as in the catalogue below — RSI_14, MACD_hist, ADX. There is no default and no list form; one call screens one series.
timeframe string optional H1 M1, M5, M15, M30, H1, H4, D1. Uppercased for you. Anything else is a 400 with INVALID_TIMEFRAME and the valid set in the detail.
min_val number optional null Inclusive lower bound. Symbols whose value is strictly below it are dropped. Omit for no lower bound.
max_val number optional null Inclusive upper bound. Symbols whose value is strictly above it are dropped. Omit for no upper bound.
sort string optional asc A DIRECTION, not a field: asc or desc, applied to the indicator value. Anything else is a 400 with INVALID_SORT. There is no `order` parameter.
offset integer optional 0 Pagination offset into the full sorted match list. Must be 0 or greater.
limit integer optional 50 Page size, 1 to 200 inclusive. A value outside that range is rejected by validation, not clamped.
Complete catalogue

All 42 series you can screen, by name.

These are the exact spellings the indicator parameter accepts — the same names the ingest pipeline, the cache and every other indicator endpoint use. Case and underscores matter; an unrecognised name returns no matches rather than an error, because the endpoint simply finds no such key in the cache.

23 series Trend

Trend

Moving averages, MACD components, directional movement, Ichimoku lines, Alligator lines, Parabolic SAR and the two smoothed averages.

  • SMA_10
  • SMA_20
  • SMA_50
  • SMA_100
  • SMA_200
  • EMA_10
  • EMA_20
  • EMA_50
  • MACD_main
  • MACD_signal
  • MACD_hist
  • ADX
  • ADX_plusDI
  • ADX_minusDI
  • Ichimoku_tenkan
  • Ichimoku_kijun
  • Ichimoku_senkou_a
  • Alligator_jaw
  • Alligator_teeth
  • Alligator_lips
  • SAR
  • TEMA_20
  • DEMA_20
8 series Oscillator

Oscillator

Bounded momentum measures. These are the ones a min/max band reads most naturally, because their scales are fixed rather than price-relative.

  • RSI_14
  • Stochastic_K
  • Stochastic_D
  • CCI_14
  • CCI_20
  • WilliamsR_14
  • Momentum_14
  • DeMarker_14
7 series Volatility

Volatility

Band levels, bandwidth, true range and standard deviation. Band levels are in price units, so a band that works on one symbol will not transfer to another.

  • BB_upper
  • BB_middle
  • BB_lower
  • BB_width
  • ATR_14
  • ATR_7
  • StdDev_20
4 series Volume

Volume

Flow and participation measures. OBV and AD are cumulative, so their absolute level is only comparable to the same symbol’s own history.

  • OBV
  • MFI_14
  • AD
  • Volumes

Pick a series whose scale is comparable across symbols. A band on an oscillator means the same thing on every instrument, because the scale is fixed. A band on a moving average, a Bollinger level or a cumulative volume series is in the symbol's own units, so a single min_val across the whole market will mostly select by price level rather than by behaviour. BB_width, ATR and StdDev_20 have the same caveat in a milder form.

Request pattern

Band it, sort it, page it.

One required parameter and a header is a working call. The rest is the band, the direction and the page — and paging is worth writing properly, because total_matches and has_more are both in the response for exactly that.

  • Required: indicator, one series name.
  • Bounds are inclusive; either end may be omitted.
  • limit is validated to 1–200, not clamped.
  • Key needs the premium permission scope.
# oversold band on the hourly, tightest first
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://tickatlas.com/v1/screener?indicator=RSI_14&max_val=30&sort=asc"

# a band has both ends; sort desc to see the extreme first
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://tickatlas.com/v1/screener?indicator=RSI_14&min_val=50&max_val=70&timeframe=H4&sort=desc"
# page through every match, 200 at a time
URL = "https://tickatlas.com/v1/screener"
H = { "X-API-Key": KEY }

offset, rows = 0, []
while True:
    d = requests.get(URL, headers=H, params={
        "indicator": "ADX", "timeframe": "H4",
        "min_val": 25, "sort": "desc",
        "offset": offset, "limit": 200,   # limit ceiling is 200
    }).json()["data"]

    rows += d["results"]
    if not d["pagination"]["has_more"]:
        break
    offset += d["pagination"]["limit"]

# total_matches is the pre-pagination count
print(len(rows), "of", d["total_matches"])

# symbol is the canonical name ("EURJPY")
total_matches
3 RSI_14 ≤ 30 · H1 · page 1 of 1
has_more false
Row shape{ symbol, value, bid }
Band, echoed backfilter.min null · filter.max 30.0
Pagingoffset · limit · total · has_more
Freshnessupdated_at (epoch)
Small rows on purpose

Three fields per match, and a total you can trust.

A screener result is a shortlist, not a report — so a row carries the symbol, the value that matched and a bid for context, and nothing else. Everything richer is a follow-up call on the handful of symbols that came back, which is the whole point of screening first.

Field reference

Everything in data.

The envelope is { "success": true, "data": { … } }. Note what is absent as much as what is present: there is no scanned-symbol count, no request timestamp and no second indicator anywhere in the payload.

Field Type What it carries
indicator string Echo of the requested series name, exactly as you sent it.
timeframe string Uppercased echo of the timeframe.
filter object The band that was applied: { min, max }. Either side is null when you omitted it — so the response records what you did and did not constrain.
results object[] The current page of matches, each { symbol, value, bid }. Nothing else — no bias, no change, no second indicator.
results[].symbol string The canonical symbol, e.g. EURJPY.
results[].value number The indicator value that was filtered and sorted on.
results[].bid number Bid from the price cache at read time, for context. null when no price is cached for that symbol.
total_matches number How many symbols matched in total, before pagination. Compare with results.length to know whether you are looking at a page or the whole set.
pagination object { offset, limit, total, has_more }. has_more is offset + limit < total, so it is false on the last page even when that page is full.
updated_at number Epoch timestamp of the last indicator-cache refresh — the same value every /v1 indicator endpoint reports.
Timeframes

The same 7 intervals as every indicator endpoint.

M1, M5, M15, M30, H1, H4, D1, defaulting to H1. The timeframe is validated against the same set /v1/indicator, /v1/indicators and /v1/summary use — the screener used to accept any string and return an empty 200, which hid a typo'd timeframe behind a plausible "no matches".

  • M1 — 1 Minute
  • M5 — 5 Minutes
  • M15 — 15 Minutes
  • M30 — 30 Minutes
  • H1 — 1 Hour (default)
  • H4 — 4 Hours
  • D1 — Daily
Developer-first behavior

Cache-only, and honest about it.

A whole-universe scan has to be cheap to be worth offering, so this endpoint reads only what is already in memory. The design consequences are visible in the response rather than hidden behind it.

SOURCE

Reads the cache, not the database

Values come from the same Redis cache /v1/indicator serves, so a scan costs no query planning and a symbol missing from the cache is simply skipped.

  • cache_ttl: M1 180s → D1 2700s
CONCURRENCY

Runs off the event loop

The route is a synchronous function on purpose: its per-symbol loop is blocking I/O, and FastAPI runs a sync route in a threadpool so that loop cannot stall every other request.

  • threadpool, not the event loop
VALIDATION

Typos fail loudly

A bad timeframe is INVALID_TIMEFRAME and a bad sort is INVALID_SORT, both 400 with the valid values listed — not an empty result set that looks like a real answer.

  • 400 with a machine-readable detail
QUOTA

2× weight, premium scope

Premium tier, multiplier 2.0 — the same as /v1/ohlc, /v1/multi, /v1/heatmap and /v1/calendar. The key needs the premium scope.

  • X-API-Key: tk_…
Developer API pricing

Included on every plan.

The screener is in the premium endpoint tier, a call counts once against your quota and costs two weighted units of credit however many symbols it evaluated. Pay-as-you-go counts as paid for endpoint access, so a new account can call it immediately.

There is no free tier and no self-serve trial. Every account starts on pay-as-you-go with $2.50 of prepaid credit, no card and no overage — monthly plans lift the starting quota.
Pay as you go
$0 to start 200 requests/day · 30/min until you top up
  • $2.50 of credit included
  • Every REST endpoint except raw ticks
  • No card required, no overage
  • 10 API keys
Start free
Starter
$29/mo 10,000 requests · 120/min
  • Every REST endpoint except raw ticks
  • WebSocket streaming, 5 symbols
  • Released calendar actuals
  • Email support
  • 3 API keys
View plan
Enterprise
$349/mo 1,000,000 requests · 6000/min
  • Everything in Pro
  • Dedicated support
  • 100 API keys
  • Custom indicators
  • SLA guarantee
  • On-premise option
Contact sales
Common software patterns

A shortlist is a primitive.

Almost everything built on this endpoint is the same move: turn the whole market into a handful of symbols, cheaply, then do the expensive thing only on those.

Shortlists

One call replaces a loop over the whole cached universe. Screen for the band you care about, then spend the expensive per-symbol endpoints only on what came back.

pre-filterswatchlists

Ranked tables

Results arrive sorted by value with a pre-pagination total, so a paged table needs no client-side sort and can show "3 of 148" honestly.

tablesleaderboards

Set-difference alerts

Store the match set per run and fire on entries and exits rather than on the whole list. The sorted, stable shape makes the diff trivial.

alertsschedulerswebhooks
Frequently asked questions

The screener, clarified.

What one call can and cannot express, what sort means, which name a symbol comes back as, and what the whole scan costs.

Can I combine several indicator conditions in one call?

No. One call screens exactly one series, with an optional min and an optional max on it — that band is the only conjunction available. Combining conditions is done client-side: screen on the most selective series first, then intersect with a second screen, or read the shortlist with /v1/multi or /v1/summary. An earlier version of this page described per-indicator comparison parameters and AND logic; they do not exist, and FastAPI ignores unknown query parameters silently, so a request using them returned an unfiltered 200.

What can sort do?

It sets the direction only: asc or desc, applied to the indicator value you screened on. There is no field selection and no separate `order` parameter. Anything other than asc or desc is a 400 with INVALID_SORT rather than a silent fallback.

Can I restrict the scan to specific symbols?

Not through this endpoint — there is no symbols parameter. The scan always covers every symbol in the live indicator cache. To work with a fixed list, use /v1/multi, which takes the symbols explicitly.

Which symbols does it scan?

Every symbol with a cached price, which is the platform’s live coverage rather than a fixed list. A symbol is skipped when it has no cached indicators for the requested timeframe, or no value for the requested series — so total_matches is measured against what was actually readable at that moment, and pairs_analyzed-style thinness shows up as a smaller result set rather than an error.

Which name does each result carry?

The canonical name: the same name /v1/indicator and /v1/summary use, so screener output joins to them directly.

How many results can one call return?

Up to 200, set by limit, which defaults to 50 and is validated to 1–200 rather than clamped. total_matches always reports the full match count before pagination, and pagination.has_more tells you whether another page exists — it is offset + limit < total, so it is correctly false on a last page that happens to be full.

What does a call cost?

One call counts once against your daily quota and costs 2 weighted units of pay-as-you-go credit, in the premium tier alongside /v1/ohlc, /v1/multi, /v1/heatmap and /v1/calendar, and the API key must carry the premium permission scope. That is one call to evaluate the whole cached universe, against one call per symbol if you looped.

Which plans include it?

Every plan; the screener has no plan gate — so a new account can call the screener on its $2.50 of starting credit. There is no free tier and no self-serve trial.

How often is it worth re-running?

The values come from the per-timeframe indicator cache, so two calls inside the same window return the same page and both cost 2×. Re-running on the cadence of the timeframe you screened — once per new candle — is what changes the answer.

42 series, every symbol, one call

Stop looping. Ask once.

One GET with one indicator name turns the whole cached universe into a sorted shortlist. Start on your $2.50 of credit and point the expensive endpoints at what comes back.