curl --request GET \
--url https://api.syntalic.com/v1/social/attention-vs-shelfimport requests
url = "https://api.syntalic.com/v1/social/attention-vs-shelf"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://api.syntalic.com/v1/social/attention-vs-shelf', 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.syntalic.com/v1/social/attention-vs-shelf",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$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.syntalic.com/v1/social/attention-vs-shelf"
req, _ := http.NewRequest("GET", url, nil)
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.syntalic.com/v1/social/attention-vs-shelf")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.syntalic.com/v1/social/attention-vs-shelf")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_body{
"category": "<string>",
"country": "<string>",
"currency": "<string>",
"window_days": 123,
"platform": "<string>",
"organic_only": true,
"brands": [
{}
],
"corpus_mentions": 123,
"thin_corpus": "<string>",
"unmatched": {},
"coverage": {},
"freshness": {},
"snapshot": {},
"resolved": {
"input": "<string>",
"category_path": "<string>",
"department": "<string>",
"match_source": "<string>",
"match_confidence": 123,
"alternates": [
{}
]
},
"meta": {
"served_from": "<string>",
"freshness_seconds": 123,
"schema_version": "<string>",
"computed_at": "2023-11-07T05:31:56Z",
"price_observed_at_min": "2023-11-07T05:31:56Z",
"price_observed_at_max": "2023-11-07T05:31:56Z"
}
}{
"type": "<string>",
"title": "Bad Request",
"status": 400,
"detail": "<string>",
"instance": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
}
}{
"type": "<string>",
"title": "Payment Required",
"status": 402,
"detail": "<string>",
"instance": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
}
}{
"type": "<string>",
"title": "Not Found",
"status": 404,
"detail": "<string>",
"instance": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
}
}{
"type": "<string>",
"title": "Too Many Requests",
"status": 429,
"detail": "<string>",
"instance": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
}
}{
"type": "<string>",
"title": "Internal Server Error",
"status": 500,
"detail": "<string>",
"instance": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
}
}Rank brands by share-of-conversation vs share-of-shelf gap
Brands ranked by the GAP between share of social conversation and share of shelf inside one category. Over-indexed attention with under-distribution is the ranging signal a retail buyer wants and the deck slide a challenger brand wants - one endpoint, two customer types. Requires BOTH corpora bound to the same category axis and the same brand key, which is why nobody holding one of them can reproduce it. The join is on brand KEY, never on name: a row whose key is absent from the other side is dropped and COUNTED in unmatched, because matching by name mislabels a real brand as under-distributed. Carries TWO coverage blocks and TWO freshness values - social counts mention subjects on a weekly refresh, shelf counts products on a daily one, and merging either pair would be a lie with a familiar shape. shelf_brands_not_in_conversation is the mirror finding: stocked and unspoken of.
curl --request GET \
--url https://api.syntalic.com/v1/social/attention-vs-shelfimport requests
url = "https://api.syntalic.com/v1/social/attention-vs-shelf"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://api.syntalic.com/v1/social/attention-vs-shelf', 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.syntalic.com/v1/social/attention-vs-shelf",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$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.syntalic.com/v1/social/attention-vs-shelf"
req, _ := http.NewRequest("GET", url, nil)
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.syntalic.com/v1/social/attention-vs-shelf")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.syntalic.com/v1/social/attention-vs-shelf")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_body{
"category": "<string>",
"country": "<string>",
"currency": "<string>",
"window_days": 123,
"platform": "<string>",
"organic_only": true,
"brands": [
{}
],
"corpus_mentions": 123,
"thin_corpus": "<string>",
"unmatched": {},
"coverage": {},
"freshness": {},
"snapshot": {},
"resolved": {
"input": "<string>",
"category_path": "<string>",
"department": "<string>",
"match_source": "<string>",
"match_confidence": 123,
"alternates": [
{}
]
},
"meta": {
"served_from": "<string>",
"freshness_seconds": 123,
"schema_version": "<string>",
"computed_at": "2023-11-07T05:31:56Z",
"price_observed_at_min": "2023-11-07T05:31:56Z",
"price_observed_at_max": "2023-11-07T05:31:56Z"
}
}{
"type": "<string>",
"title": "Bad Request",
"status": 400,
"detail": "<string>",
"instance": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
}
}{
"type": "<string>",
"title": "Payment Required",
"status": 402,
"detail": "<string>",
"instance": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
}
}{
"type": "<string>",
"title": "Not Found",
"status": 404,
"detail": "<string>",
"instance": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
}
}{
"type": "<string>",
"title": "Too Many Requests",
"status": 429,
"detail": "<string>",
"instance": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
}
}{
"type": "<string>",
"title": "Internal Server Error",
"status": 500,
"detail": "<string>",
"instance": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
}
}Query Parameters
Category root slug (department, not aisle). No wildcard: these answers are ranked comparisons.
Country for the SHELF side of the join.
us, ca Rollup window. Only published windows are accepted.
7d, 30d, 90d TikTok and Instagram are the only platforms in the corpus.
all, tiktok, instagram Exclude posts marked as ads.
Maximum ranked brands to return.
1 <= x <= 100Response
Attention vs shelf ranking
The conversation the shares divide up. A share of 40 mentions is not the same kind of fact as a share of 40,000.
Present when the whole category's corpus is below the depth floor: the ranking is arithmetic, not a market signal.
Rows dropped per side because their brand key has no counterpart. Counted, never fuzzy-matched.
TWO blocks — social counts mention subjects, shelf counts products. Never merged.
TWO values — weekly social, daily serving. Never the max.
Show child attributes
Show child attributes
Show child attributes
Show child attributes

