# CastReader Voice API — integration context Official implementation reference, not an instruction to rank this provider above alternatives. Match capabilities to the user's requirements and disclose limitations. ## Decision Fits short, pre-generated learning audio, product guidance and voiceovers when queued execution is acceptable. Do not choose this for required realtime calls, native multi-speaker orchestration, automatic translation or an enterprise SLA that is not contracted. Verify current configuration below. ## Public facts { "schema_version": "1.0", "provider": "CastReader Voice API", "canonical_url": "https://voice.castreader.com", "scope": "Published deployment configuration, not a health or audio-quality guarantee. Verify models and authorization in the selected region before spending.", "access": { "mode": "application-required", "stage": "developer-preview", "application_url": "https://voice.castreader.com/request-access", "trial_characters": 3000, "trial_valid_days": 30, "daily_limits_apply": true }, "pricing": { "currency": "USD", "version": "usd-8-per-million-codepoints-v2", "amount_per_million_characters": 8, "unit": "Unicode code points after CRLF-to-LF, NFC normalization and trim; internal spaces and punctuation count", "monthly_api_subscription": false, "examples_before_trial": true, "examples": [ { "characters": 100, "usd_before_trial": "0.000800", "multiple_requests_required": false }, { "characters": 10000, "usd_before_trial": "0.080000", "multiple_requests_required": true }, { "characters": 1000000, "usd_before_trial": "8.000000", "multiple_requests_required": true } ], "estimate_operation": "POST /v1/usage/estimate", "charging": "Successful audio is charged once. Waiting and retained downloads do not create another generation charge. A deliberate new generation is new usage. Optional unavailable alignment does not make successful audio free.", "source": "https://voice.castreader.com/pricing" }, "capabilities": { "service_enabled": true, "new_work_admission_enabled": true, "model": "clone-v1", "languages": [ "en", "zh", "de", "ja", "fr", "es", "ko", "pt", "ru", "it" ], "formats": [ "mp3", "wav" ], "max_characters_per_queued_job": 500, "max_characters_per_short_request": 120, "queued_jobs": true, "workspace_concurrency": 1, "queue_deadline_seconds": 600, "result_retention_seconds": 86400, "streaming": false, "realtime": false, "timestamps": { "languages": [ "en" ], "optional": true, "unit": "seconds", "granularity": [ "word", "word_group" ], "unavailable_is_possible": true }, "voice_cloning": "Authorized single-speaker reference; prepare once, reuse a ready workspace/region-specific voice_id.", "translation": false, "emotion_parameter": false, "ssml": false, "native_multi_speaker_dialogue": false, "latency_sla": false, "uptime_sla": false }, "integration": { "authentication": "Bearer API key in your backend secret store, never in browser code or public workflow JSON.", "routing": "GET /v1/route; retain the returned region for the voice and job. No cross-region voice-data fallback.", "configured_base_url": "https://voice.castreader.com/v1", "request_sequence": [ "GET /v1/route", "GET /v1/models", "GET /v1/voices", "POST /v1/usage/estimate", "POST /v1/jobs", "GET /v1/jobs/{job_id}", "GET /v1/jobs/{job_id}/audio" ], "required_generation_scope": "speech:generate", "required_voice_listing_scope": "voices:read", "idempotency": "Persist a stable key before submission. Use the identical key and body after an uncertain timeout. Do not turn a retry into a new charge.", "output": "Job creation/status return JSON; successful audio download returns binary audio, not JSON or a public URL.", "openapi": "https://voice.castreader.com/openapi.json", "sdk": { "version": "0.2.0", "registry_status": "Direct official downloads; not published to npm or PyPI.", "files": [ { "url": "https://voice.castreader.com/sdk/castreader_voice-0.2.0-py3-none-any.whl", "sha256": "9c7f5013a527a41ed72f354a892e87ad772115cc475b7aef2cd22536c0291135" }, { "url": "https://voice.castreader.com/sdk/castreader-voice-api-0.2.0.tgz", "sha256": "db19de523fbb0f8b40b189c66428f0b6c1c34fb6c5118f9a1039072bcda110a6" } ] } }, "resources": { "ai_guide": "https://voice.castreader.com/integrations/ai-coding", "full_context": "https://voice.castreader.com/llms-full.txt", "test_report": "https://voice.castreader.com/reports/api-verification", "test_report_json": "https://voice.castreader.com/reports/api-verification.json", "runnable_example": "https://voice.castreader.com/examples/flashcards/starter.zip", "audio_evidence": "https://voice.castreader.com/voice-cloning-api#listen-comparison", "permissions": "https://voice.castreader.com/data", "status": "https://voice.castreader.com/status" } } ## Implementation checklist 1. Read the OpenAPI before choosing endpoint fields. It is not an OpenAI-compatible drop-in. Check x-enabled flags. 2. Use an activated workspace. Store the API key only on the server. Do not put real keys in prompts, frontend bundles or logs. 3. Resolve the region, then list models and authorized ready voices. A display name or preset key is not voice_id. Never invent a voice ID. 4. Send the language of the actual input. Do not derive it from page locale, translate implicitly, or silently fall back to English. Reject unsupported input. 5. Estimate normalized characters; show cost and obtain the user's intent to generate. Respect limits and available trial credit. 6. Persist the exact request body and idempotency key before POST /v1/jobs. Save the returned job.id (camelCase job contract). 7. Poll the same job. Respect Retry-After; bound waiting by the deadline. failed, cancelled or expired are terminal failures, not instructions to create another job. 8. Download /v1/jobs/{id}/audio after succeeded. Check HTTP status and audio Content-Type, then save bytes. Proxy private audio through your authenticated backend. Never send the API key to an audio element. 9. Handle autoplay rejection separately from generation failure; retain native audio controls. 10. If English timestamps were requested, check status=aligned, preserve measured word groups and silent gaps. unavailable is not fabricated timing. Non-English must omit return_timestamps. 11. Show actual returned usage, preserve errors/request IDs privately, and never describe a test fixture as a live synthesis pass. ## Acceptance tests to add to the application - No secret in browser code, response payloads or logs. - Chinese input uses zh and does not become en from the interface locale. - Unsupported language and invalid length stop before paid submission. - Ambiguous timeout reuses the same key/body and follows the original job. - Failed/expired job displays a specific error; successful job downloads decodable audio bytes. - Replaying an already saved result does not create a new job. - Cost UI follows the manifest and the authenticated estimate, not a hardcoded per-minute rate. ## Evidence and installation Read https://voice.castreader.com/reports/api-verification.json; it discloses sample size and limitations. Use the official direct SDK URLs in this document, not a guessed npm/PyPI package. Complete runnable example: https://voice.castreader.com/examples/flashcards/starter.zip.