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

# Extracting tables

> Read table cells by row and column from the structured result, including tables with merged and spanning cells

When a document carries data in tables, you usually want that data as rows and columns rather than as rendered text. The structured result returns each table as a grid of cells, each cell placed by its row and column index and carrying any spans. This guide turns that grid into a structure your code can iterate.

## Prerequisites

* A completed parse job. See [Parse your first document](/document-parsing/tutorials/parse-your-first-document) if you need one.
* The structured result, fetched with `get_structured_result`, which is where the cell grid is exposed.

## Find the tables

The structured result groups elements by page. Walk the pages and filter their elements by `label` to pull out the tables. Each table element holds a `table` object with its cell grid, its `num_rows` and `num_cols` counts, the page it sits on, and a `confidence` score.

<CodeGroup>
  ```python Python theme={null}
  from meibel import ParseLayoutLabel

  result = client.documents.get_structured_result(job_id=job_id)

  tables = [
      el for page in result.pages for el in page.elements if el.label == ParseLayoutLabel.TABLE
  ]
  print(f"Found {len(tables)} tables")
  ```

  ```typescript TypeScript theme={null}
  const result = await client.documents.getStructuredResult(jobId);

  const tables = result.pages
    .flatMap((page) => page.elements)
    .filter((el) => el.label === 'Table');
  console.log(`Found ${tables.length} tables`);
  ```
</CodeGroup>

## Read cells into a grid

Each cell reports its `row`, its `col`, and its `text`. Because the grid dimensions are known from `num_rows` and `num_cols`, you can allocate a 2D array and place every cell at its coordinate. This gives you the table as nested lists, ready to write to a CSV, load into a dataframe, or compare against expected values.

<CodeGroup>
  ```python Python theme={null}
  def to_grid(table):
      grid = [["" for _ in range(table.num_cols)] for _ in range(table.num_rows)]
      for cell in table.cells:
          grid[cell.row][cell.col] = cell.text
      return grid

  for element in tables:
      grid = to_grid(element.table)
      for row in grid:
          print(row)
  ```

  ```typescript TypeScript theme={null}
  function toGrid(table) {
    const grid = Array.from({ length: table.numRows }, () =>
      Array.from({ length: table.numCols }, () => ''),
    );
    for (const cell of table.cells) {
      grid[cell.row][cell.col] = cell.text;
    }
    return grid;
  }

  for (const element of tables) {
    const grid = toGrid(element.table);
    grid.forEach((row) => console.log(row));
  }
  ```
</CodeGroup>

## Handle merged and spanning cells

Real tables sometimes merge cells, most often in headers. A cell that spans more than one column or row reports `col_span` or `row_span` greater than 1. The cell's `text` belongs at its starting `row` and `col`; the positions it covers hold no separate cell of their own. To keep the grid rectangular, write the text across every position the cell spans.

<CodeGroup>
  ```python Python theme={null}
  def to_grid(table):
      grid = [["" for _ in range(table.num_cols)] for _ in range(table.num_rows)]
      for cell in table.cells:
          for r in range(cell.row, cell.row + cell.row_span):
              for c in range(cell.col, cell.col + cell.col_span):
                  grid[r][c] = cell.text
      return grid
  ```

  ```typescript TypeScript theme={null}
  function toGrid(table) {
    const grid = Array.from({ length: table.numRows }, () =>
      Array.from({ length: table.numCols }, () => ''),
    );
    for (const cell of table.cells) {
      for (let r = cell.row; r < cell.row + cell.rowSpan; r++) {
        for (let c = cell.col; c < cell.col + cell.colSpan; c++) {
          grid[r][c] = cell.text;
        }
      }
    }
    return grid;
  }
  ```
</CodeGroup>

<Note>
  Each cell carries `is_header`, so you can separate header cells from data cells directly rather than assuming the first row. Group the header cells to build column names, and treat the rest as the body.
</Note>

## Check confidence before trusting a table

A table element carries a `confidence` score. Complex or scanned tables score lower than clean digital ones. When you extract tables at scale, gate on this score and route low-confidence tables to review rather than into a system of record.

```python Python theme={null}
LOW = 0.7

for page in result.pages:
    for element in page.elements:
        if element.label == ParseLayoutLabel.TABLE and element.confidence < LOW:
            print(f"Low-confidence table on page {page.page_number}, flag for review")
```

## Related

<CardGroup cols={2}>
  <Card title="Output schema" icon="code" href="/document-parsing/reference/output-schema">
    The full cell and table field definitions.
  </Card>

  <Card title="Confidence Scoring" icon="chart-bar" href="/concepts/confidence-scoring">
    How Meibel scores the quality of extracted content.
  </Card>
</CardGroup>
