> For a complete page index of the Captain API documentation, fetch https://docs.filesearch.dev/llms.txt?excludeSpec=true

# Get Job Status

GET https://api.filesearch.dev/v2/jobs/{job_id}

Get status, progress, and file-level details for an indexing job. Use files_limit and files_cursor to page through indexed files when the job contains many inputs.

To be notified when a job finishes instead of polling this endpoint, register a webhook endpoint. Each job then sends one signed event with its final status and file counts. This endpoint remains the source of truth for per-file detail. See [Webhooks](/guides/webhooks).

Reference: https://docs.filesearch.dev/reference/jobs/get

## Request

### Path parameters

- `job_id` (string, required) — ID of the job to look up, as returned by the indexing endpoint that started it.

### Query parameters

- `files_limit` (integer, optional, default: 50) — Number of files to return per page
- `files_cursor` (string, optional, nullable) — Pagination cursor for files

### Headers

- `authorization` (string, optional, nullable)

## Response

### 200

Successful Response

- `job_id` (string, required) — Unique identifier for the indexing job, as returned by the indexing endpoint that started it.
- `status` (string, required) — Current job status. * `pending`: queued, not yet started * `running`: in progress; see `progress` for the current stage * `completed`: all files indexed successfully * `completed_with_errors`: finished, but some files failed; see `files` and `result` * `failed`: the job failed; see `error_code` and `error_message` * `cancelled`: cancelled via `DELETE /v2/jobs/{job_id}` * `timed_out`: execution exceeded the time limit
- `billing` (JobBilling, optional, nullable) — Credit usage for this job. Present only when the job has completed.
- `cancelled_at` (string, optional, nullable) — RFC 3339 timestamp when the job was cancelled.
- `collection_name` (string, optional, nullable) — Name of the collection this job is indexing into.
- `manifest` (JobManifestReport, optional, nullable) — Manifest jobs only: how many manifest lines were accepted, rejected or unreachable, with a sample of each. Null until the manifest has been read, and for every other kind of job.
- `identity_conflicts` (JobIdentityConflicts, optional, nullable) — Files whose source_identity is already in the collection with different content. They are neither skipped nor indexed; send overwrite_existing: true to replace the indexed version. Null when there are none.
- `completed_at` (string, optional, nullable) — RFC 3339 timestamp when the job reached a terminal state.
- `created_at` (string, optional, nullable) — RFC 3339 timestamp when the job was created.
- `error_code` (string, optional, nullable) — Machine-readable error code. Present when the job failed.
- `error_message` (string, optional, nullable) — Human-readable error message. Present when the job failed.
- `estimated_time_remaining_seconds` (integer, optional, nullable) — Estimated seconds until the job finishes. 0 once the job is in a terminal state.
- `files` (list of FileStatus, optional, nullable) — Per-file status for the files in this job. Paginated: control the page with the `files_limit` and `files_cursor` query parameters.
- `files_page` (FilesPagination, optional, nullable) — Pagination details for `files`. Pass `next_cursor` as the `files_cursor` query parameter to fetch the next page.
- `job_type` (string, optional, nullable) — The indexing operation that created this job, e.g. `index_s3_directory`. Matches the endpoint the job was started from.
- `progress` (JobProgress, optional, nullable) — Detailed progress for a running job: the current pipeline stage and per-file counts.
- `progress_message` (string, optional, nullable) — Human-readable summary of the job's current progress.
- `result` (JobResult, optional, nullable) — Final file counts and duration once the job reaches a terminal state.
- `started_at` (string, optional, nullable) — RFC 3339 timestamp when processing began. Null while the job is pending.
- `updated_at` (string, optional, nullable) — RFC 3339 timestamp when the job was last updated.
- `custom_metadata` (map from string to any, optional, nullable)
- `youtube` (list of YoutubeVideoRecord, optional, nullable) — Per-video records for YouTube indexing jobs: which pipeline each video actually ran through and whether an on_missing_transcript fallback fired (a fallback changes what the job bills, so it is always visible here). Absent for non-YouTube jobs.
- `pii_report` (PiiReportHintV2, optional, nullable) — Pointer to the job's PII masking report. Present only for jobs that ran with `mask_pii: true`.

## Types

### JobBilling

Billing/credit information for a completed indexing job. Returned when job status is 'completed'.

- `credits` (BillingCredits, required) — Credits consumed by this job and the remaining balance.
- `included_credits` (integer, required) — Total credits included in the current billing period.
- `period_end` (integer, optional, nullable) — Billing period end, as a Unix timestamp in milliseconds.
- `period_start` (integer, optional, nullable) — Billing period start, as a Unix timestamp in milliseconds.

### JobManifestReport

What a manifest job did with the lines of its manifest.

