Forex API Documentation

Real-time and historical market data for forex, stocks and crypto — REST, WebSocket and MCP.

Introduction

ForexAPI serves 1,400+ forex pairs, 1,800+ crypto pairs and US stocks from a single REST API, with sub-50ms cached responses for live prices and full OHLC candle history.

Base URL

https://connect.forexapi.co

Formats

JSON everywhere · UTF-8 · epoch-seconds timestamps

All symbol names are case-insensitive and suffix-optional: aapl resolves to AAPL.

Quick Start

  1. Get an API key — sign in and create a key on the API Keys page, then export it: export FXAPI_KEY="fxapi_live_...".
  2. Make your first request — live prices for one or many symbols:
bash
1
2
curl "https://connect.forexapi.co/v1/prices?symbols=EURUSD,USDJPY" \
  -H "Authorization: Bearer $FXAPI_KEY"

3. Read the response — prices keyed by symbol:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
  "EURUSD": {
    "symbol": "EURUSD",
    "category": "Forex",
    "price": 1.105,
    "change": 0.002,
    "bid": 1.1049,
    "ask": 1.1051,
    "digits": 5
  },
  "USDJPY": {
    "symbol": "USDJPY",
    "category": "Forex",
    "price": 157.261,
    "change": -0.0661,
    "bid": 157.26,
    "ask": 157.264,
    "digits": 3
  }
}

Authentication

Every endpoint requires an API key created on your API Keys page. Keys look like fxapi_live_Xk3m9QpT4vXa_…, are shown in full exactly once at creation (store them safely — only a hash is kept server-side), and support editing, rotation with a grace period, instant revocation, optional IP/CIDR allowlists and scopes.

Send the key in the Authorization header; keep it in an environment variable rather than in source code:

1
2
3
export FXAPI_KEY="fxapi_live_..."   # from the API Keys page
curl "https://connect.forexapi.co/v1/prices?symbols=EURUSD" \
  -H "Authorization: Bearer $FXAPI_KEY"

Requests from an address outside a key's IP allowlist get 403 Forbidden. Requests outside a key's scopes (forex/crypto) also get 403. WebSocket and MCP use the same key — see their sections below. Legacy JWT tokens keep working until the deprecation date shown on the API Keys page.

Rate Limits & Plans

Fixed 60-second windows per API key, plus a monthly request quota that resets with your billing cycle. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset; 429 responses add Retry-After. Rate-limited requests are not billed.

PlanMonthlyPer minuteHistoryCryptoWebSocket
Free ($0/mo)1,00010———
Starter ($19/mo)4,000301 year——
Builder ($29/mo)10,000605 years✓—
Developer ($49/mo)15,0001005 years✓—
Professional ($149/mo)50,00050010 years✓✓

Exceeding a limit returns a 429 whose message names your plan, the limit and the reset — see Errors.

Market Data

Live quotes and reference data for forex, metals, indices, commodities and stocks. Responses shown were captured from the live feed — to run your own requests, open the Playground in the dashboard.

GET /v1/prices

Latest Prices

Live quote for one or many symbols — forex, metals, indices, commodities and stocks. Up to 100 symbols per request, comma-separated.

Parameters

ParameterTypeDefaultDescription
symbols*string—Comma-separated symbol list, e.g. EURUSD,USDJPY,XAUUSD (required, max 100)

Example Request

bash
1
2
3
curl -G "https://connect.forexapi.co/v1/prices" \
  -H "Authorization: Bearer $FXAPI_KEY" \
  -d symbols=EURUSD,USDJPY

Response

json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
  "EURUSD": {
    "symbol": "EURUSD",
    "category": "Forex",
    "price": 1.13673,
    "change": -0.0352,
    "ask": 1.13673,
    "askHigh": 1.13745,
    "askLow": 1.13627,
    "bid": 1.13673,
    "bidHigh": 1.13743,
    "bidLow": 1.13627,
    "digits": 5
  },
  "USDJPY": {
    "symbol": "USDJPY",
    "category": "Forex",
    "price": 157.261,
    "change": -0.0661,
    "ask": 157.264,
    "askHigh": 157.588,
    "askLow": 157.209,
    "bid": 157.261,
    "bidHigh": 157.585,
    "bidLow": 157.206,
    "digits": 3
  }
}

💡 Unknown symbols are returned as null instead of erroring, so one bad ticker never fails the batch. Quotes update on every tick — for continuous delivery use the WebSocket.

GET /v1/currencies

Supported Currencies

Every supported fiat currency with display metadata: localized names, flag artwork, sign, and circulating coins/banknotes — ready for building currency pickers.

Example Request

bash
1
2
curl "https://connect.forexapi.co/v1/currencies" \
  -H "Authorization: Bearer $FXAPI_KEY"

Response

