Docs

TradingView alerts to MT5 — no EA, no VPS

Turn TradingView alerts into MetaTrader 5 orders with a secret webhook URL: create a hook in the console or the API, set up the TradingView alert step by step, message formats and placeholders for indicators and strategies, closing, idempotency, the IP allowlist, troubleshooting and limits.

TradingView can send an alert to a URL, but it cannot add an Authorization header — so it cannot call a normal API. The usual answer is an Expert Advisor on a Windows VPS, or a paid bridge. fxapis gives you a secret alert URL instead: paste it into a TradingView alert, and the alerts that fire become orders on your MT5 account. We run the terminal; nothing of yours has to stay switched on.

TradingView alert fires
   → POST https://api.fxapis.com/hooks/tradingview/<secret>     answered at once (202)
   → fxapis places the order on your MT5 account(s)             the same path as POST /v1/…/orders/market
   → the alert and its result appear under TradingView in the console

Test with a demo account

An alert hook places real orders on whatever account it points at. Point your first hook at a broker demo account and fire a test alert before using a real-money account.

What you need

  • An fxapis workspace with an MT5 account connected (Connect MT5 accounts). For alerts that must act within a second, set the account to always connected — an account that comes online on demand has to connect first, which takes about 10 seconds in our measurement.
  • A TradingView plan that includes webhook alerts — TradingView's pricing page lists webhook notifications on its paid plans, not on Basic — and two-factor authentication switched on in your TradingView account: TradingView sends webhooks only from accounts with 2FA (About webhooks). Both checked 30 September 2026.

1. Create a hook

In the console

Open Console → TradingView → Create a TradingView hook, then:

  1. Name it after the strategy or chart it serves.
  2. Choose the account. With several accounts, each alert places one multi-account order — this needs a plan with multi-account orders.
  3. Set the default volume in lots. Alerts that do not send a volume use it.
  4. Tell it how your broker names symbols: a symbol map (XAUUSD=XAUUSD.a, one per line) and/or a suffix added to every other symbol (.a, m, .pro…).
  5. Leave Only from TradingView on unless you are sending test alerts yourself.

The console then shows the URL, once, with ready-to-paste messages. Copy it now: we store only a hash, so it cannot be shown again. If you lose it, rotate the hook to get a new one.

Creating and changing hooks needs a workspace owner or admin — a hook's URL can open positions, so it takes the same standing as creating a trading key.

With the API

With a key holding trading:execute:

curl -sS -X POST "https://api.fxapis.com/v1/alert-hooks" \
  -H "Authorization: Bearer $FXAPIS_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Gold breakout",
    "accountIds": ["2b0c7c1e-5d0b-4d8e-9a53-0e6f3b1c9a10"],
    "defaults": {
      "volume": "0.10",
      "deviationPoints": 30,
      "symbolMap": { "XAUUSD": "XAUUSD.a" },
      "symbolSuffix": "",
      "stripExchangePrefix": true,
      "allowClose": true,
      "onlyTradingViewIps": true
    }
  }'
{
  "data": {
    "id": "8f1d…",
    "name": "Gold breakout",
    "url": "https://api.fxapis.com/hooks/tradingview/3q2-7ZkT0d8m1sQe9yVbW4nX6pLr5aHcUgJf0oKxQx4w",
    "urlHint": "https://api.fxapis.com/hooks/tradingview/…Qx4w",
    "enabled": true,
    "…": "…"
  }
}

url appears in this response and in POST /v1/alert-hooks/{id}/rotate, and nowhere else. Every other endpoint shows urlHint.

EndpointWhat it doesScope
GET /v1/alert-hooksList hookstrading:read
POST /v1/alert-hooksCreate one; returns url oncetrading:execute
GET /v1/alert-hooks/{id}Read onetrading:read
PATCH /v1/alert-hooks/{id}Change name, accounts or defaults — the URL staystrading:execute
POST /v1/alert-hooks/{id}/rotateNew URL, returned once; the old one stops at oncetrading:execute
POST /v1/alert-hooks/{id}/disablePause: alerts are refused and loggedtrading:reduce
POST /v1/alert-hooks/{id}/enableResumetrading:execute
DELETE /v1/alert-hooks/{id}Delete; the URL stops working for goodtrading:execute
GET /v1/alert-hooks/{id}/deliveriesEvery alert received and what became of ittrading:read

2. Set up the alert in TradingView

Open the alert dialog

