curl --request GET \
--url https://devtune.ai/api/v2/projects/{projectId}/content-gaps/list \
--header 'Authorization: Bearer <token>'import requests
url = "https://devtune.ai/api/v2/projects/{projectId}/content-gaps/list"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://devtune.ai/api/v2/projects/{projectId}/content-gaps/list', 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://devtune.ai/api/v2/projects/{projectId}/content-gaps/list",
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://devtune.ai/api/v2/projects/{projectId}/content-gaps/list"
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://devtune.ai/api/v2/projects/{projectId}/content-gaps/list")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://devtune.ai/api/v2/projects/{projectId}/content-gaps/list")
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": {
"contentGaps": [
{
"gapType": "topic",
"targetKey": "<string>",
"targetLabel": "<string>",
"windowDays": 30,
"asOfDate": "2023-12-25",
"citationCount": 1,
"primaryCitations": 1,
"competitorCitations": 1,
"averagePosition": 123,
"share": 0.5,
"estimated": false
}
],
"coverage": {
"status": "available",
"windowDays": 30,
"availableDays": 1,
"isPartial": true,
"asOfDate": "2023-12-25"
},
"pagination": {
"offset": 1,
"limit": 50,
"totalCount": 1
},
"window": {
"days": 123,
"dateFrom": "2023-12-25",
"dateTo": "2023-12-25"
}
},
"meta": {
"timestamp": "2023-11-07T05:31:56Z",
"projectId": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
}{
"error": "<string>",
"message": "<string>",
"status": 123
}{
"error": "<string>",
"message": "<string>",
"status": 123
}{
"error": "<string>",
"message": "<string>",
"status": 123
}{
"error": "<string>",
"message": "<string>",
"status": 123
}List content gaps
Returns measured rollup facts for topic, source, and brand content gaps over a fixed 30 or 90 day window. Your own sources and brand are excluded, as are topics you hold at least half the citations on: those are your presence rather than a gap. Excluded targets are absent from the list and from totalCount. Every count is a citation: content-gap rollups are built only from cited results, so this endpoint reports no mention counts. This is a breaking contract change: pipeline-derived impactScore, confidenceScore, freshnessScore, actionabilityScore, signalStrength, signals, evidence, candidateKey, summary, prompt counts, and third-party citation counts were removed because the rollups cannot reproduce them without inventing evidence. An unavailable coverage state means the content-gap window has not been rolled up yet, not that measurement found zero gaps; an available window still reports availableDays and isPartial so a partly covered window is not read as a complete one.
curl --request GET \
--url https://devtune.ai/api/v2/projects/{projectId}/content-gaps/list \
--header 'Authorization: Bearer <token>'import requests
url = "https://devtune.ai/api/v2/projects/{projectId}/content-gaps/list"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://devtune.ai/api/v2/projects/{projectId}/content-gaps/list', 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://devtune.ai/api/v2/projects/{projectId}/content-gaps/list",
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://devtune.ai/api/v2/projects/{projectId}/content-gaps/list"
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://devtune.ai/api/v2/projects/{projectId}/content-gaps/list")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://devtune.ai/api/v2/projects/{projectId}/content-gaps/list")
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": {
"contentGaps": [
{
"gapType": "topic",
"targetKey": "<string>",
"targetLabel": "<string>",
"windowDays": 30,
"asOfDate": "2023-12-25",
"citationCount": 1,
"primaryCitations": 1,
"competitorCitations": 1,
"averagePosition": 123,
"share": 0.5,
"estimated": false
}
],
"coverage": {
"status": "available",
"windowDays": 30,
"availableDays": 1,
"isPartial": true,
"asOfDate": "2023-12-25"
},
"pagination": {
"offset": 1,
"limit": 50,
"totalCount": 1
},
"window": {
"days": 123,
"dateFrom": "2023-12-25",
"dateTo": "2023-12-25"
}
},
"meta": {
"timestamp": "2023-11-07T05:31:56Z",
"projectId": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
}{
"error": "<string>",
"message": "<string>",
"status": 123
}{
"error": "<string>",
"message": "<string>",
"status": 123
}{
"error": "<string>",
"message": "<string>",
"status": 123
}{
"error": "<string>",
"message": "<string>",
"status": 123
}Authorizations
API key with dtk_live_ prefix. Obtain it from API Keys in the account sidebar. New keys start with all supported scopes selected for the chosen project, and you can narrow them to specific read/write scopes as needed.
Path Parameters
Query Parameters
Fixed rolling window in days. Supported values are 30 and 90. Defaults to 30.
30, 90 Filter by rollup key type.
topic, source, brand Case-insensitive search on the display label.
1 - 200x >= 01 <= x <= 100