Python SDK
Work with API resources through Python objects.
The ProteinIQ Python client provides methods for account metadata, tools, jobs, files, workflows, and results. It uses the same authentication, permissions, credits, and error codes as the REST API.
Installation availability
The client requires Python 3.10 or later. A public proteiniq package is not currently available on PyPI, so pip install proteiniq is not a working public installation path.
Use the standard-library quickstart to run an analysis without the SDK. Contact support for SDK distribution availability.
If you already have the ProteinIQ source repository, install its client locally:
python -m pip install ./sdks/pythonThe examples below apply to that client.
Configure a client
Set PROTEINIQ_API_KEY in your environment. The optional PROTEINIQ_BASE_URL defaults to https://proteiniq.io.
Use a context manager to close the HTTP connection when finished:
from proteiniq import ProteinIQ
with ProteinIQ() as client:
account = client.account.get()
tools = client.tools.list()
for tool in tools.data:
print(tool.id, tool.name)You can also pass api_key, base_url, and a request timeout to ProteinIQ(...). Read these values from your secret or environment configuration.
Resource methods
The client provides these resource groups:
client.account:get()for workspace limits and key metadata.client.tools:list()anddescribe(tool_id)for tool contracts.client.jobs:quote(...),submit(...),list(...),status(job_id),wait(job_id), andcancel(job_id).client.files:upload(path, ...),list(...),get(file_id), anddelete(file_id).client.results:get(job_id),wait(job_id), anddownload_files(job_id, directory).client.workflows: List and inspect workflows, start runs, read results, manage curation, continue, cancel, or retry.
Responses provide common fields as attributes and preserve the original response in raw. Lists return data, has_more, and next_cursor; request later pages explicitly.
Some newer REST fields, including job project assignment and max_quoted_credits, are not supported by the current SDK method signatures. Use the Jobs API directly when you need them.
Quote and submit
Both methods accept tool, input, optional settings, and optional billing. Submission also requires name and accepts idempotency_key.
input uses the same inputs[] entries as the tool input model. Check quote.blocking_errors and the quoted credits before submission. A quote does not reserve credits.
Keep a unique idempotency key and the exact submitted payload for each intended analysis. Reuse both after an uncertain submission outcome; use a new key for changed work.
Wait for results
Set PROTEINIQ_JOB_ID to an existing job ID:
import os
from pathlib import Path
from proteiniq import ProteinIQ
with ProteinIQ() as client:
job = client.jobs.wait(os.environ["PROTEINIQ_JOB_ID"], timeout=3600)
if job.status != "COMPLETED":
raise RuntimeError(f"{job.id}: {job.status}: {job.error}")
result = client.results.wait(job.id, timeout=120)
print(result.results)
for index, file in enumerate(result.files, start=1):
if file.url:
name = Path(file.name or "result-file").name
path = Path("outputs") / job.id / f"{index}-{name}"
print(client.download_url(file.url, path))jobs.wait() stops at a terminal state and returns the job even when it failed. results.wait() retries job_not_completed. Both honor Retry-After during their normal waiting behavior. A polling timeout stops waiting without canceling compute.
This example downloads from the result already fetched. The convenience method download_files() fetches a fresh result itself, so calling it immediately after get() or wait() can hit the five-second result polling limit. Use one result fetch or handle RateLimitError before trying another.
download_files() returns an empty path list when there are no files and raises ValueError for an entry without a signed URL. The example above skips entries without URLs.
Reuse a saved file
Upload a sequence file, then use its returned input_reference as the source in a later quote or submission:
with ProteinIQ() as client:
file = client.files.upload("./protein.fasta", tags=["example"])
saved_input = {
"inputs": [{
"id": "protein_1",
"slotId": "protein",
"kind": "protein",
"format": "fasta",
"source": file.input_reference,
}]
}
quote = client.jobs.quote(tool="esmfold", input=saved_input, settings={})
print(quote.estimated_credits, quote.blocking_errors)Uploading needs files:write; quoting with a saved file needs jobs:write and files:read.
Workflow pauses
client.workflows.wait(run_id) waits for a terminal status and does not return merely because a run is PAUSED. For workflows with checkpoints, poll client.workflows.status(run_id) yourself and stop polling when user action is required. Inspect the pause reason before continuing. For a curation checkpoint, retrieve curation(run_id), review the candidates, record your selection with curate(...), then call continue_run(run_id).
The workflow API defines input node IDs, publication requirements, retry scopes, and checkpoint decisions. Do not select the first workflow or first candidate without inspecting it.
Handle errors
The SDK raises typed exceptions for unsuccessful API responses:
from proteiniq import ProteinIQ
from proteiniq.errors import ProteinIQError, RateLimitError
try:
with ProteinIQ() as client:
print(client.account.get().raw)
except RateLimitError as exc:
print("Retry after:", exc.retry_after)
except ProteinIQError as exc:
print(exc.code, exc.message, exc.details)The client does not automatically retry every HTTP error. Catch RateLimitError, wait for its retry delay, and retry the same operation. Preserve the idempotency key for submission retries. Branch on exc.code, since readable messages can change.