> For a complete page index of the Captain API documentation, fetch https://docs.filesearch.dev/llms.txt?excludeSpec=true

# Search Medical Papers

POST https://api.filesearch.dev/v2/datasets/scientific/medical/ask
Content-Type: application/json

Search medical and biomedical papers with a natural-language question.
Federates PubMed, PMC full-text, ClinicalTrials.gov, and Semantic Scholar,
then synthesizes a cited answer.

Implementation: invokes the `medical-agent` Lambda (TypeScript, Mastra,
Claude Sonnet). Streaming responses flow through the `captain-stream` S2
basin — identical pattern to `/v2/collections/{name}/query`.

Reference: https://docs.filesearch.dev/reference/datasets/scientific/medical/search-medical-papers

## Request

### Body (application/json)

This endpoint expects a ScientificAskRequest.

- `question` (string, required) — Natural-language question.
- `include_trials` (boolean, optional, default: true) — Whether the agent may call ClinicalTrials.gov.
- `max_sources` (integer, optional, default: 10) — Target number of cited sources in the final answer.
- `recency_years` (integer, optional, nullable, default: 10) — Prefer evidence within the last N years where the question allows.
- `stream` (boolean, optional, default: false) — If true, response is text/event-stream; otherwise JSON.

## Response

### 200

Synthesized answer with cited sources. Returns JSON by default; when `stream=true` the response is `text/event-stream` with `tool.start`, `tool.end`, `text.delta`, and `run.end` events.

- `answer` (string, required) — Synthesized answer with inline citations.
- `domain` (string, required) — The scientific domain, e.g. 'medical'.
- `gaps` (list of string, optional) — Things the agent could not find or verify.
- `latency_ms` (integer, optional, default: 0) — End-to-end wall-clock latency.
- `sources` (list of ScientificSource, optional)
- `tool_calls` (integer, optional, default: 0) — Number of tool calls the agent made.

## Errors

### 400 Bad Request Error

Malformed body or input validation error.

- `error` (string, required) — Error kind. `HTTP_ERROR` for errors raised by the route.
- `message` (string, required) — What went wrong.
- `path` (string, required) — The request path.
- `status_code` (integer, optional) — The HTTP status code.

### 401 Unauthorized Error

Missing or invalid API key.

- `error` (string, required) — Error kind. `HTTP_ERROR` for errors raised by the route.
- `message` (string, required) — What went wrong.
- `path` (string, required) — The request path.
- `status_code` (integer, optional) — The HTTP status code.

### 403 Forbidden Error

API key does not belong to the organization, datasets access disabled, or trial/billing limits exceeded.

- `error` (string, required) — Error kind. `HTTP_ERROR` for errors raised by the route.
- `message` (string, required) — What went wrong.
- `path` (string, required) — The request path.
- `status_code` (integer, optional) — The HTTP status code.

## Types

### ScientificSource

A single cited source surfaced by the agent.

- `url` (string, required)
- `title` (string, optional, nullable)
- `snippet` (string, optional, nullable)
- `dataset` (string, optional, nullable)
- `published_at` (string, optional, nullable)
- `authors` (list of string, optional, nullable)
- `metadata` (map from string to any, optional, nullable)

## Examples

**Request**

```json
{
  "question": "What is the evidence that BRCA1-mutated breast cancer patients benefit from PARP inhibitors?",
  "include_trials": true,
  "max_sources": 10,
  "recency_years": 10
}
```

**Response**

