Skip to content

MTQE API (Machine Translation Quality Estimation)

The MTQE API scores the quality of translations from 0 to 100, without the need for a human reference, and explains the main issue found in each segment. It can judge them against your own translation memories and glossaries, and post-edit automatically the segments that fall short. See the introduction for what the service does.

To use the API, an API key provided by Pangeanic at the time of sale is required. To get one, contact our sales team at clients@pangeanic.com. Every example below can be copied and run as is: replace YOUR_API_KEY with your API key.

  • Base URL: https://api.pangeanic.com/mtqe/v2
  • Authentication: send your key in the X-API-Key header of every request.
  • Languages: codes like en-us or es-es. See supported languages.
  • Two ways to work: up to 50 segments per request, answered at once; or a job for up to 2,000.
  • Interactive reference: https://api.pangeanic.com/mtqe/v2/docs
What Endpoint
Your usage GET /usage
Supported languages GET /languages
Score up to 50 segments, answered at once POST /score-with-id-resources · POST /score-with-text-resources
Upload memories and glossaries POST /resource/add_new · GET /resource/status · GET /resource/list
Score up to 2,000 segments as a job POST /jobs/upload-file-job · POST /jobs/score-with-id-resources · POST /jobs/score-with-text-resources
Follow a job and get its result GET /jobs/status-job · GET /jobs/get-job · GET /jobs/get-job-results · GET /jobs/list-jobs

Supported Languages

Languages are sent as language-REGION codes (e.g. en-us, es-es). Any two supported languages can be combined as a language pair.

Notes:

  • Codes are case-insensitive; the API returns them in lowercase.
  • The bare language code is also accepted and maps to its default variant, for example en → en-us, es → es-es, pt → pt-pt and zh → zh-cn.
  • GET /languages (step 2) returns the same list.
Available languages (click to expand)
Language Code
Amharic am-et
Arabic ar-001
Armenian hy-am
Azerbaijani az-az
Basque eu-es
Bengali bn-bd
Bulgarian bg-bg
Burmese my-mm
Catalan ca-es
Chinese (Simplified) zh-cn
Chinese (Traditional) zh-tw
Croatian hr-hr
Czech cs-cz
Danish da-dk
Dutch nl-nl
Egyptian Arabic ar-eg
English en-us
English en-gb
Estonian et-ee
Filipino fil-ph
Finnish fi-fi
French fr-fr
French fr-ca
Galician gl-es
Georgian ka-ge
German de-de
German de-at
Greek el-gr
Gujarati gu-in
Hausa ha-ng
Hebrew he-il
Hindi hi-in
Hungarian hu-hu
Icelandic is-is
Indonesian id-id
Italian it-it
Japanese ja-jp
Kannada kn-in
Korean ko-kr
Latvian lv-lv
Lithuanian lt-lt
Macedonian mk-mk
Malay ms-my
Malayalam ml-in
Marathi mr-in
Mexican Spanish es-mx
Norwegian nb-no
Norwegian Nynorsk nn-no
Occitan oc-fr
Persian fa-ir
Polish pl-pl
Portuguese pt-pt
Portuguese pt-br
Punjabi pa-in
Quechua qu-pe
Romanian ro-ro
Russian ru-ru
Saudi Arabic ar-sa
Serbian sr-rs
Slovak sk-sk
Slovenian sl-si
Spanish es-es
Swahili (Kenya) sw-ke
Swahili (Tanzania) sw-tz
Swedish sv-se
Tamil ta-in
Telugu te-in
Thai th-th
Turkish tr-tr
Ukrainian uk-ua
Urdu ur-pk
Vietnamese vi-vn
Zulu zu-za

Getting started

1. Check your key and your usage

GET /usage

URL: https://api.pangeanic.com/mtqe/v2/usage

Confirms that your key works and shows what you have used: characters for scoring, characters for automatic post-edition, your limit and what is left, per endpoint, and the memory and glossary files you have used in the period (files_used, files). It costs nothing.

import requests

BASE = "https://api.pangeanic.com/mtqe/v2"
HEADERS = {"X-API-Key": "YOUR_API_KEY"}