- `lines` (integer, required) — Non-empty lines read from the manifest.
- `accepted` (integer, required) — Lines that were usable.
- `rejected` (integer, required) — Lines that could not be used: invalid JSON, a uri outside the bucket, a duplicate uri, an unknown key, or a value over a per-line limit.
- `unreachable` (integer, required) — Usable lines whose object was not indexed: missing, access denied, an unsupported file type, or a checksum that does not match the object.
- `rejected_sample` (list of JobManifestIssue, optional) — Up to 20 rejected lines, each with its line number and reason.
- `unreachable_sample` (list of JobManifestIssue, optional) — Up to 20 unreachable objects, each with its uri and reason.

### JobIdentityConflicts

Files whose source_identity is already in the collection with different content. They are neither skipped nor indexed.

- `count` (integer, required)
- `sample` (list of JobIdentityConflict, optional) — Up to 20 of them.

### FileStatus

Status of an individual file in an indexing job

- `status` (string, required) — Status of this file. One of `queued`, `processing`, `completed`, `failed`, `skipped`, `cancelled`.
- `uri` (string, required) — Source URI of the file, e.g. `s3://bucket/path/file.pdf`.
- `document_id` (string, optional, nullable) — Document ID assigned once the file is indexed. Use it with `GET /v2/collections/{collection_name}/documents/{document_id}`.
- `error_code` (string, optional, nullable) — Machine-readable error code. Present when the file failed.
- `error_message` (string, optional, nullable) — Human-readable error message. Present when the file failed.
- `progress_message` (string, optional, nullable) — Human-readable progress for this file.
- `stage` (string, optional, nullable) — Pipeline stage this file is in. One of `scanning`, `extracting`, `indexing`, `embedding`, `finalizing`, ending at `completed` (indexed or skipped; `status` carries which) or `failed`.
- `updated_at` (string, optional, nullable) — RFC 3339 timestamp of the last update to this file's status.

### FilesPagination

- `limit` (integer, optional, nullable) — Maximum number of files returned in this page.
- `next_cursor` (string, optional, nullable) — Cursor for the next page of files. Pass it as the `files_cursor` query parameter. Null when there are no more files.
- `total_estimated` (integer, optional, nullable) — Estimated total number of files in the job.

### JobProgress

Detailed progress information for running jobs

- `current_stage` (string, required) — Current pipeline stage. One of `scanning`, `extracting`, `indexing`, `embedding`, `finalizing`, ending at `completed`, `failed`, or `cancelled` once the job is terminal.
- `stage_description` (string, required) — Human-readable description of the current stage.
- `elapsed_seconds` (integer, optional, nullable) — Seconds elapsed since processing began.
- `files_failed` (integer, optional, nullable) — Number of files that failed processing.
- `files_processed` (integer, optional, nullable) — Number of files processed so far.
- `files_skipped` (integer, optional, nullable) — Number of files skipped, e.g. unsupported file types.
- `files_total` (integer, optional, nullable) — Total number of files the job will process.

### JobResult

Final result data when job is completed

- `duration_seconds` (integer, optional, nullable) — Total processing time in seconds.
- `failed_files` (list of FailedFileDetail, optional, nullable) — Details for each file that failed.
- `files_failed` (integer, optional, nullable) — Number of files that failed.
- `files_indexed` (integer, optional, nullable) — Number of files indexed successfully.
- `files_skipped` (integer, optional, nullable) — Number of files skipped.
- `warnings` (list of string, optional, nullable) — Non-fatal warnings raised during the job.

### YoutubeVideoRecord

Per-video processing record on YouTube indexing jobs.

- `video_id` (string, required)
- `mode_used` (string, required) — The pipeline this video actually ran through, which determines how it bills: 'transcript' (basic text pages), 'audio' (5 credits/minute), 'video' (20 credits/minute), or 'failed' (dropped at submission; nothing indexed, nothing billed).
- `url` (string, optional, nullable)
- `fell_back_from` (string, optional, nullable) — 'transcript' when on_missing_transcript escalated this video to a media mode; null when the requested mode ran directly.
- `transcript_available` (boolean, optional, nullable) — Whether the video has captions. Null when availability was not determined (media modes probe best-effort).
- `languages` (list of string, optional, default: []) — Caption languages available or used for this video.
- `error` (string, optional, nullable) — Set only when mode_used is 'failed': why this video was dropped.

### PiiReportHintV2

- `state` (string, required) — `retained`, `deleted`, or `expired`.
- `url` (string, optional, nullable) — `/v2/jobs/{job_id}/pii` while retained.
- `deleted_at` (string, optional, nullable) — Set when `state` is `deleted` (RFC3339).
- `engine` (enum, optional, nullable) — The engine the request selected: `kev`, `jev` or `presidio` (earlier jobs: `captain-jev` or `captain-presidio`). Null for jobs that predate engine reporting.
  - Allowed values: `kev`, `jev`, `presidio`, `captain-jev`, `captain-presidio`
- `fallback` (boolean, optional, nullable) — True when at least one file in the job was masked by a fallback engine. Null for jobs that predate fallback reporting.

### BillingCredits

Credit usage for a completed indexing job

