API Platform
Keyword Data API
Get comprehensive keyword metrics including search volume, CPC, competition, and search intent for any keyword across multiple languages and countries.
Get comprehensive keyword metrics including search volume, CPC, competition, and search intent for any keyword across multiple languages and countries.
Product page: Keyword Data API.
Authentication
All requests require header-based authentication. Include your API credentials in every request.
| Header | Type | Description |
|---|---|---|
SEMUST-API-USER | string | Your API user's username |
SEMUST-API-PASSWORD | string | Your API user's password |
Create API credentials from the API Access page.
SEMUST-API-USER: your_username
SEMUST-API-PASSWORD: your_passwordRequest Parameters
Send a application/json body with the following parameters:
Body
keywordstringrequiredThe keyword to analyze (e.g., 'seo tools', 'best coffee shops')
countrystringdefault: USISO 3166-1 alpha-2 country code for geo-targeted data. Examples: "US", "GB", "DE", "TR".
languagestringdefault: enLanguage code (ISO 639-1). Controls the language of search results. Examples: "en", "tr", "de", "fr", "es".
limitintegerdefault: 100Maximum number of keyword results to return. Minimum: 1, Maximum: 1000. Higher limits may increase cost.
match_typestringdefault: broadKeyword match strategy. Options: "broad" (default — semantically related keywords), "phrase_match" (only keywords containing the seed phrase), "exact_match" (only the exact keyword, forces limit=1), "related" (pure semantic results without substring matches).
match_type
| Value | What it returns |
|---|---|
broad | Semantically related keywords (default) |
phrase_match | Only keywords containing the seed phrase |
exact_match | Only the exact keyword — forces limit to 1 |
related | Pure semantic results without substring matches |
{
"keyword": "seo tools",
"country": "US",
"language": "en",
"limit": 100,
"match_type": "broad"
}Code Examples
Examples of calling the Keyword Data API from different languages.
curl -X POST https://data.semust.com/v1/keyword-data \
-H "Content-Type: application/json" \
-H "SEMUST-API-USER: your_username" \
-H "SEMUST-API-PASSWORD: your_password" \
-d '{
"keyword": "seo tools",
"country": "US",
"language": "en",
"limit": 100,
"match_type": "broad"
}'import requests
response = requests.post(
"https://data.semust.com/v1/keyword-data",
headers={
"SEMUST-API-USER": "your_username",
"SEMUST-API-PASSWORD": "your_password",
},
json={
"keyword": "seo tools",
"country": "US",
"language": "en",
"limit": 100,
"match_type": "broad",
},
)
if response.status_code == 200:
data = response.json()
print(f"Success: {data['success']}")
print(f"Result Count: {data['result_count']}")
print(f"Cost: ${data['cost']:.6f}\n")
for kw in data.get('data', []):
print(f"Keyword: {kw['keyword']}")
print(f" Search Volume: {kw['search_volume']:,}")
print(f" CPC: ${kw['cpc']:.2f}")
print(f" Competition: {kw['competition']} ({kw['competition_level']})")
print(f" Intent: {kw['intent']}")
if kw.get('related_keywords'):
print(f" Related: {', '.join(kw['related_keywords'][:3])}")
else:
print(f"Error [{response.status_code}]: {response.text}")const response = await fetch("https://data.semust.com/v1/keyword-data", {
method: "POST",
headers: {
"Content-Type": "application/json",
"SEMUST-API-USER": "your_username",
"SEMUST-API-PASSWORD": "your_password",
},
body: JSON.stringify({
keyword: "seo tools",
country: "US",
language: "en",
limit: 100,
match_type: "broad",
}),
});
if (response.ok) {
const data = await response.json();
console.log(`Success: ${data.success}`);
console.log(`Cost: $${data.cost.toFixed(6)}`);
data.data?.forEach((kw) => {
console.log(`\n${kw.keyword}`);
console.log(` Search Volume: ${kw.search_volume.toLocaleString()}`);
console.log(` CPC: $${kw.cpc.toFixed(2)}`);
console.log(` Competition: ${kw.competition} (${kw.competition_level})`);
console.log(` Intent: ${kw.intent}`);
if (kw.related_keywords?.length) {
console.log(` Related: ${kw.related_keywords.slice(0, 3).join(", ")}`);
}
});
} else {
const text = await response.text();
console.error(`Error [${response.status}]: ${text}`);
}package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
payload := map[string]interface{}{
"keyword": "seo tools",
"country": "US",
"language": "en",
"limit": 100,
"match_type": "broad",
}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST",
"https://data.semust.com/v1/keyword-data",
bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("SEMUST-API-USER", "your_username")
req.Header.Set("SEMUST-API-PASSWORD", "your_password")
resp, err := (&http.Client{}).Do(req)
if err != nil {
fmt.Printf("Request failed: %v\n", err)
return
}
defer resp.Body.Close()
respBody, _ := io.ReadAll(resp.Body)
if resp.StatusCode == 200 {
var data map[string]interface{}
json.Unmarshal(respBody, &data)
fmt.Printf("Success: %v\n", data["success"])
fmt.Printf("Cost: $%.6f\n", data["cost"])
if keywordData, ok := data["data"].([]interface{}); ok {
for _, item := range keywordData {
kw := item.(map[string]interface{})
fmt.Printf("\n%v\n", kw["keyword"])
fmt.Printf(" Search Volume: %.0f\n", kw["search_volume"])
fmt.Printf(" CPC: $%.2f\n", kw["cpc"])
fmt.Printf(" Competition: %.0f (%v)\n", kw["competition"], kw["competition_level"])
fmt.Printf(" Intent: %v\n", kw["intent"])
}
}
} else {
fmt.Printf("Error %d: %s\n", resp.StatusCode, string(respBody))
}
}<?php
$payload = json_encode([
"keyword" => "seo tools",
"country" => "US",
"language" => "en",
"limit" => 100,
"match_type" => "broad",
]);
$ch = curl_init("https://data.semust.com/v1/keyword-data");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Content-Type: application/json",
"SEMUST-API-USER: your_username",
"SEMUST-API-PASSWORD: your_password",
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200) {
$data = json_decode($response, true);
echo "Success: " . ($data["success"] ? "true" : "false") . "\n";
echo "Cost: $" . number_format($data["cost"], 6) . "\n\n";
foreach ($data["data"] ?? [] as $kw) {
echo $kw["keyword"] . "\n";
echo " Search Volume: " . number_format($kw["search_volume"]) . "\n";
echo " CPC: $" . number_format($kw["cpc"], 2) . "\n";
echo " Competition: " . $kw["competition"] . " (" . $kw["competition_level"] . ")\n";
echo " Intent: " . $kw["intent"] . "\n\n";
}
} else {
echo "Error [{$httpCode}]: {$response}\n";
}Response
Returns a JSON object with keyword metrics and cost information.
Note
Results are sorted by search volume from highest to lowest.
Response Fields Reference
| Field | Type | Description |
|---|---|---|
success | boolean | Always true on successful response |
result_count | integer | Number of keywords returned |
cost | float | Actual cost charged in USD |
data | array | Array of keyword data objects |
Keyword Data Object
| Field | Type | Description |
|---|---|---|
keyword | string | The keyword term |
search_volume | integer | Monthly search volume |
cpc | float | Cost-per-click in USD |
competition | float | Competition level (0-100) |
country | string | Code |
language | string | Code |
intent | string | Search intent classification |
competition_level | string | Competition level: "low" (0-33), "medium" (34-66), or "high" (67-100) |
related_keywords | array | null | Related keyword suggestions (array of strings or null) |
{
"success": true,
"result_count": 3,
"cost": 0.01,
"data": [
{
"keyword": "seo tools",
"search_volume": 12100,
"cpc": 15.75,
"competition": 85,
"competition_level": "high",
"country": "us",
"language": "en",
"intent": "commercial",
"related_keywords": ["best seo tools", "free seo tools"]
},
{
"keyword": "best seo tools",
"search_volume": 8100,
"cpc": 12.50,
"competition": 72,
"competition_level": "high",
"country": "us",
"language": "en",
"intent": "commercial",
"related_keywords": null
},
{
"keyword": "free seo tools",
"search_volume": 5400,
"cpc": 8.25,
"competition": 45,
"competition_level": "medium",
"country": "us",
"language": "en",
"intent": "informational",
"related_keywords": null
}
]
}Pricing
Result-based pricing with upfront charge and automatic refund.
Formula
Cost = max($0.01, (result_count / 100) × $0.03)Pricing Examples
| Results | Calculation | Final Cost |
|---|---|---|
| 0-33 | Minimum charge | $0.01 |
| 50 | (50/100) × $0.03 | $0.015 |
| 100 | (100/100) × $0.03 | $0.03 |
| 150 | (150/100) × $0.03 | $0.045 |
| 200 | (200/100) × $0.03 | $0.06 |
Upfront Charge
You are charged $0.03 upfront (maximum price) and refunded the excess after processing completes. This ensures your balance is sufficient while only charging for actual results received.
Full Refund on Failures
If your request fails due to timeout, worker errors, or any server issues, you will receive a full refund automatically. You are only charged when the API successfully returns keyword data.
Supported Languages
Loading languages…
If a language code is not found, the system defaults to Turkish (tr).
Error Codes
All errors return a JSON object with a human-readable message and an error code.
| HTTP | Code | Meaning |
|---|---|---|
| 400 | INVALID_REQUEST | Request body is malformed or missing required fields |
| 400 | KEYWORD_REQUIRED | The keyword field is empty or missing |
| 400 | INVALID_MATCH_TYPE | The match_type value is not valid. Allowed values: broad, phrase_match, exact_match, related. |
| 401 | INVALID_API_KEY | Authentication failed — invalid username or password |
| 401 | INVALID_CREDENTIALS | Invalid username or password |
| 401 | CREDENTIALS_EXPIRED | API user credentials have expired |
| 402 | INSUFFICIENT_CREDITS | Your account does not have enough credits |
| 403 | IP_NOT_WHITELISTED | Your IP address is not whitelisted |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests — rate limit exceeded |
| 500 | INTERNAL_ERROR | An internal server error occurred |
| 502 | WORKER_FAILED | Failed to fetch keyword data |
| 504 | TIMEOUT | The request timed out (max 10 minutes) |
{
"error": "Your account does not have enough credits",
"code": "INSUFFICIENT_CREDITS"
}Credits & Rate Limits
Credits
Each request consumes credits from your balance. Cost may vary based on the mobile parameter. If your balance is insufficient, the API returns HTTP 402.
Rate Limits
Requests are subject to per-minute and per-day limits based on your plan. Exceeding limits returns HTTP 429.
