First, get TradingView installed on your PC → tradingviewdownloadforpc.com — authorized Windows setup guide with screenshots.
Last updated: May 2026 | Author: Michael Hannah | Difficulty: Intermediate | Prerequisites: TradingView account, basic familiarity with REST APIs
TL;DR
TradingView processes alerts server-side and can deliver them as webhook HTTP POST requests to any publicly reachable URL. This lets you build event-driven trading infrastructure: Pine Script defines the signal, TradingView fires the alert, and your backend executes the logic — order placement, notifications, database logging, or workflow triggers. This guide covers the full stack from alert configuration to production-grade webhook receivers in Python, Node.js, and Go.
How TradingView Alerts Work (Architecture)
┌─────────────────────────┐
│ Pine Script Indicator │ alertcondition() fires when condition is met
└────────────┬────────────┘
│ alert event
▼
┌─────────────────────────┐
│ TradingView Servers │ processes alert server-side (no local client needed)
└────────────┬────────────┘
│ HTTP POST
▼
┌─────────────────────────┐
│ Your Webhook Endpoint │ receives JSON payload, triggers downstream action
└────────────┬────────────┘
│
┌───────┴───────┐
▼ ▼
┌─────────┐ ┌─────────────┐
│ Broker │ │ Slack/DB/ │
│ API │ │ Workflow │
└─────────┘ └─────────────┘
Key point: TradingView fires webhooks from its own servers. Your PC, browser, or desktop app does NOT need to be running for webhooks to fire. The TradingView desktop app only needs to be open for OS-level desktop notifications — webhooks are entirely server-side.
Alert Types Reference
| Alert Type | Trigger Condition | Supports Webhook |
|---|---|---|
| Price crossing | Asset crosses a set price level | Yes |
| Price moving up/down | Price moves by % or amount | Yes |
| Crossing indicator | Price crosses a plotted indicator | Yes |
Custom (alertcondition) |
Any boolean Pine Script expression | Yes |
| Strategy entry/exit |
strategy.entry() / strategy.exit() fires |
Yes |
| Drawing alert | Price crosses a manually drawn line | Yes |
Step 1: Configure an Alert on TradingView Desktop
With TradingView open on Windows (install guide):
Via keyboard shortcut:
- Press
Alt+Ato open the Create Alert dialog instantly
Via chart right-click:
- Right-click anywhere on the chart → "Add Alert on..."
In the alert dialog:
- Condition — select your indicator output or price action
- Value/threshold — set the trigger level
-
Frequency — choose when the alert fires:
-
Once— fires once, then deactivates -
Once per bar— once per closed bar meeting the condition -
Once per bar close— fires at bar close only -
Every time— fires on every tick where the condition is true (use carefully — can generate many requests)
-
- Expiration — set an end date/time for the alert to auto-deactivate
- Alert actions — check "Webhook URL" and paste your endpoint
The Webhook Payload
TradingView sends a plain HTTP POST with the body you define in the "Message" field of the alert dialog. You can embed dynamic placeholders that TradingView fills in at fire time:
Available Placeholders
| Placeholder | Type | Description |
|---|---|---|
{{ticker}} |
string | Symbol ticker (e.g., BTCUSDT) |
{{exchange}} |
string | Exchange (e.g., BINANCE) |
{{close}} |
number | Current bar close price |
{{open}} |
number | Current bar open price |
{{high}} |
number | Current bar high price |
{{low}} |
number | Current bar low price |
{{volume}} |
number | Current bar volume |
{{time}} |
string | Bar open time (ISO 8601) |
{{timenow}} |
string | Current wall-clock time (ISO 8601) |
{{interval}} |
string | Chart timeframe (e.g., 60 for 1H) |
{{plot_0}} |
number | Value of the first plot() in the script |
Example Alert Message (JSON format)
{
"ticker": "{{ticker}}",
"exchange": "{{exchange}}",
"price": {{close}},
"volume": {{volume}},
"time": "{{timenow}}",
"interval": "{{interval}}",
"action": "buy",
"strategy": "ema_cross"
}
Note: TradingView does not set a
Content-Type: application/jsonheader by default. Your receiver should parse the raw body as JSON regardless of the incomingContent-Type.
Alert Limits by Plan
| Plan | Max Active Alerts | Webhook Delay |
|---|---|---|
| Free | 1 | ~seconds |
| Essential | 20 | ~seconds |
| Plus | 100 | ~seconds |
| Premium | 400 | Priority queue |
Step 2: Write the Webhook Receiver
Python (Flask)
from flask import Flask, request, abort
import json, os
app = Flask(__name__)
SHARED_SECRET = os.environ.get("WEBHOOK_SECRET", "change-me")
@app.route("/webhook", methods=["POST"])
def handle_tradingview_alert():
raw_body = request.get_data()
try:
data = json.loads(raw_body)
except json.JSONDecodeError:
abort(400)
# Verify shared secret embedded in payload
if data.get("secret") != SHARED_SECRET:
abort(403)
ticker = data.get("ticker", "UNKNOWN")
price = data.get("price", 0)
action = data.get("action", "").lower()
strategy = data.get("strategy", "")
print(f"[ALERT] {strategy} | {action.upper()} {ticker} @ {price}")
if action == "buy":
place_buy_order(ticker, price)
elif action == "sell":
place_sell_order(ticker, price)
return {"status": "ok", "ticker": ticker, "action": action}, 200
def place_buy_order(ticker, price):
# Implement your broker API call here
print(f"BUY {ticker} @ {price}")
def place_sell_order(ticker, price):
print(f"SELL {ticker} @ {price}")
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
Node.js (Express)
const express = require("express");
const app = express();
const SHARED_SECRET = process.env.WEBHOOK_SECRET || "change-me";
app.use(express.json({ type: "*/*" })); // Accept any Content-Type
app.post("/webhook", (req, res) => {
const { ticker, price, action, strategy, secret } = req.body;
if (secret !== SHARED_SECRET) {
return res.status(403).json({ error: "Forbidden" });
}
console.log(`[ALERT] ${strategy} | ${action.toUpperCase()} ${ticker} @ ${price}`);
if (action === "buy") placeBuyOrder(ticker, price);
if (action === "sell") placeSellOrder(ticker, price);
res.json({ status: "ok", ticker, action });
});
function placeBuyOrder(ticker, price) { console.log(`BUY ${ticker} @ ${price}`); }
function placeSellOrder(ticker, price) { console.log(`SELL ${ticker} @ ${price}`); }
app.listen(3000, () => console.log("Listening on :3000"));
Go (net/http)
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
)
type AlertPayload struct {
Ticker string `json:"ticker"`
Exchange string `json:"exchange"`
Price float64 `json:"price"`
Action string `json:"action"`
Strategy string `json:"strategy"`
Secret string `json:"secret"`
}
var sharedSecret = envOr("WEBHOOK_SECRET", "change-me")
func envOr(key, fallback string) string {
if v := os.Getenv(key); v != "" { return v }
return fallback
}
func webhookHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var p AlertPayload
if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
http.Error(w, "Bad request", http.StatusBadRequest)
return
}
if p.Secret != sharedSecret {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
log.Printf("[ALERT] %s | %s %s @ %.2f", p.Strategy, p.Action, p.Ticker, p.Price)
w.Header().Set("Content-Type", "application/json")
fmt.Fprintf(w, `{"status":"ok","ticker":"%s","action":"%s"}`, p.Ticker, p.Action)
}
func main() {
http.HandleFunc("/webhook", webhookHandler)
log.Println("Webhook server listening on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
Step 3: Pine Script Alert Conditions
Define the alert conditions in Pine Script so they appear in the TradingView alert dialog:
//@version=5
indicator("EMA Cross Alert System", overlay=true)
fast = input.int(9, "Fast EMA")
slow = input.int(21, "Slow EMA")
secret = input.string("change-me", "Webhook Secret")
ema_fast = ta.ema(close, fast)
ema_slow = ta.ema(close, slow)
crossUp = ta.crossover(ema_fast, ema_slow)
crossDown = ta.crossunder(ema_fast, ema_slow)
plot(ema_fast, "Fast EMA", color=color.green)
plot(ema_slow, "Slow EMA", color=color.red)
plotshape(crossUp, "Buy", shape.triangleup, location.belowbar, color.green)
plotshape(crossDown, "Sell", shape.triangledown, location.abovebar, color.red)
// Build JSON payload strings
buyMsg = '{"ticker":"' + syminfo.ticker + '","price":' + str.tostring(close) + ',"action":"buy","strategy":"ema_cross","secret":"' + secret + '"}'
sellMsg = '{"ticker":"' + syminfo.ticker + '","price":' + str.tostring(close) + ',"action":"sell","strategy":"ema_cross","secret":"' + secret + '"}'
alertcondition(crossUp, title="EMA Bullish Cross", message=buyMsg)
alertcondition(crossDown, title="EMA Bearish Cross", message=sellMsg)
After adding to chart: right-click → Add Alert → select "EMA Bullish Cross" or "EMA Bearish Cross" → paste your webhook URL → Save.
Step 4: Local Testing with ngrok
Before deploying to production, test your receiver locally:
# Terminal 1 — start your receiver
python app.py # Flask on :5000
# or: node server.js # Express on :3000
# or: go run main.go # Go on :8080
# Terminal 2 — expose via ngrok
ngrok http 5000
# ngrok outputs something like:
# Forwarding https://abc123.ngrok-free.app -> http://localhost:5000
Paste the https://abc123.ngrok-free.app/webhook URL into TradingView's webhook field. Trigger the alert manually to confirm your server receives and logs the payload.
Production Deployment Checklist
- [ ] Deploy receiver to a public host (VPS, AWS Lambda, Railway, Fly.io, etc.)
- [ ] Use HTTPS — TradingView rejects plain HTTP webhook URLs
- [ ] Implement a shared secret check (see examples above)
- [ ] Add idempotency — TradingView may occasionally fire duplicate alerts; track processed timestamps
- [ ] Return a 200 response within 3 seconds; process heavy work asynchronously
- [ ] Log all incoming payloads for debugging and audit purposes
- [ ] Monitor your active alert count against your plan limit
- [ ] Set alert expiration dates to avoid stale alerts consuming your limit
Common Errors and Fixes
| Error | Cause | Fix |
|---|---|---|
| Webhook never fires | Endpoint is plain HTTP | Use HTTPS only |
| 403 from your server | Secret mismatch | Verify secret matches in Pine Script input and env var |
| Duplicate orders placed | TradingView sent duplicate alerts | Track processed timenow in Redis or DB |
| Server receives alert but returns 500 | Exception in handler | Add try/except around processing logic |
| Alert fires on every tick | Frequency set to "Every time" | Change to "Once per bar" |
| Server times out | Heavy sync processing | Return 200 immediately; offload work to a queue |
Frequently Asked Questions
Q: Does TradingView's desktop app need to be running for webhooks to fire?
No. TradingView processes alerts and fires webhooks from its own servers. Your PC, browser tab, and desktop app can all be off — webhooks fire as long as your TradingView account is active and the alert has not expired.
Q: How quickly does TradingView fire a webhook after a condition is met?
Delivery is typically 1–5 seconds after the triggering bar closes. On the Premium plan, TradingView prioritizes alert processing. During high-traffic market events, there may be additional delay on lower-tier plans.
Q: Does TradingView support HTTPS-only webhook URLs?
Yes. TradingView requires HTTPS. Plain HTTP endpoints are silently rejected — no error message is shown in the TradingView UI.
Q: Does TradingView sign webhook payloads (like GitHub's HMAC signature)?
No. TradingView does not natively sign webhook payloads. The recommended pattern is to embed a shared secret in the JSON message body (shown in the Pine Script example above) and verify it server-side.
Q: Can I send different payloads for buy and sell signals from the same script?
Yes. Use separate alertcondition() calls with different message strings. When creating the alert in TradingView, select the specific condition you want for that alert action.
Q: What happens if my server returns a non-200 response?
TradingView does not retry failed webhook deliveries. If your endpoint returns a 5xx or times out, the delivery is dropped silently. Ensure your server responds with 200 quickly, even if processing continues asynchronously.
Q: Can I use TradingView webhooks with n8n or Zapier?
Yes. Both n8n and Zapier support custom webhook triggers. Create a webhook node in n8n or a "Webhooks by Zapier" trigger, copy the HTTPS URL, and paste it into TradingView's webhook field. Your alert payload will flow directly into your automation workflow.
Resources
- TradingView Alerts Documentation
- Pine Script
alertcondition()Reference - TradingView PC Install Guide
- ngrok Documentation
By Michael Hannah — trading software analyst and Pine Script developer. Updated May 2026.
Top comments (0)