- `remaining` (double, required) — Credits remaining in the billing period after this job.
- `used_advanced` (integer, required) — Advanced pages processed by this job (2.5 credits each).
- `used_basic` (integer, required) — Basic pages processed by this job (1 credit each).
- `used_total` (double, required) — Total credits consumed: (basic * 1) + (advanced * 2.5).

### JobManifestIssue

One manifest line, or one object it named, that was not indexed.

- `reason` (string, required) — Why it was not indexed.
- `line` (integer, optional, nullable) — 1-based line number in the manifest, for a line that could not be used.
- `uri` (string, optional, nullable) — The object the line named, for an object that could not be read.

### JobIdentityConflict

- `source_identity` (string, required) — The identity that is already in the collection.
- `file_name` (string, optional, nullable)
- `existing_checksum` (string, optional, nullable) — Checksum of the version already indexed.
- `candidate_checksum` (string, optional, nullable) — Checksum of the object this job was given.

### FailedFileDetail

Detail about a file that failed indexing

- `error` (string, optional, nullable) — Why the file failed.
- `file_name` (string, optional, nullable) — Name of the file that failed.

## Examples

### Pending

**Response**

```json
{
  "job_id": "abc123xyz-1234567890",
  "status": "pending",
  "collection_name": "my_documents",
  "created_at": "2024-01-15T10:30:00Z",
  "estimated_time_remaining_seconds": 0,
  "job_type": "index_s3_directory",
  "progress_message": "Job pending",
  "started_at": null,
  "updated_at": "2024-01-15T10:30:00Z"
}
```

**SDK Code**

```python Python REST
import requests
import json

BASE_URL = "https://api.filesearch.dev"
API_KEY = "your_api_key"
JOB_ID = "job_abc123"

headers = {
    "Authorization": f"Bearer {API_KEY}",
}

response = requests.get(
    f"{BASE_URL}/v2/jobs/{JOB_ID}",
    headers=headers,
    timeout=30.0
)

if response.status_code == 200:
    data = response.json()
    print(f"Status: {data['status']}")
    for f in data.get("files") or []:
        print(f"  {f['uri']} -> {f['status']} (document_id={f.get('document_id')})")
else:
    print(f"Error: {response.status_code}")
```

```typescript TypeScript REST
const BASE_URL = "https://api.filesearch.dev";
const API_KEY = "your_api_key";
const JOB_ID = "job_abc123";

const response = await fetch(`${BASE_URL}/v2/jobs/${JOB_ID}`, {
    headers: { "Authorization": `Bearer ${API_KEY}` },
});

if (response.ok) {
    const data = await response.json();
    console.log(`Status: ${data.status}`);
    for (const f of data.files ?? []) {
        console.log(`  ${f.uri} -> ${f.status} (document_id=${f.document_id})`);
    }
} else {
    console.error(`Error: ${response.status}`);
}
```

### Running Scanning

**Response**

```json
{
  "job_id": "abc123xyz-1234567890",
  "status": "running",
  "collection_name": "my_documents",
  "created_at": "2024-01-15T10:30:00Z",
  "estimated_time_remaining_seconds": 30,
  "files": null,
  "files_page": null,
  "job_type": "index_s3_directory",
  "progress": {
    "current_stage": "scanning",
    "stage_description": "Scanning bucket for files...",
    "elapsed_seconds": 5,
    "files_failed": null,
    "files_processed": null,
    "files_skipped": null,
    "files_total": null
  },
  "progress_message": "Scanning bucket for files...",
  "started_at": "2024-01-15T10:30:00Z",
  "updated_at": "2024-01-15T10:30:05Z"
}
```

**SDK Code**

```python Python REST
import requests
import json

BASE_URL = "https://api.filesearch.dev"
API_KEY = "your_api_key"
JOB_ID = "job_abc123"

headers = {
    "Authorization": f"Bearer {API_KEY}",
}

response = requests.get(
    f"{BASE_URL}/v2/jobs/{JOB_ID}",
    headers=headers,
    timeout=30.0
)

if response.status_code == 200:
    data = response.json()
    print(f"Status: {data['status']}")
    for f in data.get("files") or []:
        print(f"  {f['uri']} -> {f['status']} (document_id={f.get('document_id')})")
else:
    print(f"Error: {response.status_code}")
```

```typescript TypeScript REST
const BASE_URL = "https://api.filesearch.dev";
const API_KEY = "your_api_key";
const JOB_ID = "job_abc123";

const response = await fetch(`${BASE_URL}/v2/jobs/${JOB_ID}`, {
    headers: { "Authorization": `Bearer ${API_KEY}` },
});

if (response.ok) {
    const data = await response.json();
    console.log(`Status: ${data.status}`);
    for (const f of data.files ?? []) {
        console.log(`  ${f.uri} -> ${f.status} (document_id=${f.document_id})`);
    }
} else {
    console.error(`Error: ${response.status}`);
}
```

### Running Extracting

**Response**