usage = requests.get(f"{BASE}/usage", headers=HEADERS).json()
print(usage["total_source_chars_used"], "used ·", usage["chars_remaining"], "left")
curl "https://api.pangeanic.com/mtqe/v2/usage" \
  -H "X-API-Key: YOUR_API_KEY"
{
  "client": "acme",
  "client_id": 7,
  "is_admin": false,
  "expires_at": null,
  "char_quota": 1000000,
  "source_chars_used": 24105,
  "ape_source_chars_used": 770,
  "total_source_chars_used": 24875,
  "chars_remaining": 975125,
  "files_used": 3,
  "files": [
    {
      "id": "tm-5c1f0a9e2b7d",
      "resource_type": "tm",
      "filename": "memory_1.tmx",
      "source_language": "en-us",
      "target_language": "es-es",
      "first_used_at": 1792050000.0,
      "last_used_at": 1792310000.0,
      "requests": 14
    },
    {
      "id": "tm-9a3e7c51d2f4",
      "resource_type": "tm",
      "filename": "memory_2.tmx",
      "source_language": "en-us",
      "target_language": "es-es",
      "first_used_at": 1792050000.0,
      "last_used_at": 1792310000.0,
      "requests": 14
    },
    {
      "id": "gl-3b7e1d0c5a92",
      "resource_type": "glossary",
      "filename": "terms.tbx",
      "source_language": "en-us",
      "target_language": "es-es",
      "first_used_at": 1792220000.0,
      "last_used_at": 1792220000.0,
      "requests": 1
    }
  ],
  "by_endpoint": [
    {
      "endpoint": "jobs/upload-file-job",
      "requests": 2,
      "segments": 300,
      "source_chars": 19900,
      "ape_segments": 12,
      "ape_source_chars": 770
    },
    {
      "endpoint": "score-with-id-resources",
      "requests": 41,
      "segments": 82,
      "source_chars": 4205,
      "ape_segments": 0,
      "ape_source_chars": 0
    }
  ]
}

Note

char_quota and chars_remaining are null when your key has no limit.

2. List the supported languages

GET /languages

URL: https://api.pangeanic.com/mtqe/v2/languages

The language codes you can use as source_language and target_language, with their names.

import requests

BASE = "https://api.pangeanic.com/mtqe/v2"
HEADERS = {"X-API-Key": "YOUR_API_KEY"}

languages = requests.get(f"{BASE}/languages", headers=HEADERS).json()["languages"]
for lang in languages:
    print(lang["code"], lang["name"])
curl "https://api.pangeanic.com/mtqe/v2/languages" \
  -H "X-API-Key: YOUR_API_KEY"
{
  "languages": [
    {
      "code": "am-et",
      "name": "Amharic"
    },
    {
      "code": "ar-001",
      "name": "Arabic"
    },
    {
      "code": "hy-am",
      "name": "Armenian"
    },
    {
      "code": "…",
      "name": "… (73 in all)"
    }
  ]
}

Scoring, answered at once

3. Basic score: no memories, no glossary, no post-edition

POST /score-with-id-resources

URL: https://api.pangeanic.com/mtqe/v2/score-with-id-resources

Send the segments with their language pair. Each one comes back with its score and the main issue found.

Request Parameters

Parameter Type Location Description
X-API-Key string Header Your API access key.
segments array Body 1 to 50 objects with source and target.
source_language string Body Language of every source (e.g. en-us). See supported languages.
target_language string Body Language of every target (e.g. es-es). See supported languages.
tm_id array Body Optional. Ids of your translation memories (step 4).
glossary_id array Body Optional. Ids of your glossaries (step 4).
ape boolean Body Optional. true to post-edit automatically the segments that fall short (step 7). Default false.
ape_threshold integer Body Optional. 0-100. Segments below it are post-edited. Default 90.
import requests

BASE = "https://api.pangeanic.com/mtqe/v2"
HEADERS = {"X-API-Key": "YOUR_API_KEY"}

segments = [
    {"source": "Please save your work before closing the application.",
     "target": "Guarde su trabajo antes de cerrar la aplicación."},
    {"source": "He did not finish the work on time.",
     "target": "Él terminó el trabajo a tiempo."},
]

