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

# Bulk Search

> Perform OMOP concept search on multiple queries simultaneously with optimized batch processing - ideal for ETL pipelines and terminology mapping jobs.

## Overview

This endpoint allows you to submit multiple search queries in a single request, enabling efficient batch processing of search operations. It's ideal for applications that need to search for many terms simultaneously, such as data processing pipelines, bulk concept mapping, or batch validation workflows.

Each query uses full-text search with a default limit of 20 results per query (configurable via `page_size`, max 100). Up to 50 queries can be submitted per request.

<Note>
  For paginated results or semantic search, use the [Basic Search](/api-reference/search/basic-search) or [Semantic Search](/api-reference/search/semantic-search) endpoints instead.
</Note>

## Request Body

<ParamField body="searches" type="array" required>
  Array of search query objects (1-50 items)

  <Expandable title="Search Object">
    <ParamField body="search_id" type="string" required>
      Unique identifier for this search within the batch
    </ParamField>

    <ParamField body="query" type="string" required>
      Search term or phrase
    </ParamField>

    <ParamField body="bulk_mode" type="boolean" default="true">
      Enable bulk-optimized search mode for better performance
    </ParamField>

    <ParamField body="vocabulary_ids" type="string[]">
      Filter results to specific vocabularies (e.g., `["SNOMED", "ICD10CM"]`). Overrides `defaults.vocabulary_ids` for this search.
    </ParamField>

    <ParamField body="domain_ids" type="string[]">
      Filter results to specific domains (e.g., `["Condition", "Drug"]`). Overrides `defaults.domain_ids` for this search.
    </ParamField>

    <ParamField body="concept_class_ids" type="string[]">
      Filter results to specific concept classes (e.g., `["Clinical Finding"]`). Overrides `defaults.concept_class_ids` for this search.
    </ParamField>

    <ParamField body="standard_concept" type="string">
      Filter by standard concept status: `"S"` (Standard) or `"C"` (Classification). Overrides `defaults.standard_concept` for this search.
    </ParamField>

    <ParamField body="include_invalid" type="boolean" default="false">
      Include invalid/deprecated concepts. Overrides `defaults.include_invalid` for this search.
    </ParamField>

    <ParamField body="page_size" type="integer" default="20">
      Number of results per search (1-100). Overrides `defaults.page_size` for this search.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="defaults" type="object">
  Default filter parameters applied to all searches. Individual searches can override any default.

  <Expandable title="Defaults Object">
    <ParamField body="vocabulary_ids" type="string[]">
      Default vocabulary filter for all searches (e.g., `["SNOMED"]`)
    </ParamField>

    <ParamField body="domain_ids" type="string[]">
      Default domain filter for all searches (e.g., `["Condition"]`)
    </ParamField>

    <ParamField body="concept_class_ids" type="string[]">
      Default concept class filter for all searches
    </ParamField>

    <ParamField body="standard_concept" type="string">
      Default standard concept filter: `"S"` or `"C"`
    </ParamField>

    <ParamField body="include_invalid" type="boolean" default="false">
      Default invalid concept inclusion
    </ParamField>

    <ParamField body="page_size" type="integer" default="20">
      Default results per search (1-100)
    </ParamField>
  </Expandable>
</ParamField>

## Query Parameters

<ParamField query="vocab_release" type="string">
  Specific vocabulary release version (defaults to latest)
</ParamField>

## Response

<ResponseField name="success" type="boolean">
  Indicates if the request was successful
</ResponseField>

