Heatmap API · Two modes, one endpoint

Currency-level strength, on a fixed scale.

GET /v1/heatmap aggregates RSI across the 28 major pairs and returns one 0–10 strength value per currency, with the strongest, the weakest and the dispersion between them. Switch one parameter and the same endpoint returns an 8 × 8 Pearson correlation matrix instead.

8 majors · 28 pairs H1, H4, D1, W1 2× request weight
tickatlas.com / v1 / heatmap 200 OK
GET /v1/heatmap?type=strength&timeframe=H4
strongest · strength
CAD 6.8
bullish
weakestJPY 3.1
range3.7
currencies keys8
pairs_analyzed7 of 7
CAD6.8 · bullish
USD6.3 · bullish
GBP5.6 · neutral
CHF5.1 · neutral
{
  "success": true,
  "data": {
    "type": "strength",
    "timeframe": "H4",
    "currencies": {
      "USD": { "strength": 6.3, "trend": "bullish",
               "change": 0.4, "pairs_analyzed": 7 },
      "EUR": { "strength": 4.7, "trend": "neutral",
               "change": -0.1, "pairs_analyzed": 7 },
      "GBP": { "strength": 5.6, "trend": "neutral",
               "change": 0.2, "pairs_analyzed": 7 },
      "JPY": { "strength": 3.1, "trend": "bearish",
               "change": -0.6, "pairs_analyzed": 7 },
      "CHF": { "strength": 5.1, "trend": "neutral",
               "change": 0, "pairs_analyzed": 7 },
      "AUD": { "strength": 3.8, "trend": "bearish",
               "change": -0.4, "pairs_analyzed": 7 },
      "CAD": { "strength": 6.8, "trend": "bullish",
               "change": 0.5, "pairs_analyzed": 7 },
      "NZD": { "strength": 4.2, "trend": "neutral",
               "change": -0.2, "pairs_analyzed": 7 },
    },
    "strongest": "CAD",
    "weakest": "JPY",
    "range": 3.7,
    "timestamp": "2026-09-18T14:00:00+00:00"
  }
}
8Major currencies
28Pairs aggregated
0–10Strength scale
2×Request weight
Strength mode

Eight currencies on one comparable scale.

currencies comes back as an object keyed by code, not a sorted list — the ordering below is the client's. Every value sits on the same 0–10 scale with 5.0 as the neutral midpoint, which is what makes two currencies directly comparable.

  • CAD 6.8 out of 10, trend bullish
  • USD 6.3 out of 10, trend bullish
  • GBP 5.6 out of 10, trend neutral
  • CHF 5.1 out of 10, trend neutral
  • EUR 4.7 out of 10, trend neutral
  • NZD 4.2 out of 10, trend neutral
  • AUD 3.8 out of 10, trend bearish
  • JPY 3.1 out of 10, trend bearish

Illustrative values, real arithmetic. The numbers above are a sample, but trend and change are derived from strength by the endpoint's own rules — bullish above 6, bearish below 4, and change = (strength − 5) × 0.3 — so the sample cannot contradict the API. The bar length is strength ÷ 10, and every row also prints its number, so magnitude is never carried by colour or length alone.

How it is derived

Seven pairs per currency, one inverted.

A currency is only measurable against something else, so the endpoint reads it through the seven pairs that contain it. Where the currency is the base, a high RSI means strength; where it is the quote, the same reading means the opposite — so that value is flipped to 100 − RSI before it joins the average.

  • RSI_14 on the requested timeframe, from the same cache /v1/indicator reads.
  • Inverted to 100 − RSI on the quote side of the pair.
  • Averaged over whatever was cached, then divided by 10 and rounded to one decimal.
CURRENCY_PAIRS7 per currency · 28 distinct
"USD": ["EURUSD", "GBPUSD", "USDJPY", "USDCHF", "AUDUSD", "USDCAD", "NZDUSD"]
"EUR": ["EURUSD", "EURGBP", "EURJPY", "EURCHF", "EURAUD", "EURCAD", "EURNZD"]
"GBP": ["GBPUSD", "EURGBP", "GBPJPY", "GBPCHF", "GBPAUD", "GBPCAD", "GBPNZD"]
"JPY": ["USDJPY", "EURJPY", "GBPJPY", "CHFJPY", "AUDJPY", "CADJPY", "NZDJPY"]
"CHF": ["USDCHF", "EURCHF", "GBPCHF", "CHFJPY", "AUDCHF", "CADCHF", "NZDCHF"]
"AUD": ["AUDUSD", "EURAUD", "GBPAUD", "AUDJPY", "AUDCHF", "AUDCAD", "AUDNZD"]
"CAD": ["USDCAD", "EURCAD", "GBPCAD", "CADJPY", "CADCHF", "AUDCAD", "NZDCAD"]
"NZD": ["NZDUSD", "EURNZD", "GBPNZD", "NZDJPY", "NZDCHF", "AUDNZD", "NZDCAD"]

