Query Task Status
Synchronously query the current status and progress of a translation task. Designed for polling — this endpoint does not write to call logs, so high-frequency polling is safe (recommended interval ≥ 3s).
When to use
- Integrators without
callback_urlor unable to receive external webhooks - Agents / command-line scripts that need to wait synchronously for completion
- Manual diagnosis when a task appears stuck
If you can receive webhooks, prefer callbacks (see Create Translation Task · Callbacks) — they are more efficient and require no polling.
Endpoint
GET {BASE_URL}/api/open/v1/tasks/statusPOST is also accepted (place task_id in the JSON body).
Auth Headers
Same as Create Translation Task:
| Header | Value |
|---|---|
X-Loxily-AppKey | Project App Key |
X-Loxily-Timestamp | Unix seconds (5-min window) |
X-Loxily-Sign | MD5(timestamp + "" + appSecret) — GET has no body; the empty string participates in the signature |
Query Params
| Param | Type | Required | Description |
|---|---|---|---|
task_id | string | Yes | The task_id returned when creating the task |
Response
Success (HTTP 200):
{
"success": true,
"code": 0,
"msg": "ok",
"trace_id": "8a3f12ab-4d2a-9f51-7e2c6d8a9101",
"data": {
"task_id": "68a12cb7e1f9a88b4ea23c77",
"name": "Q2 onboarding strings",
"status": "running",
"current_stage": "translate",
"current_step": "translateBatch",
"source_language": "zh-hans",
"target_languages": ["en", "ja", "ko"],
"strings_count": 512,
"words_count": 512,
"progress": { "rows_total": 1536, "rows_done": 612 },
"jobs": [
{
"job_id": "translate_68a12cb7_...",
"job_type": "TRANSLATE",
"status": "RUNNING",
"step": "translateBatch",
"row_count": 1536,
"error": null,
"created_at": "2026-04-22T10:22:35.001Z",
"updated_at": "2026-04-22T10:25:11.301Z"
}
],
"smart_optimize_status": "pending",
"requires_confirmation": false,
"is_completed": false,
"is_failed": false,
"failure_reason": null,
"failure_code": null,
"accepted_at": "2026-04-22T10:22:31.123Z",
"updated_at": "2026-04-22T10:25:11.301Z"
}
}The 9 possible values of status
| status | Meaning | Typical current_stage |
|---|---|---|
pending | Task accepted; backend has not started yet | pending |
importing | Backend parsing inputs, generating CSV, uploading to GCS, triggering first workflow | import |
scanning | TB_SCAN (term scan) workflow in progress | tb_scan |
term_translating | Newly scanned terms are being auto-categorized and translated before entering the term base (only when auto_apply_terms=true and new terms were found) | tb_translate |
waiting_confirmation | TB_SCAN finished; waiting for /tasks/confirm-terms (only when auto_apply_terms=false) | tb_scan |
running | TRANSLATE workflow in progress. See current_step table below | translate |
optimizing | Smart Optimization (TRANSLATE_QA) workflow in progress. Translations exist but low-score rows are being re-translated — wait for completed before exporting | translate_qa |
completed | All stages done, including Smart Optimization — call /export/ini to download results | done |
failed | A stage terminated unsuccessfully; see failure_reason / failure_code | failed |
completed is double-gated
completed requires both the translation and Smart Optimization to reach a terminal state. During the window right after translation finishes — while the system is still deciding whether Smart Optimization is needed — status stays optimizing instead of leaking an early completed. Once you see completed, it is safe to publish / export immediately.
smart_optimize_status — Smart Optimization's own state
| Value | Meaning |
|---|---|
pending | Optimization decision not made yet (translation still running, or just finished and low-score rows are being counted) |
running | Smart Optimization (TRANSLATE_QA) workflow in progress |
completed | Smart Optimization finished |
skipped | No low-score rows; this task needs no optimization |
failed | Smart Optimization failed (does not affect the task's overall completed; translations remain usable, just unoptimized) |
null | Legacy task created before this feature shipped |
When status='completed', this field is guaranteed terminal (completed / skipped / failed).
Fully-automatic term pipeline with auto_apply_terms=true
With scan_terms=true + auto_apply_terms=true, newly scanned terms are automatically categorized, translated into all project target languages, and written to the term base — before the task translation starts, so the current task already benefits from the enforced term translations. If the term-translation stage fails, the pipeline degrades gracefully to "import source terms only and continue", so the task never gets stuck.
The 7 possible values of current_step (only when status='running')
The 7 sub-steps within the TRANSLATE workflow — matches the callback event names in Create Task docs:
| step | step_index |
|---|---|
tmMatching | 1 |
tbMatching | 2 |
kbExtraction | 3 |
translateBatch | 4 |
lqaBatch | 5 |
translateEnhanceBatch | 6 |
aiScoringBatch | 7 |
progress field
| Field | Type | Description |
|---|---|---|
rows_total | number | Total rows in the current stage (typically strings_count × target_languages.length) |
rows_done | number | Rows processed (sourced from the latest RUNNING job's detail.rows_processed) |
Semantics of rows_done
Progress reflects only the current sub-step — it resets to 0 when a sub-step transitions. For an overall completion view, use current_step's step_index (1–7).
Extra fields on failure
When status='failed':
| Field | Description |
|---|---|
failure_reason | Human-readable error message |
failure_code | Internal HTTP code (400 / 422 / 500 / 502, etc.) |
Error Codes
| code | Meaning |
|---|---|
| 400 | Missing task_id |
| 401 | Invalid signature / expired timestamp |
| 403 | Task does not belong to this App Key (cross-project access) |
| 404 | task_id not found |
| 500 | Internal server error |
Examples
cURL (GET)
APP_KEY="5685414646a54423c891d87194d87f3f"
APP_SECRET="<your App Secret>"
BASE_URL="https://api.loxily.com"
TASK_ID="68a12cb7e1f9a88b4ea23c77"
TIMESTAMP=$(date +%s)
# GET has no body; empty string participates in the signature
SIGN=$(printf '%s' "${TIMESTAMP}${APP_SECRET}" | md5)
curl -s "${BASE_URL}/api/open/v1/tasks/status?task_id=${TASK_ID}" \
-H "X-Loxily-AppKey: ${APP_KEY}" \
-H "X-Loxily-Timestamp: ${TIMESTAMP}" \
-H "X-Loxily-Sign: ${SIGN}"Node.js — poll until done
import crypto from 'crypto';
async function waitForTask(taskId, { timeoutMs = 30 * 60 * 1000, intervalMs = 3000 } = {}) {
const start = Date.now();
while (Date.now() - start < timeoutMs) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const sign = crypto.createHash('md5')
.update(`${timestamp}${process.env.LOXILY_APP_SECRET}`, 'utf8')
.digest('hex');
const resp = await fetch(
`${process.env.LOXILY_BASE_URL}/api/open/v1/tasks/status?task_id=${taskId}`,
{
headers: {
'X-Loxily-AppKey': process.env.LOXILY_APP_KEY,
'X-Loxily-Timestamp': timestamp,
'X-Loxily-Sign': sign,
},
},
);
const { data } = await resp.json();
if (data.is_completed) return data;
if (data.is_failed) throw new Error(`Task failed: ${data.failure_reason}`);
await new Promise((r) => setTimeout(r, intervalMs));
}
throw new Error(`Polling timeout (${timeoutMs}ms)`);
}
const final = await waitForTask('68a12cb7e1f9a88b4ea23c77');
console.log('Done at stage', final.current_stage);FAQ
Q: How is this different from the term_scan.completed / task.completed callbacks? Callbacks are pushed; this endpoint is pulled. Callbacks are real-time but require a public endpoint. Polling works in any environment — local scripts, Agent sandboxes, CLI tools. The data sources are identical.
Q: How do I get the translated content once a task is completed? Call /export/ini (or other export endpoints) to download results. This endpoint does not return translation content — it stays small for safe polling.
Q: Is there a rate limit? This endpoint does not write call logs and has no dedicated rate limit. We recommend polling no faster than once every 3 seconds — more frequent polling will not return results sooner.