```json
{
  "job_id": "abc123xyz-1234567890",
  "status": "running",
  "collection_name": "my_documents",
  "created_at": "2024-01-15T10:30:00Z",
  "estimated_time_remaining_seconds": 100,
  "files": [
    {
      "status": "completed",
      "uri": "s3://my-bucket/reports/report1.pdf",
      "document_id": "ce7c51e59b278362b288c340d5bbaa91",
      "progress_message": "Content extracted",
      "stage": "extracting",
      "updated_at": "2024-01-15T10:30:40Z"
    },
    {
      "status": "processing",
      "uri": "s3://my-bucket/reports/report2.pdf",
      "progress_message": "Extracting content...",
      "stage": "extracting",
      "updated_at": "2024-01-15T10:30:45Z"
    },
    {
      "status": "queued",
      "uri": "s3://my-bucket/reports/data.xlsx",
      "progress_message": "Waiting to process",
      "stage": null,
      "updated_at": "2024-01-15T10:30:10Z"
    }
  ],
  "files_page": {
    "limit": 50,
    "next_cursor": null,
    "total_estimated": 25
  },
  "job_type": "index_s3_directory",
  "progress": {
    "current_stage": "extracting",
    "stage_description": "Extracting content from documents...",
    "elapsed_seconds": 45,
    "files_failed": 0,
    "files_processed": 5,
    "files_skipped": 2,
    "files_total": 25
  },
  "progress_message": "Extracting content: 5/25 files",
  "started_at": "2024-01-15T10:30:00Z",
  "updated_at": "2024-01-15T10:30:45Z"
}
```

**SDK Code**

```python Python REST
import requests
import json

BASE_URL = "https://api.filesearch.dev"
API_KEY = "your_api_key"
JOB_ID = "job_abc123"

headers = {
    "Authorization": f"Bearer {API_KEY}",
}

response = requests.get(
    f"{BASE_URL}/v2/jobs/{JOB_ID}",
    headers=headers,
    timeout=30.0
)

if response.status_code == 200:
    data = response.json()
    print(f"Status: {data['status']}")
    for f in data.get("files") or []:
        print(f"  {f['uri']} -> {f['status']} (document_id={f.get('document_id')})")
else:
    print(f"Error: {response.status_code}")
```

```typescript TypeScript REST
const BASE_URL = "https://api.filesearch.dev";
const API_KEY = "your_api_key";
const JOB_ID = "job_abc123";

const response = await fetch(`${BASE_URL}/v2/jobs/${JOB_ID}`, {
    headers: { "Authorization": `Bearer ${API_KEY}` },
});

if (response.ok) {
    const data = await response.json();
    console.log(`Status: ${data.status}`);
    for (const f of data.files ?? []) {
        console.log(`  ${f.uri} -> ${f.status} (document_id=${f.document_id})`);
    }
} else {
    console.error(`Error: ${response.status}`);
}
```

### Running Indexing

**Response**

```json
{
  "job_id": "abc123xyz-1234567890",
  "status": "running",
  "collection_name": "my_documents",
  "created_at": "2024-01-15T10:30:00Z",
  "estimated_time_remaining_seconds": 50,
  "files": [
    {
      "status": "completed",
      "uri": "s3://my-bucket/reports/report1.pdf",
      "document_id": "ce7c51e59b278362b288c340d5bbaa91",
      "progress_message": "Indexing document chunks",
      "stage": "indexing",
      "updated_at": "2024-01-15T10:32:00Z"
    }
  ],
  "files_page": {
    "limit": 50,
    "next_cursor": null,
    "total_estimated": 25
  },
  "job_type": "index_s3_directory",
  "progress": {
    "current_stage": "indexing",
    "stage_description": "Tagging, indexing, and embedding document chunks...",
    "elapsed_seconds": 120,
    "files_failed": 1,
    "files_processed": 15,
    "files_skipped": 2,
    "files_total": 25
  },
  "progress_message": "Indexing documents: 15/25 files",
  "started_at": "2024-01-15T10:30:00Z",
  "updated_at": "2024-01-15T10:32:00Z"
}
```

**SDK Code**

```python Python REST
import requests
import json

BASE_URL = "https://api.filesearch.dev"
API_KEY = "your_api_key"
JOB_ID = "job_abc123"

headers = {
    "Authorization": f"Bearer {API_KEY}",
}

response = requests.get(
    f"{BASE_URL}/v2/jobs/{JOB_ID}",
    headers=headers,
    timeout=30.0
)

if response.status_code == 200:
    data = response.json()
    print(f"Status: {data['status']}")
    for f in data.get("files") or []:
        print(f"  {f['uri']} -> {f['status']} (document_id={f.get('document_id')})")
else:
    print(f"Error: {response.status_code}")
```

```typescript TypeScript REST
const BASE_URL = "https://api.filesearch.dev";
const API_KEY = "your_api_key";
const JOB_ID = "job_abc123";

const response = await fetch(`${BASE_URL}/v2/jobs/${JOB_ID}`, {
    headers: { "Authorization": `Bearer ${API_KEY}` },
});

if (response.ok) {
    const data = await response.json();
    console.log(`Status: ${data.status}`);
    for (const f of data.files ?? []) {
        console.log(`  ${f.uri} -> ${f.status} (document_id=${f.document_id})`);
    }
} else {
    console.error(`Error: ${response.status}`);
}
```

