> ## 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.

# Get Concept Descendants

> Retrieve all descendant OMOP concepts for a given concept - hierarchical children and specialized terms for phenotype and cohort definitions.

This endpoint returns all descendant concepts (children, grandchildren, etc.) for a specific concept, allowing exploration of more specific terms and sub-classifications within the medical vocabulary hierarchy.

## Path Parameters

<ParamField path="concept_id" type="integer" required>
  The unique identifier of the concept to retrieve descendants for

  <br />

  **Example:** `73211009` (Diabetes mellitus)
</ParamField>

## Query Parameters

<ParamField query="vocabulary_ids" type="string" optional>
  Filter descendants to specific vocabularies (comma-separated)

  <br />

  **Example:** `SNOMED,ICD10CM`
</ParamField>

<ParamField query="domain_ids" type="string" optional>
  Filter descendants to specific domains (comma-separated)

  <br />

  **Example:** `Condition,Drug`
</ParamField>

<ParamField query="max_levels" type="integer" optional default="10">
  Maximum number of hierarchy levels to traverse downward

  <br />

  **Range:** `1-20`
</ParamField>

<Warning>
  **This endpoint always traverses the `Is a` / `Subsumes` hierarchy.** It reads
  OMOP's precomputed `concept_ancestor` closure, which stores only transitive
  subsumption - it has no relationship column to filter on. Every descendant is
  therefore returned with `relationship_id: "Subsumes"`.

  A `relationship_types` parameter is accepted for backwards compatibility but has
  **no effect here**: passing `Has part`, or even a nonexistent relationship, returns
  the same descendants. To traverse other relationship types, use
  [`GET /v1/concepts/{concept_id}/relationships`](/api-reference/concepts/get-concept-relationships)
  or [`POST /v1/concepts/relationships/traverse`](/api-reference/concepts/traverse-relationships),
  which query `concept_relationship` directly.
</Warning>

<ParamField query="include_distance" type="boolean" optional default="false">
  Include `hierarchy_level` field for each descendant (distance from source concept)
</ParamField>

<ParamField query="include_paths" type="boolean" optional default="false">
  Include `path_length` field for each descendant
</ParamField>

<ParamField query="include_invalid" type="boolean" optional default="false">
  Include deprecated/invalid concepts in descendants (by default they are excluded)
</ParamField>

<ParamField query="max_results" type="integer" optional default="5000">
  Maximum size of the descendant set to retrieve before pagination is applied.

  <br />

  **Range:** `1-5000`

  <br />

  Defaults to the maximum, so paging through the response returns every descendant
  **that matches this request** - that is, those within `max_levels` and any
  `domain_ids` / `vocabulary_ids` filters you supplied. (`relationship_types` is not
  applied - see above.) It is not necessarily the concept's entire descendant
  closure: `max_levels` defaults to `10`, so deeper descendants are excluded, and
  `truncated` does **not** flag that (it reports the row cap only). For a full
  closure, pass `max_levels=20` as well.

  Lower `max_results` only if you deliberately want a partial set - note that
  `total_items` then reflects the truncated set, not the true descendant count. To
  read the true, unfiltered count without fetching the set, use
  [`GET /v1/concepts/{concept_id}/level`](/api-reference/concepts/get-concept-level).
</ParamField>

<ParamField query="page" type="integer" optional default="1">
  Page number for pagination (1-based)
</ParamField>

<ParamField query="page_size" type="integer" optional default="100">
  Number of descendant concepts to return per page. Values above `200` are
  clamped to `200`.
</ParamField>

<ParamField query="vocab_release" type="string" optional>
  Specific vocabulary release version to query

  <br />

  **Example:** `2025.1`
</ParamField>

## Response

<ResponseField name="concept_id" type="integer">
  The concept ID for which descendants were retrieved
</ResponseField>

<ResponseField name="concept_name" type="string">
  Standard name of the source concept
</ResponseField>

