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

# Parse your first document

> Submit a PDF, wait for the job to finish, and read the structured result, all through the Meibel API

A PDF carries its content in a visual layout: headings, tables, and a reading order a person takes in at a glance but that code cannot act on directly. Parsing turns that document into structured content you can search, index, or feed to an agent.

By the end of this tutorial you will have taken a PDF from your disk and read it back two ways: clean Markdown for a person or a model to read, and the strongly-typed structured result for your code to work with. The core flow is three calls: submit the file, poll until it finishes, then fetch the result. You fetch twice here, once as Markdown and once as structured data, to see both renderings. You will work on one document throughout, a public jobs report from the U.S. Bureau of Labor Statistics (BLS), so each step builds on the last.

## Prerequisites

* A Meibel API key. Set it as an environment variable so the examples can read it.
* One of the Meibel SDKs installed, or `curl` for the raw HTTP examples.
* A PDF to parse. The first step downloads a sample; any PDF of your own works too, and a report or an invoice with a table in it shows off the structure best.

<CodeGroup>
  ```bash Environment theme={null}
  export MEIBEL_API_KEY="your-api-key"
  ```

  ```bash Install (Python) theme={null}
  pip install meibel
  ```

  ```bash Install (TypeScript) theme={null}
  npm install meibel
  ```
</CodeGroup>

## 1. Get the sample document

This tutorial works on a public jobs report from the U.S. Bureau of Labor Statistics. It is a good document to parse because it mixes the structure parsing recovers: a title and section headings, a summary table, and a couple of charts. Download the copy Meibel hosts for this tutorial.

```bash theme={null}
curl -fsSL https://storage.googleapis.com/meibel-examples/tutorials/employment-situation.pdf \
  -o employment-situation.pdf
```

## 2. Submit the document