body = {"segments": segments, "source_language": "en-us", "target_language": "es-es"}
r = requests.post(f"{BASE}/score-with-id-resources", headers=HEADERS, json=body)
r.raise_for_status()
for seg in r.json()["results"]:
    print(seg["score"], seg["explanation"])
curl -X POST "https://api.pangeanic.com/mtqe/v2/score-with-id-resources" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "segments": [
    {"source": "Please save your work before closing the application.",
     "target": "Guarde su trabajo antes de cerrar la aplicación."},
    {"source": "He did not finish the work on time.",
     "target": "Él terminó el trabajo a tiempo."}
  ],
  "source_language": "en-us",
  "target_language": "es-es"
}'
{
  "request_id": "5b2e9f7c1d3a4b6e8f0a2c4d6e8f1a3b",
  "source_language": "en-us",
  "target_language": "es-es",
  "results": [
    {
      "source": "Please save your work before closing the application.",
      "target": "Guarde su trabajo antes de cerrar la aplicación.",
      "source_chars": 53,
      "score": 96.0,
      "explanation": "",
      "references": {
        "tm": [],
        "glossary": []
      },
      "error": null
    },
    {
      "source": "He did not finish the work on time.",
      "target": "Él terminó el trabajo a tiempo.",
      "source_chars": 35,
      "score": 22.5,
      "explanation": "The negation was dropped: the target says the opposite of the source.",
      "references": {
        "tm": [],
        "glossary": []
      },
      "error": null
    }
  ]
}

4. Upload translation memories or glossaries

POST /resource/add_new

URL: https://api.pangeanic.com/mtqe/v2/resource/add_new

Upload your memories once and reuse them in every request. Each file gets an id; keep them. A glossary is uploaded the same way, with resource_type glossary.

Request Parameters

Parameter Type Location Description
X-API-Key string Header Your API access key.
files file Form One or more files, up to 20 MB each. Memories: .tmx, .csv, .tsv. Glossaries: .tbx, .csv, .tsv.
resource_type string Form tm or glossary.
source_language string Form Source language of the files (e.g. en-us).
target_language string Form Target language of the files (e.g. es-es).
import requests

BASE = "https://api.pangeanic.com/mtqe/v2"
HEADERS = {"X-API-Key": "YOUR_API_KEY"}

# uploading two TM
files = [("files", open("memory_1.tmx", "rb")),
         ("files", open("memory_2.tmx", "rb"))]
data = {"resource_type": "tm", "source_language": "en-us", "target_language": "es-es"}
r = requests.post(f"{BASE}/resource/add_new", headers=HEADERS, files=files, data=data)
r.raise_for_status()
ids = [res["id"] for res in r.json()["resources"]]
print(ids)
# uploading two TM
curl -X POST "https://api.pangeanic.com/mtqe/v2/resource/add_new" \
  -H "X-API-Key: YOUR_API_KEY" \
  -F "files=@memory_1.tmx" \
  -F "files=@memory_2.tmx" \
  -F "resource_type=tm" \
  -F "source_language=en-us" \
  -F "target_language=es-es"
{
  "resources": [
    {
      "id": "tm-5c1f0a9e2b7d",
      "resource_type": "tm",
      "filename": "memory_1.tmx",
      "source_language": "en-us",
      "target_language": "es-es",
      "status": "SCHEDULED",
      "error_code": null,
      "error_message": null,
      "failed_entries": 0,
      "created_at": 1792735200.0,
      "shared": null
    },
    {
      "id": "tm-9a3e7c51d2f4",
      "resource_type": "tm",
      "filename": "memory_2.tmx",
      "source_language": "en-us",
      "target_language": "es-es",
      "status": "SCHEDULED",
      "error_code": null,
      "error_message": null,
      "failed_entries": 0,
      "created_at": 1792735200.0,
      "shared": null
    }
  ]
}

Note

A memory only serves segments of its language pair. Upload each pair separately.

5. Wait until the memories are ready

GET /resource/status

