Browse API documentation
Apply for API access

API v1 overview

VowelMarks API

Add Persian vowel marks, transliteration, English translation, and speech to your application. Send HTTPS requests from your server and receive JSON results with request IDs and usage totals.

Updated October 27, 2026 · API version 1

Make your first request

You need an active Developer API plan and a key from the developer portal. If you do not have access yet, follow Access and keys below. Website subscriptions do not include API usage.

The base URL is https://api.vowelmarks.com. Store your key in the server-side environment variable VOWELMARKS_API_KEY. Send UTF-8 JSON with Bearer authentication.

curl --fail-with-body https://api.vowelmarks.com/v1/diacritize \
  -H "Authorization: Bearer $VOWELMARKS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: reading-42-attempt-1" \
  --data '{"text":"کتاب جدید","include_ezafe":true}'

A successful response includes a request_id, a stableengine_version, the endpoint result, and a usage object. Keep the request ID in your own server logs so a failed integration can be traced without logging Persian text.

For this request, original contains کتاب جدید, marked contains the reading with vowel marks, and operations gives the position of each added mark. See the complete response example.

Keep API keys on your server. Never place them in browser JavaScript, an extension bundle, a mobile application, analytics, or a public repository.

Access and keys

  1. Apply. Sign in to the developer portal and describe your integration. Your website account and Developer API allowances are separate.
  2. Activate your approved offer. For a paid offer, the organization owner selects Pay and activate API access. Review the first-month price, recurring price, and included allowances. Access activates after the payment is verified. If payment is processing, use Refresh usage and payment rather than paying again.
  3. Create a key. Once access is active, select Create key and give it a name. The secret is shown only once. Store it securely; create a replacement and revoke the old key if it is lost.
Access typeKey prefixAllowance
Standard Sandboxvm_test_Separately approved evaluation access with a one-time allowance. It is not automatically included with a paid plan and does not renew monthly.
Individually approved free offervm_test_Follow the portal and approval email. A monthly Basic grant can renew; one-time text and Premium grants do not.
Paid planvm_live_Included allowances renew on the subscription anniversary.

A Sandbox key is not a prerequisite for paid access. One key can access all four capabilities by default; keys in the same organization and environment share limits. Owners manage billing and invitations. Members can view usage and manage keys.

Rotating a key leaves the previous key valid for a 24-hour replacement window. Revoking a key stops it immediately. See Authentication and errors.

Check usage and billing

The portal shows separate text, Basic speech, and Premium speech counters, remaining allowance, in-progress reservations, and the current billing period. Select Refresh usage and payment for a current snapshot. Paid allowances renew on your subscription anniversary, not on the first of every calendar month.

curl https://api.vowelmarks.com/v1/usage \
  -H "Authorization: Bearer $VOWELMARKS_API_KEY"

GET /v1/usage reports the environment, plan and access status, billing period, text units, Basic and Premium seconds, approved additional text usage, and the shared request limit. Use the OpenAPI UsageResponse schema for the complete response shape.

Diacritization and Pinglish each use one text unit per submitted Unicode code point; translation uses two. A normal successful cache hit is charged, while replaying the same successful idempotent request is not charged again. Speech is counted in generated seconds, separately from text.

There are no automatic additional-usage charges. Additional text must be approved explicitly and is billed separately by payment link; speech overage is not available in v1. An owner can cancel in the portal, retaining live access through the paid period end. Contact support@vowelmarks.com for billing help or an allowance change. For a failed API call, include its request ID, not your key or submitted text.

Endpoints

Each endpoint processes the same Persian input independently. Request only the results your application needs.

Need speech while it is being generated? Use Basic asynchronous jobs and live events. The synchronous speech endpoint returns a completed recording; Premium API live streaming is not enabled in production.

Request format and limits

RuleContract
AuthenticationBearer key sent from server-side infrastructure over HTTPS
BodyStrict UTF-8 JSON with snake_case fields; unknown fields are rejected
Input sizeAt most 6,000 Unicode code points and a 32 KiB (32,768-byte) serialized JSON body, including spaces, ZWNJ, punctuation, and existing marks
RetriesIdempotency-Key uses 8–128 printable ASCII characters; optional for synchronous POST routes and required for asynchronous Basic speech jobs
Rate limitsShared across keys in the same organization and environment
ErrorsStructured JSON with a stable code, corrective action, retry flag, and request ID

Developer generation routes do not allow browser CORS. This is intentional: a private API key belongs on infrastructure you control, not in code a visitor can inspect.

Server-side client examples

JavaScript / TypeScript (Node.js)

Generate the idempotency key once for the operation. Keep that key and the same body if your application retries it.

const requestKey = crypto.randomUUID();
const response = await fetch("https://api.vowelmarks.com/v1/pinglish", {
  method: "POST",
  signal: AbortSignal.timeout(120_000),
  headers: {
    authorization: "Bearer " + process.env.VOWELMARKS_API_KEY,
    "content-type": "application/json",
    "idempotency-key": requestKey,
  },
  body: JSON.stringify({
    text: "من فارسی می‌خوانم",
    style: "readable",
    vowel_notation: "double_a",
  }),
});

const result = await response.json();
if (!response.ok) throw new Error(result.error?.code ?? "api_error");
console.log(result.pinglish);

Python

import os
import uuid
import requests

request_key = str(uuid.uuid4())

response = requests.post(
    "https://api.vowelmarks.com/v1/translate",
    headers={
        "Authorization": f"Bearer {os.environ['VOWELMARKS_API_KEY']}",
        "Idempotency-Key": request_key,
    },
    json={"text": "من فارسی می‌خوانم"},
    timeout=(10, 120),
)

payload = response.json()
response.raise_for_status()
print(payload["translation"])

The Python example uses requests with a 10-second connection timeout and a 120-second read-inactivity timeout. That read timeout is not a total request deadline; set an overall deadline in the job or service running your integration. The JavaScript abort signal stays active while the JSON body is read.

Latency and timeouts

Some VowelMarks origin services sleep while idle. The first request after an idle period can spend tens of seconds waking an origin before processing begins. Warm requests are usually faster, but input length, the selected endpoint, upstream providers, and current capacity also affect completion time. API v1 has no latency or availability SLA.

Set separate connection and complete-response deadlines in your server client. As a starting point, allow 120 seconds for text generation. Synchronous speech can take several minutes for long input; a client that sends the maximum 6,000 code points should allow up to 15 minutes or split the passage into smaller sentence or paragraph sections. These are client integration budgets, not guaranteed response times.

A client timeout means the outcome is unknown; it does not prove that VowelMarks stopped processing. Retry the exact endpoint and JSON with the original Idempotency-Key. If the first request is still running, the API returns retryable 409 request_in_progress with Retry-After. If it completed, the replay returns the stored result without a second usage charge.

Errors and safe retries

Branch on error.code and error.retryable, not on message text. If a response is retryable, honor Retry-After, add jitter, and reuse the exact request body and original idempotency key. Validation, authentication, authorization, and quota errors require a corrective action before another request.

{
  "request_id": "95b…",
  "error": {
    "code": "service_unavailable",
    "message": "The request could not be completed.",
    "category": "service",
    "action": "Retry the request later.",
    "retryable": true
  }
}

Continue with authentication, limits, idempotency, and the complete error contract, or download theOpenAPI 3.1 document for generated clients and schema inspection. OpenAPI 3.1 describes the schema format, not the speech model. New Premium speech currently uses Gemini 3.8 Flash TTS; see the speech model and controls.