> ## Documentation Index
> Fetch the complete documentation index at: https://docs.biomni.phylo.bio/llms.txt
> Use this file to discover all available pages before exploring further.

# Recipes

> Common API patterns: batch runs, long-running tasks, and cancellation

Task-focused patterns on top of the [core loop](/api/quickstart). All snippets
assume `BIOMNI_API_KEY` is set and use `https://api.phylo.bio/api/v1`.

## Run a batch of analyses

Start many tasks, then collect results as each finishes. Because tasks run
asynchronously, kick them all off first and poll afterwards.

```python theme={null}
import os, time, requests

BASE = "https://api.phylo.bio/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['BIOMNI_API_KEY']}"}

def post(path, body):
    r = requests.post(f"{BASE}{path}", headers=HEADERS, json=body); r.raise_for_status(); return r.json()
def get(path, **p):
    r = requests.get(f"{BASE}{path}", headers=HEADERS, params=p); r.raise_for_status(); return r.json()

project_id = post("/projects", {"name": "Batch run"})["id"]

prompts = [
    "Summarize the clinical significance of variant rs80357065.",
    "Summarize the clinical significance of variant rs28897696.",
    "Summarize the clinical significance of variant rs80357382.",
]

# 1. Fire off all tasks
task_ids = [
    post("/messages", {"project_id": project_id, "content": p})["task"]["id"]
    for p in prompts
]

# 2. Poll until all complete
pending = set(task_ids)
while pending:
    for tid in list(pending):
        if get(f"/tasks/{tid}")["status"] in ("completed", "failed"):
            pending.discard(tid)
    if pending:
        time.sleep(10)

# 3. Collect results
for tid in task_ids:
    files = get(f"/tasks/{tid}/results", with_urls="true").get("data", [])
    print(tid, [f["name"] for f in files])
```

<Tip>
  Keep your concurrency reasonable and add backoff. If you hit a `429`, slow
  down — see [Errors & rate limits](/api/errors).
</Tip>

## Handle long-running tasks

Agent tasks can run for minutes. Don't block a single request on completion —
poll with a bounded interval and a timeout:

```python theme={null}
def wait_for_task(task_id, *, interval=10, timeout=3600):
    deadline = time.time() + timeout
    while time.time() < deadline:
        task = get(f"/tasks/{task_id}")
        if task["status"] in ("completed", "failed"):
            return task
        time.sleep(interval)
    raise TimeoutError(f"{task_id} did not finish within {timeout}s")
```

For interactive or streaming output, use the streaming endpoint instead of
polling — see the [API reference](https://api.phylo.bio/api/v1/docs).

## Continue a task

Send another message to the **same task** with its `task_id` to ask a
follow-up. The agent keeps the task's context.

```bash theme={null}
curl -X POST https://api.phylo.bio/api/v1/messages \
  -H "Authorization: Bearer $BIOMNI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "task_id": "sess_d9711ec9b477",
    "content": "Now cross-check those variants against the latest ClinVar release."
  }'
```

## Cancel a running task

Stop the agent while it's still working:

```bash theme={null}
curl -X POST https://api.phylo.bio/api/v1/tasks/sess_d9711ec9b477/cancel \
  -H "Authorization: Bearer $BIOMNI_API_KEY"
```