On the chart, open Create alert (the alarm-clock icon, or Alt+A). For a strategy, choose the strategy as the Condition; for an indicator or a drawing, choose it and the crossing or level you want.

Write the message

In Message, paste one of the messages below. TradingView replaces the {{…}} placeholders when the alert fires.

Paste the URL

Under Notifications, tick Webhook URL and paste the hook's URL.

Create it and check the log

Save the alert. When it fires, it appears under Console → TradingView → your hook within a second, with its result. TradingView's own alert log has a webhook status column too.

3. Messages

A message can be JSON or a line of text; TradingView sends JSON as application/json and anything else as text/plain, and fxapis reads both.

Indicators and drawings

An indicator alert knows nothing about positions, so it sends a fixed action. Make one alert per direction:

Buy
{ "action": "buy", "symbol": "{{ticker}}", "id": "{{ticker}}-{{timenow}}" }
Sell
{ "action": "sell", "symbol": "{{ticker}}", "id": "{{ticker}}-{{timenow}}" }

Add levels or a volume if the alert knows them:

{ "action": "buy", "symbol": "{{ticker}}", "volume": "0.20", "sl": "2310.5", "tp": "2385", "id": "{{ticker}}-{{timenow}}" }

Strategies

One alert covers every entry, exit and reversal of a strategy:

Strategy
{
  "action": "{{strategy.order.action}}",
  "symbol": "{{ticker}}",
  "position": "{{strategy.market_position}}",
  "prev_position": "{{strategy.prev_market_position}}",
  "id": "{{strategy.order.id}}-{{timenow}}"
}

{{strategy.order.action}} alone cannot tell an exit from an entry — closing a long is a sell. So when position is present, the position after the order decides:

position after the orderprev_positionWhat fxapis does
flatanythingCloses every open position on the symbol
shortlong (a reversal)Closes the symbol's positions, then sells
longshort (a reversal)Closes the symbol's positions, then buys
same side as the orderflat or the same sideOpens (an entry, or adding to one)
opposite side to the orderthe sameIgnored — a partial exit. Logged, not traded

Partial exits are not traded because there is no single honest MT5 translation: on a hedging account a "sell 1 of 3" could close part of a position or open a hedge, and guessing wrong is worse than doing nothing. Exit fully, or let stops take partial profits.

Volume is lots, not contracts

TradingView sizes forex strategies in units: {{strategy.order.contracts}} is 100000 for what MT5 calls one lot. The template above sends no volume, so the hook's default lots are used. Send volume only if it is in lots. Anything above 1000 lots is refused as a units mistake.

Plain text

Action, symbol, optional volume, then optional key=value pairs:

buy {{ticker}} 0.10
sell {{ticker}} 0.10 sl=1.1050 tp=1.0900 id={{timenow}}
close {{ticker}}
close_all

Fields

FieldAlso accepted asMeaning
actionside, signalbuy (long), sell (short), close (exit), close_long, close_short, flat, close_all
symboltickerUsually {{ticker}}. Mapped as described below. Not needed for close_all
volumeqty, lots, contractsLots, e.g. "0.10". Default: the hook's volume
slstopLoss, stop_lossAbsolute price. 0 means none
tptakeProfit, take_profitAbsolute price. 0 means none
idalert_idUnique per alert — see duplicates
positionmarket_position{{strategy.market_position}}
prev_positionprev_market_position{{strategy.prev_market_position}}
commentWritten on the MT5 order (first 31 characters). Default TradingView

Numbers may be sent as JSON numbers or strings. Placeholders you might use: {{ticker}}, {{exchange}}, {{close}}, {{time}}, {{timenow}}, {{interval}}, and in strategies {{strategy.order.action}}, {{strategy.order.contracts}}, {{strategy.order.price}}, {{strategy.order.id}}, {{strategy.order.comment}}, {{strategy.market_position}}, {{strategy.prev_market_position}}, {{strategy.position_size}}.

Quote your placeholders

"volume": {{strategy.order.contracts}} is valid JSON only if the placeholder fills in. Quoted ("{{…}}") it always is. A message that is almost JSON is refused with a reason saying so.

Symbols

In order:

  1. Exchange prefix removed — OANDA:XAUUSD becomes XAUUSD (unless you turn this off).
  2. Symbol map — if the map names the symbol, its value is used exactly: XAUUSD → XAUUSD.a.
  3. Suffix — otherwise the suffix is added: with .a, EURUSD → EURUSD.a.