json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
{
  "AUD": {
    "symbol": "AUD",
    "name": "Australian Dollar",
    "countryCode": "AU",
    "countryName": "Australia",
    "iso_code": "036",
    "sign": "$",
    "coins": [
      "$2",
      "$1",
      "50¢",
      "20¢",
      "10¢",
      "5¢"
    ],
    "banknotes": [
      "$100",
      "$50",
      "$20",
      "$10",
      "$5"
    ]
  },
  "EUR": {
    "symbol": "EUR",
    "name": "Euro",
    "countryCode": "EU",
    "countryName": "European Union",
    "iso_code": "978",
    "sign": "€",
    "coins": [
      "€2",
      "€1",
      "50¢",
      "20¢",
      "10¢",
      "5¢",
      "2¢",
      "1¢"
    ],
    "banknotes": [
      "€500",
      "€200",
      "€100",
      "€50",
      "€20",
      "€10",
      "€5"
    ]
  }
}
GET /v1/symbols

Supported Symbols

Full instrument directory for a category with the latest price attached — stock entries carry exchange, ISIN, industry and sector.

Parameters

ParameterTypeDefaultDescription
category*string—One of: Forex, Stocks, Indices, Commodities, Crypto (required)

Example Request

bash
1
2
3
curl -G "https://connect.forexapi.co/v1/symbols" \
  -H "Authorization: Bearer $FXAPI_KEY" \
  -d category=Forex

Response

json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
  "NOVOB": {
    "symbol": "NOVOB.DK",
    "name": "Novo Nordisk A/S",
    "digits": 4,
    "category": "Stocks",
    "country": "DNK",
    "isin": "DK0062498333",
    "industry": "Biotechnology",
    "sector": "Healthcare",
    "exchange": "Nasdaq Copenhagen",
    "price": 642.5
  },
  "DSV": {
    "symbol": "DSV.DK",
    "name": "DSV A/S",
    "digits": 4,
    "category": "Stocks",
    "country": "DNK",
    "isin": "DK0060079531",
    "industry": "Integrated Freight & Logistics",
    "sector": "Industrials",
    "exchange": "Nasdaq Copenhagen",
    "price": 1583
  }
}

Conversion

Currency conversion for fiat and crypto — cross pairs are routed automatically when no direct symbol exists. Responses shown were captured from the live feed — to run your own requests, open the Playground in the dashboard.

GET /v1/convert

Currency Conversion

Convert an amount between any two supported currencies (including cross pairs with no direct symbol) using the latest rates.

Parameters

ParameterTypeDefaultDescription
from*string—Base currency code, e.g. EUR (required)
to*string—Quote currency code, e.g. USD (required)
amount*float—Amount to convert (required)

Example Request

bash
1
2
3
curl -G "https://connect.forexapi.co/v1/convert" \
  -H "Authorization: Bearer $FXAPI_KEY" \
  -d from=EUR -d to=USD -d amount=100

Response

json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
  "success": true,
  "query": {
    "from": "EUR",
    "to": "USD",
    "amount": 100
  },
  "info": {
    "amount": 100,
    "exchange_rate": 1.08913,
    "timestamp": 1790654316
  },
  "result": 108.913
}
GET /v1/crypto/convert

Crypto Conversion

Convert between crypto assets (and USDT) using live aggregate rates — multi-hop routes are resolved automatically when no direct pair exists.

Parameters

ParameterTypeDefaultDescription
from_symbolstringBTCAsset code to convert from, e.g. BTC
to_symbolstringUSDTAsset code to convert to, e.g. USDT or USD
amountfloat5Amount to convert

Example Request

bash
1
2
3
curl -G "https://connect.forexapi.co/v1/crypto/convert" \
  -H "Authorization: Bearer $FXAPI_KEY" \
  -d from_symbol=BTC -d to_symbol=USDT -d amount=0.5

Response

json
1
2
3
4
5
{
  "amount": 0.5,
  "converted_amount": 204068,
  "exchange_rate": 408136
}

💡 Requires the Builder plan or higher. Unreachable pairs return a 400 "No conversion path found between {from} and {to}".

OHLC Candles

Open-high-low-close candles, from the last tick to years of history. Closed bars only. Responses shown were captured from the live feed — to run your own requests, open the Playground in the dashboard.

POST /v1/ohlc/last

Last OHLC Candle

The most recent candle for one symbol. Takes a small JSON body instead of query parameters.

Parameters

ParameterTypeDefaultDescription
symbol*string (body)—Instrument symbol (required)
periodstring (body)PERIOD_M1Candle timeframe, e.g. PERIOD_M5, PERIOD_H1, PERIOD_D1

Example Request

bash
1
2
3
4
curl -X POST "https://connect.forexapi.co/v1/ohlc/last" \
  -H "Authorization: Bearer $FXAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"symbol":"EURUSD","period":"PERIOD_H1"}'

