patternfetch Api API

Agent-first API + MCP: a stock, ETF, or crypto ticker + timeframe -> a token-compact market-state brief (compact candles, chart/candlestick patterns, support/resistance, regime, interpreted indicators, and a one-line summary). Covers US stocks & ETFs (Yahoo, split & dividend adjusted, delayed/EOD) a

Operations 25

POST /v1/brief Token-compact market-state brief for a stock, ETF, or crypto ticker + timeframe ($0.01) #
POST /v1/multi Multi-timeframe market-state view + cross-timeframe alignment ($0.025) #
POST /v1/delta Only what changed since your last brief ($0.008, $0.001 when nothing changed) #
POST /v1/candles Compact candle codec only — rows + SAX shape signature ($0.005) #
POST /v1/analogs Historical analogs as a full outcome distribution — not a prediction ($0.05) #
POST /v1/scan Screener — scan US stocks, ETFs & crypto for tickers in a regime or printing a pattern, RANKED by honest backtested base rate ($0.02) #
POST /v1/demo Free no-signup demo brief (rate-limited, never billed) #
POST /v1/keys Self-serve free API key with $3.00 starter credit (300 briefs) #
POST /v1/recover Mail a single-use link that issues a replacement API key #
GET /v1/recover Redeem a recovery link and receive the replacement key (HTML) #
GET /v1/topup Read the x402 payment requirements for a credit top-up #
POST /v1/topup Top up credit with x402 (USDC on Base, no account) #
POST /v1/checkout Create a Stripe hosted-checkout URL to top up credit #
POST /v1/subscribe Create a Stripe checkout URL for the Studio subscription ($19/month, 2,500 briefs included) #
POST /v1/portal Create a Stripe billing-portal URL to manage your own billing #
GET /v1/platforms Capabilities matrix (assets, timeframes, endpoints) #
GET /v1/status Service status (product, version, sources, assets) #
GET /healthz Liveness check #
POST /mcp MCP (Model Context Protocol) over Streamable HTTP #
GET /v1/watch List watchlist subscriptions #
POST /v1/watch Watch a ticker and get an HTTPS callback when its state changes #
DELETE /v1/watch/{id} Delete a watchlist subscription #
GET /v1/newsletter Read the changelog subscription state for this account #
POST /v1/newsletter Subscribe to or unsubscribe from the changelog #
POST /v1/demo-multi Multi-timeframe demo — no key required #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/patternfetch-com:patternfetch-api-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

