API Platform
Trends API
Google Trends data for up to five keywords — interest over time, by region, related searches — plus age and gender demographics, at a flat price per request.
Find out how search interest in up to five keywords moves over time, where it comes from, what people search alongside it and who searches. One endpoint serves four reports, selected with the type parameter. Interest, regions and related searches are Google Trends data; demographics are a clickstream-based estimate, because Google Trends has none.
Requests longer than 60 seconds
This endpoint runs requests as tasks. A request that runs longer than 60 seconds returns 202 with a task_id; collect the result from GET /v1/tasks/{task_id}. Every response, including a normal 200, carries the task id in the X-Task-Id header. Use ?wait= to change how long the request waits, and an Idempotency-Key header to retry safely.
Authentication
| Header | Type | Description |
|---|---|---|
SEMUST-API-USER | string | Your API user's username |
SEMUST-API-PASSWORD | string | Your API user's password |
Accept-Language | string | Optional. Send tr to receive error messages in Turkish |
Create API credentials from the API Access page.
SEMUST-API-USER: your_username
SEMUST-API-PASSWORD: your_password
Content-Type: application/jsonReport Types
type | Returns | Keywords | Price |
|---|---|---|---|
interest (default) | Interest over time, one point per interval, plus each keyword's average | 1–5 | $0.0121 |
regions | Interest by region (provinces for Turkey), and how the keywords split each region | 1–5 | $0.0121 |
related | Top and rising related searches and related topics | 1 | $0.0121 |
demographics | Interest by age group and gender | 1–5 | $0.00264 |
The price is per request: comparing five keywords costs the same as one, whatever the period or search type.
{
"keywords": ["iphone 17", "samsung s26"],
"period": "past_12_months"
}{
"type": "related",
"keywords": ["iphone 17"]
}Request Parameters
A parameter that does not apply to the chosen type is rejected rather than ignored, and a misspelled one is named in the error with a suggestion. A JSON null is treated as "not sent".
Body
typestringdefault: interestinterest, regions, related or demographics.
keywordsstring[]Required. 1–5 keywords to compare (related: exactly one). Each up to 100 characters, without commas. Repeats that differ only in case are dropped, and the first spelling is kept.
countrystringdefault: TRTwo-letter country code. See Markets below.
languagestringNot for demographics. The language of region names and related topics. Defaults to tr for TR and en elsewhere. It does not change which searches are counted.
periodstringdefault: past_12_monthspast_day, past_7_days, past_30_days, past_90_days, past_12_months or past_5_years.
search_typestringdefault: webNot for demographics. Where searches are counted: web, news, images, youtube or shopping.
idempotency_keystringOptional, up to 128 characters. Repeating a request with the same key returns the same task — its 202 while it runs, its stored result once it finishes — and it is charged once. The key is reserved until 72 hours after the task finishes. Same as the Idempotency-Key header; if you send both, this field wins.
Query
waitintegerdefault: 60Seconds to wait for the result, 0–80. If the request is still running after that, the response is 202 with a task_id. 0 answers 202 at once.
Headers
Idempotency-KeystringOptional. Works like idempotency_key above; if you send both, the body field wins.
GET /v1/trends/markets lists every supported country and language. It is free.
A request still running after 60 seconds returns 202. The call() helper on Tasks & Long-Running Requests polls until the result is ready.
curl -X POST https://data.semust.com/v1/trends \
-H "SEMUST-API-USER: your_username" \
-H "SEMUST-API-PASSWORD: your_password" \
-H "Content-Type: application/json" \
-d '{"keywords": ["iphone 17", "samsung s26"], "period": "past_90_days"}'import requests
response = requests.post(
"https://data.semust.com/v1/trends",
headers={
"SEMUST-API-USER": "your_username",
"SEMUST-API-PASSWORD": "your_password",
},
json={"keywords": ["iphone 17", "samsung s26"]},
timeout=90, # longer than ?wait= (60 seconds by default)
)
data = response.json()
if response.status_code == 202:
# Still running. Collect it later from data["poll_url"],
# or use the call() helper from the Tasks page.
raise SystemExit(f"Task {data['task_id']} is still running")
if response.status_code != 200:
raise SystemExit(f"{data['code']}: {data['error']} {data.get('hint', '')}")
# values line up with data["keywords"]
for point in data["items"]:
print(point["time"], dict(zip(data["keywords"], point["values"])))Response
interest, regions and demographics return rows in items; related returns one data object. Every per-keyword list — values, averages, interest, share — is in the same order as keywords in the envelope, so you can read it by position.
Interest is relative, not a search count. In interest, 100 is the highest point of any keyword in the period, so the keywords can be compared directly. In regions, interest puts each keyword on its own scale (100 is that keyword's strongest region) and share says how the keywords split a region's searches, in percent. Demographic values are 0–100 against the keyword's largest group.
A keyword with no data is a valid answer: items is empty and values are null. It is billed like any other request.
The examples below are real responses.
{
"success": true,
"type": "interest",
"keywords": ["iphone 17", "samsung s26"],
"country": "TR",
"language": "tr",
"period": "past_12_months",
"search_type": "web",
"averages": [56, 7],
"count": 53,
"items": [
{ "time": "2025-11-02T00:00:00Z", "values": [57, 2], "partial": false },
{ "time": "2025-11-09T00:00:00Z", "values": [60, 2], "partial": false },
{ "time": "2026-11-01T00:00:00Z", "values": [46, 9], "partial": true }
],
"cost": 0.0121,
"cached": false,
"request_id": "0b7e6f1a-3c2d-4e5f-9a8b-1c2d3e4f5a6b"
}{
"success": true,
"type": "regions",
"keywords": ["iphone 17", "samsung s26"],
"country": "TR",
"language": "tr",
"period": "past_12_months",
"search_type": "web",
"count": 81,
"items": [
{ "region": "Tunceli", "region_code": "TR-62", "interest": [100, 100], "share": [87, 13] },
{ "region": "Kırıkkale", "region_code": "TR-71", "interest": [96, 65], "share": [91, 9] },
{ "region": "Kocaeli", "region_code": "TR-41", "interest": [94, 65], "share": [90, 10] }
],
"cost": 0.0121,
"cached": false,
"request_id": "5d4c3b2a-1f0e-4d9c-8b7a-6f5e4d3c2b1a"
}{
"success": true,
"type": "related",
"keywords": ["iphone 17"],
"country": "TR",
"language": "tr",
"period": "past_12_months",
"search_type": "web",
"data": {
"top_queries": [
{ "query": "iphone 17 pro", "value": 100 },
{ "query": "17 pro", "value": 94 }
],
"rising_queries": [
{ "query": "iphone 17e", "value": 1650 }
],
"top_topics": [
{ "topic": "iPhone", "category": "Cep telefonu", "value": 100 },
{ "topic": "iPhone 17", "category": "Cep telefonu", "value": 69 }
],
"rising_topics": [
{ "topic": "Apple iPhone 17 Pro Max", "category": "Cep telefonu", "value": 223050 }
]
},
"cost": 0.0121,
"cached": false,
"request_id": "9a8b7c6d-5e4f-4a3b-2c1d-0e9f8a7b6c5d"
}{
"success": true,
"type": "demographics",
"keywords": ["iphone 17"],
"country": "TR",
"period": "past_12_months",
"count": 1,
"items": [
{
"keyword": "iphone 17",
"age": [
{ "group": "18-24", "value": 37 },
{ "group": "25-34", "value": 97 },
{ "group": "35-44", "value": 100 },
{ "group": "45-54", "value": 41 }
],
"gender": [
{ "group": "female", "value": 89 },
{ "group": "male", "value": 100 }
]
}
],
"cost": 0.00264,
"cached": false,
"request_id": "1e2d3c4b-5a6f-4e7d-8c9b-0a1f2e3d4c5b"
}Response Fields Reference
Envelope
| Field | Type | Description |
|---|---|---|
keywords | string[] | The keywords as run, after duplicates were removed. Every per-keyword list follows this order |
averages | (integer | null)[] | interest only. Each keyword's average over the period |
count | integer | Rows in items |
interest row
| Field | Type | Description |
|---|---|---|
time | string | Start of the interval, ISO 8601 in UTC. The interval follows the period: minutes for past_day, hours for past_7_days, days or weeks for the longer periods (weeks for past_12_months) |
values | (integer | null)[] | Interest per keyword, 0–100 |
partial | boolean | true for an interval still in progress; its value will change |
regions row
| Field | Type | Description |
|---|---|---|
region, region_code | string, string | null | Region name in language, and its ISO 3166-2 code (TR-34) |
interest | (integer | null)[] | Each keyword's interest here, 0–100 on its own scale |
share | (integer | null)[] | null | How the keywords split this region's searches, in percent. null for a single keyword |
Rows are sorted by the first keyword's interest, highest first.
related → data
top_queries and rising_queries are {query, value}; top_topics and rising_topics are {topic, category, value}, where category is the kind of topic in language. In the top lists value is relative interest, 0–100. In the rising lists it is growth over the previous period, in percent.
demographics row
keyword, age and gender, each a list of {group, value}. A keyword without data has empty lists.
Errors
Every error carries a stable code, a readable error, and retryable; where it helps also field, hint and allowed. Send Accept-Language: tr for Turkish messages. You are never charged for an error. The full list, shared with the other APIs, is on Error Codes. Specific to this endpoint:
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 202 | — | Not an error: still running after wait seconds | Poll GET /v1/tasks/{task_id}; see Tasks |
| 400 | KEYWORDS_REQUIRED | No keywords, or an empty list | Send 1–5 keywords |
| 400 | TOO_MANY_KEYWORDS | More than 5 keywords, or more than one for related | Split the request |
| 400 | INVALID_PARAMETER | e.g. a keyword over 100 characters or with a comma, or an unknown period; field names it and allowed lists the values | Fix the field |
| 400 | UNSUPPORTED_MARKET | Unknown country or language | GET /v1/trends/markets |
{
"error": "type=related accepts at most 1 keyword(s)",
"code": "TOO_MANY_KEYWORDS",
"field": "keywords",
"hint": "interest, regions and demographics compare up to 5 keywords; related takes one keyword per request",
"retryable": false
}{
"error": "'language' cannot be used with type=demographics",
"code": "PARAMETER_NOT_ALLOWED",
"field": "language",
"hint": "Accepted by type: interest, regions, related",
"retryable": false
}Credits & Limits
| Request | Cost |
|---|---|
interest, regions or related | $0.0121 |
demographics | $0.00264 |
The price does not depend on the number of keywords, the period or the search type. An answer with no data is billed at the same price.
Results are cached — 1 hour for past_day, 6 hours for past_7_days, 24 hours for the longer periods. A cached answer is marked "cached": true and billed at the same price as a live one.
Per-minute and per-day limits are set on your account (429 RATE_LIMIT_EXCEEDED). At most 5 requests per API user run at the same time; more get 429 TOO_MANY_CONCURRENT_REQUESTS with Retry-After. A request that has returned 202 keeps its place until its task finishes. At most 100 tasks per API user can be unfinished at once (429 TOO_MANY_PENDING_TASKS). Polling has its own limit and does not count against any of these; see Tasks.