URL: https://api.pangeanic.com/mtqe/v2/resource/status

A memory is indexed in the background after the upload. Check its status until it is SUCCESS; then it can be used. FAILED means it could not be used (see error_message).

import requests

BASE = "https://api.pangeanic.com/mtqe/v2"
HEADERS = {"X-API-Key": "YOUR_API_KEY"}
import time

ids = ["tm-5c1f0a9e2b7d", "tm-9a3e7c51d2f4"]   # the ids from the upload
while True:
    r = requests.get(f"{BASE}/resource/status", headers=HEADERS, params={"id": ids})
    status = {res["id"]: res["status"] for res in r.json()["resources"]}
    print(status)
    if all(s in ("SUCCESS", "FAILED", "REJECTED") for s in status.values()):
        break
    time.sleep(10)
curl "https://api.pangeanic.com/mtqe/v2/resource/status?id=tm-5c1f0a9e2b7d&id=tm-9a3e7c51d2f4" \
  -H "X-API-Key: YOUR_API_KEY"
{
  "resources": [
    {
      "id": "tm-5c1f0a9e2b7d",
      "resource_type": "tm",
      "filename": "memory_1.tmx",
      "source_language": "en-us",
      "target_language": "es-es",
      "status": "SUCCESS",
      "error_code": null,
      "error_message": null,
      "failed_entries": 0,
      "created_at": 1792735200.0,
      "shared": null
    },
    {
      "id": "tm-9a3e7c51d2f4",
      "resource_type": "tm",
      "filename": "memory_2.tmx",
      "source_language": "en-us",
      "target_language": "es-es",
      "status": "SUCCESS",
      "error_code": null,
      "error_message": null,
      "failed_entries": 0,
      "created_at": 1792735200.0,
      "shared": null
    }
  ]
}

6. Score with the two memories

POST /score-with-id-resources

URL: https://api.pangeanic.com/mtqe/v2/score-with-id-resources

The same request as the basic score, with the memory ids in tm_id (always a list). Each segment is judged against the most relevant entries of your memories, and they come back in references. Glossaries go in glossary_id the same way.

import requests

BASE = "https://api.pangeanic.com/mtqe/v2"
HEADERS = {"X-API-Key": "YOUR_API_KEY"}

segments = [
    {"source": "Please save your work before closing the application.",
     "target": "Guarde su trabajo antes de cerrar la aplicación."},
    {"source": "He did not finish the work on time.",
     "target": "Él terminó el trabajo a tiempo."},
]

body = {
    "segments": segments,
    "source_language": "en-us",
    "target_language": "es-es",
    "tm_id": ["tm-5c1f0a9e2b7d", "tm-9a3e7c51d2f4"],
}
r = requests.post(f"{BASE}/score-with-id-resources", headers=HEADERS, json=body)
r.raise_for_status()
for seg in r.json()["results"]:
    print(seg["score"], seg["explanation"], len(seg["references"]["tm"]), "memory matches")
curl -X POST "https://api.pangeanic.com/mtqe/v2/score-with-id-resources" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "segments": [
    {"source": "Please save your work before closing the application.",
     "target": "Guarde su trabajo antes de cerrar la aplicación."},
    {"source": "He did not finish the work on time.",
     "target": "Él terminó el trabajo a tiempo."}
  ],
  "source_language": "en-us",
  "target_language": "es-es",
  "tm_id": ["tm-5c1f0a9e2b7d", "tm-9a3e7c51d2f4"]
}'
{
  "request_id": "8d1e4c2a9b7f4e3d8c6a5b4f3e2d1c0b",
  "source_language": "en-us",
  "target_language": "es-es",
  "results": [
    {
      "source": "Please save your work before closing the application.",
      "target": "Guarde su trabajo antes de cerrar la aplicación.",
      "source_chars": 53,
      "score": 97.5,
      "explanation": "",
      "references": {
        "tm": [
          {
            "source": "Please save your changes before closing the window.",
            "target": "Guarde los cambios antes de cerrar la ventana.",
            "score": 0.86
          }
        ],
        "glossary": []
      },
      "error": null
    },
    {
      "source": "He did not finish the work on time.",
      "target": "Él terminó el trabajo a tiempo.",
      "source_chars": 35,
      "score": 24.0,
      "explanation": "The negation was dropped: the target says the opposite of the source.",
      "references": {
        "tm": [
          {
            "source": "He did not finish the report on time.",
            "target": "No terminó el informe a tiempo.",
            "score": 0.91
          }
        ],
        "glossary": []
      },
      "error": null
    }
  ]
}