### Running Embedding

**Response**

```json
{
  "job_id": "abc123xyz-1234567890",
  "status": "running",
  "collection_name": "my_documents",
  "created_at": "2024-01-15T10:30:00Z",
  "estimated_time_remaining_seconds": 15,
  "files": [
    {
      "status": "completed",
      "uri": "s3://my-bucket/reports/report1.pdf",
      "document_id": "ce7c51e59b278362b288c340d5bbaa91",
      "progress_message": "Embeddings generated",
      "stage": "embedding",
      "updated_at": "2024-01-15T10:34:00Z"
    }
  ],
  "files_page": {
    "limit": 50,
    "next_cursor": null,
    "total_estimated": 25
  },
  "job_type": "index_s3_directory",
  "progress": {
    "current_stage": "embedding",
    "stage_description": "Generating vector embeddings...",
    "elapsed_seconds": 240,
    "files_failed": 1,
    "files_processed": 22,
    "files_skipped": 2,
    "files_total": 25
  },
  "progress_message": "Generating embeddings...",
  "started_at": "2024-01-15T10:30:00Z",
  "updated_at": "2024-01-15T10:34:00Z"
}
```

**SDK Code**

```python Python REST
import requests
import json

BASE_URL = "https://api.filesearch.dev"
API_KEY = "your_api_key"
JOB_ID = "job_abc123"

headers = {
    "Authorization": f"Bearer {API_KEY}",
}

response = requests.get(
    f"{BASE_URL}/v2/jobs/{JOB_ID}",
    headers=headers,
    timeout=30.0
)

if response.status_code == 200:
    data = response.json()
    print(f"Status: {data['status']}")
    for f in data.get("files") or []:
        print(f"  {f['uri']} -> {f['status']} (document_id={f.get('document_id')})")
else:
    print(f"Error: {response.status_code}")
```

```typescript TypeScript REST
const BASE_URL = "https://api.filesearch.dev";
const API_KEY = "your_api_key";
const JOB_ID = "job_abc123";

const response = await fetch(`${BASE_URL}/v2/jobs/${JOB_ID}`, {
    headers: { "Authorization": `Bearer ${API_KEY}` },
});

if (response.ok) {
    const data = await response.json();
    console.log(`Status: ${data.status}`);
    for (const f of data.files ?? []) {
        console.log(`  ${f.uri} -> ${f.status} (document_id=${f.document_id})`);
    }
} else {
    console.error(`Error: ${response.status}`);
}
```

### Running Finalizing

**Response**

```json
{
  "job_id": "abc123xyz-1234567890",
  "status": "running",
  "collection_name": "my_documents",
  "created_at": "2024-01-15T10:30:00Z",
  "estimated_time_remaining_seconds": 5,
  "files": [
    {
      "status": "completed",
      "uri": "s3://my-bucket/reports/report1.pdf",
      "document_id": "ce7c51e59b278362b288c340d5bbaa91",
      "progress_message": "Indexed successfully",
      "stage": "indexing",
      "updated_at": "2024-01-15T10:34:40Z"
    }
  ],
  "files_page": {
    "limit": 50,
    "next_cursor": null,
    "total_estimated": 25
  },
  "job_type": "index_s3_directory",
  "progress": {
    "current_stage": "finalizing",
    "stage_description": "Tracking usage...",
    "elapsed_seconds": 280,
    "files_failed": 1,
    "files_processed": 22,
    "files_skipped": 2,
    "files_total": 25
  },
  "progress_message": "Finalizing job...",
  "started_at": "2024-01-15T10:30:00Z",
  "updated_at": "2024-01-15T10:34:40Z"
}
```

**SDK Code**

```python Python REST
import requests
import json

BASE_URL = "https://api.filesearch.dev"
API_KEY = "your_api_key"
JOB_ID = "job_abc123"

headers = {
    "Authorization": f"Bearer {API_KEY}",
}

response = requests.get(
    f"{BASE_URL}/v2/jobs/{JOB_ID}",
    headers=headers,
    timeout=30.0
)

if response.status_code == 200:
    data = response.json()
    print(f"Status: {data['status']}")
    for f in data.get("files") or []:
        print(f"  {f['uri']} -> {f['status']} (document_id={f.get('document_id')})")
else:
    print(f"Error: {response.status_code}")
```

```typescript TypeScript REST
const BASE_URL = "https://api.filesearch.dev";
const API_KEY = "your_api_key";
const JOB_ID = "job_abc123";

const response = await fetch(`${BASE_URL}/v2/jobs/${JOB_ID}`, {
    headers: { "Authorization": `Bearer ${API_KEY}` },
});

if (response.ok) {
    const data = await response.json();
    console.log(`Status: ${data.status}`);
    for (const f of data.files ?? []) {
        console.log(`  ${f.uri} -> ${f.status} (document_id=${f.document_id})`);
    }
} else {
    console.error(`Error: ${response.status}`);
}
```

