Attest tools · text_stats

Text Stats: words, characters, tokens and SMS segments

a task needs counts for a piece of text: words, user-perceived characters, bytes, sentences, paragraphs, lines, reading or speaking time, OpenAI token counts, SMS segments, or a check against length limits such as a c…

1 credit per successful callPOST /api/v1/text/stats

Bearer key · JSON

POST /api/v1/text/stats

curl -s -X POST https://getattest.com.au/api/v1/text/stats \
  -H "authorization: Bearer $ATTEST_API_KEY" \
  -H "content-type: application/json" \
  -d '{"text":"Hello there. General Kenobi!"}'

Send the key as a bearer token in the authorization header. One key and one credit balance cover every service, including Attest Capture. A failed call is refunded automatically. No key yet? POST /api/v1/keys issues one with 25 free credits and no signup — see the key guide.

Captured 2026-09-21

Example

This request was run against the running service on 2026-09-21 and the response below is what came back, unedited.

Request JSON

{
  "text": "Hello there. General Kenobi!"
}

Captured response JSON

{
  "call_id": "08ec544d-8ef5-4a40-9ab4-de81a788a6e1",
  "characters": 28,
  "code_points": 28,
  "utf16_units": 28,
  "bytes_utf8": 28,
  "words": 4,
  "sentences": 2,
  "paragraphs": 1,
  "lines": 1,
  "whitespace": 3,
  "reading_time_minutes": 0.02,
  "speaking_time_minutes": 0.02,
  "time_basis": {
    "reading_words_per_minute": 238,
    "speaking_words_per_minute": 183
  },
  "tokens": {
    "o200k_base": 7,
    "cl100k_base": 7,
    "note": "OpenAI tokenizers, per the model table in js-tiktoken 1.0.21: o200k_base is listed for GPT-4o, GPT-4o mini, GPT-4.1, GPT-4.5 Preview, o1, o3, o4-mini and GPT-5; cl100k_base for GPT-4, GPT-4 Turbo and GPT-3.5 Turbo. Other models, including other vendors', may tokenize differently, so treat these counts as estimates for them."
  },
  "sms": {
    "encoding": "GSM-7",
    "segments": 1,
    "characters_per_segment": 160,
    "units": 28,
    "max_segments": 255,
    "within_concatenation_limit": true,
    "note": "GSM 7-bit default alphabet without national language shift tables; extension characters (^ { } [ ] ~ | \\ € and form feed) cost two. Code 0x09 is capital Ç in the 3GPP table, so small ç is counted as UCS-2, the encoding a gateway following that table sends; a gateway following Unicode's GSM0338.TXT accepts ç as one septet and may need fewer segments than reported. Multipart sizes assume the standard 6-octet concatenation header; at most 255 parts fit in one concatenated message, so when segments exceeds max_segments, within_concatenation_limit is false and the text cannot be sent as a single concatenated SMS. Characters outside the Basic Multilingual Plane are never split between segments; senders that also keep whole emoji sequences together may need one more. Gateways may substitute characters or choose a different encoding."
  },
  "limits": [],
  "sources": [
    {
      "name": "Brysbaert, M. (2019). How many words do we read per minute? A review and meta-analysis of reading rate. Journal of Memory and Language, 109, 104047.",
      "url": "https://doi.org/10.1016/j.jml.2019.104047",
      "used_for": "reading_time_minutes (238 wpm, adult silent reading of non-fiction) and speaking_time_minutes (183 wpm, adult reading aloud)"
    },
    {
      "name": "OpenAI tiktoken encodings o200k_base and cl100k_base (MIT), rank files from js-tiktoken (MIT)",
      "url": "https://github.com/openai/tiktoken",
      "used_for": "tokens"
    },
    {
      "name": "3GPP TS 23.038 (GSM 7-bit default alphabet and extension table) and 3GPP TS 23.040 clause 9.2.3.24.1 (concatenated short messages)",
      "url": "https://www.3gpp.org/ftp/Specs/archive/23_series/",
      "used_for": "sms"
    },
    {
      "name": "Unicode GSM0338.TXT mapping — cited only for the divergence at GSM code 0x09, which it maps to small c-cedilla where the 3GPP table draws the capital; we follow 3GPP",
      "url": "https://www.unicode.org/Public/MAPPINGS/ETSI/GSM0338.TXT",
      "used_for": "sms"
    }
  ],
  "notice": "General information only. Reading and speaking times are averages for adult English readers; individual rates vary.",
  "checked_at": "2026-09-20T16:55:57.499Z",
  "credits_charged": 1,
  "credits_remaining": 9992
}