7. Score with the two memories and automatic post-edition

POST /score-with-id-resources

URL: https://api.pangeanic.com/mtqe/v2/score-with-id-resources

Add "ape": true. Every segment below ape_threshold (default 90) or with an issue is post-edited automatically and the post-edition is scored. hpe_needed says whether a person still has to review it. The post-edition is charged only when it fixes the segment.

import requests

BASE = "https://api.pangeanic.com/mtqe/v2"
HEADERS = {"X-API-Key": "YOUR_API_KEY"}

segments = [
    {"source": "Please save your work before closing the application.",
     "target": "Guarde su trabajo antes de cerrar la aplicación."},
    {"source": "He did not finish the work on time.",
     "target": "Él terminó el trabajo a tiempo."},
]

body = {
    "segments": segments,
    "source_language": "en-us",
    "target_language": "es-es",
    "tm_id": ["tm-5c1f0a9e2b7d", "tm-9a3e7c51d2f4"],
    "ape": True,
    "ape_threshold": 90,   # optional, 0-100
}
r = requests.post(f"{BASE}/score-with-id-resources", headers=HEADERS, json=body)
r.raise_for_status()
for seg in r.json()["results"]:
    if seg["ape_needed"]:
        print(seg["score"], "→", seg["APE"]["score_ape"], seg["APE"]["target_ape"],
              "· human review" if seg["hpe_needed"] else "")
curl -X POST "https://api.pangeanic.com/mtqe/v2/score-with-id-resources" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "segments": [
    {"source": "Please save your work before closing the application.",
     "target": "Guarde su trabajo antes de cerrar la aplicación."},
    {"source": "He did not finish the work on time.",
     "target": "Él terminó el trabajo a tiempo."}
  ],
  "source_language": "en-us",
  "target_language": "es-es",
  "tm_id": ["tm-5c1f0a9e2b7d", "tm-9a3e7c51d2f4"],
  "ape": true,
  "ape_threshold": 90
}'
{
  "request_id": "0f1e2d3c4b5a49687766554433221100",
  "source_language": "en-us",
  "target_language": "es-es",
  "results": [
    {
      "source": "Please save your work before closing the application.",
      "target": "Guarde su trabajo antes de cerrar la aplicación.",
      "source_chars": 53,
      "score": 97.5,
      "explanation": "",
      "references": {
        "tm": [
          {
            "source": "Please save your changes before closing the window.",
            "target": "Guarde los cambios antes de cerrar la ventana.",
            "score": 0.86
          }
        ],
        "glossary": []
      },
      "error": null,
      "ape_needed": false,
      "APE": {
        "target_ape": "",
        "score_ape": null,
        "explanation_ape": ""
      },
      "hpe_needed": false,
      "ape_source_chars": 0
    },
    {
      "source": "He did not finish the work on time.",
      "target": "Él terminó el trabajo a tiempo.",
      "source_chars": 35,
      "score": 24.0,
      "explanation": "The negation was dropped: the target says the opposite of the source.",
      "references": {
        "tm": [
          {
            "source": "He did not finish the report on time.",
            "target": "No terminó el informe a tiempo.",
            "score": 0.91
          }
        ],
        "glossary": []
      },
      "error": null,
      "ape_needed": true,
      "APE": {
        "target_ape": "Él no terminó el trabajo a tiempo.",
        "score_ape": 98,
        "explanation_ape": ""
      },
      "hpe_needed": false,
      "ape_source_chars": 35
    }
  ]
}

8. Score with references sent in the request

POST /score-with-text-resources

URL: https://api.pangeanic.com/mtqe/v2/score-with-text-resources