### Completed

**Response**

```json
{
  "job_id": "abc123xyz-1234567890",
  "status": "completed",
  "billing": {
    "credits": {
      "remaining": 975,
      "used_advanced": 0,
      "used_basic": 25,
      "used_total": 25
    },
    "included_credits": 1000,
    "period_end": 1706745600,
    "period_start": 1704067200
  },
  "collection_name": "my_documents",
  "completed_at": "2024-01-15T10:34:05Z",
  "created_at": "2024-01-15T10:30:00Z",
  "estimated_time_remaining_seconds": 0,
  "files": [
    {
      "status": "completed",
      "uri": "s3://my-bucket/reports/q4_report.pdf",
      "document_id": "doc_8f2a1b3c",
      "progress_message": "Processed by pdf_handler",
      "stage": "completed",
      "updated_at": "2024-01-15T10:34:00Z"
    }
  ],
  "files_page": {
    "limit": 50,
    "next_cursor": null,
    "total_estimated": 25
  },
  "job_type": "index_s3_directory",
  "progress": {
    "current_stage": "completed",
    "stage_description": "Job completed",
    "elapsed_seconds": 245,
    "files_failed": 0,
    "files_processed": 25,
    "files_skipped": 0,
    "files_total": 25
  },
  "progress_message": "Indexing completed: 25 files indexed",
  "result": {
    "duration_seconds": 245,
    "files_failed": 0,
    "files_indexed": 25,
    "files_skipped": 0
  },
  "started_at": "2024-01-15T10:30:10Z",
  "updated_at": "2024-01-15T10:34:05Z"
}
```

**SDK Code**

```python Python REST
import requests
import json

BASE_URL = "https://api.filesearch.dev"
API_KEY = "your_api_key"
JOB_ID = "job_abc123"

headers = {
    "Authorization": f"Bearer {API_KEY}",
}

response = requests.get(
    f"{BASE_URL}/v2/jobs/{JOB_ID}",
    headers=headers,
    timeout=30.0
)

if response.status_code == 200:
    data = response.json()
    print(f"Status: {data['status']}")
    for f in data.get("files") or []:
        print(f"  {f['uri']} -> {f['status']} (document_id={f.get('document_id')})")
else:
    print(f"Error: {response.status_code}")
```

```typescript TypeScript REST
const BASE_URL = "https://api.filesearch.dev";
const API_KEY = "your_api_key";
const JOB_ID = "job_abc123";

const response = await fetch(`${BASE_URL}/v2/jobs/${JOB_ID}`, {
    headers: { "Authorization": `Bearer ${API_KEY}` },
});

if (response.ok) {
    const data = await response.json();
    console.log(`Status: ${data.status}`);
    for (const f of data.files ?? []) {
        console.log(`  ${f.uri} -> ${f.status} (document_id=${f.document_id})`);
    }
} else {
    console.error(`Error: ${response.status}`);
}
```

### Completed with PII masking report

**Response**

```json
{
  "job_id": "abc123xyz-1234567890",
  "status": "completed",
  "billing": {
    "credits": {
      "remaining": 975,
      "used_advanced": 0,
      "used_basic": 25,
      "used_total": 25
    },
    "included_credits": 1000,
    "period_end": 1706745600,
    "period_start": 1704067200
  },
  "collection_name": "my_documents",
  "completed_at": "2024-01-15T10:34:05Z",
  "created_at": "2024-01-15T10:30:00Z",
  "estimated_time_remaining_seconds": 0,
  "files": [
    {
      "status": "completed",
      "uri": "s3://my-bucket/reports/q4_report.pdf",
      "document_id": "doc_8f2a1b3c",
      "progress_message": "Processed by pdf_handler",
      "stage": "completed",
      "updated_at": "2024-01-15T10:34:00Z"
    }
  ],
  "files_page": {
    "limit": 50,
    "next_cursor": null,
    "total_estimated": 25
  },
  "job_type": "index_s3_directory",
  "progress": {
    "current_stage": "completed",
    "stage_description": "Job completed",
    "elapsed_seconds": 245,
    "files_failed": 0,
    "files_processed": 25,
    "files_skipped": 0,
    "files_total": 25
  },
  "progress_message": "Indexing completed: 25 files indexed, PII masked",
  "result": {
    "duration_seconds": 245,
    "files_failed": 0,
    "files_indexed": 25,
    "files_skipped": 0
  },
  "started_at": "2024-01-15T10:30:10Z",
  "updated_at": "2024-01-15T10:34:05Z",
  "pii_report": {
    "state": "retained",
    "url": "/v2/jobs/abc123xyz-1234567890/pii",
    "deleted_at": null
  }
}
```

**SDK Code**

