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.

Bash
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.

JSON
{
  "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.

JSON
{
  "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.

Bash
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:

JSON
{
  "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.

Bash
curl -s \
  -H "Authorization: Bearer $PROTEINIQ_API_KEY" \
  "https://proteiniq.io/api/v1/jobs?limit=20"

List jobs supports:

  • limit: Integer from 1 to 100, defaults to 20
  • starting_after: Cursor returned as next_cursor from a previous page

The response is ordered by newest job first.

JSON
{
  "object": "list",
  "data": [],
  "has_more": false,
  "next_cursor": null
}

Get job status

GET /api/v1/jobs/{jobId}/status returns the current job resource.

Bash
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.

Bash
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.

JSON
{
  "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.

Bash
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 permissions
  • PUBLIC: Allows anyone with the job link to view the job
  • AUTHENTICATED: Allows any signed-in ProteinIQ user to view the job
  • INVITED: Allows invited email addresses to view the job, alongside normal workspace job permissions

Update fields are:

  • visibility: One of PRIVATE, PUBLIC, AUTHENTICATED, or INVITED
  • add_invites: Array of email addresses to add to the invited list
  • remove_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.

Bash
curl -s -X POST \
  -H "Authorization: Bearer $PROTEINIQ_API_KEY" \
  "https://proteiniq.io/api/v1/jobs/job_123/cancel"

Successful cancellation returns:

JSON
{
  "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.