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.
Share a job
GET /api/v1/jobs/{jobId}/share returns the current sharing settings for a job in the API key workspace. The endpoint requires jobs:read.
curl -s \
-H "Authorization: Bearer $PROTEINIQ_API_KEY" \
"https://proteiniq.io/api/v1/jobs/job_123/share"The response includes the job visibility and invited email shares.
{
"object": "job_sharing",
"job_id": "job_123",
"visibility": "INVITED",
"shares": [
{
"id": "share_123",
"object": "job_share",
"email": "collaborator@example.com",
"claimed": true,
"claimed_at": "2026-06-12T08:01:00.000Z",
"invitee_name": "Collaborator",
"created_at": "2026-06-12T08:00:00.000Z"
}
]
}PATCH /api/v1/jobs/{jobId}/share updates the same sharing settings available in the web app. The endpoint requires jobs:write, and the API key creator must have permission to update the job.
curl -s -X PATCH \
-H "Authorization: Bearer $PROTEINIQ_API_KEY" \
-H "Content-Type: application/json" \
"https://proteiniq.io/api/v1/jobs/job_123/share" \
-d '{
"visibility": "INVITED",
"add_invites": ["collaborator@example.com"],
"remove_invites": ["old-collaborator@example.com"]
}'Visibility values are:
PRIVATE: Keeps access within normal workspace job permissionsPUBLIC: Allows anyone with the job link to view the jobAUTHENTICATED: Allows any signed-in ProteinIQ user to view the jobINVITED: Allows invited email addresses to view the job, alongside normal workspace job permissions
Update fields are:
visibility: One ofPRIVATE,PUBLIC,AUTHENTICATED, orINVITEDadd_invites: Array of email addresses to add to the invited listremove_invites: Array of email addresses to remove from the invited list
Snake_case field names are preferred. The API also accepts addInvites and removeInvites aliases for clients that share request code with the web app.
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.