Response

json
1
2
3
4
5
6
7
8
9
10
[
  {
    "symbol": "EURUSD",
    "open": 1.13675,
    "close": 1.13667,
    "high": 1.13676,
    "low": 1.13666,
    "period": "PERIOD_H1"
  }
]
GET /v1/ohlc/history

OHLC History

Historical closed candles in two modes: an epoch/ISO range (start + end) or the most recent N bars (bars). Only CLOSED bars are returned — the still-forming candle is never included. Identical windows are cached.

Parameters

ParameterTypeDefaultDescription
symbol*string—Instrument symbol (required)
periodstringPERIOD_H1Candle timeframe
start*int | ISO—Range mode: inclusive start (epoch seconds or ISO 8601)
end*int | ISO—Range mode: exclusive end (epoch seconds or ISO 8601)
bars*int—Count mode: number of most-recent closed bars (1-5000). Use either start+end or bars, never both.

Example Request

bash
1
2
3
curl -G "https://connect.forexapi.co/v1/ohlc/history" \
  -H "Authorization: Bearer $FXAPI_KEY" \
  -d symbol=EURUSD -d period=PERIOD_H1 -d bars=3

Response

json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
  "symbol": "EURUSD",
  "requestedSymbol": "EURUSD",
  "period": "PERIOD_H1",
  "count": 2,
  "gmtOffsetSeconds": 10800,
  "bars": [
    {
      "time": 1758240000,
      "open": 1.1042,
      "high": 1.1051,
      "low": 1.1038,
      "close": 1.1049,
      "tickVolume": 1234
    },
    {
      "time": 1758243600,
      "open": 1.1049,
      "high": 1.1056,
      "low": 1.1041,
      "close": 1.1053,
      "tickVolume": 1102
    }
  ]
}

💡 Depth is plan-gated: Free has no history, Starter 1 year, Builder/Developer 5 years, Professional 10 years — a request past your horizon returns a 403 naming the earliest available date. Bar timestamps are feed server time; convert with gmtOffsetSeconds.

Crypto

Aggregated crypto market data from Binance, Gate.io and Bybit. Requires the Builder plan or higher. Responses shown were captured from the live feed — to run your own requests, open the Playground in the dashboard.

GET /v1/crypto/prices

Crypto Prices

Live 24-hour statistics for crypto pairs aggregated from Binance, Gate.io and Bybit. Up to 10 symbols per request.

Parameters

ParameterTypeDefaultDescription
symbols*string—Comma-separated pair list, e.g. BTCUSDT,ETHUSDT (required, max 10)

Example Request

bash
1
2
3
curl -G "https://connect.forexapi.co/v1/crypto/prices" \
  -H "Authorization: Bearer $FXAPI_KEY" \
  -d symbols=BTCUSDT,ETHUSDT

Response

json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
  "BTCUSDT": {
    "price": 408136,
    "change24h": -2150.4,
    "change24hPct": -0.52,
    "open24h": 410286.4,
    "high24h": 412500,
    "low24h": 406900,
    "bid": 408135.9,
    "ask": 408136.1,
    "timestamp": 1790654300
  },
  "ETHUSDT": null
}

💡 Requires the Builder plan or higher — Free and Starter receive a 403 naming their plan. Unavailable pairs return null per symbol.

GET /v1/crypto/currencies

Supported Crypto Currencies

Paginated directory of supported crypto assets with metadata (name, website, socials) sourced from LiveCoinWatch.

Parameters

ParameterTypeDefaultDescription
limitint100Page size, 1-100
pageint1Page number

Example Request

bash
1
2
3
curl -G "https://connect.forexapi.co/v1/crypto/currencies" \
  -H "Authorization: Bearer $FXAPI_KEY" \
  -d limit=2 -d page=1

Response

json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
  "data": {
    "BTC": {
      "name": "Bitcoin",
      "sign": "BTC",
      "webp32": "https://s3.coinmarketcap.com/…/btc.webp",
      "website": "https://bitcoin.org",
      "twitter": "https://twitter.com/bitcoin",
      "telegram": null,
      "discord": null
    },
    "ETH": {
      "name": "Ethereum",
      "sign": "ETH",
      "webp32": "https://s3.coinmarketcap.com/…/eth.webp",
      "website": "https://ethereum.org",
      "twitter": "https://twitter.com/ethereum",
      "telegram": null,
      "discord": null
    }
  },
  "page": 1,
  "limit": 2,
  "total": 1200,
  "total_pages": 600
}

💡 Requires the Builder plan or higher.

Technical Analytics

Indicators are computed inside the price terminal from CLOSED bars only — the still-forming candle is never included — so values are deterministic and match your charting platform. Each request consumes API credits like any other endpoint. The example responses below were captured from the live feed.

GET /v1/indicators/ma

