Keyword Research
Get search volume, CPC, competition, and difficulty for up to 100 keywords per request.
/v1/seo/keywordsUsage
const res = await fetch('https://api.yepapi.com/v1/seo/keywords', {
method: 'POST',
headers: {
'x-api-key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({ keywords: ['nextjs seo', 'vibe coding'], location_code: 2840 }),
});
const { data } = await res.json();
console.log(data);curl -X POST https://api.yepapi.com/v1/seo/keywords \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"keywords": ["nextjs seo", "vibe coding"], "location_code": 2840}'Request Body
| Parameter | Type | Required | Description | Default |
|---|---|---|---|---|
keywords | string[] | Yes | Array of keywords to analyze (max 100). See validity rules below. | — |
location_code | number | No | Target country code (e.g., 2840 for US). See Location Codes. | 2840 |
language | string | No | ISO language code (e.g., en, es) | "en" |
We support 90+ countries via location_code. See the full list at Location Codes Reference. You can also pass a location string (e.g. "us") as a fallback.
Keyword Validity
Each keyword must be non-empty, at most 80 characters and 10 words, and
contain only letters, numbers, spaces, hyphens (-) and apostrophes (').
Keywords with other symbols (?, ,, (, ), @, etc.) are not accepted by
the search-volume data source.
Rather than failing the whole request when one keyword is invalid, we drop the
invalid keywords, return metrics for the valid ones, and list what was skipped
(and why) in the skipped array of the response. Reasons are empty,
too_long, too_many_words, and invalid_characters. If every keyword is
invalid, keywords comes back empty with all entries in skipped. Duplicate
keywords (case-insensitive) are collapsed.
Filtering & Sorting
Narrow and order results with filters, match, and sort. Filter on the same
friendly field names returned in the response — volume, cpc, difficulty,
competition, competitionLevel, intent, keyword, plus words and
length (keyword length).
{
"keywords": ["running shoes", "trail running shoes", "best running shoes"],
"filters": [
{ "field": "volume", "op": ">=", "value": 500 },
{ "field": "competitionLevel", "op": "=", "value": "LOW" }
],
"match": "all",
"sort": ["volume:desc"]
}For this endpoint, filters and sorting are applied to the returned list (exact
search-volume lookups can't be filtered upstream), but the format and behavior
are identical to the other keyword endpoints. See
Keyword Filters for the full list of fields,
operators (>, >=, <, <=, =, !=, in, nin, like, not_like),
and limits (max 8 filters, 3 sort rules).
Response
{
"ok": true,
"data": {
"keywords": [
{
"keyword": "nextjs seo",
"volume": 390,
"cpc": 0.0,
"difficulty": 0,
"competition": 0.01,
"competitionLevel": "LOW",
"intent": "unknown",
"avgBacklinks": 0.0,
"avgReferringDomains": 0.0,
"serpFeatures": [],
"totalResults": 0,
"trend": [
{ "month": "2026-03", "volume": 390 },
{ "month": "2026-02", "volume": 140 },
{ "month": "2026-01", "volume": 170 }
]
},
{
"keyword": "vibe coding",
"volume": 0,
"cpc": 0.0,
"difficulty": 0,
"competition": 0.0,
"competitionLevel": "unknown",
"intent": "unknown",
"avgBacklinks": 0.0,
"avgReferringDomains": 0.0,
"serpFeatures": [],
"totalResults": 0,
"trend": []
}
],
"skipped": [
{ "keyword": "what is an api?", "reason": "invalid_characters" }
]
}
}Response Fields
| Field | Type | Description |
|---|---|---|
ok | boolean | Whether the request succeeded |
data.keywords | object[] | Array of keyword analysis results |
data.keywords[].keyword | string | The analyzed keyword |
data.keywords[].volume | number | Average monthly search volume |
data.keywords[].cpc | number | Cost per click in USD |
data.keywords[].difficulty | number | SEO difficulty score (0-100) |
data.keywords[].competition | number | PPC competition index (0-1) |
data.keywords[].competitionLevel | string | Competition category (LOW, MEDIUM, HIGH, or unknown) |
data.keywords[].intent | string | Search intent (informational, commercial, navigational, transactional, or unknown) |
data.keywords[].avgBacklinks | number | Average number of backlinks for top-ranking pages |
data.keywords[].avgReferringDomains | number | Average number of referring domains for top-ranking pages |
data.keywords[].serpFeatures | string[] | SERP features present for this keyword (e.g., images, video, peopleAlsoAsk) |
data.keywords[].totalResults | number | Total number of search results |
data.keywords[].trend | object[] | Monthly search volume trend data |
data.keywords[].trend[].month | string | Month in YYYY-MM format |
data.keywords[].trend[].volume | number | Search volume for that month |
data.skipped | object[] | Keywords dropped before lookup (empty array when all were valid) |
data.skipped[].keyword | string | The original keyword that was skipped |
data.skipped[].reason | string | Why it was skipped (empty, too_long, too_many_words, invalid_characters) |
Under the hood, this queries our SEO data engine for keyword metrics. We normalize the response, map location/language codes automatically, and flatten nested result arrays.