Webhooks や ライブWebSocket を使えば、ジョブが完了した瞬間に通知を受け取れます。ですが、翌朝になって確認したいとき、デプロイ後に振り返りたいとき、あるいは直近1時間で失敗したロケールをすべて洗い出したいときには、それだけでは足りません。瞬間は過ぎ、イベントは流れていきます。けれどジョブは残ります。送信元のプロセスがすでに先へ進んでいても、各ジョブはプラットフォーム上に永続的な記録として残り続けます。
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 とすれば、1つのエンジンで失敗したジョブだけを返し、それ以外は含まれません。この組み合わせは、実際にインシデント対応で必ず出てくる このエンジンで失敗したものを全部見せて という問いに、そのまま答えられます。組織内の全ジョブを取得して、クライアント側で絞り込む必要はありません。
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 を使って 単一のジョブを取得 を呼び出してください。一覧は見つけるため、詳細取得は読むためにあります。
未知のステータス値も安全に扱う
既知のステータス値にはマッチさせつつ、それ以外はデフォルト分岐に流すようにしてください。見たことのない値が来たからといって、コンシューマーをクラッシュさせるべきではありません。自分で管理していない文字列 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 に上げれば往復回数を減らせます。変わるのは結果ではなく、読み切るまでにたどるページ数だけです。ずれていく offset も、同期を取り続けるページ数もありません。カーソルが現在位置を保持し、null がすべて読み終えたことを知らせます。
次のステップ#
ジョブの id は、もう手元にあります。キャッチアップ用のチャネルでここまでたどり着いたら、ここから先は結果を読むだけです。あるいは、次回は発生したその瞬間に受け取れるよう、ライブチャネルを接続しておきましょう。