<ResponseField name="vocabulary_id" type="string">
  Vocabulary containing the source concept
</ResponseField>

<ResponseField name="descendants" type="array">
  Array of descendant concepts in hierarchical order

  <Expandable title="Descendant Concept Object">
    <ResponseField name="concept_id" type="integer">
      Unique identifier for the descendant concept
    </ResponseField>

    <ResponseField name="concept_name" type="string">
      Standard name of the descendant concept
    </ResponseField>

    <ResponseField name="concept_code" type="string">
      Original code from the vocabulary
    </ResponseField>

    <ResponseField name="vocabulary_id" type="string">
      Vocabulary containing this descendant concept
    </ResponseField>

    <ResponseField name="vocabulary_name" type="string">
      Human-readable vocabulary name
    </ResponseField>

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

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

    <ResponseField name="standard_concept" type="string">
      Standard concept designation ('S', 'C', or null)
    </ResponseField>

    <ResponseField name="level" type="integer">
      Distance from source concept (always present)
    </ResponseField>

    <ResponseField name="min_levels_of_separation" type="integer">
      Minimum levels of separation from source concept
    </ResponseField>

    <ResponseField name="max_levels_of_separation" type="integer">
      Maximum levels of separation from source concept
    </ResponseField>

    <ResponseField name="relationship_id" type="string">
      Relationship type ID (e.g., "Subsumes")
    </ResponseField>

    <ResponseField name="relationship_name" type="string">
      Relationship type name (e.g., "Subsumes")
    </ResponseField>

    <ResponseField name="hierarchy_level" type="integer" optional>
      Distance from source concept (only when include\_distance=true, same as level)
    </ResponseField>

    <ResponseField name="path_length" type="integer" optional>
      Path length from source concept (only when include\_paths=true, same as level)
    </ResponseField>

    <ResponseField name="valid_start_date" type="string">
      Date when concept became valid (ISO format)
    </ResponseField>

    <ResponseField name="valid_end_date" type="string">
      Date when concept became invalid (ISO format)
    </ResponseField>

    <ResponseField name="invalid_reason" type="string" optional>
      Reason for concept invalidation if applicable
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="hierarchy_summary" type="object">
  Summary statistics about the descendant hierarchy

  <Expandable title="Hierarchy Summary Object">
    <ResponseField name="total_descendants" type="integer">
      Number of descendant concepts retrieved. When `truncated` is `true` this is
      the size of the retrieved subset, **not** the true descendant count.
    </ResponseField>

    <ResponseField name="truncated" type="boolean">
      `true` when the concept has more matching descendants than `result_limit`
      and the response holds only the first `result_limit` of them. The pagination
      metadata then describes the retrieved subset, so paging to the last page
      does **not** give you everything.

      Always check this before building a concept set from the response - a
      truncated set is otherwise indistinguishable from a complete one, and the
      resulting cohort is silently under-counted. To recover: narrow the query
      with `domain_ids` / `vocabulary_ids`, or split the concept into several
      narrower roots.

      <Warning>
        `truncated` reports the **row cap only**. It does not tell you whether
        descendants were dropped by `max_levels`, which defaults to `10` - a
        response can be depth-limited and still report `truncated: false`. Pass
        `max_levels=20` if you need the full closure.
      </Warning>

      [`GET /v1/concepts/{concept_id}/level`](/api-reference/concepts/get-concept-level)
      reports the true (unfiltered, undepth-limited) descendant count, which is
      the reliable way to check a response for completeness.
    </ResponseField>

    <ResponseField name="result_limit" type="integer">
      The row cap applied to this request - the effective `max_results`.
    </ResponseField>

    <ResponseField name="max_hierarchy_depth" type="integer">
      Maximum depth of the descendant tree
    </ResponseField>

    <ResponseField name="unique_vocabularies" type="array">
      List of vocabulary IDs represented in descendants
    </ResponseField>

    <ResponseField name="relationship_types_used" type="array">
      List of relationship type IDs found in hierarchy
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="meta" type="object">
  Response metadata and pagination information

  <Expandable title="Metadata Object">
    <ResponseField name="pagination" type="object">
      <ResponseField name="page" type="integer">Current page number</ResponseField>
      <ResponseField name="page_size" type="integer">Items per page</ResponseField>
      <ResponseField name="total_items" type="integer">Total descendant concepts</ResponseField>
      <ResponseField name="total_pages" type="integer">Total number of pages</ResponseField>
      <ResponseField name="has_next" type="boolean">Whether next page exists</ResponseField>
      <ResponseField name="has_previous" type="boolean">Whether previous page exists</ResponseField>
    </ResponseField>
  </Expandable>
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X GET "https://api.omophub.com/v1/concepts/73211009/descendants?include_distance=true&max_levels=3" \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```

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

  concept_id = 73211009  # Diabetes mellitus
  url = f"https://api.omophub.com/v1/concepts/{concept_id}/descendants"
  params = {
      "include_distance": True,
      "max_levels": 3,
      "page_size": 50
  }

  headers = {
      "Authorization": "Bearer YOUR_API_KEY"
  }

  response = requests.get(url, params=params, headers=headers)
  descendant_data = response.json()

  print(f"Concept: {descendant_data['concept_name']}")
  print(f"Total descendants: {descendant_data['hierarchy_summary']['total_descendants']}")
  ```

  ```ts TypeScript theme={null}
  import { OMOPHub } from '@omophub/omophub-node';

  const client = new OMOPHub();
  const { data: descendantData } = await client.hierarchy.descendants(73211009, {
    includeDistance: true,
    maxLevels: 3,
  });

  console.log(`Found ${descendantData?.hierarchy_summary.total_descendants} descendants`);
  console.log(`Max depth: ${descendantData?.hierarchy_summary.max_hierarchy_depth}`);
  ```

  ```bash cURL (filtered by domain) theme={null}
  curl -X GET "https://api.omophub.com/v1/concepts/73211009/descendants?domain_ids=Condition&max_levels=2&page_size=25" \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```

  ```python Python (with hierarchy grouping) theme={null}
  import requests
  from collections import defaultdict

  concept_id = 73211009
  response = requests.get(
      f"https://api.omophub.com/v1/concepts/{concept_id}/descendants",
      headers={"Authorization": "Bearer YOUR_API_KEY"},
      params={"include_distance": True, "max_levels": 3}
  )

  data = response.json()
  by_level = defaultdict(list)

  for desc in data['descendants']:
      level = desc.get('hierarchy_level', 0)
      by_level[level].append(desc['concept_name'])

  for level in sorted(by_level.keys()):
      print(f"Level {level}: {len(by_level[level])} concepts")
  ```
