Skip to content

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

Try the Text Analyzer API

Free tier is for development & small projects. 75 requests/day with a registered account.

cookies

We use analytics cookies to see how the site gets used. Nothing loads until you accept. Privacy policy