Parsing runs as a job so your program stays responsive while a large file is processed. Submitting a file returns a job ID right away, before the work finishes. You hold onto that ID to check progress and collect the result.

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

  client = MeibelClient(api_key=os.environ["MEIBEL_API_KEY"])

  with open("employment-situation.pdf", "rb") as f:
      job = client.documents.parse(file=f, file_name="employment-situation.pdf")

  print(f"Submitted. Job ID: {job.job_id}")
  ```

  ```typescript TypeScript theme={null}
  import { MeibelClient } from 'meibel';
  import { readFile } from 'node:fs/promises';

  const client = new MeibelClient({ apiKey: process.env.MEIBEL_API_KEY });

  const file = new Blob([await readFile('employment-situation.pdf')]);
  const job = await client.documents.parse(file, 'employment-situation.pdf');

  console.log(`Submitted. Job ID: ${job.jobId}`);
  ```

  ```bash curl theme={null}
  curl -X POST https://api.meibel.ai/v2/documents \
    -H "Meibel-API-Key: $MEIBEL_API_KEY" \
    -F "file=@employment-situation.pdf"
  ```
</CodeGroup>

The response carries the `job_id` and an initial `status` of `queued`. The job ID is the handle for everything that follows, so store it.

## 3. Wait for it to finish

A successful job moves through three statuses: `queued`, then `processing`, then `completed`. Polling the status endpoint tells you where it is, and once it reaches `completed` the status also reports what parsing found: the page count, how many elements and tables were extracted, and an overall confidence score. Those numbers are a quick sanity check before you read the full result.

<CodeGroup>
  ```python Python theme={null}
  import time

  while True:
      status = client.documents.get_status(job_id=job.job_id)
      print(f"Status: {status.status}")

      if status.status == "completed":
          print(f"{status.pages} pages, {status.elements} elements, {status.tables} tables")
          print(f"Confidence: {status.confidence}")
          break
      if status.status == "failed":
          raise RuntimeError("Parsing failed")

      time.sleep(2)
  ```

  ```typescript TypeScript theme={null}
  let status;
  do {
    status = await client.documents.getStatus(job.jobId);
    console.log(`Status: ${status.status}`);

    if (status.status === 'failed') {
      throw new Error('Parsing failed');
    }
    if (status.status !== 'completed') {
      await new Promise((r) => setTimeout(r, 2000));
    }
  } while (status.status !== 'completed');

  console.log(`${status.pages} pages, ${status.elements} elements, ${status.tables} tables`);
  ```

  ```bash curl theme={null}
  curl https://api.meibel.ai/v2/documents/$JOB_ID \
    -H "Meibel-API-Key: $MEIBEL_API_KEY"
  ```
</CodeGroup>

<Note>
  A 2-second polling interval works well for most documents. Larger files take longer, so the loop simply runs a few more times.
</Note>

## 4. Read the result as Markdown

With the job complete, you can fetch the result. Markdown is the format to start with: it is the document as readable text, with headings kept as headings, lists as lists, and tables rendered as Markdown tables. This is what you would hand to a language model or drop into a page for a person to read.

<CodeGroup>
  ```python Python theme={null}
  markdown = client.documents.get_result(job_id=job.job_id, format="markdown")
  print(markdown)
  ```

  ```typescript TypeScript theme={null}
  const markdown = await client.documents.getResult(job.jobId, {
    format: 'markdown',
  });
  console.log(markdown);
  ```

  ```bash curl theme={null}
  curl "https://api.meibel.ai/v2/documents/$JOB_ID/result?format=markdown" \
    -H "Meibel-API-Key: $MEIBEL_API_KEY"
  ```
</CodeGroup>

Read through the output. The section headings from your PDF appear as Markdown headings, and any table has become a grid of pipes and dashes. The reading order matches how you would read the page, even if the source had columns.

## 5. Read the same result as structured data

Markdown is for reading. When your code needs to act on the content, fetch the strongly-typed structured result instead. It comes back organized by page, each holding its elements in reading order, and every element is a typed object with a `label`, its `text`, a `bbox` giving its position, a `reading_order`, and a `confidence` score. A `Title` or `SectionHeader` carries a `heading_level`, and a `Table` carries a grid of cells you can address by row and column.

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

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

  for page in result.pages:
      for element in page.elements:
          if element.label in (ParseLayoutLabel.TITLE, ParseLayoutLabel.SECTIONHEADER):
              print("#" * (element.heading_level or 1), element.text)
          elif element.label == ParseLayoutLabel.TABLE and element.table:
              t = element.table
              print(f"[table: {t.num_rows}x{t.num_cols} on page {page.page_number}]")
  ```

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

  for (const page of result.pages) {
    for (const element of page.elements) {
      if (element.label === 'Title' || element.label === 'SectionHeader') {
        console.log('#'.repeat(element.headingLevel ?? 1), element.text);
      } else if (element.label === 'Table' && element.table) {
        const t = element.table;
        console.log(`[table: ${t.numRows}x${t.numCols} on page ${page.pageNumber}]`);
      }
    }
  }
  ```

  ```bash curl theme={null}
  curl "https://api.meibel.ai/v2/documents/$JOB_ID/structured" \
    -H "Meibel-API-Key: $MEIBEL_API_KEY"
  ```
</CodeGroup>

The same document you submitted is now a set of pages, each a list of typed, positioned elements. You have the readable Markdown for people and models, and the structured result for your program.

## What you learned

You submitted a document, waited for the job, and read a single parse back as both Markdown and structured data. That same flow, submit then poll then fetch, handles any supported input: a digital PDF, a scan, or an office document all return the same structured content, whether you parse one file or run many through these steps. From here:

<CardGroup cols={2}>
  <Card title="Choosing an output format" icon="list-check" href="/document-parsing/guides/choosing-an-output-format">
    When Markdown, the structured result, or annotated output fits your task.
  </Card>

  <Card title="Extracting tables" icon="table-cells" href="/document-parsing/guides/extracting-tables">
    Turn the table cells you saw here into rows your code can use.
  </Card>

  <Card title="How parsing works" icon="gears" href="/document-parsing/concepts/how-parsing-works">
    Understand the stages behind the result you just read.
  </Card>

  <Card title="Output schema" icon="code" href="/document-parsing/reference/output-schema">
    The full field list for the structured result you just printed.
  </Card>
</CardGroup>
