웹훅과 실시간 WebSocket은 작업이 완료되는 즉시 알려줍니다. 하지만 다음 날 아침, 배포 이후, 혹은 지난 한 시간 동안 실패한 모든 로캘을 확인하고 싶을 때는 둘 다 도움이 되지 않습니다. 그 순간은 이미 지나갔고 이벤트도 사라졌습니다. 하지만 작업은 남아 있습니다. 각 작업은 제출한 프로세스가 이미 끝난 뒤에도 플랫폼에 오래 보존되는 영속적인 기록입니다.
GET /jobs/localization은 바로 그런 기록을 다시 찾아볼 때 사용하는 엔드포인트입니다. 작업을 최신순으로 반환하고, 커서 기반 페이지네이션으로 차례대로 조회할 수 있으며, 실행된 로컬라이제이션 엔진이나 최종 상태로 범위를 좁힐 수 있습니다. 즉, 이건 놓친 내용을 따라잡는 채널입니다. 실시간으로 보고 있지 않았을 때 조회하는 영속적인 기록이죠.
GET /jobs/localization비동기 로컬라이제이션이 처음이라면 개요부터 시작하세요. 이 페이지는 이미 조회할 작업이 있다고 가정합니다. 다른 모든 엔드포인트와 마찬가지로 X-API-Key로 인증합니다.
필터와 페이지네이션#
GET /jobs/localization?engineId=eng_abc123&status=completed&limit=20&cursor=...| 매개변수 | 유형 | 설명 |
|---|---|---|
engineId | string (선택 사항) | 이 로컬라이제이션 엔진(eng_...)에서 실행된 작업만 반환합니다. |
status | string (선택 사항) | 이 상태의 작업만 반환합니다: queued, processing, completed, completed_with_warnings, 또는 failed. |
limit | number (선택 사항) | 페이지 크기입니다. 기본값은 20이고 최대값은 100입니다. |
cursor | string (선택 사항) | 이전 응답의 nextCursor에서 받은 불투명한 커서 값입니다. 첫 페이지에서는 생략하세요. |
두 필터는 모두 선택 사항이며 함께 조합해 사용할 수 있습니다. engineId=eng_abc123&status=failed은 특정 엔진에서 실패한 작업만 반환하고 그 외의 작업은 제외합니다. 이 조합은 실제 장애 대응 중에 하게 되는 질문, 이 엔진에서 실패한 것만 전부 보여줘 에 바로 답해 줍니다. 조직의 모든 작업을 클라이언트로 가져와 직접 필터링할 필요가 없습니다.
cursor는 페이지 번호가 아니라 결과 스트림 내 위치를 가리킵니다. 직접 계산하는 값이 아니라 응답으로 받는 값입니다. 각 응답은 nextCursor를 돌려주고, 다음 페이지를 가져오려면 그 값을 그대로 다시 전달하면 됩니다.
응답#
각 페이지는 items 배열과 nextCursor로 구성됩니다. 마지막 페이지에서는 nextCursor가 null입니다. 이는 오류가 아니라 루프를 종료하는 조건입니다.
{
"items": [
{
"id": "ljb_C3d4E5f6G7h8I9j0",
"groupId": "ljg_A1b2C3d4E5f6G7h8",
"targetLocale": "ja",
"status": "completed",
"warnings": [],
"createdAt": "2026-03-16T10:30:00.000Z",
"completedAt": "2026-03-16T10:30:06.000Z"
}
],
"nextCursor": "eyJ0IjoiMjAyNi0wMy0xNlQxMDozMDowMC4wMDBaIiwiaSI6ImxqYl9CMmMzRDRlNUY2ZzdIOGk5In0"
}각 항목은 요약 정보입니다. 어떤 로캘인지, 어떤 그룹인지, 상태가 무엇인지, 언제 생성되었고 언제 완료되었는지까지, 작업을 식별하고 결과를 파악하는 데 필요한 정보만 담고 있습니다. 번역된 출력은 의도적으로 포함하지 않습니다. 이 작업들 중 하나의 전체 outputData와 단계별 steps를 보려면 해당 id를 사용해 단일 작업 가져오기를 호출하세요. 목록은 찾기 위한 것이고, 조회는 읽기 위한 것입니다.
알 수 없는 status 값도 안전하게 처리하세요
알고 있는 status 값은 분기 처리하고, 나머지는 기본 분기로 흘려보내 예상하지 못한 값 때문에 소비자가 중단되지 않게 하세요. 직접 관리하지 않는 문자열 enum은 알 수 없는 값을 허용하는 것이 가장 안전한 기본값입니다. 분류할 수 없는 입력에서 예외를 던지기보다, 리더가 계속 동작하도록 만드는 편이 낫습니다.
모든 결과 페이지 순회하기#
핵심은 종료 조건입니다. nextCursor가 null로 돌아올 때까지 계속 요청하세요. 한 응답의 nextCursor를 다음 요청의 cursor로 넘기면 루프는 자연스럽게 끝납니다.
async function listFailedJobs(engineId) {
const failed = [];
let cursor = undefined; // first page: no cursor
do {
const url = new URL("https://api.lingo.dev/jobs/localization");
url.searchParams.set("engineId", engineId);
url.searchParams.set("status", "failed");
url.searchParams.set("limit", "100"); // fewer round-trips
if (cursor) url.searchParams.set("cursor", cursor);
const response = await fetch(url, {
headers: { "X-API-Key": process.env.LINGO_API_KEY },
});
const { items, nextCursor } = await response.json();
failed.push(...items);
cursor = nextCursor; // null on the last page -> loop ends
} while (cursor);
return failed; // every failed job for this engine
}백로그가 큰 경우 limit를 100으로 올리면 왕복 요청 횟수를 줄일 수 있습니다. 결과가 달라지는 것은 아니고, 읽기 위해 거쳐야 하는 페이지 수만 줄어듭니다. 어긋날 수 있는 오프셋도 없고 따로 맞춰야 할 페이지 수도 없습니다. 커서가 현재 위치를 들고 있고, null가 모든 내용을 다 읽었는지 알려줍니다.
다음 단계#
이제 작업의 id를 확보했습니다. 놓친 내용을 따라잡는 채널은 여기까지입니다. 이제 결과를 읽거나, 다음번에는 발생하는 즉시 알 수 있도록 실시간 채널을 연결하면 됩니다.