</RequestExample>

<ResponseExample>
  ```json theme={null}
  {
    "success": true,
    "data": {
      "concept_id": 73211009,
      "concept_name": "Diabetes mellitus",
      "vocabulary_id": "SNOMED",
      "descendants": [
        {
          "concept_id": 44054006,
          "concept_name": "Type 2 diabetes mellitus",
          "concept_code": "44054006",
          "vocabulary_id": "SNOMED",
          "vocabulary_name": "SNOMED Clinical Terms",
          "domain_id": "Condition",
          "concept_class_id": "Clinical Finding",
          "standard_concept": "S",
          "level": 1,
          "min_levels_of_separation": 1,
          "max_levels_of_separation": 1,
          "relationship_id": "Subsumes",
          "relationship_name": "Subsumes",
          "hierarchy_level": 1,
          "valid_start_date": "1970-01-01",
          "valid_end_date": "2099-12-31",
          "invalid_reason": null
        },
        {
          "concept_id": 46635009,
          "concept_name": "Type 1 diabetes mellitus",
          "concept_code": "46635009",
          "vocabulary_id": "SNOMED",
          "vocabulary_name": "SNOMED Clinical Terms",
          "domain_id": "Condition",
          "concept_class_id": "Clinical Finding",
          "standard_concept": "S",
          "level": 1,
          "min_levels_of_separation": 1,
          "max_levels_of_separation": 1,
          "relationship_id": "Subsumes",
          "relationship_name": "Subsumes",
          "hierarchy_level": 1,
          "valid_start_date": "1970-01-01",
          "valid_end_date": "2099-12-31",
          "invalid_reason": null
        },
        {
          "concept_id": 237599002,
          "concept_name": "Insulin dependent diabetes mellitus",
          "concept_code": "237599002",
          "vocabulary_id": "SNOMED",
          "vocabulary_name": "SNOMED Clinical Terms",
          "domain_id": "Condition",
          "concept_class_id": "Clinical Finding",
          "standard_concept": "S",
          "level": 2,
          "min_levels_of_separation": 2,
          "max_levels_of_separation": 2,
          "relationship_id": "Subsumes",
          "relationship_name": "Subsumes",
          "hierarchy_level": 2,
          "valid_start_date": "2002-01-31",
          "valid_end_date": "2099-12-31",
          "invalid_reason": null
        }
      ],
      "hierarchy_summary": {
        "total_descendants": 847,
        "truncated": false,
        "result_limit": 5000,
        "max_hierarchy_depth": 6,
        "unique_vocabularies": ["SNOMED"],
        "relationship_types_used": ["Subsumes"]
      }
    },
    "meta": {
      "pagination": {
        "page": 1,
        "page_size": 100,
        "total_items": 847,
        "total_pages": 9,
        "has_next": true,
        "has_previous": false
      },
      "request_id": "req_descendants_123",
      "timestamp": "2024-12-22T10:00:00Z",
      "vocab_release": "2025.2"
    }
  }
  ```