patternfetch-com-api-openapi.json Raw ↑
{
  "openapi": "3.1.0",
  "info": {
    "title": "patternfetch",
    "version": "0.3.0",
    "description": "Agent-first API + MCP: a stock, ETF, or crypto ticker + timeframe -> a token-compact market-state brief (compact candles, chart/candlestick patterns, support/resistance, regime, interpreted indicators, and a one-line summary). Covers US stocks & ETFs (Yahoo, split & dividend adjusted, delayed/EOD) and crypto (Binance spot, realtime) in one schema. Built so an LLM agent never dumps raw OHLCV into context. Free tier: keyless POST /v1/demo, free MCP tools/list, and a free key with $3.00 starter credit (300 briefs) via POST /v1/keys — no card. The credit is staged: at least $0.50 is usable immediately, and confirming your email address releases the remainder (where confirmation is not required, the full $3.00 lands at signup). Confirming never gates API access; it only releases the rest of the credit. Studio subscription: $19/month with 2,500 briefs ($25 of usage) included, then pay-per-call. Agents can also connect via OAuth (one-click, auto free-tier key) — see /.well-known/oauth-protected-resource. Impersonal market data, NOT investment advice.",
    "termsOfService": "https://patternfetch.com/terms",
    "contact": {
      "name": "patternfetch",
      "url": "https://patternfetch.com/docs",
      "email": "mail@patternfetch.com"
    }
  },
  "servers": [
    {
      "url": "https://patternfetch.com"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    },
    {
      "apiKeyAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key from POST /v1/keys (or an OAuth-minted token). Header: Authorization: Bearer pf_..."
      },
      "apiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "description": "The SAME credential and the SAME header as bearerAuth (Authorization: Bearer pf_...), spelled as an apiKey-in-header scheme. Both are listed because x402/agentcash discovery crawlers only recognise an API key when the scheme's type is literally `apiKey` — with `type: http` alone, every protected operation was reported as `auth mode missing` and the whole API read as unmonetized. The two entries describe one header, so the OR between them is exact."
      }
    },
    "schemas": {
      "Query": {
        "type": "object",
        "required": [
          "ticker",
          "timeframe"
        ],
        "properties": {
          "ticker": {
            "type": "string",
            "example": "AAPL",
            "description": "Stock, ETF, or crypto ticker (e.g. AAPL, SPY, BTC/USDT). A bare symbol is always the US listing: BTC is a listed ETF, not Bitcoin - use BTC/USDT for the coin. Affected responses carry a notice field."
          },
          "timeframe": {
            "type": "string",
            "enum": [
              "1m",
              "5m",
              "15m",
              "30m",
              "1h",
              "4h",
              "1d",
              "1w"
            ]
          },
          "limit": {
            "type": "integer",
            "minimum": 20,
            "maximum": 1000,
            "description": "Candles to analyze (default 200) NOTE: values above the maximum are CLAMPED, not rejected — limit:5000 returns 200 with 1000 candles. Values below the minimum are a 400."
          },
          "market": {
            "type": "string",
            "enum": [
              "crypto",
              "stock"
            ],
            "description": "Asset-class hint. Omitted = inferred from the symbol, which is exactly where an ambiguous ticker goes wrong: bare \"BTC\" is a real US security, not Bitcoin. Send market:'crypto' with BTC/USDT, or market:'stock' to force the equity leg. Any other value is a 400."
          }
        }
      },
      "MultiQuery": {
        "type": "object",
        "required": [
          "ticker"
        ],
        "properties": {
          "ticker": {
            "type": "string",
            "example": "BTC/USDT",
            "description": "Stock, ETF, or crypto ticker (e.g. AAPL, SPY, BTC/USDT). A bare symbol is always the US listing: BTC is a listed ETF, not Bitcoin - use BTC/USDT for the coin. Affected responses carry a notice field."
          },
          "timeframes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "1m",
                "5m",
                "15m",
                "30m",
                "1h",
                "4h",
                "1d",
                "1w"
              ]
            },
            "description": "Timeframes to compare (default [\"1h\",\"4h\",\"1d\"]); max 4, deduped, shortest to longest."
          },
          "limit": {
            "type": "integer",
            "minimum": 20,
            "maximum": 1000,
            "description": "Candles per timeframe (default 200)"
          },
          "market": {
            "type": "string",
            "enum": [
              "crypto",
              "stock"
            ],
            "description": "Asset-class hint. Omitted = inferred from the symbol, which is exactly where an ambiguous ticker goes wrong: bare \"BTC\" is a real US security, not Bitcoin. Send market:'crypto' with BTC/USDT, or market:'stock' to force the equity leg. Any other value is a 400."
          }
        }
      },
      "AnalogsQuery": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Query"
          },
          {
            "type": "object",
            "properties": {
              "window": {
                "type": "integer",
                "minimum": 2,
                "maximum": 500,
                "description": "Shape-match window length (default 32)"
              },
              "horizon": {
                "type": "integer",
                "minimum": 2,
                "maximum": 500,
                "description": "Forward horizon for the outcome distribution (default 20)"
              }
            }
          },
          {
            "type": "object",
            "properties": {
              "market": {
                "type": "string",
                "enum": [
                  "crypto",
                  "stock"
                ],
                "description": "Asset-class hint. Omitted = inferred from the symbol, which is exactly where an ambiguous ticker goes wrong: bare \"BTC\" is a real US security, not Bitcoin. Send market:'crypto' with BTC/USDT, or market:'stock' to force the equity leg. Any other value is a 400."
              }
            }
          }
        ]
      },
      "Brief": {
        "type": "object",
        "description": "Token-compact market-state brief. Returned by /v1/brief and /v1/demo.",
        "properties": {
          "header": {
            "type": "object",
            "properties": {
              "sym": {
                "type": "string",
                "example": "BTC/USDT"
              },
              "tf": {
                "type": "string",
                "example": "4h"
              },
              "src": {
                "type": "string",
                "example": "binance",
                "enum": [
                  "binance",
                  "vwap-composite",
                  "yahoo",
                  "twelvedata"
                ],
                "description": "The venue that ACTUALLY served this series — not the one routing would have guessed. A failover shows up here."
              },
              "n": {
                "type": "integer",
                "example": 200
              },
              "t0": {
                "type": "integer",
                "description": "first candle open time (epoch ms)"
              },
              "t1": {
                "type": "integer",
                "description": "last candle open time (epoch ms)"
              },
              "asOf": {
                "type": "integer",
                "description": "Epoch ms of the last bar in the series. What the brief is actually AS OF — not when it was requested."
              },
              "delay": {
                "type": "string",
                "enum": [
                  "realtime",
                  "delayed/EOD"
                ],
                "description": "Freshness class of the serving source. Equity sources are delayed/EOD; crypto is realtime."
              },
              "adjusted": {
                "type": "boolean",
                "description": "Present (true) only for equity sources, meaning the series is split/dividend adjusted. Omitted entirely for crypto."
              }
            }
          },
          "codec": {
            "type": "object",
            "description": "Compact candle codec.",
            "properties": {
              "rows": {
                "type": "string",
                "description": "Per-candle o,h,l,c,v joined by ';' (no per-row timestamp)."
              },
              "sax": {
                "type": "string",
                "description": "a-g SAX shape signature (<=8 chars)."
              },
              "precision": {
                "type": "integer",
                "description": "Decimal precision used in rows."
              }
            }
          },
          "analysis": {
            "type": "object",
            "properties": {
              "patterns": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string",
                      "description": "Pattern name. Open set — new detectors add names, so do not treat this as a closed enum."
                    },
                    "confidence": {
                      "type": "number",
                      "description": "geometric quality score 0..1 (NOT a probability of profit)"
                    },
                    "at": {
                      "type": "integer"
                    },
                    "span": {
                      "type": "integer"
                    },
                    "evidence": {
                      "$ref": "#/components/schemas/PatternEvidence"
                    }
                  }
                }
              },
              "levels": {
                "type": "object",
                "properties": {
                  "support": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "price": {
                          "type": "number"
                        },
                        "strength": {
                          "type": "number"
                        }
                      }
                    }
                  },
                  "resistance": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "price": {
                          "type": "number"
                        },
                        "strength": {
                          "type": "number"
                        }
                      }
                    }
                  }
                }
              },
              "regime": {
                "type": "object",
                "properties": {
                  "trend": {
                    "type": "string",
                    "enum": [
                      "up",
                      "down",
                      "range"
                    ]
                  },
                  "strength": {
                    "type": "number"
                  },
                  "volPct": {
                    "type": "number",
                    "description": "ATR as % of price"
                  }
                }
              },
              "indicators": {
                "type": "object",
                "description": "Interpreted indicator states, keyed by indicator name. OPEN-ENDED: the code returns a map, so do not assume only rsi/ema are present.",
                "properties": {
                  "rsi": {
                    "type": "object",
                    "properties": {
                      "v": {
                        "type": "number"
                      },
                      "state": {
                        "type": "string",
                        "enum": [
                          "overbought",
                          "oversold",
                          "neutral"
                        ]
                      }
                    }
                  },
                  "ema": {
                    "type": "object",
                    "properties": {
                      "v": {
                        "type": "number"
                      },
                      "state": {
                        "type": "string"
                      }
                    }
                  }
                },
                "additionalProperties": true
              },
              "nl": {
                "type": "string",
                "description": "One-line natural-language summary the agent can act on."
              }
            }
          },
          "disclaimer": {
            "type": "string"
          },
          "methodologyUrl": {
            "type": "string",
            "example": "https://patternfetch.com/methodology"
          },
          "notice": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Caveats that change how the payload should be read — today the cross-listing warning (e.g. \"BTC\" is a US security here, not Bitcoin). Serialized as the FIRST key so it is seen before the data it qualifies. Absent when there is nothing to warn about."
          }
        }
      },
      "ScanRequest": {
        "type": "object",
        "description": "Optional filters for the screener (POST /v1/scan). Every field is optional; an absent field puts no constraint on that axis. A filter-less scan returns the top of the universe (limit defaults to 50).",
        "properties": {
          "assetClass": {
            "type": "string",
            "enum": [
              "stock",
              "crypto",
              "all"
            ],
            "description": "Restrict to US stocks/ETFs, crypto pairs, or all (default all)."
          },
          "regime": {
            "type": "string",
            "enum": [
              "up",
              "down",
              "range"
            ],
            "description": "Only tickers whose current regime trend matches (up=uptrend, down=downtrend, range=sideways)."
          },
          "tf": {
            "type": "string",
            "enum": [
              "1m",
              "5m",
              "15m",
              "30m",
              "1h",
              "4h",
              "1d",
              "1w"
            ],
            "description": "Only signals computed on this timeframe (the universe is currently precomputed at 1d)."
          },
          "pattern": {
            "type": "string",
            "description": "Require the ticker's top pattern to be this one, e.g. double_bottom, double_top, head_and_shoulders, bullish_engulfing, bearish_engulfing, hammer."
          },
          "minLift": {
            "type": "number",
            "minimum": -1,
            "maximum": 1,
            "description": "Drop tickers whose top pattern does not beat its OWN pattern-free baseline by at least this many rate points (0.02 = 2pp; 0 = at or above baseline; negative values allowed). PREFER THIS OVER minBaseRate: a raw base rate is not comparable across bullish and bearish rows, so minBaseRate:0.55 mostly selects bullish patterns in a rising universe before any of them carries information, whereas minLift selects patterns that measurably add something. Rows whose evidence carries no baseline are always excluded — absence of a lift is not a lift of 0."
          },
          "minBaseRate": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Drop tickers whose top-pattern backtested base rate is below this (0..1). Kept for existing consumers; see minLift for the drift-free equivalent, which is the better filter for an unbiased shortlist."
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 500,
            "description": "Max rows to return (default 50)."
          }
        }
      },
      "ScanResultRow": {
        "type": "object",
        "description": "One ranked discovery row from the screener.",
        "properties": {
          "sym": {
            "type": "string",
            "example": "AAPL",
            "description": "Ticker (stock/ETF symbol or crypto pair)."
          },
          "tf": {
            "type": "string",
            "example": "1d",
            "description": "Timeframe the signal was computed on."
          },
          "assetClass": {
            "type": "string",
            "enum": [
              "stock",
              "crypto"
            ]
          },
          "regime": {
            "type": "string",
            "enum": [
              "up",
              "down",
              "range"
            ],
            "description": "Current regime trend."
          },
          "pattern": {
            "type": [
              "string",
              "null"
            ],
            "description": "Top pattern name, or null if none."
          },
          "baseRate": {
            "type": [
              "number",
              "null"
            ],
            "description": "Backtested directional base rate 0..1 — the primary ranking key; null when no sufficiently-powered evidence bucket exists (ranks last)."
          },
          "ci95": {
            "type": [
              "number",
              "null"
            ],
            "description": "95% confidence-interval half-width on baseRate."
          },
          "n": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Independent historical occurrences behind the base rate."
          },
          "scope": {
            "type": [
              "string",
              "null"
            ],
            "description": "Asset-class corpus the base rate was measured over, e.g. 'US stocks & ETFs' or 'major crypto pairs'."
          },
          "confidence": {
            "type": [
              "number",
              "null"
            ],
            "description": "Geometric fit score 0..1 of the top pattern (secondary ranking key)."
          },
          "asOf": {
            "type": [
              "string",
              "null"
            ],
            "example": "2026-07-18",
            "description": "ISO date (UTC) of the last bar behind this signal. Freshness, and the final ranking tie-break."
          },
          "baseline": {
            "type": [
              "number",
              "null"
            ],
            "description": "Direction-matched pattern-free rate over the same timeframe and horizon: what the SAME bet pays with no pattern present. null on rows built from an evidence table without baselines."
          },
          "lift": {
            "type": [
              "number",
              "null"
            ],
            "description": "baseRate - baseline, in rate points. THE COMPARABLE NUMBER: baseRate alone is not comparable between bullish and bearish rows, because in a drifting universe a bullish pattern starts ahead and a bearish one starts behind with no information involved. Negative means the pattern underperforms its own baseline. null when no baseline exists."
          },
          "liftCi95": {
            "type": [
              "number",
              "null"
            ],
            "description": "95% confidence-interval half-width on lift. |lift| <= liftCi95 means the edge is not distinguishable from zero at this sample size."
          },
          "liftReading": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "above-baseline",
              "below-baseline",
              "indistinguishable-from-baseline",
              null
            ],
            "description": "Plain reading of lift against its interval, so a caller does not have to redo the statistics. Most rows in a healthy universe are indistinguishable-from-baseline; that is an honest result, not a missing value."
          }
        }
      },
      "ScanResponse": {
        "type": "object",
        "description": "Ranked screener results. Returned by POST /v1/scan. Rows are ordered by baseRate desc, then confidence desc, then timeframe, then symbol.",
        "properties": {
          "asOf": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO date the universe was last precomputed."
          },
          "tfs": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Timeframes present in the scanned universe."
          },
          "universe": {
            "type": "integer",
            "description": "Number of tickers scanned."
          },
          "count": {
            "type": "integer",
            "description": "Number of rows returned after filtering + limit."
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ScanResultRow"
            }
          },
          "note": {
            "type": "string",
            "description": "Impersonal-data / not-a-prediction note."
          },
          "disclaimer": {
            "type": "string"
          }
        }
      },
      "Error": {
        "type": "object",
        "description": "Uniform error envelope. Every non-2xx JSON response from /v1 has this shape; `error` is a stable machine code and `message` is human text that may change.",
        "required": [
          "error",
          "message"
        ],
        "properties": {
          "error": {
            "type": "string",
            "enum": [
              "INVALID_TICKER",
              "INVALID_TIMEFRAME",
              "INVALID_PARAM",
              "INSUFFICIENT_DATA",
              "UNAUTHORIZED",
              "PAYMENT_REQUIRED",
              "RATE_LIMITED",
              "PAYLOAD_TOO_LARGE",
              "UPSTREAM_ERROR",
              "NOT_FOUND",
              "GEO_BLOCKED",
              "NOT_CONFIGURED"
            ],
            "description": "Stable error code. Branch on THIS, never on the message or the HTTP status alone."
          },
          "message": {
            "type": "string",
            "description": "Human-readable explanation. Wording is not part of the contract."
          }
        }
      },
      "WatchSubscription": {
        "type": "object",
        "description": "A watchlist subscription. The signing secret is NOT part of this projection — it is returned exactly once, by the create call.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "ticker": {
            "type": "string",
            "example": "BTC/USDT"
          },
          "timeframe": {
            "type": "string",
            "example": "1h"
          },
          "market": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "crypto",
              "stock",
              null
            ],
            "description": "Asset-class hint, or null to infer from the symbol."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "HTTPS callback target."
          },
          "intervalSec": {
            "type": "integer",
            "description": "Poll interval actually in force (>= the timeframe floor)."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "lastPollAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastDeliveryAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Last time a callback was actually SENT (a poll that finds no change sends nothing)."
          },
          "nextPollAt": {
            "type": "string",
            "format": "date-time"
          },
          "failCount": {
            "type": "integer",
            "description": "Consecutive delivery failures; drives exponential backoff."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused",
              "disabled"
            ],
            "description": "'paused' recovers by itself (insufficient_credit resumes after a top-up); 'disabled' needs a new subscription."
          },
          "statusReason": {
            "type": [
              "string",
              "null"
            ],
            "example": "insufficient_credit"
          }
        }
      },
      "NewsletterState": {
        "type": "object",
        "description": "Consent record. The status is DERIVED from the three timestamps, never stored as a fourth field that could disagree with them.",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "none",
              "pending",
              "subscribed",
              "unsubscribed"
            ]
          },
          "optInAt": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Epoch ms of the opt-in (timestamp 1 of 3)."
          },
          "confirmedAt": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Epoch ms of the double-opt-in confirmation (timestamp 2 of 3)."
          },
          "unsubAt": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Epoch ms of the unsubscribe (timestamp 3 of 3)."
          }
        }
      },
      "LiftEvidence": {
        "type": "object",
        "description": "The pattern's measured edge over a pattern-free baseline. THIS is the number that says whether the pattern carries information, as opposed to merely riding a drifting market.",
        "properties": {
          "baseline": {
            "type": "number",
            "description": "Direction-matched unconditional rate: P(up) for a bullish pattern, P(down) for a bearish one."
          },
          "baselineN": {
            "type": "integer",
            "description": "Windows behind the baseline."
          },
          "lift": {
            "type": "number",
            "description": "hitRate - baseline, in rate points. Can be negative."
          },
          "ci95": {
            "type": "number",
            "description": "95% confidence-interval half-width on lift."
          },
          "ci95Clustered": {
            "type": "number",
            "description": "Half-width widened for overlapping windows. Present when the occurrences are not independent; it is the more honest of the two."
          },
          "informative": {
            "type": "boolean",
            "description": "Whether lift is distinguishable from zero given the interval. False is a normal, honest outcome — most patterns most of the time."
          },
          "reading": {
            "type": "string",
            "enum": [
              "above-baseline",
              "below-baseline",
              "indistinguishable-from-baseline"
            ],
            "description": "Plain-language verdict, so a caller does not have to redo the statistics."
          }
        }
      },
      "PatternEvidence": {
        "type": "object",
        "description": "Backtested evidence for a detected pattern. Absent when the pattern has no sufficiently-powered evidence bucket — an omitted `evidence` means \"we have not measured this\", never \"it does not work\".",
        "properties": {
          "hitRate": {
            "type": "number",
            "description": "Directional hit rate over the horizon, 0..1."
          },
          "horizon": {
            "type": "integer",
            "description": "Bars forward the outcome was measured over."
          },
          "n": {
            "type": "integer",
            "description": "Independent historical occurrences."
          },
          "scope": {
            "type": "string",
            "example": "US stocks & ETFs",
            "description": "Corpus the rate was measured over."
          },
          "ci95": {
            "type": "number",
            "description": "95% confidence-interval half-width on hitRate."
          },
          "lift": {
            "$ref": "#/components/schemas/LiftEvidence"
          }
        }
      },
      "OutcomeDistribution": {
        "type": "object",
        "description": "FULL forward-return distribution, losers included. Never a single headline number.",
        "properties": {
          "n": {
            "type": "integer",
            "description": "Matches contributing a forward return."
          },
          "horizon": {
            "type": "integer",
            "description": "Bars forward."
          },
          "winRate": {
            "type": "number"
          },
          "median": {
            "type": "number"
          },
          "mean": {
            "type": "number"
          },
          "min": {
            "type": "number"
          },
          "max": {
            "type": "number"
          }
        }
      },
      "AnalogsResponse": {
        "type": "object",
        "description": "In-series analogs: the latest window matched against earlier windows of the SAME series.",
        "properties": {
          "notice": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Cross-listing caveat when the symbol is ambiguous. Derived here from the ROUTING decision (which is the decision that can be wrong), not from a serving source — this endpoint reports no src."
          },
          "sym": {
            "type": "string"
          },
          "tf": {
            "type": "string"
          },
          "window": {
            "type": "integer",
            "description": "Window length actually used."
          },
          "distribution": {
            "$ref": "#/components/schemas/OutcomeDistribution"
          },
          "matches": {
            "type": "integer",
            "description": "Analog windows found after de-duplication."
          },
          "note": {
            "type": "string"
          },
          "disclaimer": {
            "type": "string"
          }
        }
      },
      "MultiResponse": {
        "type": "object",
        "description": "One brief per timeframe plus a cross-timeframe alignment read.",
        "properties": {
          "sym": {
            "type": "string"
          },
          "views": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "tf": {
                  "type": "string"
                },
                "trend": {
                  "type": "string",
                  "enum": [
                    "up",
                    "down",
                    "range"
                  ]
                },
                "strength": {
                  "type": "number"
                },
                "nl": {
                  "type": "string"
                }
              }
            }
          },
          "alignment": {
            "type": "object",
            "properties": {
              "trends": {
                "type": "object",
                "additionalProperties": {
                  "type": "string",
                  "enum": [
                    "up",
                    "down",
                    "range"
                  ]
                },
                "description": "Trend per timeframe."
              },
              "aligned": {
                "t

# --- truncated at 32 KB (95 KB total) ---
# Full source: https://artifacts.apis.io/patternfetch-com/openapi/_original/patternfetch-com-api-openapi.json