Moving Averages

Called without extra parameters this returns the full MA matrix — SMA and EMA at 10, 25, 100 and 200, each computed from closed bars — with `result.price` being the last closed close the scores were measured against. Pass method + length (+ optional price) for one custom average instead.

Parameters

ParameterTypeDefaultDescription
symbol*string—Instrument symbol, case-insensitive (required)
periodstringPERIOD_H1Candle timeframe, e.g. PERIOD_M5, PERIOD_H1, PERIOD_D1
methodstringoptionalsma, ema or wma — switches to single-MA mode (must be sent with length)
lengthintoptionalBar window length 2–500 for single-MA mode
pricestringcloseApplied price for single-MA mode: close, open, high, low, hl2, hlc3, ohlc4

Example Request

bash
1
2
3
curl -G "https://connect.forexapi.co/v1/indicators/ma" \
  -H "Authorization: Bearer $FXAPI_KEY" \
  -d symbol=EURUSD -d period=PERIOD_H1

Response

json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
{
  "symbol": "EURUSD",
  "period": "PERIOD_H1",
  "indicator": "MA",
  "params": {},
  "result": {
    "ema10": {
      "v": 1.13644,
      "s": "Neutral",
      "n": "EMA 10",
      "score": -1
    },
    "ema25": {
      "v": 1.13701,
      "s": "Neutral",
      "n": "EMA 25",
      "score": -2
    },
    "ema100": {
      "v": 1.13923,
      "s": "Neutral",
      "n": "EMA 100",
      "score": -6
    },
    "ema200": {
      "v": 1.14281,
      "s": "Neutral",
      "n": "EMA 200",
      "score": -12
    },
    "sma10": {
      "v": 1.1366,
      "s": "Neutral",
      "n": "SMA 10",
      "score": -2
    },
    "sma25": {
      "v": 1.137,
      "s": "Neutral",
      "n": "SMA 25",
      "score": -2
    },
    "sma100": {
      "v": 1.13843,
      "s": "Neutral",
      "n": "SMA 100",
      "score": -5
    },
    "sma200": {
      "v": 1.1427,
      "s": "Neutral",
      "n": "SMA 200",
      "score": -12
    },
    "price": 1.13569
  },
  "timestamp": 1790664713
}

💡 Each entry carries v (value), s (signal), n (display name) and score (-100..100) measuring how far price has moved away from that average: ±100 saturates at a 5% gap; ≥50 Strong Buy, ≥15 Buy, ±15 Neutral band, ≤-50 Strong Sell. The EMA is seeded with an SMA and computed over the full warm-up window for convergence.

GET /v1/indicators/oscillators

Oscillators Summary

Seven momentum / trend / volatility readings on fixed defaults in one call — RSI(14), STOCH %K(14), MACD level(12,26,9), ATR(14), ADX(14), Ultimate Oscillator(7/14/28) and Parabolic SAR(0.02/0.2) — computed from closed bars.

Parameters

ParameterTypeDefaultDescription
symbol*string—Instrument symbol, case-insensitive (required)
periodstringPERIOD_H1Candle timeframe, e.g. PERIOD_M5, PERIOD_H1, PERIOD_D1

Example Request

bash
1
2
3
curl -G "https://connect.forexapi.co/v1/indicators/oscillators" \
  -H "Authorization: Bearer $FXAPI_KEY" \
  -d symbol=EURUSD -d period=PERIOD_H1

Response

json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
{
  "symbol": "EURUSD",
  "period": "PERIOD_H1",
  "indicator": "OSCILLATORS",
  "params": {},
  "result": {
    "rsi": {
      "v": 39.9457,
      "s": "Buy",
      "n": "RSI",
      "score": 25
    },
    "stoch": {
      "v": 20.8191,
      "s": "Strong Buy",
      "n": "STOCH K%",
      "score": 73
    },
    "macd": {
      "v": -0.00049933,
      "s": "Neutral",
      "n": "MACD Level",
      "score": -6
    },
    "atr": {
      "v": 0.00086964,
      "s": "Neutral",
      "n": "ATR",
      "score": 0
    },
    "adx": {
      "v": 12.6954,
      "s": "Sell",
      "n": "ADX",
      "score": -25
    },
    "uo": {
      "v": 33.7262,
      "s": "Buy",
      "n": "Ultimate Oscillator",
      "score": 33
    },
    "psar": {
      "v": 1.13862,
      "s": "Neutral",
      "n": "Parabolic SAR",
      "score": -4
    }
  },
  "timestamp": 1790666620
}

💡 Every entry carries v (value), s (signal), n (display name) and score (-100..100; ≥50 Strong Buy, ≥15 Buy, ±15 Neutral, ≤-50 Strong Sell). RSI, STOCH and UO are mean-reversion: oversold reads as a buy signal, overbought as a sell. MACD rates the histogram in ATR units, ADX rates the +DI/−DI direction scaled by trend strength, PSAR scores the gap to the stop like the MA matrix — ATR is directionless and stays Neutral.

