← Về thư mục
📄 / / profiles / r-and-d / raw_evidence / scientific-agent-skills / skills / tamarind / references / api_reference.md

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

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": {...} }, ...] }.

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.

Authentication and secrets