The symbol the alert resolved to is shown on every delivery, so a wrong mapping is easy to spot.

Closing

ActionCloses
close, flat (or a strategy going flat)Every open position on the symbol, buys and sells, on each of the hook's accounts
close_long / close_shortOnly the symbol's buys / only its sells
close_allEvery open position on the hook's accounts, every symbol

Closing acts on the positions the account holds, not only on those an alert opened: MT5 does not record who opened a position in a way that can be trusted, and "close XAUUSD" meaning "close the XAUUSD I can see" is what traders expect. Positions are read fresh from the terminal first. Each position closed is its own order in your history. With nothing open, the alert is logged as ignored.

Turn Act on close alerts off ("allowClose": false) if you exit with stops only — close actions are then logged as ignored, and reversals are ignored too.

Closing is never refused for your plan's limits: you can always get flat.

Duplicates

TradingView fills {{timenow}} once, when the alert fires, so a resend of one alert carries the same id and a new alert a new one. fxapis sends each id once: a repeat is answered 200 with "status": "duplicate", logged, and not traded — even if the copies arrive at the same moment.

Without an id, the same message twice within 60 seconds is treated as one alert. That also means two genuinely separate identical alerts within a minute are traded once — another reason to include an id.

Security

  • The URL is the credential. 32 random bytes, shown once, stored only as a SHA-256 hash. Anyone holding the URL can trade the hook's accounts, so keep it out of screenshots and shared templates. Rotate if it leaks: the old URL stops working at once.
  • Only from TradingView (on by default) refuses any request not sent from TradingView's published webhook addresses: 52.89.214.238, 34.212.75.30, 54.218.53.128, 52.32.178.7. Turn it off only to send test alerts from your own machine.
  • Pause a hook to refuse its alerts without touching TradingView. Delete it to end the URL for good; its orders stay in your history.
  • Alerts go through exactly the checks an API order does: the account must be able to trade, your plan must allow it, and nothing is sent while trading is switched off for your workspace.
  • Don't put secrets in alert messages. The message is stored (up to 2 KB) with each delivery so you can see what arrived; if you paste the hook URL into a message, it is removed before storing.

Troubleshooting

Alerts that reach a hook are listed under Console → TradingView → your hook (or GET /v1/alert-hooks/{id}/deliveries) for 30 days, with a status and a reason in plain words. Past 30 alerts a minute, only one row a minute is recorded:

StatusMeaning
pendingReceived; the order is being sent
acceptedAt least one order reached the broker. Each order's own result — filled, or rejected by the broker with its reason — is in the order history
rejectedNothing was sent. The reason says why
duplicateA repeat of an alert already taken
ignoredUnderstood and deliberately not traded: a partial exit, nothing to close, closing switched off, an unknown action

What TradingView sees:

HTTPMeaning
202Taken; the order is being sent
200Duplicate or ignored — nothing more will happen
400 INVALID_ALERTThe message could not be read — see the reason
403 SENDER_NOT_ALLOWEDNot from TradingView's addresses
404No such hook — a mistyped, rotated or deleted URL
409 HOOK_DISABLEDThe hook is paused
429 RATE_LIMITEDMore than 30 alerts in a minute

Common causes:

  • Nothing in the list at all — the alert did not reach us. Check the URL was pasted whole, that Webhook URL is ticked, and TradingView's alert log. A rotated or deleted URL answers 404.
  • rejected — not from TradingView's addresses — you sent a test with curl or another tool. Turn off Only from TradingView while testing.
  • rejected — the account is offline / not ready — the account was not connected. Keep it always connected, or check it under Accounts.
  • The broker rejected an unknown symbol — the resolved symbol is shown on the delivery; add a symbol map entry or a suffix.
  • rejected — plan limits — the month's orders are used up, or several accounts on a plan without multi-account orders.

Limits

  • 30 alerts a minute per hook. Past that, alerts are refused with 429, and one row records it.
  • 25 hooks per workspace.
  • Market orders only from alerts, with optional stop loss and take profit. Pending orders go through the API.
  • One account per hook on every plan; several accounts on plans with multi-account orders — up to the plan's accounts-per-order limit.
  • Alerts are orders: they count towards the plan's monthly orders.
  • Deliveries are kept 30 days. Orders stay in the order history.
  • fxapis answers TradingView immediately — TradingView cancels a webhook request that takes longer than three seconds (About webhooks, checked 30 September 2026) — and sends the order just after, in the order alerts arrived for that hook.

On this page