curl --request POST \
--url https://api.example.com/api/v1/brands/{brand_id}/brand-intelligence/seo-group-insights \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"domains": [
"<string>"
],
"group_label": "<string>"
}
'import requests
url = "https://api.example.com/api/v1/brands/{brand_id}/brand-intelligence/seo-group-insights"
payload = {
"domains": ["<string>"],
"group_label": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({domains: ['<string>'], group_label: '<string>'})
};
fetch('https://api.example.com/api/v1/brands/{brand_id}/brand-intelligence/seo-group-insights', 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.example.com/api/v1/brands/{brand_id}/brand-intelligence/seo-group-insights",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'domains' => [
'<string>'
],
'group_label' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/api/v1/brands/{brand_id}/brand-intelligence/seo-group-insights"
payload := strings.NewReader("{\n \"domains\": [\n \"<string>\"\n ],\n \"group_label\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.example.com/api/v1/brands/{brand_id}/brand-intelligence/seo-group-insights")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"domains\": [\n \"<string>\"\n ],\n \"group_label\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/api/v1/brands/{brand_id}/brand-intelligence/seo-group-insights")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"domains\": [\n \"<string>\"\n ],\n \"group_label\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"brand_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"generated_at": "2023-11-07T05:31:56Z",
"group_key": "<string>",
"module": "seo",
"group_label": "<string>",
"brand_domain": "<string>",
"domains": [
"<string>"
],
"cohort_domains": [
"<string>"
],
"coverage": {
"targets_total": 0,
"measured": 0,
"missing": [
"<string>"
],
"fetch_failed": [
"<string>"
],
"persist_failed": [
"<string>"
],
"skipped_cost": [
"<string>"
],
"last_run_at": "2023-11-07T05:31:56Z",
"brand_measured": false,
"brand_domain": "<string>",
"brand_not_measured_reason": "<string>",
"near_duplicate_pairs": [
{
"domain_a": "<string>",
"domain_b": "<string>",
"edit_distance": 123
}
],
"near_duplicate_scan_truncated": false,
"near_duplicate_scanned_count": 0
},
"position": [
{
"dimension": "<string>",
"label": "<string>",
"higher_is_better": true,
"brand_value": 123,
"brand_rank": 123,
"tied_count": 123,
"measured_count": 0,
"cohort_total": 0,
"best_domain": "<string>",
"best_tied_count": 123,
"best_value": 123,
"median_value": 123,
"not_measured_reason": "<string>"
}
],
"concentration": {
"measured_count": 0,
"total_est_traffic": 123,
"top_domain": "<string>",
"top_tied_count": 123,
"top_share_pct": 123,
"top3_share_pct": 123,
"brand_share_pct": 123,
"verdict": "not_measured",
"brand_actual_clicks_28d": 123,
"brand_traffic_source": "estimated"
},
"table_stakes": {
"cohort_domains_measured": 0,
"threshold_domains": 0,
"keywords": [
{
"keyword": "<string>",
"cohort_domains": 0,
"search_volume": 123,
"brand_ranks": false,
"brand_position": 123
}
],
"brand_missing_count": 0,
"measured": false,
"basis_notice": "Computed over the stored keyword samples for each competitor, not the full domain-wide keyword population."
},
"content_type_mix": {
"rows": [
{
"content_type": "<string>",
"cohort_count": 0,
"cohort_share_pct": 123,
"brand_count": 0,
"brand_share_pct": 123
}
],
"cohort_urls_classified": 0,
"brand_urls_classified": 0,
"urls_labeled_from_content": 0,
"content_labeling_enabled": false,
"measured": false,
"basis_notice": "Computed over the stored keyword samples for each competitor, not the full domain-wide keyword population."
},
"keyword_intent_mix": {
"rows": [
{
"intent": "informational",
"keyword_count": 0,
"share_pct": 123
}
],
"keywords_sampled": 0,
"keywords_classified": 0,
"keywords_unclassified": 0,
"domains_measured": 0,
"domains_not_measured": [
"<string>"
],
"measured": false,
"basis_notice": "Computed over the stored keyword samples for each competitor, not the full domain-wide keyword population."
},
"authority": {
"enabled": false,
"measured_count": 0,
"dimensions": [
{
"dimension": "<string>",
"label": "<string>",
"higher_is_better": true,
"brand_value": 123,
"brand_rank": 123,
"tied_count": 123,
"measured_count": 0,
"cohort_total": 0,
"best_domain": "<string>",
"best_tied_count": 123,
"best_value": 123,
"median_value": 123,
"not_measured_reason": "<string>"
}
],
"not_measured_reason": "<string>"
},
"authority_history": {
"enabled": false,
"group_range": "month",
"series": [
{
"domain": "<string>",
"is_brand": false,
"points": [
{
"period": "<string>",
"measured": false,
"domain_rank": 123,
"backlinks": 123,
"referring_domains": 123,
"referring_pages": 123
}
],
"measured_points": 0,
"first_referring_domains": 123,
"latest_referring_domains": 123,
"delta_referring_domains": 123
}
],
"domains_measured": 0,
"periods_covered": 0,
"measured": false,
"not_measured_reason": "<string>"
},
"backlink_gaps": {
"enabled": false,
"domains": [
{
"referring_domain": "<string>",
"competitor_domains": 0,
"best_backlinks": 123,
"referring_pages": 123,
"rank": 123,
"first_seen": "<string>",
"spam_score": 123,
"example_domain": "<string>"
}
],
"measured_domains": 0,
"max_gap_population": 123,
"brand_referring_domains_known": 0,
"measured": false,
"not_measured_reason": "<string>"
},
"gaps": {
"keywords": [
{
"keyword": "<string>",
"competitor_domains": 0,
"best_position": 123,
"search_volume": 123,
"cpc": 123,
"example_domain": "<string>"
}
],
"measured_domains": 0,
"max_gap_population": 123,
"semantics": "",
"measured": false
},
"weaknesses": {
"keywords": [
{
"keyword": "<string>",
"competitor_domain": "<string>",
"competitor_position": 123,
"brand_position": 123,
"position_delta": 123,
"search_volume": 123
}
],
"competitors_measured": 0,
"shared_keywords_evaluated": 0,
"max_competitor_position": 0,
"min_position_delta": 0,
"measured": false
},
"comparison_basis": {
"measured": false,
"series_with_captures": 0,
"series_with_prior_capture": 0,
"has_prior_snapshot": false
},
"momentum": [
{
"domain": "<string>",
"is_brand": false,
"est_organic_traffic": 123,
"prior_est_organic_traffic": 123,
"delta_est_organic_traffic": 123
}
],
"findings": [
{
"rule_id": "<string>",
"module": "seo",
"panel": "<string>",
"title": "<string>",
"detail": "<string>",
"severity": "info",
"evidence_refs": [
"<string>"
]
}
],
"estimate_notice": ""
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}Get Brand Intelligence Seo Group Insights Route
Cohort SEO analysis for one competitor Brand Group (INSIGHTS tab).
POST because the cohort is a client-curated domain set, mirroring the Paid
Media group route. An empty domains list means “the whole tracked set”.
Reads only already-persisted DataForSEO data — no new provider spend.
curl --request POST \
--url https://api.example.com/api/v1/brands/{brand_id}/brand-intelligence/seo-group-insights \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"domains": [
"<string>"
],
"group_label": "<string>"
}
'import requests
url = "https://api.example.com/api/v1/brands/{brand_id}/brand-intelligence/seo-group-insights"
payload = {
"domains": ["<string>"],
"group_label": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({domains: ['<string>'], group_label: '<string>'})
};
fetch('https://api.example.com/api/v1/brands/{brand_id}/brand-intelligence/seo-group-insights', 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.example.com/api/v1/brands/{brand_id}/brand-intelligence/seo-group-insights",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'domains' => [
'<string>'
],
'group_label' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/api/v1/brands/{brand_id}/brand-intelligence/seo-group-insights"
payload := strings.NewReader("{\n \"domains\": [\n \"<string>\"\n ],\n \"group_label\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.example.com/api/v1/brands/{brand_id}/brand-intelligence/seo-group-insights")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"domains\": [\n \"<string>\"\n ],\n \"group_label\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/api/v1/brands/{brand_id}/brand-intelligence/seo-group-insights")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"domains\": [\n \"<string>\"\n ],\n \"group_label\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"brand_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"generated_at": "2023-11-07T05:31:56Z",
"group_key": "<string>",
"module": "seo",
"group_label": "<string>",
"brand_domain": "<string>",
"domains": [
"<string>"
],
"cohort_domains": [
"<string>"
],
"coverage": {
"targets_total": 0,
"measured": 0,
"missing": [
"<string>"
],
"fetch_failed": [
"<string>"
],
"persist_failed": [
"<string>"
],
"skipped_cost": [
"<string>"
],
"last_run_at": "2023-11-07T05:31:56Z",
"brand_measured": false,
"brand_domain": "<string>",
"brand_not_measured_reason": "<string>",
"near_duplicate_pairs": [
{
"domain_a": "<string>",
"domain_b": "<string>",
"edit_distance": 123
}
],
"near_duplicate_scan_truncated": false,
"near_duplicate_scanned_count": 0
},
"position": [
{
"dimension": "<string>",
"label": "<string>",
"higher_is_better": true,
"brand_value": 123,
"brand_rank": 123,
"tied_count": 123,
"measured_count": 0,
"cohort_total": 0,
"best_domain": "<string>",
"best_tied_count": 123,
"best_value": 123,
"median_value": 123,
"not_measured_reason": "<string>"
}
],
"concentration": {
"measured_count": 0,
"total_est_traffic": 123,
"top_domain": "<string>",
"top_tied_count": 123,
"top_share_pct": 123,
"top3_share_pct": 123,
"brand_share_pct": 123,
"verdict": "not_measured",
"brand_actual_clicks_28d": 123,
"brand_traffic_source": "estimated"
},
"table_stakes": {
"cohort_domains_measured": 0,
"threshold_domains": 0,
"keywords": [
{
"keyword": "<string>",
"cohort_domains": 0,
"search_volume": 123,
"brand_ranks": false,
"brand_position": 123
}
],
"brand_missing_count": 0,
"measured": false,
"basis_notice": "Computed over the stored keyword samples for each competitor, not the full domain-wide keyword population."
},
"content_type_mix": {
"rows": [
{
"content_type": "<string>",
"cohort_count": 0,
"cohort_share_pct": 123,
"brand_count": 0,
"brand_share_pct": 123
}
],
"cohort_urls_classified": 0,
"brand_urls_classified": 0,
"urls_labeled_from_content": 0,
"content_labeling_enabled": false,
"measured": false,
"basis_notice": "Computed over the stored keyword samples for each competitor, not the full domain-wide keyword population."
},
"keyword_intent_mix": {
"rows": [
{
"intent": "informational",
"keyword_count": 0,
"share_pct": 123
}
],
"keywords_sampled": 0,
"keywords_classified": 0,
"keywords_unclassified": 0,
"domains_measured": 0,
"domains_not_measured": [
"<string>"
],
"measured": false,
"basis_notice": "Computed over the stored keyword samples for each competitor, not the full domain-wide keyword population."
},
"authority": {
"enabled": false,
"measured_count": 0,
"dimensions": [
{
"dimension": "<string>",
"label": "<string>",
"higher_is_better": true,
"brand_value": 123,
"brand_rank": 123,
"tied_count": 123,
"measured_count": 0,
"cohort_total": 0,
"best_domain": "<string>",
"best_tied_count": 123,
"best_value": 123,
"median_value": 123,
"not_measured_reason": "<string>"
}
],
"not_measured_reason": "<string>"
},
"authority_history": {
"enabled": false,
"group_range": "month",
"series": [
{
"domain": "<string>",
"is_brand": false,
"points": [
{
"period": "<string>",
"measured": false,
"domain_rank": 123,
"backlinks": 123,
"referring_domains": 123,
"referring_pages": 123
}
],
"measured_points": 0,
"first_referring_domains": 123,
"latest_referring_domains": 123,
"delta_referring_domains": 123
}
],
"domains_measured": 0,
"periods_covered": 0,
"measured": false,
"not_measured_reason": "<string>"
},
"backlink_gaps": {
"enabled": false,
"domains": [
{
"referring_domain": "<string>",
"competitor_domains": 0,
"best_backlinks": 123,
"referring_pages": 123,
"rank": 123,
"first_seen": "<string>",
"spam_score": 123,
"example_domain": "<string>"
}
],
"measured_domains": 0,
"max_gap_population": 123,
"brand_referring_domains_known": 0,
"measured": false,
"not_measured_reason": "<string>"
},
"gaps": {
"keywords": [
{
"keyword": "<string>",
"competitor_domains": 0,
"best_position": 123,
"search_volume": 123,
"cpc": 123,
"example_domain": "<string>"
}
],
"measured_domains": 0,
"max_gap_population": 123,
"semantics": "",
"measured": false
},
"weaknesses": {
"keywords": [
{
"keyword": "<string>",
"competitor_domain": "<string>",
"competitor_position": 123,
"brand_position": 123,
"position_delta": 123,
"search_volume": 123
}
],
"competitors_measured": 0,
"shared_keywords_evaluated": 0,
"max_competitor_position": 0,
"min_position_delta": 0,
"measured": false
},
"comparison_basis": {
"measured": false,
"series_with_captures": 0,
"series_with_prior_capture": 0,
"has_prior_snapshot": false
},
"momentum": [
{
"domain": "<string>",
"is_brand": false,
"est_organic_traffic": 123,
"prior_est_organic_traffic": 123,
"delta_est_organic_traffic": 123
}
],
"findings": [
{
"rule_id": "<string>",
"module": "seo",
"panel": "<string>",
"title": "<string>",
"detail": "<string>",
"severity": "info",
"evidence_refs": [
"<string>"
]
}
],
"estimate_notice": ""
}{
"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.
Path Parameters
Body
Response
Successful Response
"seo"N-of-M acquisition coverage for one module's cohort (the ★S0/★C0 strip).
measured counts cohort domains that produced a stored metric row.
missing are targets that never did — their fetch or persist failed, or
they have not been acquired yet. For Content, fetch_failed is a typed
subset of missing, not an additional shortfall, so clients must
de-duplicate rather than sum those lengths. A competitor with a failed
acquisition must render as a failure chip, never as a measured zero.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
★S2 — is the cohort's modelled organic traffic concentrated or spread.
top_domain is ONE name out of the domains holding the highest measured
traffic; top_tied_count counts them inclusively (1 = it really is one
domain). Without it, two domains at 50%/50% render as one dominating the
cohort — the ★S2 twin of the tied_count disclosure on
ModuleDimensionRank. None when nothing was measured, never 0.
Show child attributes
Show child attributes
★S3 — keywords the WHOLE measured cohort ranks for: the must-haves.
Show child attributes
Show child attributes
★S6 — what KIND of page wins in this niche (classified ranking URLs).
Two classifiers feed one set of counts, and the split is published.
Most labels come from the URL's own path (free, deterministic, and right
whenever the path carries a recognisable token). ★S13b adds a second
source for the paths that carry no such token: the page's actual parsed
content, bought per URL. urls_labeled_from_content is how many labels
came from the paid source across BOTH denominators — cohort_urls_classified
plus brand_urls_classified, summed. The brand's own benchmark row spends
its own budget exactly as each competitor row does, so a cohort-only count
would understate the provenance and could not be rendered against either
number alone.
That count is NOT a quality score and must not be rendered as one. It is
bounded by a per-refresh budget spent only on URLs the free path could not
read, so a LOW number means "the free classifier handled almost everything"
— the good case — and is indistinguishable, by design, from "the paid leg
is switched off". content_labeling_enabled is what separates them.
Show child attributes
Show child attributes
★S5 — classified intent mix over the stored cohort keyword samples.
keywords_unclassified is a separate denominator: missing labels are
not folded into a bucket and domains whose classification call failed are
named under domains_not_measured rather than represented as zeros.
Show child attributes
Show child attributes
★S8 — backlink/DR distribution and where the brand sits.
enabled mirrors DATAFORSEO_BACKLINKS_ENABLED; when it is off the
authority metrics were never bought, so the panel is not measured rather
than a cohort of zeros.
Show child attributes
Show child attributes
★S8 history — the authority side's first historical story.
enabled mirrors the acquisition gate (the link-history flag AND the
backlinks transport switch); when it is off nothing was ever bought, so
this is not measured rather than a cohort of flat zero lines.
Show child attributes
Show child attributes
★S9b — sites that link to the cohort and not to you.
The backlink twin of :class:SeoGapsPanel, and it carries the same three
invariants. Rows come from the provider's own server-side exclusion query,
so a site that links to the brand from a page we never sampled is still
excluded. brand_referring_domains_known is the client-side belt's
denominator: when it is 0 the belt was not available for this cohort and
only the server-side exclusion applied. max_gap_population is a MAX
across competitors, never a SUM — link-gap populations overlap heavily and
adding them would invent a cohort-wide number nobody measured.
Show child attributes
Show child attributes
★S9 — the cohort ranks, you don't.
Rows come from DataForSEO's own server-side exclusion query
(domain_intersection with intersections: false), so a keyword the
brand ranks for at position 240 is not reported as a gap. semantics
names that contract; an empty string means the stored snapshot predates it.
Show child attributes
Show child attributes
★S10 — shared keywords where a competitor MATERIALLY outranks the brand.
The thresholds are the free SEO audit's, imported rather than copied, so BI and the audit can never disagree on what "materially outranks" means.
Show child attributes
Show child attributes
Does a STORED predecessor exist for this cohort — at any age?
The SEO twin of :class:ContentComparisonBasisPanel and
:class:GoogleAdsComparisonBasisPanel. Same two-field contract, same
three states, over SEO's own grain and market invariant:
measured=False— no tracked domain has a stored capture at all, so we cannot say anything about history. An unknown, not a first generation.measured=Trueandhas_prior_snapshot=False— captures exist but none has a predecessor IN ITS OWN MARKET. This is the FIRST-EVER generation for the group, not "we compared and nothing changed".has_prior_snapshot=True— a real market-matched comparison basis exists somewhere in the cohort.
Grain is the (domain, market) SERIES, not the bare domain.
CompetitorSeoMetric carries no location_code/language_code of
its own — both live on the parent CompetitorSeoSnapshot
(app/models/competitor_dashboard.py) — so a predecessor pair must be
scoped to the SAME market as the domain's current capture, never a
different one. competitor_seo_dashboard_service.py::get_historical_traffic_series
states this exact invariant in its own docstring: a brand's current-market
live captures must never be joined to a different-location/language
historical one. That is why the counts below are named
series_with_* rather than domains_with_* (Google Ads' naming,
which is legitimately domain-grain because paid-search captures carry no
market split) — a domain that changed tracked market reads as TWO series
here, and a stale cross-market pairing would silently diff unrelated
numbers.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Was this page helpful?