No upload needed: each segment carries its own memory entries (tm) and glossary terms (glossary), as source/target pairs, up to 20 of each. Useful when the references live in your own system. They are not charged, and the response does not repeat them. Takes ape like the previous steps.

import requests

BASE = "https://api.pangeanic.com/mtqe/v2"
HEADERS = {"X-API-Key": "YOUR_API_KEY"}

body = {
    "segments": [{
        "source": "Hold the start key for four seconds to restart the scanner.",
        "target": "Mantenga la tecla de inicio cuatro segundos para reiniciar el escaner.",
        "tm": [{"source": "Hold the start key for two seconds.",
                "target": "Mantenga pulsada la tecla de inicio dos segundos."}],
        "glossary": [{"source": "scanner", "target": "escáner"}],
    }],
    "source_language": "en-us",
    "target_language": "es-es",
}
r = requests.post(f"{BASE}/score-with-text-resources", headers=HEADERS, json=body)
r.raise_for_status()
print(r.json()["results"][0])
curl -X POST "https://api.pangeanic.com/mtqe/v2/score-with-text-resources" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "segments": [{
    "source": "Hold the start key for four seconds to restart the scanner.",
    "target": "Mantenga la tecla de inicio cuatro segundos para reiniciar el escaner.",
    "tm": [{"source": "Hold the start key for two seconds.",
            "target": "Mantenga pulsada la tecla de inicio dos segundos."}],
    "glossary": [{"source": "scanner", "target": "escáner"}]
  }],
  "source_language": "en-us",
  "target_language": "es-es"
}'
{
  "request_id": "3f9c2b7e1a4d4e0c9b8a7f6e5d4c3b2a",
  "source_language": "en-us",
  "target_language": "es-es",
  "results": [
    {
      "source": "Hold the start key for four seconds to restart the scanner.",
      "target": "Mantenga la tecla de inicio cuatro segundos para reiniciar el escaner.",
      "source_chars": 59,
      "score": 74.0,
      "explanation": "«escaner» does not follow the glossary: «escáner».",
      "error": null
    }
  ]
}

Many segments: jobs

9. Score a whole file

POST /jobs/upload-file-job

URL: https://api.pangeanic.com/mtqe/v2/jobs/upload-file-job

Upload an .xlsx or .csv with the columns source_language, target_language, source, target (one segment per row; pairs may be mixed). The job runs in the background; you get a job_id at once. Memory and glossary ids go one field per id; ape works as above. GET /jobs/template downloads an example file.

import requests

BASE = "https://api.pangeanic.com/mtqe/v2"
HEADERS = {"X-API-Key": "YOUR_API_KEY"}

files = {"file": open("segments.xlsx", "rb")}
data = [("tm_id", "tm-5c1f0a9e2b7d"), ("tm_id", "tm-9a3e7c51d2f4"), ("ape", "true")]
r = requests.post(f"{BASE}/jobs/upload-file-job", headers=HEADERS, files=files, data=data)
r.raise_for_status()
job_id = r.json()["job_id"]
print(job_id)
curl -X POST "https://api.pangeanic.com/mtqe/v2/jobs/upload-file-job" \
  -H "X-API-Key: YOUR_API_KEY" \
  -F "file=@segments.xlsx" \
  -F "tm_id=tm-5c1f0a9e2b7d" \
  -F "tm_id=tm-9a3e7c51d2f4" \
  -F "ape=true"
{
  "job_id": "a1b2c3d4e5f64789a0b1c2d3e4f5a6b7",
  "status": "pending",
  "total": 1200,
  "source_chars": 79200,
  "ape": true,
  "ape_threshold": 90
}

Note

The file is checked before the job is created: if something is wrong, the answer is a 422 listing every problem, and nothing is charged.

10. Score many segments sent as JSON

POST /jobs/score-with-id-resources · POST /jobs/score-with-text-resources

URL: https://api.pangeanic.com/mtqe/v2/jobs/score-with-id-resources · https://api.pangeanic.com/mtqe/v2/jobs/score-with-text-resources

The same body as the requests answered at once, with up to 2,000 segments. Use /jobs/score-with-id-resources with your memory and glossary ids, or /jobs/score-with-text-resources with the references in each segment.

