Send a block of text, get back word count, character count, sentences, paragraphs, reading time and the ten most used words. One POST to the Text Analyzer API, no library to install. This page shows the call, what each number means, and where the counting is simpler than you might expect.
The call
curl -X POST "https://apixies.io/api/v1/analyze-text" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "The quick brown fox jumps over the lazy dog. This classic sentence contains every letter of the English alphabet."}'
{
"status": "success",
"http_code": 200,
"code": "SUCCESS",
"message": "Text analysis completed",
"data": {
"characters": 113,
"characters_no_spaces": 95,
"words": 19,
"sentences": 2,
"paragraphs": 1,
"lines": 1,
"avg_word_length": 4.9,
"reading_time_min": 0.1,
"speaking_time_min": 0.1,
"top_words": {
"the": 3,
"quick": 1,
"brown": 1,
"fox": 1,
"jumps": 1,
"over": 1,
"lazy": 1,
"dog": 1,
"this": 1,
"classic": 1
}
}
}
You need an API key. It's free, and you get one after signing up. Without the header the answer is a 401 with code MISSING_AUTH.
Look at top_words for a second. "The" and "the" were counted together, so case is ignored. And dog lost its full stop: punctuation at either end of a word is dropped before counting. That's about as clever as this endpoint gets, and the next section spells out the rest.
What each number means
The counting is plain on purpose. No language model, no dictionary. Here's exactly what happens to your text.
| Field | How it's counted |
|---|---|
words |
The text is split on whitespace. Every chunk is a word. |
characters |
Characters as you'd count them, spaces included. café résumé is 11, an emoji is 1. |
characters_no_spaces |
The same, with spaces, tabs and line breaks removed. |
sentences |
Every run of ., ! or ? counts as one sentence end. |
paragraphs |
Blocks of text separated by a blank line. |
lines |
Line breaks plus one. |
avg_word_length |
Average length of the words, without the punctuation at their ends. |
reading_time_min |
words divided by 200, rounded to one decimal. |
speaking_time_min |
words divided by 130, rounded to one decimal. |
top_words |
The ten most frequent words, lowercased, with punctuation at the ends removed (Cat, and cat. are both cat). don't and 3.14 stay whole. Nothing is filtered out, so expect "the" and "a" on top. |
Where it's naive
I ran a set of awkward inputs through it. These are the ones worth knowing before you put the numbers in front of users.
| Input | Result | Why |
|---|---|---|
Use a cache, e.g. Redis. It costs $3.14 a month. |
5 sentences | The dots in "e.g." and "3.14" count too. A person would say 2. |
See https://apixies.io/docs for details. |
2 sentences | Same thing, the dot in the domain. |
Wait... what?! No way. |
3 sentences | This one works. ... and ?! are each one run. |
A heading with no full stop |
0 sentences | No ., ! or ?, no sentence. |
<p>Hello <strong>big</strong> world</p> |
3 words, one of them <strong>big</strong> |
HTML isn't stripped. Send plain text. |
A Markdown list with ## and - |
- and ## are words |
Markup isn't stripped either. |
Ship it 🚀 |
3 words, 9 characters | The emoji is a word. It doesn't show up in top_words, because it's a symbol and those are dropped. |
今日は天気がいいですね。散歩に行きましょう。 |
1 word, 0 sentences, 22 characters | No spaces, so one chunk. And 。 isn't ., so no sentence ends either. |
So: words is solid for any language that puts spaces between words, and it's the number everything else hangs on. sentences is a rough guide for ordinary prose and wrong for technical writing. characters is right in any script. It counts code points, so a flag or a family emoji built from several of them counts as several.
Strip HTML and Markdown before you send. I ran the Markdown source of one of our own guides through as it was and got 1,123 words. With the code blocks taken out it was 880. That's the difference between a "6 min read" badge and a "5 min read" one.
In code
JavaScript
async function analyzeText(text) {
const res = await fetch("https://apixies.io/api/v1/analyze-text", {
method: "POST",
headers: {
"X-API-Key": process.env.APIXIES_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({ text }),
});
const body = await res.json();
if (body.status !== "success") {
throw new Error(`${body.code}: ${body.message}`);
}
return body.data;
}
const stats = await analyzeText("Your article text goes here.");
console.log(stats.words, stats.reading_time_min);
Python
import os
import requests
def analyze_text(text):
res = requests.post(
"https://apixies.io/api/v1/analyze-text",
json={"text": text},
headers={"X-API-Key": os.environ["APIXIES_API_KEY"]},
timeout=15,
)
body = res.json()
if body["status"] != "success":
raise RuntimeError(f"{body['code']}: {body['message']}")
return body["data"]
stats = analyze_text("Your article text goes here.")
print(stats["words"], stats["reading_time_min"])
PHP
function analyzeText(string $text): array
{
$context = stream_context_create(['http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\nX-API-Key: " . getenv('APIXIES_API_KEY'),
'content' => json_encode(['text' => $text]),
'ignore_errors' => true,
]]);
$body = json_decode(file_get_contents('https://apixies.io/api/v1/analyze-text', false, $context), true);
if (($body['status'] ?? '') !== 'success') {
throw new RuntimeException(($body['code'] ?? 'ERROR') . ': ' . ($body['message'] ?? ''));
}
return $body['data'];
}
$stats = analyzeText('Your article text goes here.');
echo $stats['words'], ' ', $stats['reading_time_min'], "\n";
Limits
text can be up to 1,000,000 characters. One more and you get a 422 with code VALIDATION_FAILED. Size isn't a speed problem: a 200,000 word input came back in about 140 ms on my machine. Text that's empty or only whitespace is rejected the same way, as a missing field.
The free tier is 75 requests a day. That's plenty for analysing posts when they're saved. It's not enough for a counter that updates while someone types, and the word counter guide shows what to do there.
You can try it in the browser with the Text Analyzer tool. No key needed there.
Next steps
- Text Analyzer API reference: parameters and error codes
- Build a word counter: when to count locally and when to call the API
- Estimate reading time: the 200 words a minute, and how to use your own figure
- All guides