GET /v1/indicators/bbands

Bollinger Bands

SMA middle band with upper/lower bands at `deviation` × population standard deviation of the window.

Parameters

ParameterTypeDefaultDescription
symbol*string—Instrument symbol (required)
periodstringPERIOD_H1Candle timeframe
lengthint20Window length, 2–500
deviationfloat2.0Band width in standard deviations, 0–10

Example Request

bash
1
2
3
curl -G "https://connect.forexapi.co/v1/indicators/bbands" \
  -H "Authorization: Bearer $FXAPI_KEY" \
  -d symbol=EURUSD -d period=PERIOD_H1 -d length=20 -d deviation=2

Response

json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
  "symbol": "EURUSD",
  "period": "PERIOD_H1",
  "indicator": "BBANDS",
  "params": {
    "length": 20,
    "deviation": 2
  },
  "result": {
    "length": 20,
    "deviation": 2,
    "upper": 1.13778062,
    "middle": 1.1369505,
    "lower": 1.13612038
  },
  "timestamp": 1790654283
}
GET /v1/indicators/stochastic

Stochastic Oscillator

Raw %K (position of the close inside the k_length high/low range) and its simple-average %D.

Parameters

ParameterTypeDefaultDescription
symbol*string—Instrument symbol (required)
periodstringPERIOD_H1Candle timeframe
k_lengthint14%K lookback, 2–200
d_lengthint3%D smoothing of %K, 1–100

Example Request

bash
1
2
3
curl -G "https://connect.forexapi.co/v1/indicators/stochastic" \
  -H "Authorization: Bearer $FXAPI_KEY" \
  -d symbol=EURUSD -d period=PERIOD_H1

Response

json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
  "symbol": "EURUSD",
  "period": "PERIOD_H1",
  "indicator": "STOCH",
  "params": {
    "k_length": 14,
    "d_length": 3
  },
  "result": {
    "k_length": 14,
    "d_length": 3,
    "k": 22.57142857,
    "d": 34.66666667
  },
  "timestamp": 1790654283
}
GET /v1/pivot-points

Pivot Points

Floor-trader pivot levels derived from the previous completed DAILY bar. Called without parameters this returns every method at once — classic, fibonacci, camarilla (adds r4/s4), woodie and demark. Pass method for a single set.

Parameters

ParameterTypeDefaultDescription
symbol*string—Instrument symbol, case-insensitive (required)
methodstringoptionalclassic, fibonacci, camarilla, woodie or demark — returns just that set

Example Request

bash
1
2
3
curl -G "https://connect.forexapi.co/v1/pivot-points" \
  -H "Authorization: Bearer $FXAPI_KEY" \
  -d symbol=EURUSD

Response

json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
{
  "symbol": "EURUSD",
  "period": "PERIOD_D1",
  "indicator": "PIVOTS",
  "params": {},
  "result": {
    "bar_time": 1790553600,
    "classic": {
      "r3": 1.14289,
      "r2": 1.14101,
      "r1": 1.13907,
      "pp": 1.13719,
      "s1": 1.13525,
      "s2": 1.13337,
      "s3": 1.13143
    },
    "fibonacci": {
      "r3": 1.14101,
      "r2": 1.13955076,
      "r1": 1.13864924,
      "pp": 1.13719,
      "s1": 1.13573076,
      "s2": 1.13482924,
      "s3": 1.13337
    },
    "camarilla": {
      "r3": 1.1381805,
      "r2": 1.13783033,
      "r1": 1.13748017,
      "pp": 1.13719,
      "s1": 1.13677983,
      "s2": 1.13642967,
      "s3": 1.1360795,
      "r4": 1.139231,
      "s4": 1.135029
    },
    "woodie": {
      "r3": 1.14286,
      "r2": 1.140995,
      "r1": 1.13904,
      "pp": 1.137175,
      "s1": 1.13522,
      "s2": 1.133355,
      "s3": 1.1314
    },
    "demark": {
      "r1": 1.13813,
      "pp": 1.13672,
      "s1": 1.13431
    }
  },
  "timestamp": 1790665730
}

💡 Keys are lowercase (r3/r2/r1/pp/s1/s2/s3). Levels are always computed on the DAILY timeframe — bar_time is the UTC midnight of the reference (previous completed) daily bar. Demark defines only p/r1/s1; camarilla additionally returns r4/s4.

WebSocket API

Real-Time WebSocket API

Stream live quotes for forex, metals, indices, commodities and stocks. Push updates arrive the moment the feed ticks.

Connection URL

wss://connect.forexapi.co/ws

Authentication & plans