```python Python REST
import requests

BASE_URL = "https://api.filesearch.dev"
API_KEY = "your_api_key"
JOB_ID = "abc123xyz-1234567890"

headers = {
    "Authorization": f"Bearer {API_KEY}",
}

response = requests.get(
    f"{BASE_URL}/v2/jobs/{JOB_ID}",
    headers=headers,
    timeout=30.0
)

if response.status_code == 200:
    data = response.json()
    print(f"Status: {data['status']}")
    report = data.get("pii_report")
    if report and report["state"] == "retained":
        print(f"Masking report available at {report['url']}")
    elif report:
        print(f"Masking report {report['state']}")
else:
    print(f"Error: {response.status_code}")
```

```typescript TypeScript REST
const BASE_URL = "https://api.filesearch.dev";
const API_KEY = "your_api_key";
const JOB_ID = "abc123xyz-1234567890";

const response = await fetch(`${BASE_URL}/v2/jobs/${JOB_ID}`, {
    headers: { "Authorization": `Bearer ${API_KEY}` },
});

if (response.ok) {
    const data = await response.json();
    console.log(`Status: ${data.status}`);
    const report = data.pii_report;
    if (report && report.state === "retained") {
        console.log(`Masking report available at ${report.url}`);
    } else if (report) {
        console.log(`Masking report ${report.state}`);
    }
} else {
    console.error(`Error: ${response.status}`);
}
```

### Completed With Errors

**Response**

```json
{
  "job_id": "abc123xyz-1234567890",
  "status": "completed_with_errors",
  "billing": {
    "credits": {
      "remaining": 975,
      "used_advanced": 0,
      "used_basic": 23,
      "used_total": 23
    },
    "included_credits": 1000,
    "period_end": 1706745600,
    "period_start": 1704067200
  },
  "collection_name": "my_documents",
  "completed_at": "2024-01-15T10:34:05Z",
  "created_at": "2024-01-15T10:30:00Z",
  "estimated_time_remaining_seconds": 0,
  "files": [
    {
      "status": "completed",
      "uri": "s3://my-bucket/reports/q4_report.pdf",
      "document_id": "doc_8f2a1b3c",
      "progress_message": "Processed by pdf_handler",
      "stage": "completed",
      "updated_at": "2024-01-15T10:34:00Z"
    },
    {
      "status": "failed",
      "uri": "s3://my-bucket/reports/corrupt_file.pdf",
      "error_code": "EXTRACTION_FAILED",
      "error_message": "Could not extract text from file",
      "stage": "failed",
      "updated_at": "2024-01-15T10:33:00Z"
    }
  ],
  "files_page": {
    "limit": 50,
    "next_cursor": null,
    "total_estimated": 25
  },
  "job_type": "index_s3_directory",
  "progress": {
    "current_stage": "completed",
    "stage_description": "Job completed",
    "elapsed_seconds": 245,
    "files_failed": 2,
    "files_processed": 25,
    "files_skipped": 0,
    "files_total": 25
  },
  "progress_message": "Indexing completed: 23 of 25 files indexed, 2 failed",
  "result": {
    "duration_seconds": 245,
    "files_failed": 2,
    "files_indexed": 23,
    "files_skipped": 0
  },
  "started_at": "2024-01-15T10:30:10Z",
  "updated_at": "2024-01-15T10:34:05Z"
}
```

**SDK Code**

```python Python REST
import requests
import json

BASE_URL = "https://api.filesearch.dev"
API_KEY = "your_api_key"
JOB_ID = "job_abc123"

headers = {
    "Authorization": f"Bearer {API_KEY}",
}

response = requests.get(
    f"{BASE_URL}/v2/jobs/{JOB_ID}",
    headers=headers,
    timeout=30.0
)

if response.status_code == 200:
    data = response.json()
    print(f"Status: {data['status']}")
    for f in data.get("files") or []:
        print(f"  {f['uri']} -> {f['status']} (document_id={f.get('document_id')})")
else:
    print(f"Error: {response.status_code}")
```

```typescript TypeScript REST
const BASE_URL = "https://api.filesearch.dev";
const API_KEY = "your_api_key";
const JOB_ID = "job_abc123";

const response = await fetch(`${BASE_URL}/v2/jobs/${JOB_ID}`, {
    headers: { "Authorization": `Bearer ${API_KEY}` },
});

if (response.ok) {
    const data = await response.json();
    console.log(`Status: ${data.status}`);
    for (const f of data.files ?? []) {
        console.log(`  ${f.uri} -> ${f.status} (document_id=${f.document_id})`);
    }
} else {
    console.error(`Error: ${response.status}`);
}
```

### Failed

**Response**

