Status API
Check the processing status of uploaded documents.
Job status is scoped to the client that created the job. A job is only visible to the client whose API key was used to submit the /embed request. Querying a job ID that belongs to a different client returns 404 — the same as a non-existent job — to avoid leaking information across clients.
Endpoint
GET /status/{job_id}
Request
Headers
| Header | Required | Description |
|---|---|---|
X-API-Key | Yes | Valid API key from API_KEY_CLIENTS env var |
Path Parameters
| Parameter | Description |
|---|---|
job_id | Job ID returned from /embed endpoint |
Response
Pending/Started
{
"job_id": "abc123-def456",
"document_id": "doc-001",
"status": "pending"
}
Processing
{
"job_id": "abc123-def456",
"document_id": "doc-001",
"status": "processing"
}
Completed
{
"job_id": "abc123-def456",
"document_id": "doc-001",
"status": "completed",
"chunks_created": 12,
"categories": ["financial reporting"],
"classification_status": "completed",
"entities_extracted": {
"persons": ["John Smith", "Jane Doe"],
"organizations": ["Acme Corp", "Tech Inc"],
"dates": ["January 15, 2024"],
"locations": ["New York", "San Francisco"]
},
"created_at": "2026-03-26T12:19:26.452416+00:00",
"completed_at": "2026-03-26T12:19:46.580322+00:00"
}
The indexing status and classification_status are independent. A document can be
searchable with status: "completed" while classification is still "pending". Keep
polling this endpoint until classification_status becomes "completed", "failed",
or "disabled"; terminal classification and categories are refreshed from the stored
document version even when entities_extracted is already present.
Failed
{
"job_id": "abc123-def456",
"document_id": "doc-001",
"status": "failed",
"error": "Error message describing what went wrong"
}
Partial Failed
Some embedding tasks failed while others succeeded:
{
"job_id": "abc123-def456",
"document_id": "doc-001",
"status": "partial_failed",
"chunks_created": 45,
"entities_extracted": {
"persons": ["John Smith"],
"organizations": ["Acme Corp"]
},
"error": "Task xyz failed: Connection timeout"
}
When processing large documents, chunks are processed in parallel. If some chunks fail but others succeed, you get partial_failed status. The successful chunks are searchable; failed chunks are lost.
Retry
Task is scheduled for retry after a failure:
{
"job_id": "abc123-def456",
"document_id": "doc-001",
"status": "retrying",
"error": "Connection timeout to Qdrant",
"retry_count": 1,
"max_retries": 3,
"next_retry_in_seconds": 60
}
Status Values
| Status | Description |
|---|---|
pending | Job queued, waiting for worker |
started | Worker picked up the job |
processing | Actively processing (parsing/chunking/embedding) |
completed | Successfully indexed |
failed | Processing failed, see error field |
partial_failed | Some chunks succeeded, others failed |
retrying | Will be retried (up to 3 attempts) |
Examples
Basic Status Check
curl http://localhost:8000/status/abc123-def456 \
-H "X-API-Key: super-secret-key"
Poll Until Complete
#!/bin/bash
JOB_ID="abc123-def456"
API_KEY="super-secret-key"
while true; do
response=$(curl -s "http://localhost:8000/status/$JOB_ID" \
-H "X-API-Key: $API_KEY")
status=$(echo "$response" | jq -r '.status')
echo "Status: $status"
if [ "$status" = "completed" ]; then
echo "Document indexed successfully!"
echo "$response" | jq '.'
break
elif [ "$status" = "failed" ]; then
echo "Processing failed!"
echo "$response" | jq '.'
break
fi
sleep 2
done
Python Polling
import time
import requests
def wait_for_completion(job_id: str, api_key: str, timeout: int = 300) -> dict:
"""Poll status until completed or failed."""
url = f"http://localhost:8000/status/{job_id}"
headers = {"X-API-Key": api_key}
start = time.time()
while time.time() - start < timeout:
response = requests.get(url, headers=headers)
data = response.json()
print(f"Status: {data['status']}")
if data['status'] == 'completed':
print(f"Created {data['chunks_created']} chunks")
return data
elif data['status'] == 'failed':
raise Exception(f"Processing failed: {data.get('error')}")
time.sleep(2)
raise TimeoutError("Processing took too long")
# Usage
result = wait_for_completion("abc123-def456", "super-secret-key")
Response Codes
| Code | Description |
|---|---|
| 200 | Success - Returns status |
| 401 | Unauthorized - Invalid API key |
| 404 | Not Found - Job ID doesn't exist, or the job belongs to a different client |
| 429 | Rate Limited - Too many requests |
Error Responses
{
"detail": "Invalid API key",
"error_code": "auth_failed",
"request_id": "5d7f4c1a2b9e4b3e8f6f0d2a4c9b8a1e"
}
{
"detail": "Job not found",
"error_code": "not_found",
"request_id": "5d7f4c1a2b9e4b3e8f6f0d2a4c9b8a1e"
}
Best Practices
- Poll with backoff - Start with 1s intervals, increase to 5s for longer jobs
- Set timeouts - Large documents can take several minutes
- Handle failures - Check for
failedstatus and log the error - Store job IDs - Keep job IDs for later status checks or debugging
Typical Processing Times
| Document Type | Size | Approximate Time |
|---|---|---|
| Text file | < 100KB | 2-5 seconds |
| 1-5 pages | 5-15 seconds | |
| 10-50 pages | 15-45 seconds | |
| 100+ pages | 1-3 minutes | |
| DOCX | Any | Similar to PDF |
Note: First document after startup takes longer due to model loading (~30s).