curl --request GET \
--url https://api.scrunchai.com/v1/{brand_id}/signals/{signal_id} \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.scrunchai.com/v1/{brand_id}/signals/{signal_id}"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.scrunchai.com/v1/{brand_id}/signals/{signal_id}', 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://api.scrunchai.com/v1/{brand_id}/signals/{signal_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$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://api.scrunchai.com/v1/{brand_id}/signals/{signal_id}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.scrunchai.com/v1/{brand_id}/signals/{signal_id}")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.scrunchai.com/v1/{brand_id}/signals/{signal_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"id": 123,
"detected_for_date": "2023-12-25",
"fingerprint": "<string>",
"subject_kind": "brand",
"alert_type": "level_change",
"scope": "account",
"metric": "<string>",
"platform": "<string>",
"direction": "up",
"tier": "high",
"current_value": 123,
"baseline_value": 123,
"delta_absolute": 123,
"narrative_what": "<string>",
"slice": {
"platforms": [
"<string>"
],
"topic_labels": [
"<string>"
],
"geo_country": "<string>"
},
"created_at": "2023-11-07T05:31:56Z",
"score": 123,
"narrative": {
"what_happened": "<string>",
"why_it_matters": "<string>",
"what_to_do": "<string>"
},
"window_current_start": "2023-12-25",
"window_current_end": "2023-12-25",
"baseline_definition": "<string>",
"url_movers": [
{
"normalized_url": "<string>",
"owner": "brand",
"current_responses": 123,
"baseline_responses": 123,
"current_prompts": 123,
"baseline_prompts": 123,
"competitor_id": 123,
"competitor_name": "<string>",
"contribution": 123
}
]
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}Get Signal
Fetch one signal by ID, including its full narrative (what happened, why it matters, what to do) when available. Returns 404 for IDs that exist but are not user-facing (non-fired or cluster-child rows) — the same population rule as listSignals.
curl --request GET \
--url https://api.scrunchai.com/v1/{brand_id}/signals/{signal_id} \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.scrunchai.com/v1/{brand_id}/signals/{signal_id}"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.scrunchai.com/v1/{brand_id}/signals/{signal_id}', 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://api.scrunchai.com/v1/{brand_id}/signals/{signal_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$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://api.scrunchai.com/v1/{brand_id}/signals/{signal_id}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.scrunchai.com/v1/{brand_id}/signals/{signal_id}")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.scrunchai.com/v1/{brand_id}/signals/{signal_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"id": 123,
"detected_for_date": "2023-12-25",
"fingerprint": "<string>",
"subject_kind": "brand",
"alert_type": "level_change",
"scope": "account",
"metric": "<string>",
"platform": "<string>",
"direction": "up",
"tier": "high",
"current_value": 123,
"baseline_value": 123,
"delta_absolute": 123,
"narrative_what": "<string>",
"slice": {
"platforms": [
"<string>"
],
"topic_labels": [
"<string>"
],
"geo_country": "<string>"
},
"created_at": "2023-11-07T05:31:56Z",
"score": 123,
"narrative": {
"what_happened": "<string>",
"why_it_matters": "<string>",
"what_to_do": "<string>"
},
"window_current_start": "2023-12-25",
"window_current_end": "2023-12-25",
"baseline_definition": "<string>",
"url_movers": [
{
"normalized_url": "<string>",
"owner": "brand",
"current_responses": 123,
"baseline_responses": 123,
"current_prompts": 123,
"baseline_prompts": 123,
"competitor_id": 123,
"competitor_name": "<string>",
"contribution": 123
}
]
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Response
Successful Response
A detected, statistically-tested movement in an AI-visibility metric.
Signal ID. Stable within one detection; use for getSignal.
Anchor date the nightly detection ran for (YYYY-MM-DD).
Stable identity of the underlying issue across nightly re-detections (hash of the slice + metric). An ongoing signal is re-raised daily under the same fingerprint; reactions are keyed on it.
Whose movement the signal describes: your brand or a competitor.
brand, competitor Detection kind: level_change (step shift) or trend (sustained drift).
level_change, trend Slice granularity: account, account_platform, topic, or topic_platform.
account, account_platform, topic, topic_platform Metric the signal fired on (e.g. presence_rate, position_top_rate, cited_domain_rate).
AI platform the signal was detected on. (multi) means the slice spans several platforms — see slice.platforms for the real list.
Direction of the movement: up, down, none.
up, down, none Confidence tier, best first: high, confident, worth_a_look, provisional, then noise-floor tiers low_confidence, underpowered, untested (hidden by default).
high, confident, worth_a_look, provisional, low_confidence, underpowered, untested Metric value over the current window (0-1 rate).
Metric value over the baseline window (0-1 rate).
current_value - baseline_value (positive = up).
One-line headline of what changed.
The data slice the signal was detected on.
Show child attributes
Show child attributes
When the signal row was created (UTC).
Engine priority score used for the default sort; null for legacy rows.
Full narrative (what happened / why it matters / what to do), when available.
Show child attributes
Show child attributes
Start of the current comparison window.
End of the current comparison window.
Human-readable definition of the baseline window.
Per-URL citation movers behind the signal: the brand/competitor URLs whose distinct-response citation counts moved most between the baseline and current windows. Emitted for cited_domain_rate signals; empty for other metrics and older signals. Filterable via mover_url.
Show child attributes
Show child attributes