```json
{
  "job_id": "abc123xyz-1234567890",
  "status": "failed",
  "collection_name": "my_documents",
  "completed_at": "2024-01-15T10:32:00Z",
  "created_at": "2024-01-15T10:30:00Z",
  "error_code": "JOB_FAILED",
  "error_message": "Lambda function timed out after 15 minutes",
  "estimated_time_remaining_seconds": 0,
  "files": [
    {
      "status": "failed",
      "uri": "s3://my-bucket/reports/huge_file.pdf",
      "error_code": "FILE_TOO_LARGE",
      "error_message": "File exceeds maximum size limit of 100MB",
      "progress_message": "File too large",
      "stage": "failed",
      "updated_at": "2024-01-15T10:32:00Z"
    }
  ],
  "files_page": {
    "limit": 50,
    "next_cursor": null,
    "total_estimated": 25
  },
  "job_type": "index_s3_directory",
  "progress": {
    "current_stage": "failed",
    "stage_description": "Job failed during processing",
    "elapsed_seconds": 120,
    "files_failed": 3,
    "files_processed": 8,
    "files_skipped": 0,
    "files_total": 25
  },
  "progress_message": "Job failed: States.TaskFailed",
  "result": {
    "duration_seconds": 120,
    "files_failed": 3,
    "files_indexed": 8,
    "files_skipped": 0
  },
  "started_at": "2024-01-15T10:30:00Z",
  "updated_at": "2024-01-15T10:32:00Z"
}
```

**SDK Code**

```python Python REST
import requests
import json

BASE_URL = "https://api.filesearch.dev"
API_KEY = "your_api_key"
JOB_ID = "job_abc123"

headers = {
    "Authorization": f"Bearer {API_KEY}",
}

response = requests.get(
    f"{BASE_URL}/v2/jobs/{JOB_ID}",
    headers=headers,
    timeout=30.0
)

if response.status_code == 200:
    data = response.json()
    print(f"Status: {data['status']}")
    for f in data.get("files") or []:
        print(f"  {f['uri']} -> {f['status']} (document_id={f.get('document_id')})")
else:
    print(f"Error: {response.status_code}")
```

```typescript TypeScript REST
const BASE_URL = "https://api.filesearch.dev";
const API_KEY = "your_api_key";
const JOB_ID = "job_abc123";

const response = await fetch(`${BASE_URL}/v2/jobs/${JOB_ID}`, {
    headers: { "Authorization": `Bearer ${API_KEY}` },
});

if (response.ok) {
    const data = await response.json();
    console.log(`Status: ${data.status}`);
    for (const f of data.files ?? []) {
        console.log(`  ${f.uri} -> ${f.status} (document_id=${f.document_id})`);
    }
} else {
    console.error(`Error: ${response.status}`);
}
```

### Cancelled

**Response**

```json
{
  "job_id": "abc123xyz-1234567890",
  "status": "cancelled",
  "cancelled_at": "2024-01-15T10:31:30Z",
  "collection_name": "my_documents",
  "completed_at": "2024-01-15T10:31:30Z",
  "created_at": "2024-01-15T10:30:00Z",
  "estimated_time_remaining_seconds": 0,
  "files": [
    {
      "status": "completed",
      "uri": "s3://my-bucket/reports/report1.pdf",
      "document_id": "ce7c51e59b278362b288c340d5bbaa91",
      "progress_message": "Indexed successfully",
      "stage": "completed",
      "updated_at": "2024-01-15T10:31:30Z"
    },
    {
      "status": "cancelled",
      "uri": "s3://my-bucket/reports/report2.pdf",
      "progress_message": "Cancelled by user",
      "stage": null,
      "updated_at": "2024-01-15T10:31:30Z"
    }
  ],
  "files_page": {
    "limit": 50,
    "next_cursor": null,
    "total_estimated": 25
  },
  "job_type": "index_s3_directory",
  "progress": {
    "current_stage": "cancelled",
    "stage_description": "Job was cancelled",
    "elapsed_seconds": 90,
    "files_failed": 0,
    "files_processed": 10,
    "files_skipped": 0,
    "files_total": 25
  },
  "progress_message": "Job was cancelled",
  "started_at": "2024-01-15T10:30:00Z",
  "updated_at": "2024-01-15T10:31:30Z"
}
```

**SDK Code**

```python Python REST
import requests
import json

BASE_URL = "https://api.filesearch.dev"
API_KEY = "your_api_key"
JOB_ID = "job_abc123"

headers = {
    "Authorization": f"Bearer {API_KEY}",
}

response = requests.get(
    f"{BASE_URL}/v2/jobs/{JOB_ID}",
    headers=headers,
    timeout=30.0
)

if response.status_code == 200:
    data = response.json()
    print(f"Status: {data['status']}")
    for f in data.get("files") or []:
        print(f"  {f['uri']} -> {f['status']} (document_id={f.get('document_id')})")
else:
    print(f"Error: {response.status_code}")
```

```typescript TypeScript REST
const BASE_URL = "https://api.filesearch.dev";
const API_KEY = "your_api_key";
const JOB_ID = "job_abc123";

const response = await fetch(`${BASE_URL}/v2/jobs/${JOB_ID}`, {
    headers: { "Authorization": `Bearer ${API_KEY}` },
});

if (response.ok) {
    const data = await response.json();
    console.log(`Status: ${data.status}`);
    for (const f of data.files ?? []) {
        console.log(`  ${f.uri} -> ${f.status} (document_id=${f.document_id})`);
    }
} else {
    console.error(`Error: ${response.status}`);
}
```