Jobs
Jobs are asynchronous tool runs tied to one workspace and one API key workspace scope.
A job represents one execution of a ProteinIQ tool with a specific input payload and settings. API jobs use the same workspace credits, permissions, limits, and result storage model as jobs created in the web app.
Quote a job
Use POST /api/v1/jobs/quote to validate a payload and estimate credits without creating a job.
curl -s -X POST \
-H "Authorization: Bearer $PROTEINIQ_API_KEY" \
-H "Content-Type: application/json" \
"https://proteiniq.io/api/v1/jobs/quote" \
-d '{
"tool": "esmfold",
"input": {
"inputs": [
{
"id": "seqs_1",
"slotId": "protein",
"kind": "protein",
"format": "fasta",
"content": ">seq\nMKT...",
"source": { "type": "text" }
}
]
},
"settings": {}
}'Quote responses include credit estimates, available credits, blocking errors, current limits, and billing mode.
{
"object": "job_quote",
"tool": "esmfold",
"estimated_credits": 50,
"available_credits": 500,
"billable_credits": 50,
"blocking_errors": [],
"limits": {
"active_concurrent_jobs": 0,
"max_concurrent_jobs": 3,
"daily_limit_used": 2,
"daily_limit_max": 100
},
"billing": {
"mode": "fixed"
}
}Submit a job
Use POST /api/v1/jobs to create a job. The request must be JSON and must include tool, name, input, and optional settings and billing.
{
"tool": "esmfold",
"name": "Lysozyme ESMFold run",
"input": {
"inputs": [
{
"id": "seqs_1",
"slotId": "protein",
"kind": "protein",
"format": "fasta",
"content": ">seq\nMKT...",
"source": { "type": "text" }
}
]
},
"settings": {},
"billing": {
"max_reserved_credits": 100
}
}Use Idempotency-Key or X-Idempotency-Key when retrying job submission. The same key is scoped to the workspace.
curl -s -X POST \
-H "Authorization: Bearer $PROTEINIQ_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: fold-lysozyme-001" \
"https://proteiniq.io/api/v1/jobs" \
-d '{
"tool": "esmfold",
"name": "Lysozyme ESMFold run",
"input": {
"inputs": [
{
"id": "seqs_1",
"slotId": "protein",
"kind": "protein",
"format": "fasta",
"content": ">lysozyme\nKVFGRCELAAAMKRHGLDNYRGYSLGNWVCAAKFESNFNTQATNRNTDGSTDYGILQINSR",
"source": { "type": "text" }
}
]
},
"settings": {}
}'Submit returns a job resource with status code 202 for new jobs. Replayed idempotent submissions return the stored response status.
Job resource
Single-job endpoints return this resource shape:
{
"id": "job_123",
"object": "job",
"status": "PROCESSING",
"tool": "esmfold",
"name": "Lysozyme ESMFold run",
"credits_used": 50,
"created_at": "2026-06-12T08:00:00.000Z",
"started_at": "2026-06-12T08:00:02.000Z",
"completed_at": null,
"progress": 40,
"execution_time_seconds": null,
"error": null,
"billing": {
"mode": "fixed",
"reserved_credits": null,
"final_credits": null,
"rate_credits_per_minute": null,
"billable_runtime_seconds": null,
"outcome": null,
"finalized_at": null
}
}The billing object is included when billing metadata exists for the job.
List jobs
GET /api/v1/jobs lists jobs in the API key workspace.
curl -s \
-H "Authorization: Bearer $PROTEINIQ_API_KEY" \
"https://proteiniq.io/api/v1/jobs?limit=20"List jobs supports:
limit: Integer from1to100, defaults to20starting_after: Cursor returned asnext_cursorfrom a previous page
The response is ordered by newest job first.
{
"object": "list",
"data": [],
"has_more": false,
"next_cursor": null
}Get job status
GET /api/v1/jobs/{jobId}/status returns the current job resource.
curl -s \
-H "Authorization: Bearer $PROTEINIQ_API_KEY" \
"https://proteiniq.io/api/v1/jobs/job_123/status"Poll until the job reaches a terminal status. The API currently treats COMPLETED, FAILED, TIMEOUT, CANCELLED, and BUDGET_EXCEEDED as terminal statuses for client workflows.
Polling strategy
Use exponential backoff when polling job status. Start with a 5 second delay, then increase to 10 seconds, 20 seconds, and cap at 30 seconds. Add a small random jitter so many jobs submitted at the same time do not poll in lockstep. When a response includes Retry-After, wait at least that many seconds before the next request.
Do not poll /status faster than once every 5 seconds for the same job. Fast polling does not make a job finish sooner, counts against the public API rate limit, and may return rate_limited. For near-real-time updates, use GET /api/v1/jobs/{jobId}/events instead of tight polling.
Only call GET /api/v1/results/{jobId} after the status endpoint or events stream reports a result-ready terminal state. If a result request returns job_not_completed, wait for the Retry-After value before trying again.
Cancel a job
POST /api/v1/jobs/{jobId}/cancel cancels a pending or queued job when cancellation is still allowed.
curl -s -X POST \
-H "Authorization: Bearer $PROTEINIQ_API_KEY" \
"https://proteiniq.io/api/v1/jobs/job_123/cancel"Successful cancellation returns:
{
"object": "job_cancellation",
"refunded": true,
"job": {
"id": "job_123",
"object": "job",
"status": "CANCELLED"
}
}If the job is already running or terminal, the API returns conflict with details.current_status.