Files

10 KiB

title, task, lineage_type, upstream_source, upstream_sha, imported_at, prompt_class, upstream_changes, author, validated
title task lineage_type upstream_source upstream_sha imported_at prompt_class upstream_changes author validated
Tamarind Bio REST API reference import https://github.com/K-Dense-AI/scientific-agent-skills/blob/0807ddbc/skills/tamarind/references/api_reference.md 0807ddbc 2026-06-30 prompt accepted upstream false

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

Method Path Purpose
GET /tools List available tools and their inline parameter schemas. Returns the full list (no server-side filtering — filter client-side).
POST /submit-job Submit one job. Body: jobName, type, settings (+ optional projectTag).
POST /submit-batch Submit many jobs of the same tool. See payload shapes below.
GET /jobs List/inspect jobs. Query: jobName, batch, limit, startKey, organization, includeSubjobs, jobEmail.
POST /result Get a presigned download URL for job results (two-step — see below). Body: jobName (+ optional fileName, pdbsOnly, jobEmail).
POST /stop-job Stop a running or queued job. Body: jobName.
DELETE /delete-job Delete 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 /files List 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-file Remove a file/folder. Query: filePath or folder.
POST /submit-pipeline Run a multi-step pipeline defined inline via stages[].
POST /run-pipeline Run a pipeline saved in the UI. Body: pipelineName, initialInputs/inputs.
GET /usage-statistics Usage/billing. Query: statistic (weighted_hours/jobs), scope (user/org).

Request shapes

GET /tools

Returns a JSON array. Each element:

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

{
  "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:

{
  "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: Running → Aggregating → Complete (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:

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

Code Meaning
200 Success
400 Bad request — invalid parameters/settings
401 Unauthorized — invalid/missing x-api-key
403 Budget exceeded (org/team)
429 Rate limited
404 Not found (e.g. unknown job)
500 Server 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 fields — submit_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).