Word Counter API

Count words, Unicode characters, sentences, paragraphs and UTF-8 bytes, with configurable reading and speaking time estimates.

Try the Word Counter API

POST /api/v1/word-counter/

Send request transmits this input to the API server. The online tool processes input in your browser.

Request parameters

ParameterTypeRequiredRules
textstringYesText to analyze. Whitespace and line breaks are included in character counts. Maximum: 16384 code points.
readingWpmintegerNoReading speed in words per minute. Default: 238. Range: 1–2000.
speakingWpmintegerNoSpeaking speed in words per minute. Default: 150. Range: 1–2000.

Response fields

A successful response is {"ok":true,"data":{…}}. These fields are inside data.

words · integer
Number of words. Minimum: 0.
chars · integer
Unicode code points, including whitespace. Minimum: 0.
charsNoSpace · integer
Code points after removing whitespace. Minimum: 0.
sentences · integer
Number of sentences. Minimum: 0.
paragraphs · integer
Number of paragraphs. Minimum: 0.
lines · integer
Number of lines. Minimum: 0.
bytes · integer
UTF-8 bytes. Minimum: 0.
unique · integer
Case-insensitive unique word tokens. Minimum: 0.
readingSeconds · integer
Rounded reading duration in seconds. Minimum: 0.
speakingSeconds · integer
Rounded speaking duration in seconds. Minimum: 0.

Counting rules you can reproduce

The API uses the same counting core as the online word counter. Hyphenated words and contractions remain single words. Han characters and Japanese kana count individually; Korean and other space-separated scripts use word tokens. Characters are counted by Unicode code point, so a combined emoji sequence can contain several characters even when it looks like one glyph.

Interpreting the statistics

chars includes whitespace and punctuation; charsNoSpace removes all whitespace, including tabs and line breaks. bytes measures UTF-8 storage size rather than character count. unique ignores case. Sentence boundaries use punctuation heuristics rather than a linguistic parser. Reading and speaking estimates are rounded seconds based on the supplied speeds, not measurements of a particular reader.

cURL, JavaScript and Python examples

Article excerpt

Blank lines separate paragraphs.

cURL
curl --fail-with-body --request POST 'https://www.base64.sh/api/v1/word-counter/' \
  --user-agent 'Base64.sh API example/1.0' --header 'Content-Type: application/json' \
  --data-raw '{"text":"Hello world.\n\nSecond paragraph."}'
JavaScript
const response = await fetch("https://www.base64.sh/api/v1/word-counter/", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
  "text": "Hello world.\n\nSecond paragraph."
})
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Python
import json
from urllib.request import Request, urlopen

payload = json.loads("{\n  \"text\": \"Hello world.\\n\\nSecond paragraph.\"\n}")
request = Request(
    "https://www.base64.sh/api/v1/word-counter/",
    data=json.dumps(payload, ensure_ascii=False).encode("utf-8"),
    headers={"Content-Type": "application/json", "User-Agent": "Base64.sh API example/1.0"},
    method="POST",
)
with urlopen(request, timeout=15) as response:
    print(json.load(response))

Response · HTTP 200

{
  "ok": true,
  "data": {
    "words": 4,
    "chars": 31,
    "charsNoSpace": 27,
    "sentences": 2,
    "paragraphs": 2,
    "lines": 3,
    "bytes": 31,
    "unique": 4,
    "readingSeconds": 1,
    "speakingSeconds": 2
  }
}

Mixed scripts

Han characters count individually as words; emoji count as characters, not words.

cURL
curl --fail-with-body --request POST 'https://www.base64.sh/api/v1/word-counter/' \
  --user-agent 'Base64.sh API example/1.0' --header 'Content-Type: application/json' \
  --data-raw '{"text":"Hello 世界 😀"}'
JavaScript
const response = await fetch("https://www.base64.sh/api/v1/word-counter/", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
  "text": "Hello 世界 😀"
})
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Python
import json
from urllib.request import Request, urlopen

payload = json.loads("{\n  \"text\": \"Hello 世界 😀\"\n}")
request = Request(
    "https://www.base64.sh/api/v1/word-counter/",
    data=json.dumps(payload, ensure_ascii=False).encode("utf-8"),
    headers={"Content-Type": "application/json", "User-Agent": "Base64.sh API example/1.0"},
    method="POST",
)
with urlopen(request, timeout=15) as response:
    print(json.load(response))

Response · HTTP 200

{
  "ok": true,
  "data": {
    "words": 3,
    "chars": 10,
    "charsNoSpace": 8,
    "sentences": 1,
    "paragraphs": 1,
    "lines": 1,
    "bytes": 17,
    "unique": 3,
    "readingSeconds": 1,
    "speakingSeconds": 1
  }
}

Invalid request example

{
  "text": 123
}

HTTP 400

{
  "ok": false,
  "error": {
    "code": "INVALID_INPUT",
    "message": "text must be a string."
  }
}

Limits and error handling

Requests require Content-Type: application/json and a UTF-8 JSON object. The body limit is 32 KiB (32,768 bytes), text fields allow up to 16,384 Unicode code points unless a smaller limit is listed, and responses are limited to 256 KiB. Unknown parameters and unpaired Unicode surrogates in parameter strings are rejected. Send inputs in the request body, not query parameters.

Anonymous access is limited to approximately 60 POST requests per minute per IP address, shared across endpoints in each Cloudflare location. Shared networks share the allowance. Counters are eventually consistent, not a guaranteed global quota. On HTTP 429, wait for the Retry-After interval before retrying; avoid immediate retry loops.

Programmatic clients should send an identifying User-Agent, as shown in the cURL and Python examples. Browser fetch supplies its own header. Cloudflare may reject a generic or missing agent before the API runs; an edge-generated 403 can be plain text or HTML rather than the JSON envelope. See Cloudflare error 1010.

400 · Invalid input
Malformed JSON, missing fields, unsupported options or invalid encoded text. Correct the input before retrying.
403 · Edge request blocked
Check the User-Agent and the Cloudflare error response before retrying.
404 · Unknown endpoint
Use a documented /api/v1/{tool}/ endpoint.
405 · Method not allowed
Use POST. OPTIONS is supported for browser CORS requests.
413 · Size limit exceeded
Reduce the request size or the generated output.
415 · Unsupported media type
Set Content-Type: application/json.
429 · Rate limited
Wait at least the Retry-After number of seconds.
500 / 503 · Service unavailable
Retry later with bounded backoff. Availability is not guaranteed.

API data processing

API inputs are sent over HTTPS and processed in memory by a Cloudflare Worker. The application does not persist submitted text or deliberately log request bodies, and API responses use Cache-Control: no-store. Cloudflare may process network metadata under its own policies. For local processing, use the corresponding browser tool. See the privacy policy.