<ResponseField name="data" type="array">
  Array of search results, one per query

  <Expandable title="Search Result">
    <ResponseField name="search_id" type="string">
      Identifier matching the request's search\_id
    </ResponseField>

    <ResponseField name="query" type="string">
      Original search query
    </ResponseField>

    <ResponseField name="status" type="string">
      Query execution status: `completed` or `failed`
    </ResponseField>

    <ResponseField name="results" type="array">
      Array of matching concepts

      <Expandable title="Concept Result">
        <ResponseField name="concept_id" type="integer">
          Unique concept identifier
        </ResponseField>

        <ResponseField name="concept_name" type="string">
          Primary concept name
        </ResponseField>

        <ResponseField name="concept_code" type="string">
          Concept code
        </ResponseField>

        <ResponseField name="vocabulary_id" type="string">
          Source vocabulary
        </ResponseField>

        <ResponseField name="domain_id" type="string">
          Domain classification
        </ResponseField>

        <ResponseField name="concept_class_id" type="string">
          Concept class
        </ResponseField>

        <ResponseField name="standard_concept" type="string">
          Standard concept indicator
        </ResponseField>

        <ResponseField name="search_score" type="number">
          Search relevance score
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="error" type="string">
      Error message (if query failed)
    </ResponseField>

    <ResponseField name="duration" type="number">
      Processing time for this query in milliseconds
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="meta" type="object">
  <Expandable title="Metadata">
    <ResponseField name="request_id" type="string">
      Unique identifier for the request
    </ResponseField>

    <ResponseField name="timestamp" type="string">
      Request timestamp
    </ResponseField>

    <ResponseField name="vocab_release" type="string">
      Vocabulary release version used
    </ResponseField>
  </Expandable>
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://api.omophub.com/v1/search/bulk" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "defaults": {
        "vocabulary_ids": ["SNOMED"],
        "standard_concept": "S"
      },
      "searches": [
        {
          "search_id": "s1",
          "query": "diabetes"
        },
        {
          "search_id": "s2",
          "query": "hypertension"
        },
        {
          "search_id": "s3",
          "query": "aspirin",
          "vocabulary_ids": ["RxNorm"],
          "domain_ids": ["Drug"]
        }
      ]
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://api.omophub.com/v1/search/bulk', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      defaults: {
        vocabulary_ids: ['SNOMED'],
        standard_concept: 'S'
      },
      searches: [
        {
          search_id: 's1',
          query: 'diabetes'
        },
        {
          search_id: 's2',
          query: 'hypertension'
        },
        {
          search_id: 's3',
          query: 'aspirin',
          vocabulary_ids: ['RxNorm'],
          domain_ids: ['Drug']
        }
      ]
    })
  });

  const data = await response.json();
  ```

  ```python Python theme={null}
  import requests

  headers = {
      'Authorization': 'Bearer YOUR_API_KEY',
      'Content-Type': 'application/json'
  }

  payload = {
      'defaults': {
          'vocabulary_ids': ['SNOMED'],
          'standard_concept': 'S'
      },
      'searches': [
          {
              'search_id': 's1',
              'query': 'diabetes'
          },
          {
              'search_id': 's2',
              'query': 'hypertension'
          },
          {
              'search_id': 's3',
              'query': 'aspirin',
              'vocabulary_ids': ['RxNorm'],
              'domain_ids': ['Drug']
          }
      ]
  }

  response = requests.post(
      'https://api.omophub.com/v1/search/bulk',
      headers=headers,
      json=payload
  )

  data = response.json()
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "success": true,
    "data": [
      {
        "search_id": "s1",
        "query": "diabetes",
        "status": "completed",
        "results": [
          {
            "concept_id": 201826,
            "concept_name": "Type 2 diabetes mellitus",
            "concept_code": "44054006",
            "vocabulary_id": "SNOMED",
            "domain_id": "Condition",
            "concept_class_id": "Clinical Finding",
            "standard_concept": "S",
            "search_score": 100
          },
          {
            "concept_id": 4000678,
            "concept_name": "Diabetes mellitus",
            "concept_code": "73211009",
            "vocabulary_id": "SNOMED",
            "domain_id": "Condition",
            "concept_class_id": "Clinical Finding",
            "standard_concept": "S",
            "search_score": 90
          }
        ],
        "duration": 856
      },
      {
        "search_id": "s2",
        "query": "hypertension",
        "status": "completed",
        "results": [
          {
            "concept_id": 320128,
            "concept_name": "Essential hypertension",
            "concept_code": "59621000",
            "vocabulary_id": "SNOMED",
            "domain_id": "Condition",
            "concept_class_id": "Clinical Finding",
            "standard_concept": "S",
            "search_score": 100
          },
          {
            "concept_id": 316866,
            "concept_name": "Hypertensive disorder",
            "concept_code": "38341003",
            "vocabulary_id": "SNOMED",
            "domain_id": "Condition",
            "concept_class_id": "Clinical Finding",
            "standard_concept": "S",
            "search_score": 90
          }
        ],
        "duration": 1034
      },
      {
        "search_id": "s3",
        "query": "aspirin",
        "status": "completed",
        "results": [
          {
            "concept_id": 1112807,
            "concept_name": "Aspirin",
            "concept_code": "1191",
            "vocabulary_id": "RxNorm",
            "domain_id": "Drug",
            "concept_class_id": "Ingredient",
            "standard_concept": "S",
            "search_score": 100
          }
        ],
        "duration": 957
      }
    ],
    "meta": {
      "request_id": "req_bulk_search_123",
      "timestamp": "2024-01-15T10:30:00Z",
      "vocab_release": "2025.2"
    }
  }
  ```
</ResponseExample>

## Usage Examples

### Basic Bulk Search

Search multiple terms simultaneously without filters:

```bash theme={null}
curl -X POST "https://api.omophub.com/v1/search/bulk" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "searches": [
      {"search_id": "s1", "query": "diabetes"},
      {"search_id": "s2", "query": "hypertension"},
      {"search_id": "s3", "query": "asthma"}
    ]
  }'