# base side  -> RSI used as-is       (high RSI = strong)
# quote side -> 100 - RSI            (high RSI = weak)
# strength   = round(mean(values) / 10, 1)  -> 0.0 .. 10.0
# no cached RSI for any of the seven:
#   {strength: 5.0, trend: "neutral", change: 0.0, pairs_analyzed: 0}
Correlation mode

8 × 8, currency against currency.

?type=correlation — or the boolean alias ?correlations=true, which wins over type either way — returns a nested object instead of the strength map. It correlates currencies, not pairs: log returns per pair are attributed positively to the base and negatively to the quote, averaged into eight strength series, and Pearson-correlated against each other.

Illustrative values. The structural facts are real: the diagonal is exactly 1.0, the matrix is symmetric, and off-diagonal values are Pearson coefficients rounded to four decimals and clamped to [-1, 1]. Every cell prints its number, so sign and magnitude are never carried by colour alone.
Currency USDEURGBPJPYCHFAUDCADNZD
USD 1.00-0.82-0.54-0.31-0.61-0.58-0.44-0.57
EUR -0.821.000.610.180.740.350.220.33
GBP -0.540.611.000.120.440.290.190.27
JPY -0.310.180.121.000.41-0.22-0.35-0.19
CHF -0.610.740.440.411.000.160.080.14
AUD -0.580.350.29-0.220.161.000.470.86
CAD -0.440.220.19-0.350.080.471.000.41
NZD -0.570.330.27-0.190.140.860.411.00
LOOKBACK

The window is set by the timeframe

Correlation needs enough bars to be stable, so each timeframe carries its own horizon. A longer timeframe is a longer window, not more bars of the same length.

  • H1 — 7 days (168 hours)
  • H4 — 30 days (720 hours)
  • D1 — 90 days (2,160 hours)
  • W1 — 365 days (8,760 hours)
SOFT FAIL

Not enough history is a 200, not a 500

Pairs need at least ten bars each, and the endpoint then intersects timestamps across every pair it kept and needs at least ten in common. Below either threshold it returns an empty matrix with available:false and a message, so a client can render "not enough data yet" instead of an error.

  • time axis is the intersection, not a union
  • a pair on a different candle cadence is dropped
  • available is the field to branch on
Three parameters

Two products behind one path.

No required parameters at all: a bare GET /v1/heatmap is strength mode on H4. Everything else is one of three optional parameters, and invalid values are rejected with a named error code rather than coerced.

  • type — default strength. strength or correlation. Anything else is a 400 with INVALID_TYPE and the valid set in the detail.
  • timeframe — default H4. H1, H4, D1, W1. Uppercased for you; anything else is a 400 with INVALID_TIMEFRAME.
  • correlations — default —. Boolean alias. When true it promotes the request to type=correlation regardless of what type says, so the two spellings can never conflict.
# defaults: type=strength, timeframe=H4
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://tickatlas.com/v1/heatmap"

# correlation matrix on the daily timeframe
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://tickatlas.com/v1/heatmap?type=correlation&timeframe=D1"
# read dispersion, and check your inputs
d = requests.get(
    "https://tickatlas.com/v1/heatmap",
    headers={"X-API-Key": KEY},
    params={"timeframe": "H4"},
).json()["data"]

print(d["strongest"], d["weakest"], d["range"])

# currencies is an object — sort it yourself if you want a ranking
for code, c in sorted(
    d["currencies"].items(), key=lambda kv: -kv[1]["strength"]
):
    # 0 means the 5.0/neutral fallback applied, not a real reading
    if c["pairs_analyzed"] == 0:
        continue
    print(code, c["strength"], c["trend"], c["pairs_analyzed"])

# correlation: branch on `available`, never on truthiness of the matrix
m = requests.get(URL, headers=H, params={"correlations": True}).json()["data"]
if m["available"]:
    print(m["correlation_matrix"]["AUD"]["NZD"])
