SEO keyword density report
A keyword density report turns a vague impression of repetition into a small, reproducible set of measurements.
Run — free
Paste the page text, enter one target word or phrase, and choose the percentage that should trigger a warning. The report returns the exact occurrence count, the share of the text occupied by that keyword, and an over-optimization flag. It works locally and deterministically, making it suitable for editorial checks, publishing workflows, and repeatable quality-control rules without downloading a page or guessing what its visible text contains.
Measure the target phrase consistently
Keyword reviews often become subjective because one editor counts exact phrases, another includes partial words, and a third estimates frequency by scanning the draft. This report uses one stable rule. It normalizes letter case and compatible Unicode forms, splits both the page text and target keyword into words, then looks for the complete target sequence. Searching for “shoe” will not count “shoes,” and searching for “running shoe” only counts those adjacent words in that order. Punctuation between words does not prevent a match because punctuation is not a word. Matches may overlap, which matters for repeated constructions and keeps the count mathematically complete. The returned occurrence count therefore answers a precise question rather than approximating one. The report also returns total_words so reviewers can understand the denominator instead of seeing an isolated percentage. Supply the final visible copy when possible: navigation labels, hidden markup, and content omitted from the input cannot influence the result. Because the capability analyzes supplied text rather than fetching a URL, the same input always produces the same answer and remains easy to reproduce during review.
Understand density and the warning threshold
Density is calculated as the number of words occupied by matching keyword occurrences divided by the total number of words, multiplied by one hundred. A three-word phrase found twice therefore contributes six keyword words to the numerator. This definition makes phrase density comparable to single-word density and avoids understating a long phrase merely because it is reported as one occurrence. The result is rounded to two decimal places for a readable editorial signal. Set threshold to the maximum percentage your workflow permits; it defaults to three. The over_optimized flag becomes true only when density is above that threshold, not when it is exactly equal. That boundary makes automated acceptance rules predictable. A warning is a prompt for human review, not proof of a ranking penalty. Search engines do not publish a universal ideal density, and natural repetition varies with topic, format, brand names, and technical vocabulary. Use the threshold as an internal consistency rule, compare similar page types, and read flagged passages before editing. Removing every repeated phrase mechanically can make accurate writing less clear.
Add the report to an editorial workflow
Run the report after substantive editing, when headings, calls to action, captions, and body copy are close to their published form. A content team can store the count, percentage, threshold, and flag beside each draft, then rerun the same request after revisions to show exactly what changed. Agencies can apply different thresholds to product pages, glossaries, and long-form articles without changing the underlying calculation. Developers can also place the capability in a prepublication check: submit the text and focus keyword, inspect over_optimized, and route a flagged draft back to an editor rather than blocking it without explanation. The raw fields make the decision auditable and allow a user interface to display the evidence. Empty text is valid and reports zero words, zero occurrences, and zero density, while an empty keyword is rejected because there is no meaningful target to measure. The algorithm uses no network requests, randomness, clock, or model, so batch results remain deterministic. Each API request costs $0.002; the browser implementation can use the same pure calculation for immediate feedback before automation is introduced.
What you can do with it
Review a draft before publication
Measure the focus phrase in final copy and send unexpectedly repetitive drafts back for a human editorial review.
Enforce a content quality rule
Use one explicit threshold across a publishing workflow while retaining the count and percentage that explain every flag.
Compare revisions consistently
Run identical settings before and after an edit to verify that excessive repetition fell without changing the measurement method.
FAQ
How is keyword density calculated?
It divides the number of text words occupied by exact keyword matches by total words, then multiplies by 100 and rounds to two decimal places.
Does matching ignore letter case?
Yes. Text and keyword are normalized and compared without case differences, while complete word boundaries and phrase order remain significant.
When is a page flagged as over-optimized?
The flag is true when the calculated density is strictly greater than the supplied threshold, which defaults to 3 percent.
Is a flag proof that search rankings will fall?
No. It is an editorial warning based on your chosen rule, not a prediction or claim about a search engine penalty.
How much does one API request cost?
Each request costs $0.002. The deterministic calculation requires no page download or external model.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/seo/keyword-density-report \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"Technical SEO helps a site become easier to crawl. A practical technical SEO review checks structure without forcing technical SEO into every sentence.","keyword":"technical SEO"}'const res = await fetch("https://api.kit.forhosting.com/seo/keyword-density-report", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"text": "Technical SEO helps a site become easier to crawl. A practical technical SEO review checks structure without forcing technical SEO into every sentence.",
"keyword": "technical SEO"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/seo/keyword-density-report",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"text": "Technical SEO helps a site become easier to crawl. A practical technical SEO review checks structure without forcing technical SEO into every sentence.",
"keyword": "technical SEO"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/seo/keyword-density-report", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"text":"Technical SEO helps a site become easier to crawl. A practical technical SEO review checks structure without forcing technical SEO into every sentence.","keyword":"technical SEO"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"text":"Technical SEO helps a site become easier to crawl. A practical technical SEO review checks structure without forcing technical SEO into every sentence.","keyword":"technical SEO"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/seo/keyword-density-report", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"text": "Technical SEO helps a site become easier to crawl. A practical technical SEO review checks structure without forcing technical SEO into every sentence.",
"keyword": "technical SEO"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "seo.keyword_density_report",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |