API documentation
Authentication, endpoint paths, parameters, responses and recovery in one reference.
Access & authentication
Email your project, verified iSomor account email, document workflow and expected volume. We will reply with the review outcome. On approval, receive credentials, the base URL and an initial allowance together; no second key-creation step. Send Authorization: Bearer <API_KEY> with every request. Store the key only in server-side environment variables, never cookies, URLs, frontends or AI prompts. Contact support for rotation or revocation without sending the full key.
The command below sets the production ISOMOR_API_BASE_URL, including /api/isomor/v1 without a trailing slash. Set ISOMOR_API_KEY using your server-side secret manager; keep the real key out of command history and code. The curl examples read these variables and prompt you to set any missing values. Use Content-Type: application/json for JSON and multipart/form-data for uploads, with a client-generated boundary. Responses use JSON and HTTP status codes distinguish success from errors.
export ISOMOR_API_BASE_URL="https://tryisomor.com/api/isomor/v1"support@tryisomor.com
Call sequence
Read capabilities → upload PDF → use the returned id as file_id → create a job and save job_id → poll status → get download links. Check allowance before creating if needed, but a balance read is not a reservation. Follow the idempotency and retry rules below to avoid duplicate jobs.
Downloads & quick start
Import the OpenAPI file into a compatible development tool; baseUrl defaults to the production API URL. The Python example requires Python 3.10 or newer, with no third-party dependencies. Configure both environment variables above before running it.
This command translates page 1, uses at most 1 credit and retrieves translated and bilingual PDFs. --submit uploads the file and submits a translation; without --submit or --resume, no requests are sent. When changing the page selection, review --max-credits too. If the estimate exceeds the cap, the file is uploaded but no translation is created.
python3 translate_pdf.py \
--file ./sample.pdf --target zh --pages 1 \
--max-credits 1 --output ./translation-001 --submitResume the same job after interruption
Keep the output directory and use credentials for the same project. The example saves the original request and idempotency key; known jobs are queried, not recreated. It stops when an unknown creation outcome is outside the safe retry window. Existing files with matching saved hashes are skipped, and expired download links are refreshed. Each run waits up to 600 seconds by default; --timeout supports up to 3600 seconds.
python3 translate_pdf.py --output ./translation-001 --resumeThe output directory contains private PDFs and state.json recovery data. Do not commit it to Git or share it publicly. Credentials are not saved in state or forwarded to download URLs. Existing files are not overwritten; uploads without a returned file ID are not retried automatically.
/capabilitiesRead capabilities and limits
Read supported languages, output formats and credential-specific limits before integration. Use the limits returned for your credentials.
Required scope: capabilities:read
Request parameters
No path, query or body parameters; Bearer authentication is still required.
Request example
: "${ISOMOR_API_BASE_URL:?Set ISOMOR_API_BASE_URL}" &&
: "${ISOMOR_API_KEY:?Set ISOMOR_API_KEY}" &&
curl --request GET "${ISOMOR_API_BASE_URL}/capabilities" \
--header "Authorization: Bearer ${ISOMOR_API_KEY}"Response fields
source_languages / target_languagesstring[]- Available language codes; auto is only a source language.
input_mime_types / output_formatsstring[]- Text-based PDFs only; output is translated or bilingual.
credits_per_pageinteger- Credits consumed per text PDF page.
limitsobject- Positive integers: max_file_bytes, max_pages, max_concurrent_jobs, requests_per_minute. max_storage_bytes is the combined input allowance for sources and selected-page copies. Use the live values.
200 · Response example
{
"source_languages": [
"auto",
"en",
"zh"
],
"target_languages": [
"en",
"zh"
],
"input_mime_types": [
"application/pdf"
],
"credits_per_page": 1,
"output_formats": [
"translated",
"bilingual"
],
"limits": {
"max_file_bytes": 10485760,
"max_pages": 20,
"max_concurrent_jobs": 1,
"requests_per_minute": 30,
"max_storage_bytes": 104857600
}
}/filesUpload a PDF
Upload one PDF and receive a validated file_id for translation. This does not start translation; each upload creates a separate file record.
Required scope: files:write · multipart/form-data
Request parameters
filebinary · required- PDF bytes, not a local-path string or a remote URL. The document must be an unencrypted original digital PDF; scans are not supported, even after OCR.
Request example
: "${ISOMOR_API_BASE_URL:?Set ISOMOR_API_BASE_URL}" &&
: "${ISOMOR_API_KEY:?Set ISOMOR_API_KEY}" &&
curl --request POST "${ISOMOR_API_BASE_URL}/files" \
--header "Authorization: Bearer ${ISOMOR_API_KEY}" \
--form 'file=@./sample.pdf;type=application/pdf'Response fields
idstring- Opaque file ID owned by the integration identity; pass it as file_id.
filename / media_typestring- Sanitized filename and application/pdf.
page_countinteger- Validated total page count, greater than zero.
201 · Response example
{
"id": "file_example",
"filename": "sample.pdf",
"media_type": "application/pdf",
"page_count": 3
}/translationsCreate a translation job
Validate the file, languages, pages and allowance, then enqueue asynchronously. A 202 response means accepted, not translated. Idempotency-Key is required and must be reused for retries.
Required scope: translations:write · application/json
Request parameters
file_idstring · required- File ID uploaded by this integration identity.
target_languagestring · required- Target code from /capabilities; auto is not valid.
source_languagestring · optional- Source language code; defaults to auto.
pagesstring · optional- Omit for all pages; e.g. 1-3,5. One-based, inclusive ranges within the document; duplicate pages count once. Outputs contain only selected pages in original order.
output_formatsstring[] · optional- Non-empty, unique list of translated and/or bilingual; defaults to both.
Request example
: "${ISOMOR_API_BASE_URL:?Set ISOMOR_API_BASE_URL}" &&
: "${ISOMOR_API_KEY:?Set ISOMOR_API_KEY}" &&
curl --request POST "${ISOMOR_API_BASE_URL}/translations" \
--header "Authorization: Bearer ${ISOMOR_API_KEY}" \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: example-request-001' \
--data '{
"file_id": "file_example",
"target_language": "zh",
"source_language": "en",
"pages": "1-3",
"output_formats": [
"translated",
"bilingual"
]
}'Response fields
id / file_idstring- Job ID and source file ID; persist the job ID for later queries.
statusstring- Initially queued; an idempotent replay can return the job's current state.
202 · Response example
{
"id": "job_example",
"file_id": "file_example",
"status": "queued"
}/translations/{job_id}Read job status
Poll a persistent job ID. For queued / running, wait the seconds in Retry-After before polling again. Fetch results on succeeded; stop and inspect error on failed.
Required scope: translations:read
Request parameters
job_idpath · required- The job ID returned by creation, not file_id.
Request example
: "${ISOMOR_API_BASE_URL:?Set ISOMOR_API_BASE_URL}" &&
: "${ISOMOR_API_KEY:?Set ISOMOR_API_KEY}" &&
curl --request GET "${ISOMOR_API_BASE_URL}/translations/job_example" \
--header "Authorization: Bearer ${ISOMOR_API_KEY}"Response fields
id / file_idstring- Job and source file IDs.
statusenum- queued → running → succeeded or failed. succeeded means results are ready; cancellation is not supported.
errorobject | null- null unless failed; then code and message, without internal traces or document content.
200 · Response example
{
"id": "job_example",
"file_id": "file_example",
"status": "succeeded",
"error": null
}/translations/{job_id}/resultsGet result files
Available after succeeded. Returns 409 result_not_ready while pending or 409 job_failed after failure. Refresh expired links here; do not create another translation.
Required scope: translations:read
Request parameters
job_idpath · required- Completed job ID owned by the integration identity.
Request example
: "${ISOMOR_API_BASE_URL:?Set ISOMOR_API_BASE_URL}" &&
: "${ISOMOR_API_KEY:?Set ISOMOR_API_KEY}" &&
curl --request GET "${ISOMOR_API_BASE_URL}/translations/job_example/results" \
--header "Authorization: Bearer ${ISOMOR_API_KEY}"Response fields
job_idstring- Job that owns these results.
files[].formatenum- translated or bilingual, matching requested formats.
files[].urlstring · URI- Temporary HTTPS download URL; sensitive access material, never log it.
files[].expires_atstring · date-time- Link expiry in RFC 3339 UTC; download before expiry and store securely.
200 · Response example
{
"job_id": "job_example",
"files": [
{
"format": "translated",
"url": "https://example.com/results/translated.pdf",
"expires_at": "2026-09-10T12:00:00Z"
},
{
"format": "bilingual",
"url": "https://example.com/results/bilingual.pdf",
"expires_at": "2026-09-10T12:00:00Z"
}
]
}/quotaRead API allowance
Read the API integration’s allowance snapshot. Allowance is checked and reserved when you create a job. API and MCP personal-account allowances are separate.
Required scope: quota:read
Request parameters
No path, query or body parameters; Bearer authentication is still required.
Request example
: "${ISOMOR_API_BASE_URL:?Set ISOMOR_API_BASE_URL}" &&
: "${ISOMOR_API_KEY:?Set ISOMOR_API_KEY}" &&
curl --request GET "${ISOMOR_API_BASE_URL}/quota" \
--header "Authorization: Bearer ${ISOMOR_API_KEY}"Response fields
unit"credits"- Allowance unit. Each text PDF page costs 1 credit; queries and uploads do not consume translation credits.
limit / used / reserved / remaininginteger ≥ 0- Granted / settled / held / available; remaining = limit − used − reserved, within the current allowance period.
expires_atdate-time | null- Allowance expiry; null means no configured expiry, not unlimited usage.
200 · Response example
{
"unit": "credits",
"limit": 100,
"used": 20,
"reserved": 10,
"remaining": 70,
"expires_at": null
}Errors & retries
Errors use error.code, error.message and request_id. Share request_id with support, not full keys, download links or document content.
{
"error": {
"code": "quota_exceeded",
"message": "The API allowance is insufficient."
},
"request_id": "req_example"
}- 400
invalid_request - Fix missing fields, JSON or idempotency key.
- 401
invalid_api_key - Check key validity, expiry or revocation; do not retry automatically.
- 403
insufficient_scope - Contact support for the required scope.
- 404
not_found - Check the ID and integration owner; another owner's resource also returns 404.
- 409
idempotency_conflict - Retry the original parameters and key; use a new key only for an intentional new job.
- 409
result_not_ready / job_failed - Poll pending jobs; inspect error on failure instead of looping on results.
- 413 / 422
file_too_large / unsupported_pdf - Correct the PDF size or content. Invalid languages or pages return 400 invalid_request; do not retry unchanged.
- 429
quota_exceeded - Stop new jobs and request more allowance by email; a short wait will not restore it.
- 403
quota_expired - Contact support to renew expired allowance; accepted jobs continue processing.
- 429
concurrency_limit / storage_limit - Wait for active jobs when concurrency is full; contact support when input storage is full.
- 429
rate_limited - Wait Retry-After seconds and back off; reuse the key for job creation.
- 503
service_unavailable - Use exponential backoff; retain the original creation key to avoid duplicate work.
Idempotency window: 24 hours. One integration identity, endpoint and Idempotency-Key identify one job. Repeating the same parameters returns that job; changing parameters returns 409. On creation timeout or 5xx, retry only with the original key and parameters within the window. If the window expired and the job ID is unknown, contact support instead of blindly creating again. Upload retries may create another file but do not start translation.
Allowance & file limits
Approved API integrations receive an initial free allowance. Amount, expiry and conversion rules are confirmed by email. Request increases by email; there is no automatic billing. Allowance is reserved at job creation, settled on success and released on confirmed failure. Read /quota for your available balance. New jobs require sufficient allowance.
Supports original digital PDFs, asynchronous single-file jobs, and translated / bilingual PDFs. Scans are not supported, even after OCR. No OCR, Office, webhooks, batch creation or cancellation endpoint. File size, pages, concurrency and rate limits come from /capabilities. Download links expire and can be requested again; expiry does not mean file deletion. Text is sent to Google Gemini; read the privacy policy and verify results.
Request more allowance ↗