Add keyword to topic cluster
curl --request POST \
--url https://api.snowseo.com/v3/topic-clusters/{topicId}/keywords \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"teamId": "<string>",
"keyword": "<string>",
"countryId": "<string>",
"languageId": "<string>",
"locationCriteriaId": 123,
"locationCanonicalName": "<string>",
"items": [
{
"keyword": "<string>",
"countryId": "<string>",
"languageId": "<string>",
"locationCriteriaId": 123,
"locationCanonicalName": "<string>"
}
],
"track": true
}
'import requests
url = "https://api.snowseo.com/v3/topic-clusters/{topicId}/keywords"
payload = {
"teamId": "<string>",
"keyword": "<string>",
"countryId": "<string>",
"languageId": "<string>",
"locationCriteriaId": 123,
"locationCanonicalName": "<string>",
"items": [
{
"keyword": "<string>",
"countryId": "<string>",
"languageId": "<string>",
"locationCriteriaId": 123,
"locationCanonicalName": "<string>"
}
],
"track": True
}
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({
teamId: '<string>',
keyword: '<string>',
countryId: '<string>',
languageId: '<string>',
locationCriteriaId: 123,
locationCanonicalName: '<string>',
items: [
{
keyword: '<string>',
countryId: '<string>',
languageId: '<string>',
locationCriteriaId: 123,
locationCanonicalName: '<string>'
}
],
track: true
})
};
fetch('https://api.snowseo.com/v3/topic-clusters/{topicId}/keywords', 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.snowseo.com/v3/topic-clusters/{topicId}/keywords",
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([
'teamId' => '<string>',
'keyword' => '<string>',
'countryId' => '<string>',
'languageId' => '<string>',
'locationCriteriaId' => 123,
'locationCanonicalName' => '<string>',
'items' => [
[
'keyword' => '<string>',
'countryId' => '<string>',
'languageId' => '<string>',
'locationCriteriaId' => 123,
'locationCanonicalName' => '<string>'
]
],
'track' => true
]),
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.snowseo.com/v3/topic-clusters/{topicId}/keywords"
payload := strings.NewReader("{\n \"teamId\": \"<string>\",\n \"keyword\": \"<string>\",\n \"countryId\": \"<string>\",\n \"languageId\": \"<string>\",\n \"locationCriteriaId\": 123,\n \"locationCanonicalName\": \"<string>\",\n \"items\": [\n {\n \"keyword\": \"<string>\",\n \"countryId\": \"<string>\",\n \"languageId\": \"<string>\",\n \"locationCriteriaId\": 123,\n \"locationCanonicalName\": \"<string>\"\n }\n ],\n \"track\": true\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.snowseo.com/v3/topic-clusters/{topicId}/keywords")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"teamId\": \"<string>\",\n \"keyword\": \"<string>\",\n \"countryId\": \"<string>\",\n \"languageId\": \"<string>\",\n \"locationCriteriaId\": 123,\n \"locationCanonicalName\": \"<string>\",\n \"items\": [\n {\n \"keyword\": \"<string>\",\n \"countryId\": \"<string>\",\n \"languageId\": \"<string>\",\n \"locationCriteriaId\": 123,\n \"locationCanonicalName\": \"<string>\"\n }\n ],\n \"track\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.snowseo.com/v3/topic-clusters/{topicId}/keywords")
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 \"teamId\": \"<string>\",\n \"keyword\": \"<string>\",\n \"countryId\": \"<string>\",\n \"languageId\": \"<string>\",\n \"locationCriteriaId\": 123,\n \"locationCanonicalName\": \"<string>\",\n \"items\": [\n {\n \"keyword\": \"<string>\",\n \"countryId\": \"<string>\",\n \"languageId\": \"<string>\",\n \"locationCriteriaId\": 123,\n \"locationCanonicalName\": \"<string>\"\n }\n ],\n \"track\": true\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"keywordId": "<string>",
"countryId": "<string>",
"languageId": "<string>",
"locationCriteriaId": 123,
"locationCanonicalName": "<string>",
"requested": 123,
"added": 123,
"alreadyHere": 123,
"skipped": 123,
"keywordsNotTracked": 123
}{
"success": true,
"error": "<string>",
"code": "<string>",
"message": "<string>",
"reference": "<string>"
}{
"success": true,
"error": "<string>",
"code": "<string>",
"message": "<string>",
"reference": "<string>"
}{
"success": true,
"error": "<string>",
"code": "<string>",
"message": "<string>",
"reference": "<string>"
}{
"success": true,
"error": "<string>",
"code": "<string>",
"message": "<string>",
"reference": "<string>"
}Topic Clusters
Topic Keywords
Add a keyword to a topic cluster.
POST
/
v3
/
topic-clusters
/
{topicId}
/
keywords
Add keyword to topic cluster
curl --request POST \
--url https://api.snowseo.com/v3/topic-clusters/{topicId}/keywords \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"teamId": "<string>",
"keyword": "<string>",
"countryId": "<string>",
"languageId": "<string>",
"locationCriteriaId": 123,
"locationCanonicalName": "<string>",
"items": [
{
"keyword": "<string>",
"countryId": "<string>",
"languageId": "<string>",
"locationCriteriaId": 123,
"locationCanonicalName": "<string>"
}
],
"track": true
}
'import requests
url = "https://api.snowseo.com/v3/topic-clusters/{topicId}/keywords"
payload = {
"teamId": "<string>",
"keyword": "<string>",
"countryId": "<string>",
"languageId": "<string>",
"locationCriteriaId": 123,
"locationCanonicalName": "<string>",
"items": [
{
"keyword": "<string>",
"countryId": "<string>",
"languageId": "<string>",
"locationCriteriaId": 123,
"locationCanonicalName": "<string>"
}
],
"track": True
}
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({
teamId: '<string>',
keyword: '<string>',
countryId: '<string>',
languageId: '<string>',
locationCriteriaId: 123,
locationCanonicalName: '<string>',
items: [
{
keyword: '<string>',
countryId: '<string>',
languageId: '<string>',
locationCriteriaId: 123,
locationCanonicalName: '<string>'
}
],
track: true
})
};
fetch('https://api.snowseo.com/v3/topic-clusters/{topicId}/keywords', 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.snowseo.com/v3/topic-clusters/{topicId}/keywords",
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([
'teamId' => '<string>',
'keyword' => '<string>',
'countryId' => '<string>',
'languageId' => '<string>',
'locationCriteriaId' => 123,
'locationCanonicalName' => '<string>',
'items' => [
[
'keyword' => '<string>',
'countryId' => '<string>',
'languageId' => '<string>',
'locationCriteriaId' => 123,
'locationCanonicalName' => '<string>'
]
],
'track' => true
]),
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.snowseo.com/v3/topic-clusters/{topicId}/keywords"
payload := strings.NewReader("{\n \"teamId\": \"<string>\",\n \"keyword\": \"<string>\",\n \"countryId\": \"<string>\",\n \"languageId\": \"<string>\",\n \"locationCriteriaId\": 123,\n \"locationCanonicalName\": \"<string>\",\n \"items\": [\n {\n \"keyword\": \"<string>\",\n \"countryId\": \"<string>\",\n \"languageId\": \"<string>\",\n \"locationCriteriaId\": 123,\n \"locationCanonicalName\": \"<string>\"\n }\n ],\n \"track\": true\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.snowseo.com/v3/topic-clusters/{topicId}/keywords")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"teamId\": \"<string>\",\n \"keyword\": \"<string>\",\n \"countryId\": \"<string>\",\n \"languageId\": \"<string>\",\n \"locationCriteriaId\": 123,\n \"locationCanonicalName\": \"<string>\",\n \"items\": [\n {\n \"keyword\": \"<string>\",\n \"countryId\": \"<string>\",\n \"languageId\": \"<string>\",\n \"locationCriteriaId\": 123,\n \"locationCanonicalName\": \"<string>\"\n }\n ],\n \"track\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.snowseo.com/v3/topic-clusters/{topicId}/keywords")
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 \"teamId\": \"<string>\",\n \"keyword\": \"<string>\",\n \"countryId\": \"<string>\",\n \"languageId\": \"<string>\",\n \"locationCriteriaId\": 123,\n \"locationCanonicalName\": \"<string>\",\n \"items\": [\n {\n \"keyword\": \"<string>\",\n \"countryId\": \"<string>\",\n \"languageId\": \"<string>\",\n \"locationCriteriaId\": 123,\n \"locationCanonicalName\": \"<string>\"\n }\n ],\n \"track\": true\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"keywordId": "<string>",
"countryId": "<string>",
"languageId": "<string>",
"locationCriteriaId": 123,
"locationCanonicalName": "<string>",
"requested": 123,
"added": 123,
"alreadyHere": 123,
"skipped": 123,
"keywordsNotTracked": 123
}{
"success": true,
"error": "<string>",
"code": "<string>",
"message": "<string>",
"reference": "<string>"
}{
"success": true,
"error": "<string>",
"code": "<string>",
"message": "<string>",
"reference": "<string>"
}{
"success": true,
"error": "<string>",
"code": "<string>",
"message": "<string>",
"reference": "<string>"
}{
"success": true,
"error": "<string>",
"code": "<string>",
"message": "<string>",
"reference": "<string>"
}Add a keyword to an existing topic cluster. If the keyword doesn’t exist yet, it will be created automatically. A keyword can belong to only one topic per team; adding one that already sits in another topic returns
409 (single mode) or counts as skipped (bulk mode).
Body: keyword (required in single mode), or items (bulk mode, one object per keyword). In single mode, countryId, languageId, locationCriteriaId, and locationCanonicalName are optional top-level fields; in bulk mode they go inside each items[] entry, and top-level values are not applied. track is always top-level (also start rank tracking, which uses your plan’s tracking allowance; a tracking failure does not undo the add). Single mode returns { success, keywordId, countryId, languageId, ... }. Bulk mode returns { success, requested, added, alreadyHere, skipped, keywordsNotTracked }.Authorizations
bearerAuthapiKey
Your SnowSEO API key (sk_...).
Path Parameters
Topic cluster ID
Body
application/json
Team ID
Keyword text to add
Google Ads location criteria ID for city/region-level tracking. Omit/null for country-only.
Canonical location name paired with locationCriteriaId.
Bulk mode: one item per keyword. Items already linked to another topic are skipped.
Show child attributes
Show child attributes
Whether to also track this keyword for rank monitoring
Was this page helpful?