wss://connect.forexapi.co/ws?token=$FXAPI_KEY

Pass your fxapi_live_ key (export it as FXAPI_KEY and substitute, or read it from the environment in code). The WebSocket is a Professional-plan feature — keys on other plans are closed with 4403 (the close reason names your current plan). Invalid or revoked keys are closed with 4401, and exceeding the per-user connection limit with 4429. The key may also be passed in the Authorization: Bearer header instead of the query string.

How it works

  1. Open the socket with your token (query string or Authorization: Bearer header).
  2. Send a subscribe request naming the symbols you want.
  3. Receive pushes: a rates snapshot arrives immediately, then a new message on every tick.
  4. Use list to audit your subscriptions, convert for one-shot conversions, unsubscribe to stop updates.
  5. Closing the socket drops all of that connection's subscriptions; per-token connection limits apply (see Errors).

Request envelope

All messages are single JSON objects over the text frame. Field rules:

fieldtypedescription
idstring | numberRequired on every request. Echoed back on replies and errors so you can correlate them.
methodstringOne of subscribe, unsubscribe, list, convert. Case-insensitive.
typestringrates or cross. Required by subscribe/unsubscribe/list, ignored by convert.
symbolsstring[]Symbols to act on. Case-insensitive and suffix-optional for rates (aapl → AAPL.US); cross pairs use BASE/QUOTE form. Invalid entries are dropped, and a request with no valid symbols answers with an error.
from, to, amountstring, string, numberConvert only. amount must be a number or the request errors.

Client → Server Messages

Every request must include a unique id; replies echo it back so you can correlate them.

methodtypefieldspurpose
subscriberatessymbols: string[]Live quotes for direct pairs like EURUSD, XAUUSD, AAPL
unsubscriberatessymbols: string[]Stop updates for the given symbols
subscribe / unsubscribecrosssymbols: string[]Cross pairs computed on the fly, e.g. EUR/JPY, GBP/CHF
listrates | cross—Returns your current subscriptions for that type
convert—from, to, amountOne-shot conversion using the latest rates

Symbols are case-insensitive and suffix-optional: requesting aapl delivers the data for AAPL.US (the payload's symbol field shows the canonical name).

Server → Client Messages

{type: "rates", symbol, data}

Live quote — pushed on every tick for subscribed symbols

{type: "cross", data}

Cross-rate matrix, pushed ~once per second — field reference below

{id, symbols}

Reply to list requests

{id, type: "convert", data}

Reply to convert requests

{error, id?}

Protocol error — bad method, missing fields, unknown symbols. See Errors.

Message Format Examples

Subscribe — direct rates (client → server)

1
2
3
4
5
6
7
8
9
10
{
  "id": "sub-1",
  "method": "subscribe",
  "type": "rates",
  "symbols": [
    "EURUSD",
    "GBPUSD",
    "XAUUSD"
  ]
}

Rate update (server → client)

One message per tick per subscribed symbol. The data object carries the full quote: bid/ask with their session high/low, the last price, change, decimal precision (digits) and the market category.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
  "type": "rates",
  "symbol": "EURUSD",
  "data": {
    "symbol": "EURUSD",
    "category": "Forex",
    "price": 1.105,
    "change": 0.002,
    "ask": 1.1051,
    "askHigh": 1.1055,
    "askLow": 1.104,
    "bid": 1.1049,
    "bidHigh": 1.1053,
    "bidLow": 1.1038,
    "digits": 5
  }
}

Subscribe — cross pairs (client → server)

1
2
3
4
5
6
7
8
9
{
  "id": "cross-1",
  "method": "subscribe",
  "type": "cross",
  "symbols": [
    "EUR/JPY",
    "GBP/CHF"
  ]
}

Cross-rate update (server → client)

Delivered roughly once per second as one batch. data is a matrix: row i holds the conversions from your i-th subscribed pair to every subscribed pair, in the order you subscribed. Each cell has three fields: c — the target currency/pair, v — the rate (null when no conversion path exists or the cell is the diagonal), and s — tick direction: g went up, r went down, n unchanged since the last push.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
  "type": "cross",
  "data": [
    [
      {
        "c": "JPY",
        "v": 163.42,
        "s": "g"
      },
      {
        "c": "CHF",
        "v": 0.9213,
        "s": "n"
      }
    ],
    [
      {
        "c": "JPY",
        "v": 177.41,
        "s": "r"
      },
      {
        "c": "CHF",
        "v": null,
        "s": "n"
      }
    ]
  ]
}

Convert request (client → server)

1
2
3
4
5
6
7
{
  "id": "conv-1",
  "method": "convert",
  "from": "USD",
  "to": "EUR",
  "amount": 100
}

