# ChartScout Detection API > Canonical crypto chart-pattern detection events over REST and Server-Sent > Events (SSE). ChartScout continuously scans configured markets. API clients > select detections with filters; they do not start or control detector jobs. This document is a self-contained integration guide for LLMs, coding agents, and autonomous clients. The OpenAPI 3.1 document is authoritative for exact paths, parameters, scopes, schemas, and error codes. ## Launch status - The anonymous public demo is live and requires no account or API key. - Authenticated production API access is public. Any signed-in ChartScout user may verify a card, create a PAYG API account, and receive a `cs_live_` key at https://chartscout.io/api-access. - Staging is the test environment and accepts `cs_test_` keys only. - Authenticated endpoints use the public v1 contract documented below. ## Agent bootstrap: read in this order 1. GET `/.well-known/chartscout.json` for environment-specific base URL, authentication prefix, endpoint inventory, pricing, and document links. 2. Read `/llms.txt` (this file) for integration semantics and safe workflows. 3. Parse `/openapi.yaml` before generating code. OpenAPI wins over prose if they differ. 4. Fetch `/v1/exchanges`, `/v1/timeframes`, `/v1/patterns`, and `/v1/markets` with an approved key. Never invent or hard-code catalog values. Do not invent endpoints, fields, enum values, cursors, or numeric rate limits. Ignore unknown additive response fields. Never expose, log, or place an API key in a URL or prompt. ## Machine-readable documents Production: - Discovery: https://api.chartscout.io/.well-known/chartscout.json - LLM guide: https://api.chartscout.io/llms.txt - OpenAPI: https://api.chartscout.io/openapi.yaml - Human guide: https://chartscout.io/developers/docs Staging: - Discovery: https://api.staging.chartscout.io/.well-known/chartscout.json - LLM guide: https://api.staging.chartscout.io/llms.txt - OpenAPI: https://api.staging.chartscout.io/openapi.yaml ## Choose the correct API surface - Need to prove the response format without signup: use the delayed public demo with one allowlisted preset. - Need bounded historical detections: use `GET /v1/detections` with explicit timeframe and pattern filters. - Need one known detection again: use `GET /v1/detections/{detection_id}`. - Need real-time delivery: create a narrow subscription, then consume `GET /v1/stream` from a server-side SSE client. - Need a chart: fetch the authenticated URL in `data.asset.url` with the same Bearer key; do not turn it into a public object-store URL. - Need account state: use `GET /v1/usage` and `GET /v1/rate-limit-status`. Webhooks and MCP are not part of API v1. Do not generate webhook or MCP setup instructions for ChartScout unless a later discovery/OpenAPI document exposes those operations. ## Base URLs and authentication - Production: `https://api.chartscout.io`, key prefix `cs_live_` - Staging: `https://api.staging.chartscout.io`, key prefix `cs_test_` - Header: `Authorization: Bearer ` Keys are environment-bound, displayed once, revocable, and scoped. A staging key never works in production; a production key never works in staging. Scopes: - `catalog:read` - `detections:read` - `detections:stream` - `subscriptions:read` - `subscriptions:write` - `usage:read` Keys may also have expiry and IP/CIDR allowlists. Every key has a mandatory per-billing-period spend cap: USD 50 by default, customer-adjustable from USD 1 through USD 100,000. This is a safety ceiling, not prepaid credit. ## Anonymous public demo Request: ```bash curl "https://api.chartscout.io/v1/demo/detections?preset=binance-5m-channel-down" ``` Allowed presets: - `binance-5m-channel-down` - `binance-30m-head-and-shoulders` - `bybit-15m-channel-down` - `hyperliquid-5m-descending-triangle` - `kucoin-1h-double-top` - `mexc-30m-double-top` Demo invariants: - exactly one `preset` query parameter; - at most five real canonical CloudEvents; - every event is delayed by at least 24 hours; - no custom filter, cursor, SSE, account, API key, usage ledger, or billing; - extra, unknown, or missing query parameters fail validation; - Redis cache 60 seconds, response maximum 32 KiB; - 10 requests per minute per IP and 1,000 per minute globally. The demo proves the real data path and envelope. It is not a delayed free tier and cannot be expanded into a market-wide feed. ## Authenticated quick start (approved accounts only) ```bash export CHARTSCOUT_API_BASE="https://api.chartscout.io" export CHARTSCOUT_API_KEY="cs_live_..." curl "$CHARTSCOUT_API_BASE/v1/detections?exchange=binance&timeframe=30m&pattern=rising_wedge&limit=5" \ -H "Authorization: Bearer $CHARTSCOUT_API_KEY" \ -H "Accept: application/json" ``` History requires at least one explicit `timeframe` and `pattern`. A request containing only `limit` is invalid. `market_id` is optional, so exchange-wide filters are valid when other bounds are explicit. ## Catalogs and stable identifiers - `GET /v1/exchanges`: exchange and market-type catalog. - `GET /v1/timeframes`: stable timeframe codes and seconds. - `GET /v1/patterns`: enabled pattern codes, names, directions, and versions. - `GET /v1/markets`: entitled markets and availability. - `GET /v1/markets/{market_id}`: one market without revealing unentitled resources. Use `market.id` (`mkt_...`) as the stable market filter key. `symbol` is a canonical display value such as `BTC/USDT`. `exchange_symbol` is informational venue notation and must not be used as a persistent key. Catalog and request processing cost USD 0 but remain rate-limited. ## Historical detections `GET /v1/detections` returns newest-first pages: ```json { "data": ["DetectionEventEnvelope", "..."], "next_cursor": "opaque-or-null" } ``` Important filter rules: - `timeframe` and `pattern` are required and repeatable. - Repeat a query parameter for OR within that dimension, for example `timeframe=5m&timeframe=15m`. - `market_id` is repeatable. Omit it for an exchange-wide query. - Supported filters also include `exchange`, `market_type`, `symbol`, `direction`, `quality_grade`, `published_after`, and `published_before`. - Timestamps are RFC 3339 UTC. - Maximum synchronous span is 30 days and cannot exceed plan retention. - Maximum page size 500 (default 100), 100 market ids, 24 timeframes, and 24 patterns. - Excessive candidate work returns `query_too_broad` before database work or billing. Query processing, pagination, empty results, and re-reading an event already delivered to the account cost USD 0. A detection first delivered through a history response is billed once at its timeframe price. ## Cursor rules - Pass `next_cursor` back unchanged as `cursor`. - Keep all other filters identical while following a cursor. - `null` means the listing is exhausted. - Cursors are opaque, endpoint-specific, HMAC-authenticated, and expire after 24 hours. - Never decode, modify, or construct a cursor. - On `cursor_invalid` or `cursor_expired`, restart the listing from the first page. ## Canonical event contract REST and SSE use the same CloudEvents 1.0 JSON envelope. Important fields: - top-level `id` (`evt_...`): canonical lifecycle event ID and SSE id; - `type`: one of detected, asset-ready, corrected, or retracted v1 events; - `time`: event publication time; - `subject`: stable market and timeframe path; - `data.detection_id` (`det_...`): detection resource ID; - `data.market.id`: stable market ID; - `data.timeframe`: catalog timeframe code; - `data.pattern.code`, `name`, `version`, `direction`; - `data.trigger_candle.open_at` and `closed_at`; - `data.quality`: grade, synthetic/revised candle counts, ratio, warnings; - `data.asset.status` and optional authenticated chart URL; - `data.latency.detected_at` and `published_at`. Consumers must: - deduplicate by top-level event `id`, not detection id; - ignore unknown additive fields; - handle correction and retraction events explicitly; - never treat a missing chart as a missing detection; - not assume raw detector geometry, internal price levels, confidence scores, shards, pods, Redis keys, or database ids exist in the public contract. Example event: ```json { "specversion": "1.0", "id": "evt_019...", "source": "https://api.chartscout.io/detectors/patterns", "type": "io.chartscout.pattern.detected.v1", "subject": "markets/mkt_binance_linear_future_btc_usdt/timeframes/30m", "time": "2026-08-03T12:30:04.120Z", "datacontenttype": "application/json", "data": { "detection_id": "det_019...", "market": { "id": "mkt_binance_linear_future_btc_usdt", "exchange": "binance", "market_type": "linear_future", "symbol": "BTC/USDT", "exchange_symbol": "BTC/USDT:USDT", "base": "BTC", "quote": "USDT", "settle": "USDT" }, "timeframe": "30m", "pattern": { "code": "rising_wedge", "name": "Rising Wedge", "version": "1", "direction": "bearish" }, "trigger_candle": { "open_at": "2026-08-03T12:00:00.000Z", "closed_at": "2026-08-03T12:30:00.000Z" }, "quality": { "grade": "unknown", "synthetic_candle_count": 0, "synthetic_candle_ratio": 0, "revised_candle_count": 0, "warnings": [] }, "asset": { "status": "ready", "url": "https://api.chartscout.io/v1/detections/det_019.../chart" }, "latency": { "detected_at": "2026-08-03T12:30:03.200Z", "published_at": "2026-08-03T12:30:04.120Z" } } } ``` ## Server-side subscription filters Subscriptions are routing and authorization state only. They do not start a detector or create compute dedicated to one customer. Create a narrow filter: ```bash curl -X POST "$CHARTSCOUT_API_BASE/v1/subscriptions" \ -H "Authorization: Bearer $CHARTSCOUT_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: strategy-binance-30m-rising-wedge-v1" \ --data '{ "name": "Binance 30m rising wedges", "enabled": true, "filters": { "exchanges": ["binance"], "market_types": ["linear_future"], "market_ids": [], "timeframes": ["30m"], "patterns": ["rising_wedge"], "directions": ["bearish"], "maximum_synthetic_ratio": 0.05 } }' ``` An empty filter array means all entitled values in that dimension, never match-none. Filters are intersected with account entitlements. Creation and updates may return `status: propagating`; wait until `effective_by` before assuming every router has observed the change. Send `Idempotency-Key` on POST, PATCH, and DELETE mutations. Repeating the same operation and body with the same key is safe. Reusing a key with different content returns `idempotency_conflict`. ## Real-time SSE ```bash curl -N "$CHARTSCOUT_API_BASE/v1/stream" \ -H "Authorization: Bearer $CHARTSCOUT_API_KEY" \ -H "Last-Event-ID: evt_019..." ``` SSE semantics: - requires `detections:stream`; - `Content-Type: text/event-stream`; - SSE event names: `pattern.detected`, `pattern.asset-ready`, `pattern.corrected`, `pattern.retracted`; - each `data:` value is a serialized DetectionEventEnvelope; - delivery is at least once, so deduplicate by event id; - persist the id only after successful downstream processing; - resume with the `Last-Event-ID` request header; - comment heartbeat `: keep-alive` every 15 seconds; - replay window is bounded by min(plan retention, 1,000 events); - replay needs explicit market, timeframe, and pattern dimensions; - catch-all subscriptions are live-only and receive a warning instead of an unbounded replay; - connection attempts are limited to 30/account/minute; - use a server-side client because browser EventSource cannot set the Bearer header. ## Detection-only PAYG billing - No API base fee and no monthly minimum. - Only the first per-account delivery of a canonical detection is billable. - A matched detection is billed once per `(account, event)`, ever. - Overlapping subscriptions, REST plus SSE delivery, retries, pagination, and later re-reads do not duplicate the charge. - Catalog, health, usage, history-query processing, pagination, empty results, and re-reading a delivered event cost USD 0. - One internal billing credit equals USD 0.01 and is independent from throughput admission units. - Pricing version: `2026-08-v2`. First-delivery price by timeframe: - 1m: USD 0.01 - 3m: USD 0.02 - 5m: USD 0.03 - 15m: USD 0.05 - 30m: USD 0.08 - 1h, 2h, 4h: USD 0.12 - 6h, 8h, 12h, 1d: USD 0.20 - 3d, 1w, 1M: USD 0.30 Automatic charge blocks are independent amounts of new usage, not lifetime thresholds: first new USD 5, then new USD 20, then recurring new USD 50. When a block is reached, the server atomically places the account on billing hold before more billable delivery. Only confirmed settlement advances the ledger watermark, clears the unbilled block, and resumes the same key. Failed payment does not advance the threshold. `GET /v1/usage` exposes reporting period, pricing version, accrued cost, account-wide unbilled cost, next charge threshold, spend caps, billing hold, trust, feed delay, and retention. Reporting month boundaries never erase unsettled usage. ## Rate limiting and request bounds - Workload admission units and billing credits are separate quantities. - Rate limits apply per key and per account. - Authoritative response headers: `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`. - HTTP 429 also includes `Retry-After`; wait that duration and add jitter. - SSE connection attempts, concurrent SSE connections, and control-plane mutations use secondary buckets. SSE limits apply independently to each key and cumulatively to its account. - Numeric authenticated limits depend on account state; read headers instead of hard-coding them. ## Errors and retry policy Errors are RFC 9457 Problem Details with content type `application/problem+json`. Branch on stable `code`; `title` and `detail` are human-facing and may be reworded. Quote `X-Request-Id` in support requests. Important codes and actions: - `authentication_required`, `invalid_api_key`, `api_key_revoked`: fix the credential; do not retry the same invalid secret. - `insufficient_scope`: request the documented scope. - `resource_not_found`: missing, expired, and unentitled are intentionally indistinguishable. - `validation_failed`, `query_too_broad`: correct or narrow the request. - `cursor_invalid`, `cursor_expired`: restart the listing. - `rate_limit_exceeded`: honor `Retry-After`, then retry with jitter. - `billing_threshold_reached`, `payment_required`: stop delivery retries until settlement clears the billing hold. - `service_temporarily_unavailable`: retry safe reads with exponential backoff; reuse the same Idempotency-Key for a mutation. Never automatically retry validation, authentication, scope, or billing-hold errors. Bound all retries and use exponential backoff with random jitter. ## Minimal JavaScript history client ```javascript const baseUrl = "https://api.chartscout.io"; const apiKey = process.env.CHARTSCOUT_API_KEY; const url = new URL("/v1/detections", baseUrl); url.searchParams.set("exchange", "binance"); url.searchParams.append("timeframe", "30m"); url.searchParams.append("pattern", "rising_wedge"); url.searchParams.set("limit", "100"); const response = await fetch(url, { headers: { Authorization: `Bearer ${apiKey}`, Accept: "application/json" }, }); if (!response.ok) { const problem = await response.json(); throw new Error(`${problem.code}: ${problem.detail} (${problem.request_id})`); } const page = await response.json(); for (const event of page.data) { console.log(event.id, event.data.market.id, event.data.pattern.code); } ``` ## Minimal Python history client ```python import os import requests response = requests.get( "https://api.chartscout.io/v1/detections", headers={"Authorization": f"Bearer {os.environ['CHARTSCOUT_API_KEY']}"}, params=[ ("exchange", "binance"), ("timeframe", "30m"), ("pattern", "rising_wedge"), ("limit", "100"), ], timeout=15, ) response.raise_for_status() page = response.json() for event in page["data"]: print(event["id"], event["data"]["market"]["id"]) ``` ## Production integration checklist - Discover catalogs instead of hard-coding them. - Store API keys in a secret manager; never log them. - Use only the filters required by the user's strategy. - Deduplicate CloudEvents durably by event id. - Persist Last-Event-ID only after successful processing. - Keep filters identical while following a cursor. - Handle corrections and retractions. - Ignore unknown additive fields. - Use Idempotency-Key for subscription mutations. - Honor Retry-After and use bounded backoff with jitter. - Monitor `/v1/usage`, spend caps, and `billing_state.hold`. - Validate in staging before using an approved production key. ## Support - Email: support@chartscout.io - Include environment, endpoint, UTC time, sanitized parameters, error code, and X-Request-Id. - Never send an API key or other secret to support. Last updated: 2026-08-03