How to Scrape TradingView Data with an API
TradingView has no public data API. Learn the compliant architecture for charts, licensed feeds, HTTP history, WebSockets, and safer alternatives.
Short answer: TradingView does not provide a general public API for historical candles or indicator values. Its support documentation says, “We don’t have an API that gives access to data as of now.” TradingView also prohibits automated collection, including scripts, APIs, screen scraping, data mining, and robots. Reverse-engineering chart requests, automating a logged-in browser, or installing an unofficial scraper can violate the rules and result in an account ban.
The supported design is to use TradingView Advanced Charts with a datafeed that you own or license. Your server obtains market data from an exchange, broker, or licensed vendor, exposes the Datafeed API methods needed by the chart, serves historical bars over HTTP, and streams live updates over a shared WebSocket connection.
Primary sources: TradingView’s API support answer, automated-collection ban guidance, Terms and policies, and the official Datafeed API tutorial.
What “scraping TradingView” usually means
Most requests fall into one of four categories:
| Goal | Recommended approach | Why |
|---|---|---|
| Historical OHLCV bars | Exchange, broker, or licensed market-data REST API | Stable symbols, timestamps, quotas, and redistribution terms |
| Live ticks or candles | Licensed WebSocket or streaming feed | Lower latency and an explicit reconnect model |
| Charts in your product | TradingView Advanced Charts Datafeed API | TradingView renders the chart while your feed supplies data |
| A picture of a public chart | ScreenshotNeo or a permitted browser capture | Captures visual output; it does not extract market data |
Why browser scraping and reverse-engineering are poor choices
TradingView’s account-ban guidance explicitly prohibits automated data collection methods, including scripts, APIs, screen scraping, data mining, and robots. It also forbids technology intended to bypass protections against unauthorized reproduction or distribution. Replaying internal chart requests can therefore create both account and licensing risk.
The policies describe TradingView content and market data as licensed for display-only personal or internal business use. They restrict non-display uses such as automated trading, price referencing, algorithmic decision-making, and products built from TradingView content. A paid subscription does not automatically grant redistribution rights.
Common attempts that do not solve the policy problem
- Opening a logged-in browser with Selenium or Playwright and reading chart values.
- Copying WebSocket messages from developer tools and replaying them from a script.
- Using an unofficial Python package that signs in or imitates private endpoints.
- Reading pixels or OCR from a chart when the real objective is numeric data.
These techniques may break when endpoints change, trigger bot detection, return incomplete bars, or violate the terms that apply to the account and data.
The supported architecture
- Select a source. Obtain a feed from an exchange, broker, or market-data vendor whose license covers your intended use.
- Normalize on your server. Map vendor symbols to your symbols, convert timestamps to Unix seconds, define sessions and time zones, and validate OHLCV values.
- Implement symbol metadata. Return the exchange, timezone, trading session, price scale, supported resolutions, and symbol status.
- Implement historical bars. Receive a time range and resolution, query your upstream REST endpoint, and return ordered bars.
- Add streaming. Maintain one shared WebSocket connection per upstream feed where possible. Fan updates out to chart subscribers.
- Handle gaps and reconnects. Reconnect with backoff, request a small historical overlap, de-duplicate bars, and mark stale subscriptions.
- Cache within your license. Cache immutable historical ranges when the provider permits it; avoid caching or redistributing data outside the contract.
Minimal Datafeed API shape
The exact callback names depend on the Advanced Charts version, but the datafeed normally supplies configuration, symbol information, historical bars, and realtime subscription methods. The following JavaScript illustrates the core shape. Replace the internal endpoints with your own server routes.
const datafeed = {
onReady: (callback) => {
fetch('/market-data/config')
.then(r => r.json())
.then(callback);
},
resolveSymbol: (symbol, onResolve, onError) => {
fetch(`/market-data/symbols/${encodeURIComponent(symbol)}`)
.then(r => r.ok ? r.json() : Promise.reject(r.status))
.then(onResolve)
.catch(() => onError('Symbol unavailable'));
},
getBars: async (info, resolution, period, onHistory, onError) => {
try {
const q = new URLSearchParams({
symbol: info.ticker,
resolution,
from: String(period.from),
to: String(period.to),
countback: String(period.countBack || '')
});
const response = await fetch(`/market-data/bars?${q}`);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const payload = await response.json();
onHistory(payload.bars, { noData: payload.bars.length === 0 });
} catch (error) {
onError(error.message);
}
},
subscribeBars: (info, resolution, onTick, uid, onReset) => {
// Connect this subscription to your shared upstream WebSocket.
realtime.subscribe(info.ticker, resolution, uid, onTick, onReset);
},
unsubscribeBars: (uid) => realtime.unsubscribe(uid)
};
Use the official connecting-data documentation for the current callback contracts and UDF format. Do not expose upstream API keys in browser code.
Historical data: HTTP example
Your backend should translate the chart request into the licensed provider’s request. This Python example uses a placeholder upstream URL and demonstrates validation, ordering, and a provider error path.
import os
from datetime import datetime
import requests
from flask import Flask, request, jsonify
app = Flask(__name__)
UPSTREAM = os.environ['MARKET_DATA_URL']
TOKEN = os.environ['MARKET_DATA_TOKEN']
@app.get('/market-data/bars')
def bars():
symbol = request.args['symbol']
resolution = request.args['resolution']
start = int(request.args['from'])
end = int(request.args['to'])
if start >= end:
return jsonify(error='from must be earlier than to'), 400
response = requests.get(
UPSTREAM,
params={'symbol': symbol, 'resolution': resolution,
'from': start, 'to': end},
headers={'Authorization': f'Bearer {TOKEN}'},
timeout=20,
)
if response.status_code == 429:
return jsonify(error='upstream rate limit'), 503
response.raise_for_status()
raw = response.json()['bars']
normalized = []
for bar in raw:
normalized.append({
'time': int(bar['timestamp']),
'open': float(bar['open']),
'high': float(bar['high']),
'low': float(bar['low']),
'close': float(bar['close']),
'volume': float(bar.get('volume', 0)),
})
normalized.sort(key=lambda x: x['time'])
return jsonify(bars=normalized)
if __name__ == '__main__':
app.run(port=8000)
The endpoint name, authentication header, field names, and quota behavior are vendor-specific. Read the upstream contract before deploying this adapter.
Realtime WebSocket design
A chart subscription should not open a new upstream socket for every browser tab. Keep a shared connection per feed, track symbol and resolution subscriptions, and fan out normalized updates.
const socket = new WebSocket(process.env.UPSTREAM_WS_URL);
const subscribers = new Map();
socket.onmessage = (event) => {
const update = normalizeVendorMessage(JSON.parse(event.data));
for (const subscriber of subscribers.values()) {
if (subscriber.symbol === update.symbol) {
subscriber.onBar(update);
}
}
};
function subscribe(symbol, resolution, id, onBar, onReset) {
subscribers.set(id, { symbol, resolution, onBar, onReset });
sendUpstreamSubscriptionIfNeeded(symbol, resolution);
}
function unsubscribe(id) {
subscribers.delete(id);
removeUpstreamSubscriptionIfUnused(id);
}
socket.onclose = () => {
// Reconnect with exponential backoff, then backfill an overlap via HTTP.
};
Reconnect and backfill checklist
- Use exponential backoff with jitter.
- Record the last complete bar timestamp per symbol.
- After reconnecting, request an overlap window over HTTP.
- Replace bars by timestamp to remove duplicates.
- Do not treat a partial current bar as final.
- Emit a reset signal if the provider reports a session or symbol change.
Indicators and derived values
If you need RSI, moving averages, or other indicators, calculate them from the licensed OHLCV series in your service or in the client. Define the period, warm-up bars, missing-data policy, and whether the current bar is provisional. Do not call an unofficial TradingView indicator endpoint or copy values from a private chart request.
Choosing an independent market-data API
Independent vendors may advertise REST, WebSocket, SSE, technical-analysis, news, calendar, screener, or MCP endpoints. Treat them as separate companies. One provider’s terms state that it is not affiliated with TradingView, Inc. and does not provide written indemnification for redistribution or display of market data. Before adopting any vendor, verify:
| Check | Questions to answer |
|---|---|
| Coverage | Which exchanges, assets, symbols, sessions, and corporate actions are included? |
| History | How far back are bars available, and are adjustments documented? |
| Latency | What does “real time” mean, and how are delayed feeds labeled? |
| Limits | What are request, connection, message, and concurrent-symbol limits? |
| Rights | Can you display, cache, redistribute, or use the data for commercial decisions? |
| Reliability | Are status, reconnect guidance, and historical backfill documented? |
Performance, reliability, and cost
- Reduce requests: batch historical ranges, cache permitted immutable data, and avoid refetching the same resolution for every chart.
- Control fan-out: multiplex subscribers over a shared upstream stream and send only symbols each client needs.
- Protect the upstream: enforce server-side quotas, circuit breakers, timeouts, and bounded retries.
- Measure data quality: log gaps, out-of-order timestamps, duplicate bars, stale sockets, and provider response codes.
- Budget correctly: account for per-request fees, streaming connection limits, historical depth, exchange licensing, storage, and egress.
- Respect terms: a technically fast pipeline is still unsuitable if your license does not permit the intended display or redistribution.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Account banned or suspicious-activity warning | Automated collection or protection bypass | Stop the automation, review the ban guidance, and move to a licensed feed and supported Datafeed integration. |
| Chart shows no bars | Empty range, wrong timestamp units, or invalid session | Log the request, confirm Unix seconds versus milliseconds, return valid symbol metadata, and test a known range. |
| Bars appear out of order | Provider pages or streams are unsorted | Sort by timestamp and de-duplicate before returning data. |
| Realtime freezes | Socket closed, subscription expired, or heartbeat missing | Detect stale connections, reconnect with backoff, resubscribe, and backfill the overlap. |
| 429 responses | Provider quota exceeded | Share caches, batch requests, throttle clients, and request a plan with suitable limits. |
| Prices differ from TradingView | Different exchange, session, adjustment, or delayed feed | Compare symbol mapping, timezone, corporate-action policy, and entitlement. |
| Credentials exposed | Upstream key embedded in frontend code | Move calls behind your server and rotate the leaked key. |
Or skip the browser setup
If your requirement is a visual snapshot of a TradingView page rather than numeric market data, ScreenshotNeo provides a website screenshot API. Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP server lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf.
See the ScreenshotNeo API docs for all options. This call captures the rendered page; it does not turn TradingView into a market-data API.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
There are 1,000 free screenshots each month with no card. Paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
FAQ
Does a TradingView subscription include a candle API?
No. TradingView’s support answer says it does not currently provide an API that gives ordinary users access to data.
Can I use Selenium if I only need a few symbols?
Automated collection is prohibited by the account-ban guidance, regardless of volume or intent. Use a licensed source instead.
Can I use TradingView Advanced Charts without TradingView market data?
Yes. The chart library’s Datafeed API is designed to connect to a data source that you own or license.
Should I use REST or WebSockets?
Use HTTP for historical ranges and WebSockets for live updates, with reconnect and HTTP backfill after interruptions.
Is a screenshot API a substitute for OHLCV data?
No. A screenshot is an image of the rendered chart. It cannot reliably provide structured candles, indicators, timestamps, or redistribution rights.