import requests

BASE = "https://api.pangeanic.com/mtqe/v2"
HEADERS = {"X-API-Key": "YOUR_API_KEY"}

segments = [
    {"source": "Please save your work before closing the application.",
     "target": "Guarde su trabajo antes de cerrar la aplicación."},
    {"source": "He did not finish the work on time.",
     "target": "Él terminó el trabajo a tiempo."},
]   # up to 2,000

body = {
    "segments": segments,
    "source_language": "en-us",
    "target_language": "es-es",
    "tm_id": ["tm-5c1f0a9e2b7d", "tm-9a3e7c51d2f4"],
    "ape": True,
}
r = requests.post(f"{BASE}/jobs/score-with-id-resources", headers=HEADERS, json=body)
r.raise_for_status()
job_id = r.json()["job_id"]
print(job_id)
curl -X POST "https://api.pangeanic.com/mtqe/v2/jobs/score-with-id-resources" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "segments": [
    {"source": "Please save your work before closing the application.",
     "target": "Guarde su trabajo antes de cerrar la aplicación."},
    {"source": "He did not finish the work on time.",
     "target": "Él terminó el trabajo a tiempo."}
  ],
  "source_language": "en-us",
  "target_language": "es-es",
  "tm_id": ["tm-5c1f0a9e2b7d", "tm-9a3e7c51d2f4"],
  "ape": true
}'
{
  "job_id": "a1b2c3d4e5f64789a0b1c2d3e4f5a6b7",
  "status": "pending",
  "total": 1200,
  "source_chars": 79200,
  "ape": true,
  "ape_threshold": 90
}

11. Follow a job and get its result

GET /jobs/status-job · GET /jobs/get-job · GET /jobs/get-job-results

URL: https://api.pangeanic.com/mtqe/v2/jobs/status-job · https://api.pangeanic.com/mtqe/v2/jobs/get-job · https://api.pangeanic.com/mtqe/v2/jobs/get-job-results

Ask for the status until it is done (or failed). Then download the result: an .xlsx for every job, and for jobs sent as JSON also the JSON, the same as the requests answered at once.

import requests

BASE = "https://api.pangeanic.com/mtqe/v2"
HEADERS = {"X-API-Key": "YOUR_API_KEY"}
import time

job_id = "a1b2c3d4e5f64789a0b1c2d3e4f5a6b7"
while True:
    job = requests.get(f"{BASE}/jobs/status-job", headers=HEADERS,
                       params={"job_id": job_id}).json()
    print(job["status"], job["processed"], "/", job["total"])
    if job["status"] in ("done", "failed"):
        break
    time.sleep(5)

# The result as .xlsx (every job)
r = requests.get(f"{BASE}/jobs/get-job", headers=HEADERS, params={"job_id": job_id})
open("result.xlsx", "wb").write(r.content)

# The result as JSON (jobs sent as JSON)
result = requests.get(f"{BASE}/jobs/get-job-results", headers=HEADERS,
                      params={"job_id": job_id}).json()
# status
curl "https://api.pangeanic.com/mtqe/v2/jobs/status-job?job_id=a1b2c3d4e5f64789a0b1c2d3e4f5a6b7" \
  -H "X-API-Key: YOUR_API_KEY"

# result as .xlsx (every job)
curl "https://api.pangeanic.com/mtqe/v2/jobs/get-job?job_id=a1b2c3d4e5f64789a0b1c2d3e4f5a6b7" \
  -H "X-API-Key: YOUR_API_KEY" -o result.xlsx

# result as JSON (jobs sent as JSON)
curl "https://api.pangeanic.com/mtqe/v2/jobs/get-job-results?job_id=a1b2c3d4e5f64789a0b1c2d3e4f5a6b7" \
  -H "X-API-Key: YOUR_API_KEY"
{
  "job_id": "a1b2c3d4e5f64789a0b1c2d3e4f5a6b7",
  "status": "done",
  "kind": "file",
  "filename": "segments.xlsx",
  "total": 1200,
  "processed": 1200,
  "progress": 1.0,
  "tm_ids": [
    "tm-5c1f0a9e2b7d",
    "tm-9a3e7c51d2f4"
  ],
  "glossary_ids": [],
  "error": null,
  "created_at": 1792735200.0,
  "updated_at": 1792735450.0,
  "for_client_id": 7,
  "ape": true,
  "ape_threshold": 90,
  "ape_segments": 95,
  "ape_source_chars": 5910
}