currencies.CAD.strength
6.8 of 10 · H4 · 7 of 7 pairs read
bullish
Ends of the scalestrongest CAD · weakest JPY
Dispersionrange 3.7 on 0–10
Confidence in the inputpairs_analyzed 0–7
Other modecorrelation_matrix · available
Read pairs_analyzed first

The response tells you how much to trust it.

A missing currency would break a fixed-width layout, so the endpoint never omits one. It returns the documented fallback instead — 5.0, neutral, 0.0 — and sets pairs_analyzed to the number of pairs it really read. That field is the difference between a neutral market and no data.

Field reference

What comes back, in both modes.

The envelope is { "success": true, "data": { … } }. Three keys are shared; the rest depend on which mode ran, and type tells you which one did.

data. Type Mode What it carries
type string both strength or correlation — echoes which mode ran.
timeframe string both Uppercased echo of the request. H1, H4, D1 or W1.
timestamp string both ISO 8601, server time at which the response was assembled.
currencies object strength An object keyed by currency code — USD, EUR, GBP, JPY, CHF, AUD, CAD, NZD — each holding the four fields above. Always all eight keys, even when a currency had no data.
strongest string strength Code with the highest strength. null only when the currency map is empty.
weakest string strength Code with the lowest strength.
range number strength strongest.strength − weakest.strength, two decimals. A dispersion measure on the 0–10 scale, not a percentage.
correlation_matrix object correlation Nested object, currency × currency. Diagonal is exactly 1.0; off-diagonal values are Pearson coefficients to four decimals, clamped to [-1, 1]. Empty object when unavailable.
available boolean correlation false when there was not enough overlapping history to compute a stable matrix. The status is still 200 — never a 5xx.
message string correlation Present only when available is false, explaining that a shorter timeframe may have enough overlapping bars.
Every value inside currencies has these four keys, for all 8 codes, always.
currencies.<CODE>. Type What it carries
strength number Mean RSI_14 across the currency’s seven pairs, inverted to 100 − RSI where the currency is the quote side, divided by 10. One decimal, so the scale is 0–10 with 5.0 as the neutral midpoint.
trend string bullish above 6, bearish below 4, neutral in between. A classification of strength, not an independent measurement.
change number (strength − 5) × 0.3, to one decimal. It is a restatement of strength, NOT a price change — the endpoint would need historical data for a real one, and says so in its own source.
pairs_analyzed number How many of the seven pairs actually had a cached RSI_14. Below 7 the strength value is an average over fewer inputs; 0 means the whole fallback applied.
Timeframes

4 intervals, and W1 is one of them.

This endpoint's set is not the indicator endpoints' set. The sub-hourly timeframes are absent because neither an RSI aggregate nor a correlation over a handful of minutes is worth publishing, and W1 is present because correlation benefits from the longest window. H4 is the default.

  • H1
  • H4 (default)
  • D1
  • W1
Developer-first behavior

Fixed shape, named errors, no surprises.

The strength response has the same keys on every call, which is what lets a dashboard bind to it once. Where the endpoint cannot answer, it says which parameter was wrong or which data was missing.

SHAPE

Always eight currencies

No currency is ever omitted, so a bound layout cannot shift. Missing data shows up as the documented fallback plus pairs_analyzed: 0.

  • 5.0 · neutral · 0.0 · 0
ERRORS

Named codes, valid sets included

A bad type is INVALID_TYPE and a bad timeframe is INVALID_TIMEFRAME, each returning the list of values that would have worked.

  • 400 with a machine-readable detail
ALIASES

Two spellings, one precedence

correlations=true promotes the request to correlation mode regardless of type, so the two parameters can never contradict each other.

  • boolean alias wins
QUOTA

2× weight, premium scope

Premium tier, multiplier 2.0 — the same weight as /v1/ohlc, /v1/multi, /v1/screener and /v1/calendar. The key needs the premium scope.

  • X-API-Key: tk_…
Developer API pricing

Included on every plan.

The heatmap sits in the premium endpoint tier: a call counts once against your quota and costs two weighted units of credit. Pay-as-you-go counts as a paid plan for endpoint access, which means a new account can call it immediately.

There is no free tier and no self-serve trial. Every account starts on pay-as-you-go with $2.50 of prepaid credit, no card and no overage — monthly plans lift the starting quota.
Pay as you go
$0 to start 200 requests/day · 30/min until you top up
  • $2.50 of credit included
  • Every REST endpoint except raw ticks
  • No card required, no overage
  • 10 API keys