```

### Filtered Bulk Search with Defaults

Apply common filters to all searches using `defaults`, with per-search overrides:

```bash theme={null}
curl -X POST "https://api.omophub.com/v1/search/bulk" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "defaults": {
      "vocabulary_ids": ["SNOMED"],
      "domain_ids": ["Condition"],
      "standard_concept": "S"
    },
    "searches": [
      {"search_id": "s1", "query": "diabetes"},
      {"search_id": "s2", "query": "heart failure"},
      {"search_id": "s3", "query": "metformin", "vocabulary_ids": ["RxNorm"], "domain_ids": ["Drug"]}
    ]
  }'
```

In this example, searches s1 and s2 use the defaults (SNOMED, Condition domain, standard concepts only). Search s3 overrides with RxNorm vocabulary and Drug domain.

### Per-Search Filters

Apply different filters to each search individually:

```bash theme={null}
curl -X POST "https://api.omophub.com/v1/search/bulk" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "searches": [
      {"search_id": "s1", "query": "diabetes", "vocabulary_ids": ["SNOMED"], "domain_ids": ["Condition"]},
      {"search_id": "s2", "query": "metformin", "vocabulary_ids": ["RxNorm"], "domain_ids": ["Drug"]},
      {"search_id": "s3", "query": "HbA1c", "vocabulary_ids": ["LOINC"], "domain_ids": ["Measurement"]}
    ]
  }'
```

### High-Volume Processing

Process larger batches from a file:

```bash theme={null}
curl -X POST "https://api.omophub.com/v1/search/bulk" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d @bulk_queries.json
```

Example `bulk_queries.json`:

```json theme={null}
{
  "defaults": {
    "vocabulary_ids": ["SNOMED"],
    "standard_concept": "S"
  },
  "searches": [
    {"search_id": "q1", "query": "diabetes mellitus"},
    {"search_id": "q2", "query": "essential hypertension"},
    {"search_id": "q3", "query": "chronic kidney disease"},
    {"search_id": "q4", "query": "atrial fibrillation"},
    {"search_id": "q5", "query": "congestive heart failure"}
  ]
}
```

## Optimization Strategies

### Query Grouping

* **Batch Size**: Optimal batch size is 10-50 queries per request
* **Bulk Mode**: The `bulk_mode` parameter (default `true`) enables optimized query execution for better throughput
* **Result Limit**: Each query returns up to 20 results by default (configurable via `page_size`, max 100)

### Performance Tips

* Use concise, specific search terms for faster results
* Split very large query sets into multiple requests of 50 queries each
* Use the `search_id` field to correlate results with your source data
* Use `defaults` to apply common filters instead of repeating them per search
* Applying filters (vocabulary, domain) generally improves performance by narrowing the search space

### Error Handling

* Each query in the batch is processed independently
* Failed queries return `status: "failed"` with an `error` message
* Successful queries are not affected by failures in other queries

## Related Endpoints

* [Basic Search](/api-reference/search/basic-search) - Single query search with pagination
* [Advanced Search](/api-reference/search/advanced-search) - Complex search with advanced filters
* [Semantic Search](/api-reference/search/semantic-search) - AI-powered contextual search
