Full endpoint reference available on Swagger.
Quick Start
Create a clustering order with the minimum required parameters:import os
import requests
API_KEY = os.environ["KWI_API_KEY"]
BASE_URL = "https://api.keywordinsights.ai"
HEADERS = {"X-API-Key": API_KEY}
payload = {
"project_name": "My first cluster",
"keywords": ["keyword clustering", "keyword grouping", "cluster keywords"],
"search_volumes": [2300, 1200, 900],
"language": "en",
"location": "United States",
"insights": ["cluster", "context"],
"folder_id": "<your_folder_id>",
}
response = requests.post(
f"{BASE_URL}/api/keywords-insights/order/",
headers=HEADERS,
json=payload,
)
data = response.json()
print(data)
# {"status": true, "order_id": "2879fa0b-...", "cost": 150}
const API_KEY = process.env.KWI_API_KEY;
const BASE_URL = "https://api.keywordinsights.ai";
const HEADERS = {
"X-API-Key": API_KEY,
"Content-Type": "application/json",
};
const payload = {
project_name: "My first cluster",
keywords: ["keyword clustering", "keyword grouping", "cluster keywords"],
search_volumes: [2300, 1200, 900],
language: "en",
location: "United States",
insights: ["cluster", "context"],
folder_id: "<your_folder_id>",
};
async function createOrder() {
const response = await fetch(`${BASE_URL}/api/keywords-insights/order/`, {
method: "POST",
headers: HEADERS,
body: JSON.stringify(payload),
});
const data = await response.json();
console.log(data);
}
createOrder();
Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /api/keywords-insights/order/ | Create a clustering order |
| GET | /api/keywords-insights/order/?order_id={id} | Check order status |
| GET | /api/keywords-insights/order/json/{order_id}/ | Get results as JSON (paginated) |
| GET | /api/keywords-insights/order/xlsx/{order_id}/ | Export results as XLSX |
| GET | /api/keywords-insights/orders/?n_orders={n} | List recent orders |
| GET | /api/keywords-insights/order/cost/?n_keywords={n}&insights=cluster | Estimate cost |
| GET | /api/keywords-insights/languages/ | Supported languages |
| GET | /api/keywords-insights/locations/ | Supported locations |
Parameters
Required
| Parameter | Type | Description |
|---|---|---|
project_name | string | Name shown in the dashboard |
keywords | string[] | List of keywords to cluster (min 5, max 200,000) |
search_volumes | number[] | Search volume per keyword (same length as keywords) |
language | string | Language code (e.g. en). See /api/keywords-insights/languages/ |
location | string | Location name (e.g. United States). See /api/keywords-insights/locations/ |
insights | string[] | Insight types: cluster, context, rank |
folder_id | string | Dashboard folder ID to save the project into. Retrieve from the browser URL in the KWI dashboard |
Optional
| Parameter | Type | Default | Description |
|---|---|---|---|
clustering_method | string | volume | volume or agglomerative |
grouping_accuracy | integer | 4 | SERP overlap threshold (1–7). Higher = stricter clusters |
hub_creation_method | string | medium | Topical cluster similarity: soft, medium, hard |
device | string | desktop | SERP device: desktop, mobile, tablet |
mobile_type | string | — | iphone or android (when device is mobile) |
tablet_type | string | — | ipad or android (when device is tablet) |
url | string | — | Domain URL for ranking. Required when rank is in insights |
Insight Types
Theinsights array controls what analysis runs:
cluster— Groups keywords by SERP similarity. Required for all clustering orders.context— Detects search intent (informational, commercial, transactional, navigational).rank— Tracks your domain’s ranking for each keyword. Requires theurlparameter.
"insights": ["cluster", "rank", "context"]
"insights": ["context"]
Customizations
Clustering Method
volume(default) — Groups keywords based on shared SERP URLs. Controlled bygrouping_accuracy(1 = loose, 7 = strict). Best for most use cases.agglomerative— Uses hierarchical clustering for larger keyword sets. Better for discovering broader topic relationships.
Topical Clusters
Thehub_creation_method controls how tightly related keywords must be to form a topical cluster:
soft— Broad grouping, more keywords per topical clustermedium— Balanced (recommended)hard— Strict grouping, fewer but more focused clusters
Checking Order Status
Orders process asynchronously. Poll the status endpoint untilstatus is "done":
import os
import requests
API_KEY = os.environ["KWI_API_KEY"]
BASE_URL = "https://api.keywordinsights.ai"
HEADERS = {"X-API-Key": API_KEY}
order_id = "2879fa0b-deae-4aab-ad87-fd8fb8db9717"
response = requests.get(
f"{BASE_URL}/api/keywords-insights/order/",
headers=HEADERS,
params={"order_id": order_id},
)
data = response.json()
print(data["status"]) # "confirmed", "processing", or "done"
print(data["progress"]) # 0.0 to 1.0
const API_KEY = process.env.KWI_API_KEY;
const BASE_URL = "https://api.keywordinsights.ai";
const HEADERS = { "X-API-Key": API_KEY };
const orderId = "2879fa0b-deae-4aab-ad87-fd8fb8db9717";
async function checkStatus() {
const url = new URL(`${BASE_URL}/api/keywords-insights/order/`);
url.searchParams.append("order_id", orderId);
const response = await fetch(url, { headers: HEADERS });
const data = await response.json();
console.log(data.status); // "confirmed", "processing", or "done"
console.log(data.progress); // 0.0 to 1.0
}
checkStatus();
{
"status": "done",
"progress": 1.0,
"order_id": "2879fa0b-...",
"results_files": {
"xlsx": "https://api.keywordinsights.ai/api/keywords-insights/order/xlsx/2879fa0b-.../",
"google_sheets": "https://docs.google.com/spreadsheets/d/.../copy"
}
}
Retrieving Results as JSON
Get paginated cluster data:import os
import requests
API_KEY = os.environ["KWI_API_KEY"]
BASE_URL = "https://api.keywordinsights.ai"
HEADERS = {"X-API-Key": API_KEY}
order_id = "2879fa0b-deae-4aab-ad87-fd8fb8db9717"
response = requests.get(
f"{BASE_URL}/api/keywords-insights/order/json/{order_id}/",
headers=HEADERS,
params={
"page_size": 50,
"page_number": 1,
"sort_by": "search_volume",
"ascending": False,
},
)
data = response.json()
clusters = data["result"]["payload"]["clusters"]
for cluster in clusters:
print(f"{cluster['name']} — {cluster['number_of_keywords']} keywords, vol: {cluster['search_volume']}")
const API_KEY = process.env.KWI_API_KEY;
const BASE_URL = "https://api.keywordinsights.ai";
const HEADERS = { "X-API-Key": API_KEY };
const orderId = "2879fa0b-deae-4aab-ad87-fd8fb8db9717";
async function getResults() {
const url = new URL(`${BASE_URL}/api/keywords-insights/order/json/${orderId}/`);
url.searchParams.append("page_size", "50");
url.searchParams.append("page_number", "1");
url.searchParams.append("sort_by", "search_volume");
url.searchParams.append("ascending", "false");
const response = await fetch(url, { headers: HEADERS });
const data = await response.json();
const clusters = data.result.payload.clusters;
clusters.forEach((cluster) => {
console.log(
`${cluster.name} — ${cluster.number_of_keywords} keywords, vol: ${cluster.search_volume}`
);
});
}
getResults();
| Parameter | Type | Default | Description |
|---|---|---|---|
page_size | integer | 50 | Results per page (max 1,000) |
page_number | integer | 1 | Page number |
sort_by | string | search_volume | Sort field (e.g. search_volume, keyword, cluster_id) |
ascending | boolean | false | Sort direction |
filter_id | string | — | Filter ID from the filters endpoint |
Exporting as XLSX
Download results as an Excel file:import os
import requests
API_KEY = os.environ["KWI_API_KEY"]
BASE_URL = "https://api.keywordinsights.ai"
HEADERS = {"X-API-Key": API_KEY}
order_id = "2879fa0b-deae-4aab-ad87-fd8fb8db9717"
response = requests.get(
f"{BASE_URL}/api/keywords-insights/order/xlsx/{order_id}/",
headers=HEADERS,
)
# Save the XLSX file
with open("clusters.xlsx", "wb") as f:
f.write(response.content)
const fs = require("fs");
const API_KEY = process.env.KWI_API_KEY;
const BASE_URL = "https://api.keywordinsights.ai";
const HEADERS = { "X-API-Key": API_KEY };
const orderId = "2879fa0b-deae-4aab-ad87-fd8fb8db9717";
async function downloadXlsx() {
const response = await fetch(`${BASE_URL}/api/keywords-insights/order/xlsx/${orderId}/`, {
headers: HEADERS,
});
const buffer = await response.arrayBuffer();
fs.writeFileSync("clusters.xlsx", Buffer.from(buffer));
}
downloadXlsx();
Estimating Cost
Check how many credits an order will cost before creating it:response = requests.get(
f"{BASE_URL}/api/keywords-insights/order/cost/",
headers=HEADERS,
params={"n_keywords": 500, "insights": ["cluster", "context"]},
)
print(response.json()) # {"cost": 1000}
async function estimateCost() {
const url = new URL(`${BASE_URL}/api/keywords-insights/order/cost/`);
url.searchParams.append("n_keywords", "500");
url.searchParams.append("insights", "cluster");
url.searchParams.append("insights", "context");
const response = await fetch(url, { headers: HEADERS });
const data = await response.json();
console.log(data); // { cost: 1000 }
}
estimateCost();
| Insights | Cost per keyword |
|---|---|
cluster only | 1 credit |
cluster + context | 2 credits |
cluster + rank | 2 credits |
cluster + context + rank | 3 credits |
Complete Example
End-to-end workflow: create an order, wait for completion, and download results.import os
import time
import requests
API_KEY = os.environ["KWI_API_KEY"]
BASE_URL = "https://api.keywordinsights.ai"
HEADERS = {"X-API-Key": API_KEY}
def create_order(keywords, volumes):
"""Create a clustering order."""
response = requests.post(
f"{BASE_URL}/api/keywords-insights/order/",
headers=HEADERS,
json={
"project_name": "API automation example",
"keywords": keywords,
"search_volumes": volumes,
"language": "en",
"location": "United States",
"insights": ["cluster", "context"],
"folder_id": "<your_folder_id>",
"clustering_method": "volume",
"grouping_accuracy": 4,
"hub_creation_method": "medium",
},
)
response.raise_for_status()
return response.json()
def wait_for_completion(order_id):
"""Poll until the order is done."""
while True:
response = requests.get(
f"{BASE_URL}/api/keywords-insights/order/",
headers=HEADERS,
params={"order_id": order_id},
)
data = response.json()
print(f"Status: {data['status']} ({data['progress']:.0%})")
if data["status"] == "done":
return data
time.sleep(30)
def get_results(order_id):
"""Fetch all clusters as JSON."""
response = requests.get(
f"{BASE_URL}/api/keywords-insights/order/json/{order_id}/",
headers=HEADERS,
params={"page_size": 100, "page_number": 1},
)
response.raise_for_status()
return response.json()
# --- Run ---
keywords = ["best running shoes", "running shoes review", "top sneakers for running"]
volumes = [12000, 8500, 3200]
result = create_order(keywords, volumes)
order_id = result["order_id"]
print(f"Order created: {order_id} (cost: {result['cost']} credits)")
wait_for_completion(order_id)
data = get_results(order_id)
for cluster in data["result"]["payload"]["clusters"]:
print(f" {cluster['name']} — {cluster['number_of_keywords']} kw, vol: {cluster['search_volume']}")
const API_KEY = process.env.KWI_API_KEY;
const BASE_URL = "https://api.keywordinsights.ai";
const HEADERS = { "X-API-Key": API_KEY };
async function createOrder(keywords, volumes) {
const response = await fetch(`${BASE_URL}/api/keywords-insights/order/`, {
method: "POST",
headers: { ...HEADERS, "Content-Type": "application/json" },
body: JSON.stringify({
project_name: "API automation example",
keywords,
search_volumes: volumes,
language: "en",
location: "United States",
insights: ["cluster", "context"],
folder_id: "<your_folder_id>",
clustering_method: "volume",
grouping_accuracy: 4,
hub_creation_method: "medium",
}),
});
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
return await response.json();
}
async function waitForCompletion(orderId) {
while (true) {
const url = new URL(`${BASE_URL}/api/keywords-insights/order/`);
url.searchParams.append("order_id", orderId);
const response = await fetch(url, { headers: HEADERS });
const data = await response.json();
console.log(`Status: ${data.status} (${(data.progress * 100).toFixed(0)}%)`);
if (data.status === "done") return data;
await new Promise((r) => setTimeout(r, 30000));
}
}
async function getResults(orderId) {
const url = new URL(`${BASE_URL}/api/keywords-insights/order/json/${orderId}/`);
url.searchParams.append("page_size", "100");
url.searchParams.append("page_number", "1");
const response = await fetch(url, { headers: HEADERS });
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
return await response.json();
}
async function run() {
const keywords = ["best running shoes", "running shoes review", "top sneakers for running"];
const volumes = [12000, 8500, 3200];
const result = await createOrder(keywords, volumes);
const orderId = result.order_id;
console.log(`Order created: ${orderId} (cost: ${result.cost} credits)`);
await waitForCompletion(orderId);
const data = await getResults(orderId);
data.result.payload.clusters.forEach((cluster) => {
console.log(
` ${cluster.name} — ${cluster.number_of_keywords} kw, vol: ${cluster.search_volume}`
);
});
}
run();