Estimate keyword volume
Beta — behind the sonar-onboarding feature flag. Workspaces the flag is off for get 404 FEATURE_DISABLED.
An ESTIMATE of monthly mentions for up to 10 candidate keywords, measured concurrently (about 15 s, up to 25 s when busy sources need a narrower window). Never a point: each estimate is a low–high range and a band (quiet / standard / busy); read high for anything that blocks. Busy sources are re-counted over 7 days, then 1 day, instead of extrapolated. A source that times out is listed in unverifiedSources — it is never counted as zero. additionalTerms / excludeWords are evaluated on the bare sample at no extra cost and return the anchored share and the collision ratio. Creates nothing and consumes no quota.
curl --request POST \
--url https://app.octolens.com/api/v2/keywords/estimate \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"candidates": [
{
"keyword": "<string>",
"additionalTerms": "<string>",
"additionalTermsAndOr": true,
"excludeWords": "<string>"
}
]
}
'import requests
url = "https://app.octolens.com/api/v2/keywords/estimate"
payload = { "candidates": [
{
"keyword": "<string>",
"additionalTerms": "<string>",
"additionalTermsAndOr": True,
"excludeWords": "<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({
candidates: [
{
keyword: '<string>',
additionalTerms: '<string>',
additionalTermsAndOr: true,
excludeWords: '<string>'
}
]
})
};
fetch('https://app.octolens.com/api/v2/keywords/estimate', 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://app.octolens.com/api/v2/keywords/estimate",
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([
'candidates' => [
[
'keyword' => '<string>',
'additionalTerms' => '<string>',
'additionalTermsAndOr' => true,
'excludeWords' => '<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://app.octolens.com/api/v2/keywords/estimate"
payload := strings.NewReader("{\n \"candidates\": [\n {\n \"keyword\": \"<string>\",\n \"additionalTerms\": \"<string>\",\n \"additionalTermsAndOr\": true,\n \"excludeWords\": \"<string>\"\n }\n ]\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://app.octolens.com/api/v2/keywords/estimate")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"candidates\": [\n {\n \"keyword\": \"<string>\",\n \"additionalTerms\": \"<string>\",\n \"additionalTermsAndOr\": true,\n \"excludeWords\": \"<string>\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.octolens.com/api/v2/keywords/estimate")
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 \"candidates\": [\n {\n \"keyword\": \"<string>\",\n \"additionalTerms\": \"<string>\",\n \"additionalTermsAndOr\": true,\n \"excludeWords\": \"<string>\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"estimates": [
{
"keyword": "<string>",
"volume": {
"low": 4503599627370495,
"high": 4503599627370495,
"band": "quiet",
"basis": "analog",
"window": "30d",
"openEnded": true,
"perSource": [
{
"source": "<string>",
"count": 4503599627370495,
"window": "30d",
"factor": 123,
"low": 4503599627370495,
"high": 4503599627370495,
"openEnded": true
}
],
"unverifiedSources": [
"<string>"
],
"unmeasuredSources": [
{
"source": "<string>",
"typicalShare": 123,
"reason": "<string>"
}
],
"caveats": [
"<string>"
],
"prior": {
"source": "google-trends",
"monthly": 4503599627370495,
"low": 4503599627370495,
"high": 4503599627370495
},
"disagreement": true
},
"bareVolume": {
"low": 4503599627370495,
"high": 4503599627370495,
"band": "quiet",
"basis": "analog",
"window": "30d",
"openEnded": true,
"perSource": [
{
"source": "<string>",
"count": 4503599627370495,
"window": "30d",
"factor": 123,
"low": 4503599627370495,
"high": 4503599627370495,
"openEnded": true
}
],
"unverifiedSources": [
"<string>"
],
"unmeasuredSources": [
{
"source": "<string>",
"typicalShare": 123,
"reason": "<string>"
}
],
"caveats": [
"<string>"
],
"prior": {
"source": "google-trends",
"monthly": 4503599627370495,
"low": 4503599627370495,
"high": 4503599627370495
},
"disagreement": true
},
"variant": {
"sampleSize": 4503599627370495,
"anchoredShare": 0.5,
"excludedShare": 0.5,
"retainedShare": 0.5,
"collisionRatio": 0.5
},
"flagged": true
}
],
"calibration": {
"fittedAt": "<string>"
},
"elapsedMs": 4503599627370495
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found",
"status": 404,
"details": [
"<unknown>"
],
"upgradeUrl": "https://app.octolens.com/me/upgrade?src=agents",
"upgradeCommand": "octolens upgrade"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found",
"status": 404,
"details": [
"<unknown>"
],
"upgradeUrl": "https://app.octolens.com/me/upgrade?src=agents",
"upgradeCommand": "octolens upgrade"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found",
"status": 404,
"details": [
"<unknown>"
],
"upgradeUrl": "https://app.octolens.com/me/upgrade?src=agents",
"upgradeCommand": "octolens upgrade"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found",
"status": 404,
"details": [
"<unknown>"
],
"upgradeUrl": "https://app.octolens.com/me/upgrade?src=agents",
"upgradeCommand": "octolens upgrade"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found",
"status": 404,
"details": [
"<unknown>"
],
"upgradeUrl": "https://app.octolens.com/me/upgrade?src=agents",
"upgradeCommand": "octolens upgrade"
}
}Authorizations
Clerk API key. Create one in Settings → API Keys. Pass as Authorization: Bearer <key>. Keys carry a scope — read, write (implies read) or admin (implies write) — chosen when the key is minted. Each operation's security requirement (and its x-required-scope extension) names the minimum scope it needs; request the least-privileged key that covers the operations you call.
Body
One or many candidates, measured concurrently under one time budget.
1 - 10 elementsShow child attributes
Show child attributes
Was this page helpful?
curl --request POST \
--url https://app.octolens.com/api/v2/keywords/estimate \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"candidates": [
{
"keyword": "<string>",
"additionalTerms": "<string>",
"additionalTermsAndOr": true,
"excludeWords": "<string>"
}
]
}
'import requests
url = "https://app.octolens.com/api/v2/keywords/estimate"
payload = { "candidates": [
{
"keyword": "<string>",
"additionalTerms": "<string>",
"additionalTermsAndOr": True,
"excludeWords": "<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({
candidates: [
{
keyword: '<string>',
additionalTerms: '<string>',
additionalTermsAndOr: true,
excludeWords: '<string>'
}
]
})
};
fetch('https://app.octolens.com/api/v2/keywords/estimate', 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://app.octolens.com/api/v2/keywords/estimate",
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([
'candidates' => [
[
'keyword' => '<string>',
'additionalTerms' => '<string>',
'additionalTermsAndOr' => true,
'excludeWords' => '<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://app.octolens.com/api/v2/keywords/estimate"
payload := strings.NewReader("{\n \"candidates\": [\n {\n \"keyword\": \"<string>\",\n \"additionalTerms\": \"<string>\",\n \"additionalTermsAndOr\": true,\n \"excludeWords\": \"<string>\"\n }\n ]\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://app.octolens.com/api/v2/keywords/estimate")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"candidates\": [\n {\n \"keyword\": \"<string>\",\n \"additionalTerms\": \"<string>\",\n \"additionalTermsAndOr\": true,\n \"excludeWords\": \"<string>\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.octolens.com/api/v2/keywords/estimate")
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 \"candidates\": [\n {\n \"keyword\": \"<string>\",\n \"additionalTerms\": \"<string>\",\n \"additionalTermsAndOr\": true,\n \"excludeWords\": \"<string>\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"estimates": [
{
"keyword": "<string>",
"volume": {
"low": 4503599627370495,
"high": 4503599627370495,
"band": "quiet",
"basis": "analog",
"window": "30d",
"openEnded": true,
"perSource": [
{
"source": "<string>",
"count": 4503599627370495,
"window": "30d",
"factor": 123,
"low": 4503599627370495,
"high": 4503599627370495,
"openEnded": true
}
],
"unverifiedSources": [
"<string>"
],
"unmeasuredSources": [
{
"source": "<string>",
"typicalShare": 123,
"reason": "<string>"
}
],
"caveats": [
"<string>"
],
"prior": {
"source": "google-trends",
"monthly": 4503599627370495,
"low": 4503599627370495,
"high": 4503599627370495
},
"disagreement": true
},
"bareVolume": {
"low": 4503599627370495,
"high": 4503599627370495,
"band": "quiet",
"basis": "analog",
"window": "30d",
"openEnded": true,
"perSource": [
{
"source": "<string>",
"count": 4503599627370495,
"window": "30d",
"factor": 123,
"low": 4503599627370495,
"high": 4503599627370495,
"openEnded": true
}
],
"unverifiedSources": [
"<string>"
],
"unmeasuredSources": [
{
"source": "<string>",
"typicalShare": 123,
"reason": "<string>"
}
],
"caveats": [
"<string>"
],
"prior": {
"source": "google-trends",
"monthly": 4503599627370495,
"low": 4503599627370495,
"high": 4503599627370495
},
"disagreement": true
},
"variant": {
"sampleSize": 4503599627370495,
"anchoredShare": 0.5,
"excludedShare": 0.5,
"retainedShare": 0.5,
"collisionRatio": 0.5
},
"flagged": true
}
],
"calibration": {
"fittedAt": "<string>"
},
"elapsedMs": 4503599627370495
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found",
"status": 404,
"details": [
"<unknown>"
],
"upgradeUrl": "https://app.octolens.com/me/upgrade?src=agents",
"upgradeCommand": "octolens upgrade"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found",
"status": 404,
"details": [
"<unknown>"
],
"upgradeUrl": "https://app.octolens.com/me/upgrade?src=agents",
"upgradeCommand": "octolens upgrade"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found",
"status": 404,
"details": [
"<unknown>"
],
"upgradeUrl": "https://app.octolens.com/me/upgrade?src=agents",
"upgradeCommand": "octolens upgrade"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found",
"status": 404,
"details": [
"<unknown>"
],
"upgradeUrl": "https://app.octolens.com/me/upgrade?src=agents",
"upgradeCommand": "octolens upgrade"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found",
"status": 404,
"details": [
"<unknown>"
],
"upgradeUrl": "https://app.octolens.com/me/upgrade?src=agents",
"upgradeCommand": "octolens upgrade"
}
}