Convert reply (server → client)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
  "id": "conv-1",
  "type": "convert",
  "data": {
    "query": {
      "from": "USD",
      "to": "EUR",
      "amount": 100
    },
    "info": {
      "amount": 100,
      "exchange_rate": 0.9051,
      "timestamp": 1789890000
    },
    "result": 90.51
  }
}

List subscriptions (client → server)

1
2
3
4
5
{
  "id": "list-1",
  "method": "list",
  "type": "rates"
}

List reply (server → client)

1
2
3
4
5
6
7
8
{
  "id": "list-1",
  "symbols": [
    "EURUSD",
    "GBPUSD",
    "XAUUSD"
  ]
}

WebSocket Code Examples

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Real-time rates over WebSocket
// Export your key once: export FXAPI_KEY="fxapi_live_..." (Node / bundlers)
const ws = new WebSocket(`wss://connect.forexapi.co/ws?token=${process.env.FXAPI_KEY}`);

ws.onopen = () => {
    // Every message needs a unique "id" so replies can be correlated
    ws.send(JSON.stringify({
        id: 'sub-1',
        method: 'subscribe',
        type: 'rates',
        symbols: ['EURUSD', 'GBPUSD', 'XAUUSD']
    }));
};

ws.onmessage = (event) => {
    const msg = JSON.parse(event.data);
    switch (msg.type) {
        case 'rates':          // live quote for one symbol
            console.log(msg.symbol, msg.data.price);
            break;
        case 'cross':          // cross-rate batch
            console.log(msg.data);
            break;
        default:               // replies carry the request "id"
            if (msg.error) console.error(msg.error);
            else console.log('reply to', msg.id, msg);
    }
};

// Stop receiving updates for symbols you no longer need
// ws.send(JSON.stringify({ id: 'unsub-1', method: 'unsubscribe', type: 'rates', symbols: ['GBPUSD'] }));

MCP Server (for AI agents)

Connect your AI to live market data

ForexAPI ships a native Model Context Protocol server over Streamable HTTP. Point Claude Code, opencode, Cursor or any MCP client at it and the AI can pull live prices, candles and indicator values as tools — the same 13 endpoints documented above, with the same parameters and your same API key.

Server URL

https://connect.forexapi.co/mcp

Auth

Header Authorization: Bearer $FXAPI_KEY. The /mcp endpoint itself is gated — unauthenticated clients cannot connect or list tools.

Available tools (11)

get prices · list currencies · list symbols · convert · OHLC last · OHLC history · moving average · RSI · MACD · ATR · Bollinger Bands · stochastic · pivot points

Tool calls count against your plan's rate limit and monthly quota exactly like REST requests, and the same plan gates apply (crypto data needs Builder+, history depth follows your plan).

Claude Code

bash
1
2
3
4
5
6
7
# Add the ForexAPI MCP server to Claude Code (one-time)
claude mcp add --transport http forexapi https://connect.forexapi.co/mcp \
  --header "Authorization: Bearer $FXAPI_KEY"

# Verify from the Claude Code prompt
> /mcp          # lists the forexapi server and its 11 tools
> What is the current RSI(14) of EURUSD on H1?

opencode

json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// opencode.json (project root) — https://opencode.ai/docs/mcp-servers/
// The key lives in an env var; keep it out of dotfiles you commit.
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "forexapi": {
      "type": "remote",
      "url": "https://connect.forexapi.co/mcp",
      "headers": {
        "Authorization": "Bearer ${FXAPI_KEY}"
      },
      "enabled": true
    }
  }
}

Any other MCP client

bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Any MCP client speaking "Streamable HTTP" can connect:
#   URL:      https://connect.forexapi.co/mcp
#   Headers:  Authorization: Bearer $FXAPI_KEY
#
# The server speaks JSON-RPC 2.0 over HTTP POST:

# 1) Initialize the session
curl -X POST https://connect.forexapi.co/mcp \
  -H "Authorization: Bearer $FXAPI_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{
        "protocolVersion":"2025-03-26","capabilities":{},
        "clientInfo":{"name":"my-client","version":"1.0"}}}'

# 2) tools/list → 11 tools (prices, currencies, symbols, convert,
#    OHLC last/history, ma, oscillators,
#    bbands, stochastic, pivot-points)

# 3) tools/call — same parameters as the REST endpoint
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{
  "name":"get_oscillators_v1_indicators_oscillators_get",
  "arguments":{"symbol":"EURUSD","period":"PERIOD_H1"}}}

Errors

Error Response Format

Errors use conventional HTTP status codes and always carry a JSON body with a single detail string (or an array for validation errors).

