curl --request GET \
--url https://api.rankspot.ai/v1/ai-visibility/fanouts \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.rankspot.ai/v1/ai-visibility/fanouts"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.rankspot.ai/v1/ai-visibility/fanouts', 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.rankspot.ai/v1/ai-visibility/fanouts",
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.rankspot.ai/v1/ai-visibility/fanouts"
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.rankspot.ai/v1/ai-visibility/fanouts")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.rankspot.ai/v1/ai-visibility/fanouts")
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{
"data": {
"total": 100,
"offset": 0,
"limit": 100,
"count": 10,
"items": [
{
"id": "3f1c2b4e-8d5a-4a2f-9c3e-7b1d6a5e4c20",
"query": "best screen recording software for mac 2026",
"actionIds": [
"3f1c2b4e-8d5a-4a2f-9c3e-7b1d6a5e4c20"
],
"articleIds": [
"3f1c2b4e-8d5a-4a2f-9c3e-7b1d6a5e4c20"
],
"firstSeenAt": "2023-11-07T05:31:56Z",
"lastSeenAt": "2023-11-07T05:31:56Z",
"searches": 14,
"platforms": [
"chatgpt"
]
}
]
}
}List fanout queries
The searches the AI platforms ran while answering your prompts, most-run first. Where a citation tells you which page an engine trusted, a fanout query tells you what it decided it needed to know, so a query you rank for nowhere is a content gap stated in the engine’s own words. Defaults to the worklist: queries with no article planned and none written. State is derived from the work linked to the query, the same rule keywords use, so it moves on its own as actions and articles appear. Pass type=planned, type=processed, type=all or type=archived for the others. Covers the full history unless you narrow it with startDate and endDate, which lists only the queries engines ran inside that period and counts searches over it; filter by query substring to look at one theme. Coverage is not uniform across platforms: not every engine reports its fanout.
curl --request GET \
--url https://api.rankspot.ai/v1/ai-visibility/fanouts \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.rankspot.ai/v1/ai-visibility/fanouts"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.rankspot.ai/v1/ai-visibility/fanouts', 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.rankspot.ai/v1/ai-visibility/fanouts",
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.rankspot.ai/v1/ai-visibility/fanouts"
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.rankspot.ai/v1/ai-visibility/fanouts")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.rankspot.ai/v1/ai-visibility/fanouts")
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{
"data": {
"total": 100,
"offset": 0,
"limit": 100,
"count": 10,
"items": [
{
"id": "3f1c2b4e-8d5a-4a2f-9c3e-7b1d6a5e4c20",
"query": "best screen recording software for mac 2026",
"actionIds": [
"3f1c2b4e-8d5a-4a2f-9c3e-7b1d6a5e4c20"
],
"articleIds": [
"3f1c2b4e-8d5a-4a2f-9c3e-7b1d6a5e4c20"
],
"firstSeenAt": "2023-11-07T05:31:56Z",
"lastSeenAt": "2023-11-07T05:31:56Z",
"searches": 14,
"platforms": [
"chatgpt"
]
}
]
}
}Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Query Parameters
x >= 01 <= x <= 100Inclusive start of the period, YYYY-MM-DD (UTC). Omit for no lower bound.
"2026-07-01"
Inclusive end of the period, YYYY-MM-DD (UTC). Omit for no upper bound.
"2026-07-28"
Filter by query substring, case-insensitive. Stored queries are already lower-cased, so casing here does not matter either.
"screen recorder"
Derived from the work linked to the query, the same rule keywords use. new: the worklist, no article planned and none written (default) | planned: has a write_article action but no article yet | processed: has a linked article | all: every non-archived query | archived: queries hidden from the worklist
all, planned, processed, new, archived searches, lastSeenAt, firstSeenAt, query asc, desc Response
Hide child attributes
Hide child attributes
100
0
100
10
Hide child attributes
Hide child attributes
"3f1c2b4e-8d5a-4a2f-9c3e-7b1d6a5e4c20"
The search the engine ran. Normalised on write — trimmed, inner whitespace collapsed, lower-cased — so the same search in two casings is one row rather than two.
"best screen recording software for mac 2026"
The write_article actions planned for this search. Non-empty with an empty articleIds is what type=planned selects.
["3f1c2b4e-8d5a-4a2f-9c3e-7b1d6a5e4c20"]
The articles written for this search. Non-empty is what type=processed selects, whatever the actions say.
["3f1c2b4e-8d5a-4a2f-9c3e-7b1d6a5e4c20"]
When this query was first and last run, ever. Not clipped to the requested period.
Responses in the period whose engine ran this query. An engine runs a query at most once per answer, so this is both the fanout count and the number of answers behind it.
14
Platforms that ran this query in the period.
chatgpt, perplexity, google_ai_overview, google_ai_mode 