Back to Claude Scientific Skills

Tamarind Bio REST API reference

skills/tamarind/references/api_reference.md

2.54.010.2 KB
Original Source

Tamarind Bio REST API reference

Spec: the OpenAPI spec at https://app.tamarind.bio/openapi.yaml (3.0, auth ApiKeyAuth) covers the 8 core job endpoints (/submit-job, /submit-batch, /jobs, /result, /upload/{filename}, /files, /delete-job, /delete-file) — fetch it for those exact shapes. It does not include the discovery/management endpoints (/tools, /usage-statistics, /submit-pipeline, /run-pipeline, /stop-job) — for those, use this file + the live MCP getAvailableTools/getJobSchema/getJobs. This file also adds the behaviors no spec spells out (response-shape-by-query, two-step result download, batch aggregation polling, REST-vs-MCP field differences).

Base URL: https://app.tamarind.bio/api/ Authentication: x-api-key: <YOUR_KEY> header on every request. Interactive docs: app.tamarind.bio/api-docs · markdown docs at docs.tamarind.bio

There is no official Python SDK. Call the API with requests (Python) or curl. An MCP server (https://mcp.tamarind.bio/mcp, X-API-Key header) exposes the same operations with agent-friendly schemas.

Endpoints

MethodPathPurpose
GET/toolsList available tools and their inline parameter schemas. Returns the full list (no server-side filtering — filter client-side).
POST/submit-jobSubmit one job. Body: jobName, type, settings (+ optional projectTag).
POST/submit-batchSubmit many jobs of the same tool. See payload shapes below.
GET/jobsList/inspect jobs. Query: jobName, batch, limit, startKey, organization, includeSubjobs, jobEmail.
POST/resultGet a presigned download URL for job results (two-step — see below). Body: jobName (+ optional fileName, pdbsOnly, jobEmail).
POST/stop-jobStop a running or queued job. Body: jobName.
DELETE/delete-jobDelete a job and its data. Body: jobName.
PUT/upload/{filename}Upload a file (--data-binary; add ?folder= to file it). Or get a presigned URL via MCP uploadFile.
GET/filesList your account's uploaded files as a flat array of filename strings. Query: folder, includeFolders=true. Does not enumerate a specific job's outputs — use MCP listJobFiles for that.
DELETE/delete-fileRemove a file/folder. Query: filePath or folder.
POST/submit-pipelineRun a multi-step pipeline defined inline via stages[].
POST/run-pipelineRun a pipeline saved in the UI. Body: pipelineName, initialInputs/inputs.
GET/usage-statisticsUsage/billing. Query: statistic (weighted_hours/jobs), scope (user/org).

Request shapes

GET /tools

Returns a JSON array. Each element:

json
{
  "name": "alphafold",
  "displayName": "AlphaFold",
  "description": "Accurate and quick protein structure prediction ...",
  "github": "https://github.com/...",
  "paper": "https://...",
  "settings": [ { "name": "sequence", "type": "sequence", "required": true, "description": "..." }, ... ]
}

In each settings param, only name and required are guaranteed; type, default, description, options are present only when applicable (about 60% of params carry type). Read them with param.get("type"), not param["type"].

settings is the tool's inline parameter schema — read it directly, no separate schema endpoint over REST. The REST list is not filtered by query params; filter client-side on name/displayName/description. (The MCP getAvailableTools wraps the list as {"totalTools", "tools":[...]} and adds categories/tags per tool plus server-side search/category/tag filtering.)

POST /submit-job

json
{
  "jobName": "my-protein-analysis",
  "type": "alphafold",
  "settings": { "sequence": "MKT...", "numRecycles": 3 },
  "projectTag": "proj_xxxxxxxx"
}
  • jobName — unique, ^[a-zA-Z0-9_-]+$, 1-100 chars.
  • type — a tool name from /tools. The list changes often; never hardcode.
  • settings — tool-specific; match the schema from /tools (or MCP getJobSchema).
  • projectTag — optional proj_... ProjectId to file the job under a project.

Response (200): a confirmation string like myJobName submitted to queue.

POST /submit-batch

Two payload shapes appear in the official docs — the Python form uses parallel arrays; the curl form uses a jobs[] array of objects with a tool key. The parallel-array form matches the MCP submitBatch and is the recommended one:

json
{
  "batchName": "egfr-screen",
  "type": "alphafold",
  "jobNames": ["seq1", "seq2"],
  "settings": [{ "sequence": "..." }, { "sequence": "..." }],
  "maxRuntimeSeconds": 3600,
  "weightedHoursBudget": 100
}

curl-form alternative (same endpoint): { "tool": "<type>", "batchName": ..., "jobs": [{ "jobName": ..., "settings": {...} }, ...] }.

  • jobNames and settings are parallel arrays, same length, 1-100 items, all using the same tool.
  • maxRuntimeSeconds — optional per-job timeout. weightedHoursBudget — optional budget cap.
  • The MCP submitBatch schema exposes maxRuntimeSeconds + weightedHoursBudget. Some accounts/tools may accept an optional gpuType (seen in the docs UI), but it isn't in openapi.yaml or the MCP schema — treat it as unverified and confirm with support before relying on it.

GET /jobs

Response shape depends on the query:

  • List / batch query (no jobName, or ?batch=/?organization=) → { "jobs": [...], "startKey": "...", "statuses": {...} }.
  • By-name (?jobName=<name>) → the job row object directly (no jobs wrapper). Don't index ["jobs"][0] on this response.

Each job row includes JobName, Type, JobStatus, Created, Started, Completed, Settings (JSON string), Score (JSON string, tool metrics), WeightedHours. Use startKey for pagination past the limit (default 1000). Only top-level jobs return by default; add includeSubjobs=true for batch subjobs.

Batch parent rows have Type: "batch" and carry batchStatus. Fetched by name (?jobName=<batchName>), a complete batch parent also includes resultUrl (presigned download). batchStatus transitions: RunningAggregatingComplete (or AggregationFailed, with AggregationError). Poll the parent's batchStatus, not subjob JobStatus — subjobs go Complete before the aggregated output is ready.

Discriminate batch vs single by Type == "batch" (or presence of batchStatus), not by statuses. A by-name response can carry a statuses tally even for a single (non-batch) job, so statuses presence is not a reliable batch signal.

POST /result (two-step download)

POST returns a presigned URL as a bare string (not JSON). Fetch that URL with a second GET to download the results zip:

python
url = requests.post(f"{BASE}/result", headers=H, json={"jobName": "myJob"}).text.strip('"')
open("myJob.zip", "wb").write(requests.get(url).content)

Optional body fields: fileName (one file instead of the zip), pdbsOnly: true (PDB outputs only), jobEmail (a teammate's job, if permitted).

Status codes

CodeMeaning
200Success
400Bad request — invalid parameters/settings
401Unauthorized — invalid/missing x-api-key
403Budget exceeded (org/team)
429Rate limited
404Not found (e.g. unknown job)
500Server error

Field-handling rules (important)

The REST and MCP schemas expose different fields. The REST /tools entry gives a trimmed per-param view — {name, type, required, default, description, options}. The advanced gating keys exclude and conditionals appear only in MCP getJobSchema, not in REST /tools (restrictOrgs is no longer returned by either surface — see below). So don't try to hand-derive what to strip from REST schema keys — they aren't there. The reliable guard on both surfaces is validateJob (MCP): it runs /submit-job's exact validation without submitting and returns the first error.

  • Build your submit from your own settings, not validateJob's normalized output. normalized is informational (defaults filled in, sometimes platform-managed fields). Submit the same clean settings you validated, not the normalized echo.
  • Platform-internal routing fieldssubmit_method, monomer_msa, msa are set by the platform. Never pass them.
  • restrictOrgs — org-gated parameters. getJobSchema no longer returns this key (it's stripped server-side): a parameter your account isn't authorized for is dropped from the schema entirely, and any param you do see is one you may set. So you won't encounter restrictOrgs in a response — don't look for it.
  • conditionals (MCP schema only) — a field only applies when another field has a given value (e.g. pairMode applies only when useMSA is true). Don't send conditioned fields when their condition isn't met.
  • exclude: [...] (MCP schema only) — marks a field as UI/pipeline-only for a surface. Treat it as advisory; validateJob is the authority on what a given submission accepts.
  • required: true — must be present. Some tools require more than sequence (e.g. boltz requires inputFormat). Run validateJob to get the first missing/invalid field before submitting.
  • File-typed fields with a plain string value are treated as INLINE CONTENT, not a path. To reference an uploaded file, use its bare filename (target.pdb) — the platform scopes it to your account, so do NOT email-prefix it. The {email}/{filename} form is the underlying S3 key, and passing it makes submit-job 400 with "The following files have not been uploaded: <email>/<file>". To reference a prior job's output, use JobName/path/to/file.ext. Confirm the exact registered name with getFiles / GET /files (a flat list of bare names).

Authentication and secrets

  • Read the key from TAMARIND_API_KEY (env or .env); never hardcode or commit it.
  • The same key authenticates REST (x-api-key) and the MCP server (X-API-Key).
  • Query operations are scoped to the authenticated account (and, with organization=true/jobEmail, to your org if permitted).