---
title: "What the nightly scan found across the market, ranked"
method: GET
path: "/scan"
tags: ["scan"]
---

# What the nightly scan found across the market, ranked

`GET /scan`

## Request

- Base URL: `https://api.tradingpal.io/api/v1`
- URL: `https://api.tradingpal.io/api/v1/scan`
- Auth: one of:
  - HTTP bearer
  - OAuth 2 (authorization code, authorization URL `https://api.tradingpal.io/oauth/authorize`, token URL `https://api.tradingpal.io/oauth/token`, scopes: `patterns`)

Returns every setup the last nightly run sees across the whole market (stocks & crypto), ranked exactly as the TradingPal screener ranks them: rank 1 is the best-ranked setup of the night. Default status forming returns setups whose trigger has not fired; in_progress returns the ones that triggered and are running; all returns both. Each row is the same Setup object the per-ticker route returns, plus rank and the three numbers the screener shows on each setup: expected_gain_pct (Typical return, the value the ranking sorts by), win_rate_pct with win_rate_trades, and distance_to_trigger_pct (how close the last close is to the trigger). Narrow with family, direction or max_distance_pct, or run your own screen with filters, the same filter list the TradingPal screener takes (price, market cap, volume, RSI, moving averages, performance, win rate, reward to risk, security type, exchange, industry and more); page with limit and offset; count says how many matched. The book changes nightly and the answer is served from a short cache, so page through it within a session and say as_of_session when presenting it. Needs a key on an account with Premium+; no ticker needs to be tracked.

## Query parameters

- `family` 'rising_wedge' | 'falling_wedge' | 'bullish_pennant' | 'bearish_pennant' | 'triangle'
- `direction` 'bullish' | 'bearish'
- `status` 'forming' | 'in_progress' | 'all'
- `max_distance_pct` number
- `filters` string
- `limit` integer
- `offset` integer

## Headers

- `X-TradingPal-Client` string

## Response `200`

OK