1
2
3
4
5
6
{
  "status": 404,
  "body": {
    "detail": "Symbol not found: AAPLQ"
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
  "status": 422,
  "body": {
    "detail": [
      {
        "loc": [
          "query",
          "symbol"
        ],
        "msg": "Field required",
        "type": "missing"
      }
    ]
  }
}

HTTP Status Codes

Every status the API returns, with the exact messages to expect and when they occur.

400 Bad Request

The request was understood but its parameters are invalid.

Too many symbols. Maximum is 100. GET /v1/prices
PERIOD_CURRENT is not a valid period POST /v1/ohlc/last, GET /v1/ohlc/history
Use either start+end or bars, not both GET /v1/ohlc/history
Provide either start+end (range mode) or bars (count mode) GET /v1/ohlc/history
Range mode requires both start and end GET /v1/ohlc/history
start must be before end GET /v1/ohlc/history
{period} bars are {seconds}s wide, so the window … works out to {n} bars, but a single request returns at most 5000 bars … GET /v1/ohlc/history — the message states the exact bar count and which periods would fit
No conversion path found between {from} and {to} GET /v1/convert
Analytics error: invalid_length / invalid_method / invalid_price / invalid_params GET /v1/indicators/* — a parameter is outside the documented range
401 Unauthorized

Authentication failed. The key was malformed, unknown, revoked or expired.

Invalid API key. Create a new key on the API Keys page of your dashboard.
User not found
Token is no longer active legacy JWT tokens only
JWT token has expired legacy JWT tokens only
Invalid JWT token legacy JWT tokens only
403 Forbidden

Authenticated, but this request is not allowed.

Forbidden. You are not allowed to access this resource. No credentials supplied, or the caller IP is outside the key's allowlist
This API key is not scoped for crypto market data. Add the 'crypto' scope on the API Keys page of your dashboard. GET /v1/crypto/* with a key that excludes the crypto scope
Crypto market data requires the Builder plan or higher — crypto is not included in the {plan} plan. Compare plans at https://forexapi.co/pricing. GET /v1/crypto/* on Free or Starter
Historical market data is not included in the {plan} plan. Paid plans include 1-10 years of history — compare plans at https://forexapi.co/pricing. GET /v1/ohlc/history on Free
The {plan} plan includes {n} year(s) of historical data — the earliest available start for this plan is {date}. Narrow the range or upgrade … GET /v1/ohlc/history with a start older than the plan's horizon
404 Not Found

The requested symbol does not exist.

Symbol not found: {symbol} POST /v1/ohlc/last, GET /v1/ohlc/history, GET /v1/indicators/*, GET /v1/pivot-points — GET /v1/prices never 404s, it returns null for unknown symbols instead
422 Unprocessable Entity

A query parameter failed schema validation (missing, wrong type, out of range).

{"detail": [{"loc": ["query", "symbol"], "msg": "Field required", "type": "missing"}]} FastAPI validation errors list every offending parameter
429 Too Many Requests

You hit the per-minute rate limit or exhausted your monthly quota.

Rate limit exceeded: the {plan} plan allows {n} requests per minute. Try again in {s} seconds. Need more headroom? Compare plans at https://forexapi.co/pricing. Burst limit — wait for the Retry-After seconds
Monthly quota exceeded: you have used {used} of the {quota} monthly requests included in the {plan} plan. Usage resets at the start of your next billing cycle … Monthly quota is used up
500 Internal Server Error

Something unexpected failed on our side. Safe to retry with backoff.

502 Bad Gateway

The upstream price data source returned an error.

Price feed error: {reason} POST /v1/ohlc/last, GET /v1/ohlc/history
503 Service Unavailable

A required data set is not ready yet (usually right after a cold start).

Currency list is not available yet, the price feed may still be starting up GET /v1/currencies
504 Gateway Timeout

The upstream price data source did not answer in time. Worth one retry.

Price feed is not responding, try again shortly All data endpoints

WebSocket Errors

Connection Close Codes

4401 The provided key was invalid — reason: Invalid token
4401 The provided key was revoked or expired — reason: Token is no longer active
4403 The plan does not include WebSocket access — the close reason names your current plan and links to pricing
4429 Too many simultaneous connections for this token (limit: 5 per key)
1000 Clean close (you or the server ended the session normally)

Protocol Errors (delivered as messages)

Missing 'id' field. Every request must carry a unique id.
Missing 'method' field. Add method: subscribe, unsubscribe, list or convert.
Invalid method: {method} Unknown method name.
Invalid type: {type} type must be rates or cross.
No valid symbols provided for rates subscription. symbols was empty or none of the symbols resolved.
No symbols provided for cross subscription. symbols was empty.
Missing 'from' or 'to' field for conversion. convert needs both currencies.
'amount' must be a number for conversion. convert amount was absent or not numeric.
No conversion path found between {from} and {to} No route exists between the two currencies.

Malformed requests never kill the connection — the server replies with {error, id} and keeps the socket open.

Need help?

Seeing an error that isn't listed? The detail message always explains what went wrong — include it when contacting support. Test interactively in the Playground, check live system status, or manage your keys in the dashboard.