Client code in five languages

Δεν έχει μεταφραστεί ακόμη — εμφανίζεται στα αγγλικά.

We do not publish a client package. The API is one JSON call over HTTPS, and a dependency for that adds a supply chain without removing much typing. The functions below are complete; copy the one you need and adapt it.

All of them read the key from the environment variable LANGUAGE_API_KEY.

Three behaviours worth implementing

  1. Batch. Up to 50 texts per request. One call with many strings uses one

rate-limit slot and one round trip. See Request rate limits.

  1. Retry selectively. Only rate_limited, engine_unavailable,

service_unavailable and internal_error are worth a second attempt. Branch on code, never on detail. See Error codes reference.

  1. Record X-Request-Id. Without it, a support question about a specific

request cannot be answered quickly.

Python

import os
import random
import time

import requests

API = "https://api.langapi.xyz/v1/translate"
TRANSIENT = {"rate_limited", "engine_unavailable", "service_unavailable", "internal_error"}


def translate_batch(strings: list[str], target: str, *, source: str | None = None,
                    retries: int = 5, **extra) -> list[str]:
    body = {"text": strings, "target_lang": target, **extra}
    if source:
        body["source_lang"] = source
    auth = {"Authorization": f"Bearer {os.environ['LANGUAGE_API_KEY']}"}

    for n in range(retries):
        r = requests.post(API, json=body, headers=auth, timeout=40)
        if r.ok:
            return [item["text"] for item in r.json()["translations"]]
        problem = r.json()
        if problem["code"] not in TRANSIENT:
            raise RuntimeError(f"{problem['code']} [{problem['request_id']}]: {problem['detail']}")
        wait = float(r.headers.get("Retry-After", 2 ** n)) + random.uniform(0, 0.3)
        time.sleep(wait)
    raise RuntimeError("translate_batch: retries exhausted")


if __name__ == "__main__":
    print(translate_batch(["Save changes", "Discard"], "CS", source="EN", formality="prefer_less"))

TypeScript

type Translation = { text: string; detected_source_language: string };
type Reply = { translations: Translation[]; request_id: string; characters: number };
type Problem = { code: string; detail: string; request_id: string };

const TRANSIENT = new Set(['rate_limited', 'engine_unavailable', 'service_unavailable', 'internal_error']);

export async function translateBatch(
  strings: string[],
  target: string,
  extra: Record<string, unknown> = {},
  retries = 5,
): Promise<Translation[]> {
  const body = JSON.stringify({ text: strings, target_lang: target, ...extra });
  for (let n = 0; n < retries; n++) {
    const res = await fetch('https://api.langapi.xyz/v1/translate', {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.LANGUAGE_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body,
    });
    if (res.ok) {
      return ((await res.json()) as Reply).translations;
    }
    const problem = (await res.json()) as Problem;
    if (!TRANSIENT.has(problem.code)) {
      throw new Error(`${problem.code} [${problem.request_id}]: ${problem.detail}`);
    }
    const seconds = Number(res.headers.get('Retry-After') ?? 2 ** n);
    await new Promise((r) => setTimeout(r, seconds * 1000 + Math.random() * 300));
  }
  throw new Error('translateBatch: retries exhausted');
}

PHP

<?php
declare(strict_types=1);

/**
 * @param list<string> $strings
 * @param array<string, mixed> $extra
 * @return list<string>
 */
function translateBatch(array $strings, string $target, array $extra = []): array
{
    $body = json_encode(['text' => $strings, 'target_lang' => $target] + $extra, JSON_THROW_ON_ERROR);

    $handle = curl_init('https://api.langapi.xyz/v1/translate');
    curl_setopt_array($handle, [
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => $body,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 40,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . getenv('LANGUAGE_API_KEY'),
            'Content-Type: application/json',
        ],
    ]);
    $raw = curl_exec($handle);
    $status = (int) curl_getinfo($handle, CURLINFO_RESPONSE_CODE);
    curl_close($handle);

    if (!is_string($raw)) {
        throw new RuntimeException('no response from the API');
    }
    $data = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
    if ($status >= 400) {
        throw new RuntimeException(sprintf('%s [%s]: %s', $data['code'], $data['request_id'], $data['detail']));
    }
    return array_map(static fn(array $t): string => $t['text'], $data['translations']);
}

var_dump(translateBatch(['Save changes', 'Discard'], 'CS', ['source_lang' => 'EN']));

Go

package translate

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"time"
)

type payload struct {
	Text       []string `json:"text"`
	TargetLang string   `json:"target_lang"`
	SourceLang string   `json:"source_lang,omitempty"`
}

type reply struct {
	Translations []struct {
		Text string `json:"text"`
	} `json:"translations"`
	RequestID string `json:"request_id"`
}

type problem struct {
	Code      string `json:"code"`
	Detail    string `json:"detail"`
	RequestID string `json:"request_id"`
}

var httpClient = &http.Client{Timeout: 40 * time.Second}

func Batch(strings []string, target, source string) ([]string, error) {
	body, err := json.Marshal(payload{Text: strings, TargetLang: target, SourceLang: source})
	if err != nil {
		return nil, err
	}
	req, err := http.NewRequest(http.MethodPost, "https://api.langapi.xyz/v1/translate", bytes.NewReader(body))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("LANGUAGE_API_KEY"))
	req.Header.Set("Content-Type", "application/json")

	res, err := httpClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	if res.StatusCode >= 400 {
		var p problem
		_ = json.NewDecoder(res.Body).Decode(&p)
		return nil, fmt.Errorf("%s [%s]: %s", p.Code, p.RequestID, p.Detail)
	}
	var r reply
	if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
		return nil, err
	}
	out := make([]string, len(r.Translations))
	for i, t := range r.Translations {
		out[i] = t.Text
	}
	return out, nil
}

Shell

#!/usr/bin/env sh
## invoke as: ./translate.sh CS "Save changes" "Discard"
target="$1"; shift
printf '%s\n' "$@" \
  | jq -R . | jq -s --arg lang "$target" '{text: ., target_lang: $lang}' \
  | curl -sS https://api.langapi.xyz/v1/translate \
      -H "Authorization: Bearer ${LANGUAGE_API_KEY}" \
      -H "Content-Type: application/json" \
      --data-binary @- \
  | jq -r '.translations[].text'

Using a DeepL client instead

The DeepL libraries for Python and Node accept a custom server URL and work against this API without patches. That path is supported; see Switching from DeepL.

Αναθεωρήθηκε στις 7 Σεπ 2026, 12:00 π.μ.