Read text from the command line

Read text from the command line

Telereader has a small HTTP API for turning text into a narrated reading. Everything works from curl no SDK or CLI required. You onboard once, then every submit returns a URL you can open and hear. A free caller is never dead-ended: you always get back an openable URL.

Base URL: https://telereader.ai

Step 1 onboard once

The API uses the OAuth device grant (RFC 8628): the tool asks for a code, a human approves it in their browser, and the tool gets a token. You do this once and reuse the token.

not read aloud
# (a) ask for a code
curl -s -X POST https://telereader.ai/api/auth/device/code \
  -H 'content-type: application/json' -d '{"client_id":"tlread"}'
# → {"user_code":"WXYZ-1234",
#    "verification_uri_complete":"https://telereader.ai/device?user_code=WXYZ-1234",
#    "device_code":"…","interval":5,"expires_in":900}

Open the verification_uri_complete in a browser, sign in if asked, and approve. Meanwhile, poll for the token:

not read aloud
# (b) poll every `interval` seconds until approved
curl -s -X POST https://telereader.ai/api/auth/device/token \
  -H 'content-type: application/json' \
  -d '{"grant_type":"urn:ietf:params:oauth:grant-type:device_code","device_code":"<device_code>","client_id":"tlread"}'
# pending  → {"error":"authorization_pending"}   (keep polling)
# approved → {"access_token":"<TOKEN>","token_type":"Bearer",...}

Save <TOKEN>. Reuse it for every future call.

Step 2 submit text, get an openable URL

not read aloud
curl -s -X POST https://telereader.ai/api/v1/readings \
  -H "Authorization: Bearer <TOKEN>" -H 'content-type: application/json' \
  -d '{"source":{"kind":"markdown","body":"# Title\n\nThe text to read aloud."}}'

Returns 202 immediately with a readUrl. Open it and it plays text on screen, in sync with the narration.

  • Free / over-cap: {"mode":"browser","readUrl":"…"} the URL generates the audio in the browser. Hand it to the human or open it.
  • Paid + headroom: {"mode":"server","readingId":"…","readUrl":"…","pollUrl":"…"} the audio renders on our servers. **Open readUrl immediately** it streams as it generates, so hand it over right away; don't wait for generation to finish. Polling pollUrl for status is optional (only if you want to show progress).

A free caller is never returned a bare 402 you get a browser readUrl instead.

The request body

source is the only required field:

  • {"kind":"text","body":"…"} or {"kind":"markdown","body":"…"} (≤ 1,000,000 characters)
  • {"kind":"url","url":"https://…"} or {"kind":"upload",…} Telereader fetches + extracts + cleans the page/document for you, so you don't have to. Import is a paid feature: free callers get 402 (paid_feature) fetch the text yourself and submit it directly instead.

Optional: voice, title, ephemeral, idempotencyKey. Re-submitting the same content with the same key is idempotent (one reading). You can also pass the key as a header: -H "Idempotency-Key: <key>".

Errors

Every error is { "error": <code>, "message": }:

HTTPcodewhat to do
401unauthorizedonboard (Step 1) and send the Bearer token
400validation_failedfix the body (carries zodError)
429rate_limitedwait Retry-After seconds, then retry
422extraction_failedurl/upload import failed — submit text instead
5xxinternal_errorour side — grab the trace id and offer to report it (below)
HTTP. code. what to do. 401. unauthorized. onboard (Step 1) and send the Bearer token. 400. validation_failed. fix the body (carries zodError). 429. rate_limited. wait Retry-After seconds, then retry. 422. extraction_failed. url/upload import failed submit text instead. 5xx. internal_error. our side grab the trace id and offer to report it (below)

Every response carries a trace id the **x-trace-id response header, also mirrored into the JSON body as traceId**. On a 5xx, quote it to the user and offer to file a bug: POST /api/auth-authenticated multipart/form-data to /api/bug-report with a note like "[trace: <x-trace-id>] <what happened>". (An agent should ask first: "Telereader had a problem I have a request id, want me to report it?")

Generate a client don't hand-roll

The full machine-readable spec lives at [/api/v1/openapi.json](https://telereader.ai/api/v1/openapi.json) a complete OpenAPI 3.0 document (onboard + submit + poll + delete). Point your generator at it rather than hand-writing a client.

If you're wiring up an AI agent rather than a script, see Use Telereader from an agent.