Duplicate TradingView webhook alerts: why they happen and how to dedupe them
A duplicate alert is annoying on your phone and costly in an app that acts on it: two webhooks can mean the same work done twice. Duplicates usually come from one of three places.
Cause 1: TradingView resent after a 5xx
If your server answers with HTTP 500–599 (except 504), TradingView sends the webhook again after 5 seconds, with a total of 3 such resends, so one trigger can arrive up to 4 times (webhook resubmission). The trap: your app may have done the work and then crashed or errored before replying. TradingView sees a 5xx and sends it again.
alert fires -> your app processes it -> app errors (500)
-> TradingView resends after 5 s -> processed again
Cause 2: the alert fires more than once per bar
On a live bar, a script runs on every new tick, so a condition can be true, then false, then true again before the bar closes. The Pine Script docs recommend setting the frequency to "Once Per Bar Close" so the alert reflects the confirmed bar (Pine FAQ: alerts). For alert() calls, the same idea is the alert.freq_once_per_bar_close argument.
Cause 3: the condition stays true
A condition like "price is above the moving average" is true on many bars in a row, so an alert on it keeps firing. The Pine FAQ describes two ways to fire only on the first occurrence (Pine FAQ: alerts):
- Use a stricter event. Instead of "price is above the average", alert on "price crossed above the average". A cross happens on one bar, so the alert fires once per move.
- Track state. The script remembers whether it is already in a "bull" or "bear" state and only alerts when that state changes, ignoring repeat alerts in the same state.
Either way, combine it with "Once Per Bar Close" so a cross that appears and disappears inside a live bar doesn't fire. Your webhook message can stay a simple JSON template, for example:
{"ticker": "{{ticker}}", "event": "cross_up", "fired_at": "{{timenow}}"}
Fix checklist
- Use "Once Per Bar Close" unless you really need intrabar alerts.
- Fire on events, not states (crossovers, or a state flag).
- Avoid creating two alerts for the same thing. Check the Alerts Manager for copies, for example after recreating an alert following a script edit.
- Don't return 5xx after you've already acted. Return 200 as soon as you've stored the alert, then process it.
- Give each alert an id and dedupe in your app. Build it from placeholders in the message, such as
{{ticker}},{{interval}}and{{timenow}}(placeholder docs).{{timenow}}is the time the alert fired;{{time}}is the time of the bar the alert triggered on. Your app stores the ids it has acted on and ignores repeats.
What HoldAlarms dedupes, exactly
HoldAlarms has simple duplicate protection within one account. We're specific about its limits:
- If the exact same alert arrives again within 20 seconds, we answer TradingView normally but don't forward the repeat.
- It's exact matching, not fuzzy. One changed character makes it a different alert.
- It works within one account only, never across accounts.
- Identical alerts from the same account within 20s count as one. TradingView only lets you set a URL and a message. An
idempotency_keyin the HoldAlarms URL is ignored, because it would be the same for every alert. To keep real repeats apart, add"fired_at": "{{timenow}}"to your message. - If two real, identical alerts arrive within 20 seconds, the repeat is acknowledged but not forwarded. If that can happen in your setup, add
"fired_at": "{{timenow}}"to the message so each one differs. - Every forwarded alert carries
X-HookWatch-Event-Id,X-HookWatch-Event-HashandX-HookWatch-Attemptheaders, so your endpoint can dedupe our own retries too.
Our retries add their own repeat risk: if your endpoint accepts an alert but its reply is lost, we may send it again. Your endpoint should still be idempotent. Details are in our FAQ.
Our free Webhook Checker for TradingView checks that your message template is valid JSON with working placeholders, and that your endpoint answers 2xx without redirects. A 5xx from your endpoint there is a red flag for duplicates.
HoldAlarms answers TradingView with 200 once the alert is queued, which takes your server's 5xx out of TradingView's resend loop. Failed deliveries are retried up to 5 times, 2 to 60 seconds apart, and then parked in a dead-letter queue. Permanent errors (400, 401, 403, 410, 413, 414, 415) aren't retried. Failure emails are grouped and capped at up to 10 a day. Starter €19/mo, Ops €49/mo.
Questions
Why did my TradingView webhook arrive twice?
Most often your server answered 500–599 (not 504) and TradingView resent after 5 seconds, or the alert fired more than once on a live bar.
How do I stop an alert firing several times per bar?
Set the frequency to "Once Per Bar Close", or use alert.freq_once_per_bar_close in alert() calls.
Does HoldAlarms remove duplicate alerts?
Within one account, it drops an exact repeat within 20 seconds. It's exact matching, not fuzzy, and not across accounts.
Can TradingView send an Idempotency-Key header?
No. TradingView only lets you set a URL and a message, so it can't set custom headers. An idempotency_key in your HoldAlarms URL is ignored. To keep real repeats apart, add "fired_at": "{{timenow}}" to your message.
More guides
- Why didn't my TradingView alert fire?
- TradingView webhook timeout: why "request took too long" happens
- TradingView webhook not received: 7 things to check
- The TradingView webhook checklist
- Does TradingView retry webhooks?
- TradingView → HoldAlarms → n8n: step-by-step
- TradingView → HoldAlarms → TradersPost: setup and limits
- TradingView → HoldAlarms → PineConnector: setup
- TradingView → HoldAlarms → SignalStack: setup
- TradingView → HoldAlarms → PickMyTrade: setup
- TradingView → HoldAlarms → CrossTrade: setup and limits
- TradingView → HoldAlarms → Alerio: setup
- All guides
- All HoldAlarms questions
HoldAlarms is not affiliated with TradingView. TradingView is a trademark of TradingView, Inc.