For agents

Use when

Use this when a task needs counts for a piece of text: words, user-perceived characters, bytes, sentences, paragraphs, lines, reading or speaking time, OpenAI token counts, SMS segments, or a check against length limits such as a character, word, SMS or prompt-token budget.

2 fields

Input

Scroll the table horizontally to see all fields.

Field / Type / Required / What it does
FieldTypeRequiredWhat it does
textstringRequiredThe text to count. Up to 200,000 UTF-16 code units. Whitespace-only text is valid and counts zero words.
limitsarrayOptionalUp to 20 budgets to check the text against. Each is { name, max, unit } where unit is characters, words, tokens_o200k, tokens_cl100k, bytes or sms_segments.

HTTP 200

Response

Every successful response also carries call_id (your receipt for the charge), credits_charged and credits_remaining.

Scroll the table horizontally to see all fields.

Field / What it holds
FieldWhat it holds
charactersUser-perceived characters (graphemes).
code_pointsUnicode code points.
utf16_unitsJavaScript string length.
bytes_utf8UTF-8 byte length.
wordsWord count.
sentencesSentence count.
paragraphsParagraph count.
linesLine count.
reading_time_minutesAt 238 words per minute, adult silent reading of non-fiction.
speaking_time_minutesAt 183 words per minute, adult reading aloud.
tokensOpenAI o200k_base and cl100k_base token counts, with a note on which models each is listed for.
smsSMS encoding, segment count and concatenation limit, with the assumptions spelled out.
limitsEach budget sent, with the measured value and whether it fits.
sourcesThe research and specifications behind the reading, token and SMS figures.

Machine-readable failures

Errors

Errors return JSON such as {"error":"invalid_api_key"}. A failed call is refunded automatically.

Scroll the table horizontally to see all fields.

Code / Status / Meaning
CodeStatusMeaning
text_required400No text was sent.
invalid_text400text is not a string.
text_too_long400text is past the 200,000 code-unit cap; split it and add the counts up.
invalid_limits400limits is not an array.
too_many_limits400More than 20 limits were sent.
invalid_limit_unit400A limit names a unit this service does not measure.
invalid_limit_max400A limit's max is not a whole number of zero or more.
limit_name_required400A limit has no name.

On every service

Scroll the table horizontally to see all fields.

Code / Status / Meaning
CodeStatusMeaning
invalid_api_key401The authorization header is missing or the key is unknown.
insufficient_credits402The balance is below this call's cost. The response carries credits_required and credits_remaining.
rate_limit_exceeded429Free keys share 10 calls a minute across every service. The response carries limit and window_seconds.
services_unavailable503The credit store could not be reached. Retry once after 30 seconds.

Where the answer comes from

Source and scope

Reading rates from Brysbaert (2019), Journal of Memory and Language 109, 104047. Token counts from the OpenAI tiktoken encodings o200k_base and cl100k_base (MIT) via js-tiktoken (MIT). SMS segmentation from 3GPP TS 23.038 and TS 23.040.

Attest reports what it read, with the source it read it from. It is general information, not advice, and not a legal record.

All Attest tools · Service list as JSON · OpenAPI

Same key as every Attest tool

One key. One balance. 1 credit a call.

Issue a free key, then use it here, on Attest Capture and on every other tool in the box.

Get a free key