curl -X GET 'https://server.example.com/api/v2/performance-heatmap?startDate=2026-04-20&endDate=2026-05-19&rowAxis=topics&columnAxis=models&metric=winRate' \
-H 'X-API-Key: YOUR_API_KEY' \
-H 'X-Project-Id: YOUR_PROJECT_ID'import requests
url = "https://server.promptwatch.com/api/v2/performance-heatmap"
headers = {"X-API-Key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-API-Key': '<api-key>'}};
fetch('https://server.promptwatch.com/api/v2/performance-heatmap', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://server.promptwatch.com/api/v2/performance-heatmap",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://server.promptwatch.com/api/v2/performance-heatmap"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-API-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://server.promptwatch.com/api/v2/performance-heatmap")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://server.promptwatch.com/api/v2/performance-heatmap")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-API-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"rowAxis": "brands",
"columnAxis": "brands",
"metric": "visibility",
"brand": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"domain": "<string>"
},
"rows": [
{
"id": "<string>",
"label": "<string>",
"domain": "<string>",
"relation": "DIRECT_COMPETITOR"
}
],
"columns": [
{
"id": "<string>",
"label": "<string>",
"domain": "<string>",
"relation": "DIRECT_COMPETITOR"
}
],
"cells": [
{
"rowId": "<string>",
"columnId": "<string>",
"value": 123
}
]
}{
"error": "Bad Request",
"code": "DATE_RANGE_TOO_LARGE",
"message": "Date range cannot exceed 92 days"
}{
"error": "Unauthorized",
"message": "Missing or invalid X-API-Key header."
}{
"error": "Unauthorized",
"message": "Missing or invalid X-API-Key header."
}Performance Heatmap
Heatmap for any two axes and one metric. Defaults to brands by models with visibility, the same view as the dashboard chart. Use YYYY-MM-DD for startDate and endDate (full calendar days in UTC). A missing cell means no value. When neither axis is brands, every cell is the project’s own brand, named in brand; a project with no own brand returns 204. getCompetitorVisibilityHeatmap remains the brands-by-models visibility matrix.
curl -X GET 'https://server.example.com/api/v2/performance-heatmap?startDate=2026-04-20&endDate=2026-05-19&rowAxis=topics&columnAxis=models&metric=winRate' \
-H 'X-API-Key: YOUR_API_KEY' \
-H 'X-Project-Id: YOUR_PROJECT_ID'import requests
url = "https://server.promptwatch.com/api/v2/performance-heatmap"
headers = {"X-API-Key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-API-Key': '<api-key>'}};
fetch('https://server.promptwatch.com/api/v2/performance-heatmap', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://server.promptwatch.com/api/v2/performance-heatmap",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://server.promptwatch.com/api/v2/performance-heatmap"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-API-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://server.promptwatch.com/api/v2/performance-heatmap")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://server.promptwatch.com/api/v2/performance-heatmap")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-API-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"rowAxis": "brands",
"columnAxis": "brands",
"metric": "visibility",
"brand": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"domain": "<string>"
},
"rows": [
{
"id": "<string>",
"label": "<string>",
"domain": "<string>",
"relation": "DIRECT_COMPETITOR"
}
],
"columns": [
{
"id": "<string>",
"label": "<string>",
"domain": "<string>",
"relation": "DIRECT_COMPETITOR"
}
],
"cells": [
{
"rowId": "<string>",
"columnId": "<string>",
"value": 123
}
]
}{
"error": "Bad Request",
"code": "DATE_RANGE_TOO_LARGE",
"message": "Date range cannot exceed 92 days"
}{
"error": "Unauthorized",
"message": "Missing or invalid X-API-Key header."
}{
"error": "Unauthorized",
"message": "Missing or invalid X-API-Key header."
}Authorizations
API key for authentication. Get yours from the Promptwatch dashboard under Settings > API Keys.
Query Parameters
Start date (YYYY-MM-DD). Max range 92 days. Defaults to today.
End date (YYYY-MM-DD). Max range 92 days. Defaults to today.
Row dimension. Defaults to brands. Must differ from columnAxis.
brands, models, topics, countries Column dimension. Defaults to models. Must differ from rowAxis.
brands, models, topics, countries Cell metric. visibility, shareOfVoice, and winRate are 0–100. sentiment is an average score. position is an average rank where 1 is best. Defaults to visibility.
visibility, sentiment, position, shareOfVoice, winRate Filter by model ids from GET /models. Repeat for multiple: models=x&models=y
Limit the grid to one prompt. When set, llmMonitorId is ignored.
Filter by one monitor. Ignored when promptId is set.
Drop the project's own brand before the brand limit is applied.
Hide ignored brands unless relations includes IGNORED.
Brand relations to include. Used when brands is an axis, and for shareOfVoice even when brands is not an axis. Repeat for multiple.
DIRECT_COMPETITOR, SELF, OTHER, IGNORED Max brands or topics. Models and countries are never capped. Defaults to 20.
1 <= x <= 20Filter by prompt type. Repeat for multiple.
ORGANIC, BRAND_SPECIFIC, COMPETITOR_COMPARISON Filter by prompt tag ids. Repeat for multiple.
Filter by prompt topic ids. Repeat for multiple.
Response
Default Response
brands, models, topics, countries brands, models, topics, countries visibility, sentiment, position, shareOfVoice, winRate The project's own brand when neither axis is brands. Every cell is this brand's value. Null when brands is an axis.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
One entry per value. A missing row and column pair means there is no value, not zero.
Show child attributes
Show child attributes