- Scan: What the last nightly scan found across the whole market, ranked.
  - `api_version` 'v1', required: The contract version, always v1. Field names in v1 do not change, and v1 stays available at least 12 months after a v2 ships.
  - `as_of_session` string, date, nullable, required: The last trading session the data describes, as an ISO date. Setups change nightly, so say it when presenting data and do not cache setups across days. Null on routes that do not read a dated artifact (account, tickers, history, families, universe and the demo index).
  - `data_version` string, nullable: Identifier of the stored artifact the answer came from: the candidate-book generation for patterns, the sealed manifest for family records. Null when the route reads no versioned artifact.
  - `source` object, required: Who produced the data. Constant on every response.
    - `name` string: Always "TradingPal".
    - `url` string, uri: Always https://tradingpal.io.
  - `disclaimer` 'Educational breakout-pattern analysis from TradingPal, not investment advice. Past results do not guarantee future outcomes.', required: The educational-use disclaimer, constant on every response. Repeat it when presenting a setup as something the user might act on.
  - `available` boolean, required: True when the nightly has a current book. False when no book exists for the session; setups is then empty.
  - `status` 'forming' | 'in_progress' | 'all', required: The status filter applied: forming, in_progress or all.
  - `filters` object: The other filters as applied, null where none was sent.
    - `family` string, nullable: The family filter.
    - `direction` string, nullable: The direction filter.
    - `max_distance_pct` number, nullable: The distance cap.
    - `predicates` object[]: The screener predicates as applied, in canonical form and order; empty when none was sent.
  - `count` integer, required: How many setups matched the filters, before paging.
  - `limit` integer, required: The page size applied (1 to 200).
  - `offset` integer, required: The offset applied.
  - `setups` ScanSetup[], required: This page of the ranked setups, best rank first.
    - `rank` integer, required: Position in the screener's ranking for this status on this session: 1 is the best-ranked forming setup of the night (or the first in-progress trade). Ranks are assigned over the whole book before any filter, so rank 7 stays rank 7 when the request narrows by family.
    - `expected_gain_pct` number, nullable: Typical return, as the TradingPal screener labels it, and the value the ranking sorts by: the typical winning move for this pattern on this ticker in percent, blended with the family's record (ticker evidence weighted by its trade count). Null when the setup is not ranked; unranked rows sort last.
    - `win_rate_pct` number, nullable: The win rate the TradingPal screener shows on this setup, in percent: the share of past trades of this pattern that reached the target. Null without a track record.
    - `win_rate_trades` integer, nullable: How many past trades win_rate_pct counts. Null without a track record.
    - `distance_to_trigger_pct` number, nullable: How far the last close is from the trigger price, in percent of the close, signed: positive when price must rise to reach the trigger. Small is "about to break out". Null without a last close or plan.
    - `setup_id` string, required: Identifier of this pattern occurrence in the current nightly book. Pass it as setup_id to the chart.png route. It is stable while the setup stays current and is not valid across nights, so re-read the patterns when the chart route answers setup_not_found.
    - `lineage_id` string, nullable: Key of the candidate lineage this occurrence belongs to, shared by the revisions of the same detected pattern. Null when the stored candidate carries none.
    - `page_url` string, nullable: Link that opens this setup on the TradingPal site: the Screener quick view, focused on the ticker, naming this exact setup. Free to read. Give it to the user when the chat app cannot show the chart image.
    - `symbol` string, required: Exchange symbol, upper case.
    - `interval` 'D': Bar interval. Always D: daily bars.
    - `family` 'rising_wedge' | 'falling_wedge' | 'bullish_pennant' | 'bearish_pennant' | 'triangle', required: The display family of the pattern, one of rising_wedge, falling_wedge, bullish_pennant, bearish_pennant, triangle.
    - `family_label` string: Human-readable family name, for example "Falling wedge".
    - `pattern_type` string, nullable: The detector's own pattern type, which can be more specific than family (for example a triangle subtype). Null when the artifact carries none.
    - `direction` 'bullish' | 'bearish' | 'null', nullable: The side the pattern is expected to resolve to: bullish or bearish. Null when the pattern has no single direction, such as a symmetrical triangle that carries a plan for each side.
    - `status` string, required: Lifecycle stage of the setup: forming until the trigger rule fires, in_progress once a daily close has triggered it (entered is then present). Defaults to forming when the artifact stores no stage.
    - `pattern_start` string, date, nullable: Session date of the first bar of the pattern, as an ISO date. Null when unknown.
    - `pattern_end` string, date, nullable: Session date of the last bar of the detected pattern, as an ISO date; the lines run on past it to the last session. Null when unknown.
    - `as_of_session` string, date, nullable: The session this setup is valid for, as an ISO date; the same value as the envelope's as_of_session.
    - `last_close` number, nullable: The close of the last session before as_of_session, in dollars, so a caller can place the trigger relative to price without another quote; every route reports the same value for the same setup. Null when no price is stored.
    - `price_scale` 'log': Always log: the lines are straight in log price, so draw them on a log-scale price axis.
    - `lines` object, required: The two boundary lines of the pattern.
      - `upper` object, nullable: The upper boundary. Bullish plans trigger on a daily close above it. Null when the stored pattern has no upper line.
        - `start` object: The earlier endpoint: the session and price where the line begins.
          - `date` string, date, required: Session date of the bar, as an ISO date.
          - `price` number, required: Price in dollars, rounded to four decimals.
        - `end` object: The later endpoint, at the end of the pattern geometry. The trigger price sits on the extension of the line past this point.
          - `date` string, date, required: Session date of the bar, as an ISO date.
          - `price` number, required: Price in dollars, rounded to four decimals.
        - `touch_count` integer, nullable: How many bars touched the line, as the detector counted them; the length of touches when no count is stored.
        - `touches` object[]: The bars that touched the line, in the order the detector recorded them, so a charting tool can reproduce the fit. May be empty.
          - `date` string, date, required: Session date of the touching bar, as an ISO date.
          - `price` number, nullable, required: The bar high (upper line) or low (lower line) in dollars. Null when that bar is not in the stored candle tail.
      - `lower` object, nullable: The lower boundary. Bearish plans trigger on a daily close below it. Null when the stored pattern has no lower line.
        - `start` object: The earlier endpoint: the session and price where the line begins.
          - `date` string, date, required: Session date of the bar, as an ISO date.
          - `price` number, required: Price in dollars, rounded to four decimals.
        - `end` object: The later endpoint, at the end of the pattern geometry. The trigger price sits on the extension of the line past this point.
          - `date` string, date, required: Session date of the bar, as an ISO date.
          - `price` number, required: Price in dollars, rounded to four decimals.
        - `touch_count` integer, nullable: How many bars touched the line, as the detector counted them; the length of touches when no count is stored.
        - `touches` object[]: The bars that touched the line, in the order the detector recorded them, so a charting tool can reproduce the fit. May be empty.
          - `date` string, date, required: Session date of the touching bar, as an ISO date.
          - `price` number, nullable, required: The bar high (upper line) or low (lower line) in dollars. Null when that bar is not in the stored candle tail.
    - `trigger_rule` string, nullable: The trigger rule in one sentence, chosen by direction: a daily close above the upper line, below the lower line, or through either line when the pattern carries a plan per side. Always present.
    - `plans` object[], required: One trade plan per direction the pattern can resolve in: usually one, two for a symmetrical triangle. Empty when the stored candidate has no eligible branch.
      - `direction` 'bullish' | 'bearish': The side this plan trades: bullish (a close above the upper line) or bearish (a close below the lower line).
      - `trigger_price` number, nullable: The daily close that confirms the pattern, in dollars, as of the last session. It sits on the boundary line, so it moves a little each session. Null when the stored branch has none.
      - `stop_price` number, nullable: The stop level in dollars. The backtest freezes it at entry and fills it on a touch, or at the open after an adverse gap. Null when the stored branch has none.
      - `target_price` number, nullable: The measured-move target in dollars: the pattern height projected from the trigger. Null when the stored branch has none.
    - `track_record` object, required: The backtest records behind this setup, at family scope and at ticker scope.
      - `family` StatBlock, nullable: A track record: how one pattern family resolved over the last 20 years of the served universe, or how it resolved on one ticker. Null where a record would go when none is stored; never a partial block. Never present win_rate_pct without sample_size and avg_loss_pct; quote summary whole.
        - `scope` 'family' | 'ticker': family when the record covers every ticker in the served universe, ticker when it covers one symbol.
        - `family` string: The pattern family the record is for (rising_wedge, falling_wedge, bullish_pennant, bearish_pennant, triangle).
        - `symbol` string, nullable: The ticker at ticker scope; null at family scope.
        - `win_rate_pct` number, nullable: Share of resolved trades that reached the target, as a percent (54.0 means 54%), one decimal. At ticker scope the stored rate is shrunk toward the family rate so a handful of trades cannot read as 0% or 100%; wins and losses stay raw. Never quote it without sample_size and avg_loss_pct.
        - `sample_size` integer: Number of trades in the record: the stored trade count, or wins plus losses when no count is stored. The summary sentence quotes it as resolved trades.
        - `wins` integer: Resolved trades that reached the target.
        - `losses` integer: Resolved trades that hit the stop. A bar that reaches both target and stop counts as a loss.
        - `avg_win_pct` number, nullable: Mean price move of the winning trades from entry to exit, as a percent of the entry price (positive), two decimals.
        - `avg_loss_pct` number, nullable: Mean price move of the losing trades from entry to exit, as a percent of the entry price (negative), two decimals.
        - `median_win_pct` number, nullable: Median winning move as a percent of the entry price: the typical win, less sensitive to outliers than avg_win_pct.
        - `avg_trade_pct` number, nullable: Mean price move over all resolved trades, wins and losses together, as a percent of the entry price.
        - `avg_r` number, nullable: Mean R multiple over resolved trades, where one R is the entry-to-stop distance; 0.41 means the average trade made 0.41 times its risk.
        - `expected_r` number, nullable: Per-trade expectancy in R: win rate times the average winning R plus loss rate times the average losing R. Equals avg_r when both come from the same trades.
        - `profit_factor` number, nullable: Gross R of the winning trades divided by gross R of the losing trades; above 1 means the wins outweigh the losses. Null when the record has no losses.
        - `avg_hold_bars` number, nullable: Mean number of daily bars (sessions) from entry to exit over resolved trades, one decimal.
        - `backtest_range` string[], nullable: The window the trades were taken from, as [first date, last date] in ISO format, at most the last 20 years. Null when the record carries no dates.
        - `measured` string: The measurement rule in one paragraph: how entries, stops, targets and exits are counted. Identical on every block.
        - `summary` string: One sentence with the sample size, the window, the win rate, the average win, the average loss and the median win. Quote it whole rather than picking numbers out of it.
      - `ticker` StatBlock, nullable: A track record: how one pattern family resolved over the last 20 years of the served universe, or how it resolved on one ticker. Null where a record would go when none is stored; never a partial block. Never present win_rate_pct without sample_size and avg_loss_pct; quote summary whole.
        - `scope` 'family' | 'ticker': family when the record covers every ticker in the served universe, ticker when it covers one symbol.
        - `family` string: The pattern family the record is for (rising_wedge, falling_wedge, bullish_pennant, bearish_pennant, triangle).
        - `symbol` string, nullable: The ticker at ticker scope; null at family scope.
        - `win_rate_pct` number, nullable: Share of resolved trades that reached the target, as a percent (54.0 means 54%), one decimal. At ticker scope the stored rate is shrunk toward the family rate so a handful of trades cannot read as 0% or 100%; wins and losses stay raw. Never quote it without sample_size and avg_loss_pct.
        - `sample_size` integer: Number of trades in the record: the stored trade count, or wins plus losses when no count is stored. The summary sentence quotes it as resolved trades.
        - `wins` integer: Resolved trades that reached the target.
        - `losses` integer: Resolved trades that hit the stop. A bar that reaches both target and stop counts as a loss.
        - `avg_win_pct` number, nullable: Mean price move of the winning trades from entry to exit, as a percent of the entry price (positive), two decimals.
        - `avg_loss_pct` number, nullable: Mean price move of the losing trades from entry to exit, as a percent of the entry price (negative), two decimals.
        - `median_win_pct` number, nullable: Median winning move as a percent of the entry price: the typical win, less sensitive to outliers than avg_win_pct.
        - `avg_trade_pct` number, nullable: Mean price move over all resolved trades, wins and losses together, as a percent of the entry price.
        - `avg_r` number, nullable: Mean R multiple over resolved trades, where one R is the entry-to-stop distance; 0.41 means the average trade made 0.41 times its risk.
        - `expected_r` number, nullable: Per-trade expectancy in R: win rate times the average winning R plus loss rate times the average losing R. Equals avg_r when both come from the same trades.
        - `profit_factor` number, nullable: Gross R of the winning trades divided by gross R of the losing trades; above 1 means the wins outweigh the losses. Null when the record has no losses.
        - `avg_hold_bars` number, nullable: Mean number of daily bars (sessions) from entry to exit over resolved trades, one decimal.
        - `backtest_range` string[], nullable: The window the trades were taken from, as [first date, last date] in ISO format, at most the last 20 years. Null when the record carries no dates.
        - `measured` string: The measurement rule in one paragraph: how entries, stops, targets and exits are counted. Identical on every block.
        - `summary` string: One sentence with the sample size, the window, the win rate, the average win, the average loss and the median win. Quote it whole rather than picking numbers out of it.
    - `entered` object: The fill, present only once the trigger has fired (status in_progress). Absent while forming.
      - `entry_date` string, date, nullable: Session date of the bar that triggered the setup, as an ISO date. Null when the artifact stores no entry time.
      - `entry_price` number, nullable: The entry price in dollars, recorded when the trigger fired.
      - `stop_price` number, nullable: The stop in dollars, frozen at entry.
      - `target_price` number, nullable: The target in dollars, fixed at entry.

## Other responses

- `400`: bad_request (family, direction or status not one of the allowed values; max_distance_pct not a number; limit or offset not an integer; filters not a valid predicate list, the message says which predicate)
- `401`: missing_api_key (no Authorization: Bearer tp_live_… header) or invalid_api_key (unknown, revoked, or the account is inactive)
- `403`: forbidden (the account is not on Premium+, the plan with API access)
- `429`: rate_limited; wait details.retry_after_seconds

---

[API](https://skmtc.dev/tradingpal/apis/tradingpal-pattern-api.md) · [All operations](https://skmtc.dev/tradingpal/apis/tradingpal-pattern-api/llms.txt) · [OpenAPI document](https://skmtc.dev/tradingpal/apis/tradingpal-pattern-api/revisions/e96e1f20c1ce?raw)
