Справочник API
Этот документ соответствует поведению приложения Express в api/app.js и обработчикам маршрутов в api/routes/.
Ограничения и поведение
| Пункт | Значение |
|---|---|
| Размер тела JSON | До 2 МБ (express.json({ limit: '2mb' })) |
| Цели на запрос | 1–36 языковых кодов |
| Элементы в пакете | 1–100 элементов на пакетный запрос |
| Модели | standard (по умолчанию) или advanced (только платные уровни; см. ниже) |
Ежемесячный лимит токенов (бесплатный уровень): Перед вызовом модели API оценивает токены примерно как ceil(content_length / 4) × (number_of_targets + 1) и, только для уровня free, отклоняет запрос с кодом 429 / token_limit_reached, если оценка превышает оставшийся месячный лимит (FREE_TIER_MONTHLY_TOKENS, по умолчанию 100000). Платные уровни не блокируются этой предварительной проверкой в enforceTokenCap; использование всё равно регистрируется.
Ограничения по скорости: При настройке Upstash Redis (UPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN, и URL не содержит плейсхолдер your-instance) применяются ограничения по минутам в зависимости от уровня: free 5, starter 30, growth 60, scale 120, enterprise неограниченно. При превышении лимита ответ — 429 с error: "rate_limit_reached". Если Redis не настроен, ограничение скорости пропускается (см. rateLimit.js).
Успешные ответы с ограничением скорости могут включать заголовки X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.
GET /health
Без аутентификации.
Ответ 200
{
"status": "ok",
"timestamp": "2025-03-23T12:00:00.000Z"
}
GET /languages
Без аутентификации.
Возвращает канонический список поддерживаемых языков (код, отображаемое имя, флаг RTL). Всего 36 записей; коды — единственные значения, принимаемые в targets на конечных точках перевода.
Ответ 200
{
"languages": [
{ "code": "en", "name": "English", "rtl": false },
{ "code": "ar", "name": "Arabic", "rtl": true }
]
}
Источник: api/utils/languages.js.
POST /translate
Требуется Authorization: Bearer <api_key>.
Переводит одну строку content на все языки, указанные в targets. Модель возвращает один JSON-объект, ключи которого — точно запрошенные языковые коды, а значения — переведённые строки (см. formatPrompts.js).
Тело запроса
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
content | string | Да | Непустая строка для перевода. |
targets | string[] | Да | Непустой массив допустимых языковых кодов (макс. 36). |
format | string | Нет | Один из plain, markdown, json, html. Если опущено, формат автоматически определяется по content. |
source | string | Нет | Подсказка исходного языка для модели; необязательно. |
model | string | Нет | standard (по умолчанию) или advanced. advanced требует платный уровень (403 для бесплатного). |
Ответ 200
{
"translations": {
"es": "...",
"fr": "..."
},
"usage": {
"input_tokens": 120,
"output_tokens": 340,
"total_tokens": 460,
"model": "standard",
"detected_format": "markdown",
"detection_confidence": 0.95
}
}
detected_format и detection_confidence появляются только если format был опущен и выполнено автоопределение.
Пример (cURL)
curl -sS -X POST "https://api.usepolylingo.com/v1/translate" \
-H "Authorization: Bearer $POLYLINGO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "{\"title\":\"Hello\"}",
"format": "json",
"targets": ["fr", "de"]
}'
Пример (Python 3)
pip install requests
import os, requests
url = "https://api.usepolylingo.com/v1/translate"
headers = {
"Authorization": f"Bearer {os.environ['POLYLINGO_API_KEY']}",
"Content-Type": "application/json",
}
r = requests.post(url, json={
"content": "<p>Hello <strong>world</strong></p>",
"format": "html",
"targets": ["es"],
}, timeout=120)
r.raise_for_status()
print(r.json()["translations"]["es"])
POST /translate/batch
Требуется Authorization: Bearer <api_key>.
Обрабатывает каждый элемент последовательно (один вызов модели на элемент). Если какой-либо элемент не удаётся, API возвращает 500 и не возвращает частичные результаты для этого запроса.
Тело запроса
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
items | array | Да | Каждый элемент: id (string), content (string), необязательный format. |
targets | string[] | Да | Те же правила, что и для /translate. |
source | string | Нет | Необязательная подсказка исходного языка. |
model | string | Нет | standard или advanced (те же правила, что и для /translate). |
Ответ 200
{
"results": [
{ "id": "welcome", "translations": { "fr": "...", "de": "..." } },
{ "id": "goodbye", "translations": { "fr": "...", "de": "..." } }
],
"usage": {
"total_tokens": 900,
"input_tokens": 400,
"output_tokens": 500,
"model": "standard"
}
}
Пример
curl -sS -X POST "https://api.usepolylingo.com/v1/translate/batch" \
-H "Authorization: Bearer $POLYLINGO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"items": [
{ "id": "a", "content": "Hello", "format": "plain" },
{ "id": "b", "content": "## Title", "format": "markdown" }
],
"targets": ["es", "it"]
}'
POST /jobs
Требуется Authorization: Bearer <api_key>.
Добавляет задачу перевода в очередь и сразу возвращает job_id. Перевод выполняется в фоновом режиме — без риска таймаута HTTP независимо от размера содержимого. Запрашивайте результат через GET /jobs/:id.
Используйте этот endpoint вместо POST /translate при переводе больших документов (длинный Markdown, много целевых языков), когда время запроса может превысить таймаут вашего HTTP-клиента или прокси.
Тело запроса
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
content | string | Да | Непустая строка для перевода. |
targets | string[] | Да | Непустой массив допустимых языковых кодов (макс. 36). |
format | string | Нет | Один из plain, markdown, json, html. Автоматически определяется, если опущено. |
source | string | Нет | Подсказка исходного языка; необязательно. |
model | string | Нет | standard (по умолчанию) или advanced. |
Ответ 202
{
"job_id": "a1b2c3d4-...",
"status": "pending",
"created_at": "2025-03-23T12:00:00.000Z"
}
GET /jobs/:id
Требуется Authorization: Bearer <api_key>.
Запрашивает статус задачи, отправленной через POST /jobs. Запрашивайте каждые 5–10 секунд. Задачи принадлежат пользователю, который их отправил — другие пользователи получают 404.
Ответ (ожидание / обработка)
{
"job_id": "a1b2c3d4-...",
"status": "pending",
"created_at": "2025-03-23T12:00:00.000Z",
"updated_at": "2025-03-23T12:00:00.000Z",
"completed_at": null,
"queue_position": 3
}
status — pending (ожидание работника) или processing (работник взял задачу). queue_position (счёт с 1) показывает, сколько ожидающих или обрабатываемых задач было создано строго до этой — используйте для UI прогресса. Пропускается, если запрос подсчёта не удался.
Ответ (завершено)
{
"job_id": "a1b2c3d4-...",
"status": "completed",
"created_at": "2025-03-23T12:00:00.000Z",
"updated_at": "2025-03-23T12:00:02.000Z",
"completed_at": "2025-03-23T12:00:02.000Z",
"translations": {
"es": "...",
"fr": "..."
},
"usage": {
"input_tokens": 120,
"output_tokens": 340,
"total_tokens": 460,
"model": "standard"
}
}
Ответ (ошибка)
{
"job_id": "a1b2c3d4-...",
"status": "failed",
"error": "Model returned invalid JSON"
}
Пример (JavaScript)
const API = 'https://api.usepolylingo.com/v1'
const headers = {
'Authorization': `Bearer ${process.env.POLYLINGO_API_KEY}`,
'Content-Type': 'application/json',
}
// 1. Отправка
const submit = await fetch(`${API}/jobs`, {
method: 'POST',
headers,
body: JSON.stringify({ content: longMarkdown, format: 'markdown', targets: ['de', 'fr'] }),
})
const { job_id } = await submit.json()
// 2. Опрос
while (true) {
await new Promise(r => setTimeout(r, 10_000))
const poll = await fetch(`${API}/jobs/${job_id}`, { headers })
const job = await poll.json()
if (job.status === 'completed') { console.log(job.translations); break }
if (job.status === 'failed') { throw new Error(job.error) }
// Опционально: показать прогресс (queue_position с 1, пропускается если не в очереди)
if (job.queue_position != null) console.log(`Позиция в очереди: ${job.queue_position}`)
}
GET /usage
Требуется Authorization: Bearer <api_key> (стандартный поиск ключа — не внутренний путь обхода).
Возвращает использование токенов за текущий календарный месяц для аутентифицированного пользователя.
Ответ 200
{
"period_start": "2025-03-01T00:00:00.000Z",
"period_end": "2025-03-31T23:59:59.000Z",
"tokens_used": 12000,
"tokens_included": 100000,
"tokens_remaining": 88000,
"overage_tokens": 0,
"tier": "free"
}
tokens_included и tokens_remaining равны null для enterprise (неограниченный лимит в отчётах).
Форматы содержимого
Поддерживаемые значения format: plain, markdown, json, html.
| Формат | Сохраняется | Переводится |
|---|---|---|
plain | Переносы строк / абзацы | Весь видимый текст |
markdown | Синтаксис, ссылки (URL без изменений), блоки кода (дословно) | Текст и текст ссылок |
json | Ключи, структура, нестроковые типы | Только строковые значения |
html | Теги и атрибуты | Текстовые узлы и соответствующие атрибуты (см. подсказки) |
RTL и направление в вашем приложении
Для вывода plain и markdown API возвращает только переведённый текст — не добавляет dir="rtl" или обёртки. Устанавливайте направление текста в вашем UI (CSS direction, атрибут dir родительского элемента или i18n-лейаут вашего фреймворка) при отображении арабского, иврита или персидского.
Для формата html переведённая разметка может включать dir="rtl" там, где это уместно для RTL-целей; см. formatPrompts.js и HTML-тесты в scripts/test-translation.js.
Ответы с ошибками
Ошибки возвращаются в JSON, если возможно:
{
"error": "invalid_request",
"message": "Читаемое человеком описание"
}
| HTTP | error | Когда |
|---|---|---|
| 400 | invalid_request | Отсутствуют/некорректны поля тела (например, пустой content, неверные targets) |
| 400 | invalid_format | format не из поддерживаемого набора |
| 400 | invalid_language | Неизвестный код в targets |
| 401 | invalid_api_key | Отсутствует/искажён Authorization, неизвестный ключ, отозванный ключ |
| 403 | advanced_not_available | model: "advanced" на бесплатном уровне |
| 429 | token_limit_reached | Превышен месячный лимит бесплатного уровня (предварительная проверка) |
| 429 | rate_limit_reached | Лимит RPM в минуту (при включённом Redis) |
| 500 | translation_error | Ошибка модели/сети; безопасно повторить |
| 404 | not_found | GET /jobs/:id — задача не существует или принадлежит другому пользователю |
| 500 | server_error | POST /jobs — не удалось поставить в очередь; безопасно повторить |
GET /usage может вернуть 500 с общей ошибкой, если запрос Supabase не удался.
Основные модели (информационно)
API предоставляет только standard и advanced. Фактические идентификаторы моделей OpenAI настроены в api/utils/modelRouter.js и не возвращаются в ответах API.