```json
{
  "answer": "Strong evidence supports PARP inhibitor use in BRCA1-mutated breast cancer across the metastatic and adjuvant settings. The OlympiAD phase 3 trial showed olaparib monotherapy extended progression-free survival versus standard chemotherapy in metastatic HER2-negative disease (7.0 vs. 4.2 months; HR 0.58) [PMID:28578601]. EMBRACA demonstrated similar benefit with talazoparib (8.6 vs. 5.6 months; HR 0.54) [PMID:30110579]. In the adjuvant setting, OlympiA [NCT:NCT02000622] showed improvements in overall survival (HR 0.68) and invasive disease-free survival [PMID:34081848].",
  "domain": "medical",
  "gaps": [
    "Long-term (>10 year) survival data for patients on adjuvant olaparib",
    "Head-to-head comparative efficacy between BRCA1 and BRCA2 subgroups"
  ],
  "latency_ms": 7420,
  "sources": [
    {
      "url": "https://pubmed.ncbi.nlm.nih.gov/28578601/",
      "title": "Olaparib for Metastatic Breast Cancer in Patients with a Germline BRCA Mutation.",
      "dataset": "pubmed",
      "published_at": "2017",
      "metadata": {
        "type": "pubmed",
        "pmid": "28578601",
        "journal": "The New England journal of medicine",
        "year": 2017,
        "doi": "10.1056/NEJMoa1706450"
      }
    },
    {
      "url": "https://pubmed.ncbi.nlm.nih.gov/30110579/",
      "title": "Talazoparib in Patients with Advanced Breast Cancer and a Germline BRCA Mutation.",
      "dataset": "pubmed",
      "published_at": "2018",
      "metadata": {
        "type": "pubmed",
        "pmid": "30110579",
        "journal": "The New England journal of medicine",
        "year": 2018,
        "doi": "10.1056/NEJMoa1802905"
      }
    },
    {
      "url": "https://clinicaltrials.gov/study/NCT02000622",
      "title": "Olaparib as Adjuvant Treatment in Patients With Germline BRCA Mutated High Risk HER2 Negative Primary Breast Cancer (OlympiA)",
      "dataset": "clinical_trials",
      "metadata": {
        "type": "clinical_trial",
        "nct_id": "NCT02000622",
        "phase": "PHASE3",
        "status": "ACTIVE_NOT_RECRUITING"
      }
    },
    {
      "url": "https://pubmed.ncbi.nlm.nih.gov/34081848/",
      "title": "Adjuvant Olaparib for Patients with BRCA1- or BRCA2-Mutated Breast Cancer.",
      "dataset": "pubmed",
      "published_at": "2021",
      "metadata": {
        "type": "pubmed",
        "pmid": "34081848",
        "journal": "The New England journal of medicine",
        "year": 2021,
        "doi": "10.1056/NEJMoa2105215"
      }
    }
  ],
  "tool_calls": 5
}
```

**SDK Code**

```python Python REST
import requests
import uuid
import json

BASE_URL = "https://api.filesearch.dev"
API_KEY = "your_api_key"

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
    "Idempotency-Key": str(uuid.uuid4())
}

response = requests.post(
    f"{BASE_URL}/v2/datasets/scientific/medical/ask",
    headers=headers,
    json={
        "question": "What is the evidence that BRCA1-mutated breast cancer patients benefit from PARP inhibitors?",
        "max_sources": 10,
        "include_trials": True,
        "recency_years": 10
    },
    # First invocation of the day can hit a cold Lambda (~60s) + agent loop
    # (~30-60s). 180s leaves headroom for the full cold path.
    timeout=180.0,
)

# Surface non-JSON responses (504 gateway timeout, 502 bad gateway, empty
# body, HTML error pages) instead of blowing up with an opaque JSONDecodeError.
if response.headers.get("content-type", "").startswith("application/json"):
    print(json.dumps(response.json(), indent=2))
else:
    print(f"HTTP {response.status_code}  content-type={response.headers.get('content-type')!r}")
    print(f"body: {response.text[:500]!r}")
```

```typescript TypeScript REST
const BASE_URL = "https://api.filesearch.dev";
const API_KEY = "your_api_key";

// First invocation of the day can hit a cold Lambda (~60s) + agent loop
// (~30-60s). 180s leaves headroom for the full cold path.
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 180_000);

const response = await fetch(
    `${BASE_URL}/v2/datasets/scientific/medical/ask`,
    {
        method: "POST",
        headers: {
            "Authorization": `Bearer ${API_KEY}`,
            "Content-Type": "application/json",
            "Idempotency-Key": crypto.randomUUID()
        },
        body: JSON.stringify({
            question: "What is the evidence that BRCA1-mutated breast cancer patients benefit from PARP inhibitors?",
            max_sources: 10,
            include_trials: true,
            recency_years: 10
        }),
        signal: controller.signal
    }
);
clearTimeout(timeoutId);

// Surface non-JSON responses (504/502/empty body) instead of crashing.
const contentType = response.headers.get("content-type") || "";
if (!contentType.startsWith("application/json")) {
    const body = await response.text();
    console.log(`HTTP ${response.status}  content-type=${contentType}`);
    console.log(`body: ${body.slice(0, 500)}`);
} else {
    const result = await response.json();
    console.log(JSON.stringify(result, null, 2));
}
```