curl --request GET \
--url https://api.example.com/api/v1/free-audits/benchmark/{brand_id} \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.example.com/api/v1/free-audits/benchmark/{brand_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.example.com/api/v1/free-audits/benchmark/{brand_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.example.com/api/v1/free-audits/benchmark/{brand_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.example.com/api/v1/free-audits/benchmark/{brand_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.example.com/api/v1/free-audits/benchmark/{brand_id}")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/api/v1/free-audits/benchmark/{brand_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{
"audit_type": "<string>",
"available": true,
"reason": "<string>",
"basis": "<string>",
"basis_label": "<string>",
"brand_domain": "<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
},
"measured_competitors": 0,
"min_measured_competitors": 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>"
}
],
"authority_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>"
}
],
"authority_not_measured_reason": "<string>",
"keyword_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
},
"keyword_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
},
"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": {
"taxonomy_version": "<string>",
"dimensions": {},
"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."
},
"cohort_domains": [
"<string>"
],
"measured_at": "2023-11-07T05:31:56Z"
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}Get Audit Competitor Benchmark Route
Benchmark this brand’s audited position against the competitors IT named.
R17 of the audits UX plan — the differentiator a generic auditor cannot copy, because it holds no competitor set for you. Pure read of already-persisted competitor acquisition output: no provider spend, no refresh triggered.
Gated on the acquisition surface that WRITES the rows being read — the competitor-dashboard master plus the module sub-flag for the basis this audit type uses. With acquisition dark, the tables are not being populated and the only honest answer is that the capability is unavailable (404), not an empty cohort that reads like the user has no competitors.
An unsupported audit type (Website — no competitor is ever fetched or
scored, so nothing shares its measurement basis) returns 200 with
available=False and a reason. That is a real answer about the data, not
a missing endpoint.
curl --request GET \
--url https://api.example.com/api/v1/free-audits/benchmark/{brand_id} \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.example.com/api/v1/free-audits/benchmark/{brand_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.example.com/api/v1/free-audits/benchmark/{brand_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.example.com/api/v1/free-audits/benchmark/{brand_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.example.com/api/v1/free-audits/benchmark/{brand_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.example.com/api/v1/free-audits/benchmark/{brand_id}")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/api/v1/free-audits/benchmark/{brand_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{
"audit_type": "<string>",
"available": true,
"reason": "<string>",
"basis": "<string>",
"basis_label": "<string>",
"brand_domain": "<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
},
"measured_competitors": 0,
"min_measured_competitors": 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>"
}
],
"authority_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>"
}
],
"authority_not_measured_reason": "<string>",
"keyword_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
},
"keyword_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
},
"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": {
"taxonomy_version": "<string>",
"dimensions": {},
"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."
},
"cohort_domains": [
"<string>"
],
"measured_at": "2023-11-07T05:31:56Z"
}{
"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
Query Parameters
50Response
Successful Response
The audited brand's standing against the competitors IT named (R17).
available=False is a first-class, renderable answer — reason names
which one so the report can offer the matching next step instead of a
blank panel. It is NEVER an error: a brand with no competitors yet, or a
Website audit (which has no same-basis competitor measure at all), is a
normal state.
These are MEASURED dimensions on an identical basis for both sides, not
audit scores — no competitor is ever audited, so no competitor has a
dimension score to average. See
services/audit_competitor_benchmark.py for why.
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
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
★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
Was this page helpful?