Start free
Starter
$29/mo 10,000 requests · 120/min
  • Every REST endpoint except raw ticks
  • WebSocket streaming, 5 symbols
  • Released calendar actuals
  • Email support
  • 3 API keys
View plan
Enterprise
$349/mo 1,000,000 requests · 6000/min
  • Everything in Pro
  • Dedicated support
  • 100 API keys
  • Custom indicators
  • SLA guarantee
  • On-premise option
Contact sales
Common software patterns

One call, a whole visual layer.

A fixed set of eight keys on a fixed scale is unusually easy to render, which is most of why this endpoint exists in the shape it does.

Strength dashboards

Eight values on a fixed 0–10 scale with a neutral midpoint of 5.0 map onto meters and a sorted bar list with no client-side normalisation step.

metersbar listswidgets

Exposure checks

The correlation matrix is the direct answer to "are these two positions the same bet?" — read the coefficient between the two currencies rather than eyeballing two charts.

portfolio viewsrisk panels

Regime watch

Store range per cycle: a widening dispersion across the eight currencies is a different market from a narrow one, and it is one number to track.

schedulerstime seriesalerts

Prefer an embeddable version? Drop currency strength & heatmap widgets onto any page — sandboxed, self-updating, no chart library.

Frequently asked questions

The heatmap, clarified.

What the strength number really measures, what change is and is not, which timeframes exist, and how the endpoint behaves when the data is thin.

How is currency strength actually calculated?

Each currency is read through the seven pairs that contain it. The endpoint takes RSI_14 on the requested timeframe for each of those pairs, uses it as-is where the currency is the base and inverts it to 100 − RSI where the currency is the quote, averages what it found, and divides by 10. The result is a 0–10 value with 5.0 as the neutral midpoint. It is an oscillator aggregate, not an average price change.

What is the change field, if not a price move?

It is (strength − 5) × 0.3, rounded to one decimal — a linear restatement of the strength value on a smaller scale. The endpoint’s own source calls it simplified and notes that a real change figure would need historical data. Treat it as a signed distance from neutral, and read strength for the underlying number.

Which timeframes are supported?

H1, H4, D1, W1, defaulting to H4. That is deliberately not the same set the indicator endpoints accept: the sub-hourly timeframes are missing and W1 is added, because correlation needs a lookback long enough to be stable — H1 looks back 7 days, H4 looks back 30 days, D1 looks back 90 days, W1 looks back 365 days.

What does the correlation matrix correlate?

Not pairs — currencies. For each of the 28 pairs the endpoint computes log returns over a shared time axis, attributes the return to the base currency and its negative to the quote currency, averages that per currency to get eight strength series, and Pearson-correlates those series against each other. The result is an 8 × 8 object with an exact 1.0 diagonal and off-diagonal values to four decimals.

What happens when there is not enough history?

Correlation mode returns HTTP 200 with an empty correlation_matrix, available:false and a message suggesting a shorter timeframe — never a 500. It needs at least ten bars per pair and at least ten timestamps common to every pair it kept, because a matrix built on fewer is not stable enough to publish.

Which currencies and pairs are covered?

The eight majors — USD, EUR, GBP, JPY, CHF, AUD, CAD, NZD — and the 28 distinct pairs between them. There is no exotic coverage and no per-pair output of any kind in this endpoint: strength mode returns eight currency objects, correlation mode returns an 8 × 8 currency matrix. Pair-level data lives on /v1/quote, /v1/indicator and /v1/ohlc.

What does a call cost, and which plans can use it?

One call counts once against your daily quota and costs 2 weighted units of pay-as-you-go credit, in the premium tier alongside /v1/ohlc, /v1/multi, /v1/screener and /v1/calendar, and the API key must carry the premium permission scope. Every plan includes it — the heatmap carries no plan gate — so a new account can call it on its $2.50 of starting credit. There is no free tier and no self-serve trial.

Why is a currency showing exactly 5.0 with neutral and 0?

That is the documented fallback: when none of a currency’s seven pairs has a cached RSI_14, the endpoint returns strength 5.0, trend neutral, change 0.0 and pairs_analyzed 0 rather than omitting the currency or failing the request. Read pairs_analyzed before trusting any strength value — it also tells you when an average came from fewer than seven inputs.

8 currencies, 28 pairs, one request

Aggregate the majors. Skip the plumbing.

One GET returns every major currency on the same scale, with the ends and the dispersion already computed. Start on your $2.50 of credit and add the correlation mode when you need it.