</ResponseExample>

## Usage Examples

**Note:** Examples assume a preconfigured `client` from `new OMOPHub()`. For direct usage:

```ts TypeScript theme={null}
import { OMOPHub } from '@omophub/omophub-node';
const client = new OMOPHub();
const { data } = await client.hierarchy.descendants(73211009);
```

### Basic Descendant Retrieval

Get all descendants for a specific concept:

```ts TypeScript theme={null}
const { data: descendants } = await client.hierarchy.descendants(73211009);
```

### Limited Hierarchy Depth

Retrieve descendants up to a specific number of levels:

```ts TypeScript theme={null}
const { data: nearDescendants } = await client.hierarchy.descendants(73211009, { maxLevels: 2 });
```

### Domain-Specific Descendants

Filter descendants to specific medical domains:

```ts TypeScript theme={null}
const { data: conditionDescendants } = await client.hierarchy.descendants(73211009, {
  domainIds: ['Condition'],
});
```

### Detailed Descendant Information

Get descendants with hierarchy levels and path information:

```ts TypeScript theme={null}
const { data: detailedData } = await client.hierarchy.descendants(73211009, {
  includeDistance: true,
  includePaths: true,
});
```

## Related Endpoints

* [Get Concept Ancestors](/api-reference/hierarchy/get-concept-ancestors) - Retrieve parent concepts
* [Get Concept Hierarchy](/api-reference/hierarchy/get-concept-hierarchy) - Complete hierarchy view
* [Get Concept Relationships](/api-reference/relationships/get-concept-relationships) - All concept relationships
* [Search Concepts](/api-reference/search/basic-search) - Search within specific hierarchies

## Notes

* Descendant traversal follows "Is a" relationships by default, but can include other relationship types
* Large hierarchies may contain thousands of descendants - use pagination and filtering appropriately
* Standard concepts are prioritized unless explicitly disabled
* Some medical concepts may have very deep hierarchies (6+ levels)
* Cross-vocabulary concepts may have descendants from multiple vocabularies
* Deprecated concepts are excluded from descendants unless specifically requested with `include_invalid=true`