Note

The example output is the status. The .xlsx has one row per segment with score, explanation and the references used, plus the post-edition columns when ape was on.


Score Interpretation

Score Range Estimated Quality Recommended Action
90 - 100 Excellent Direct publication or minimal review.
70 - 89 Good Light human review (light MTPE). Check the explanation.
40 - 69 Medium / Low Requires mandatory deep post-editing, or use ape.
< 40 Very Low Invalid translation or incoherent content.

Disclaimer

This interpretation is solely a suggestion. The final decision on quality will always depend on the degree of strictness and the specific quality requirements (KPIs) required for each translation project.

Reference

The result of each segment

Field Meaning
score Quality from 0 to 100. null if it could not be computed (see error).
explanation The main issue found, in one sentence. Empty when there is none.
references The translation memory matches (tm) and glossary terms (glossary) the segment was judged against. Their score is how relevant the entry is to the segment (0–1), not a quality score. Not returned when you send the references yourself (step 8).
source_chars What the segment was charged: the characters of its source.
error Why score is null, if it is.

With automatic post-edition ("ape": true)

Field Meaning
ape_needed The segment scored below ape_threshold (default 90) or had an issue, so it was post-edited.
APE.target_ape The post-edited translation. Empty when it was not needed.
APE.score_ape, APE.explanation_ape The score of the post-edited translation and any issue left in it.
hpe_needed The post-edition still falls short: a person should review the segment.
ape_source_chars What the post-edition was charged: the characters of the source again, only when it fixed the segment (hpe_needed: false). Otherwise 0.

What is charged

What is counted When
Scoring The source of every segment When the request (or job) is accepted
Automatic post-edition The source again, only for the segments it fixed When the request (or job) finishes
Files Each memory or glossary (tm_id / glossary_id) used in the period, counted once however many requests use it: 3 files, then the same 3 = 3; those 3 plus a 4th = 4 When the request (or job) is accepted
Free Uploading memories and glossaries, the references sent as text with a segment, and every query: usage, languages, status, results.

Note

Your character limit covers scoring and post-edition together; files are counted apart. Step 1 shows how much you have used and how much is left. Results are not cached: sending the same segment again scores it, and charges it, again.

Limits and file formats

Segments per request 1 to 50 (more: use a job)
Segments per job Up to 2,000
Files Up to 20 MB each
Translation memories .tmx, .csv, .tsv
Glossaries .tbx, .csv, .tsv
Job files .xlsx or .csv; first row exactly source_language, target_language, source, target; one segment per row. GET /jobs/template downloads an example.
References sent with a segment Up to 20 memory entries and 20 glossary terms per segment

Other endpoints

Endpoint What it does
GET /resource/list Your memories and glossaries, with their ids and status.
GET /jobs/list-jobs Your recent jobs.
GET /jobs/template An example job file (.xlsx) to fill in.
POST /jobs/validate-job Checks a job file without creating a job or charging anything.
GET /healthcheck Whether the service is up. No key needed.
GET /docs The interactive API reference.

Common Errors

The body of an error is {"detail": "…"} with the reason.

  • 401 (Unauthorized): Missing, wrong, deactivated or expired API key.
  • 404 (Not Found): A memory, glossary or job that does not exist for your key.
  • 409 (Conflict): A job result asked for before the job is done.
  • 413 (Payload Too Large): A file over the size limit.
  • 422 (Unprocessable Entity): Something in the request is not valid: an unsupported language, a memory of another language pair, too many segments, a malformed file… The response says what.
  • 429 (Too Many Requests): Character limit reached. Nothing was charged.
  • 502, 503, 504: The service is busy or temporarily unavailable. Try again in a moment.