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

# Core Concepts

## Model Routing

Retab provides intelligent model routing through three special model identifiers: `retab-large`, `retab-small` and `retab-micro`.

These models automatically route your requests to the current best-performing model based on availability, performance, and speed metrics. This means you don't need to manually update your model selection when new, better-performing models become available—Retab handles the routing for you, ensuring your applications always use the optimal model for your use case.

## Sync & Async Client

Retab offers both **synchronous** and **asynchronous** client interfaces, making it versatile for different application needs. The asynchronous client (`AsyncRetab`) is ideal for high-performance, non-blocking applications where multiple tasks run concurrently. For simpler or blocking operations, the synchronous client (`Retab`) provides a straightforward approach.

Here's how you can use both:

<CodeGroup>
  ```python Python theme={null}
  # Async client.
  from retab import AsyncRetab


  async def parse_document(document):
      client = AsyncRetab()
      result = await client.parses.create(document=document, model="retab-small")
      print(result.output.text)


  # Sync client.
  from retab import Retab

  client = Retab()

  result = client.parses.create(document="invoice.pdf", model="retab-small")
  print(result.output.text)

  ```

  ```typescript TypeScript theme={null}
  // Async client (default)
  import { Retab } from '@retab/node';

  async function parseDocument(document) {
      const client = new Retab({ apiKey: process.env.RETAB_API_KEY });
      const result = await client.parses.create(undefined, "retab-small");
      console.log(result.output.text);
  }

  // Using promises
  const client = new Retab({ apiKey: process.env.RETAB_API_KEY });

  client.parses.create("invoice.pdf", "retab-small")
      .then(result => console.log(result.output.text));
  ```

  ```go Go theme={null}
  package main

  import (
  	"context"
  	"fmt"
  	"log"

  	retab "github.com/retab-dev/retab/clients/go"
  )

  func main() {
  	ctx := context.Background()

  	client, err := retab.NewClient("")
  	if err != nil {
  		log.Fatal(err)
  	}

  	model := "retab-small"
  	result, err := client.Parses.Create(ctx, &retab.ParsesCreateParams{
  		Document: "invoice.pdf",
  		Model:    &model,
  	})
  	if err != nil {
  		log.Fatal(err)
  	}
  	fmt.Println(result.Output.Text)
  }
  ```

  ```ruby Ruby theme={null}
  require 'retab'

  # Ruby SDK is synchronous; for concurrent requests use Thread or a concurrency gem
  client = Retab::Client.new

  result = client.parses.create(document: 'invoice.pdf')
  puts result.output.text
  ```

  ```php PHP theme={null}
  <?php
  require 'vendor/autoload.php';

  use Retab\Client;

  // PHP SDK is synchronous; for concurrent requests use Guzzle's promise/pool APIs
  $client = new Client();

  $result = $client->parses()->create(document: 'invoice.pdf');
  echo $result->output->text . PHP_EOL;
  ```

  ```rust Rust theme={null}
  use retab::resources::parses::CreateParams;
  use retab::Retab;
  use std::path::PathBuf;

  // The Rust SDK is async; for concurrent requests spawn multiple tasks with tokio.
  #[tokio::main]
  async fn main() -> Result<(), Box<dyn std::error::Error>> {
      let client = Retab::new(std::env::var("RETAB_API_KEY")?);

      let params = CreateParams::new(PathBuf::from("invoice.pdf"));
      let result = client.parses().create(params).await?;
      println!("{}", result.output.text);
      Ok(())
  }
  ```

  ```csharp C# theme={null}
  using System;
  using System.IO;
  using System.Threading.Tasks;
  using Retab;
  using RetabClient = Retab.Retab;

  // The .NET SDK is async by design; every method returns Task<T>.
  var client = new RetabClient("YOUR_API_KEY");

  var result = await client.Parses.CreateAsync(
      new ParsesCreateOptions
      {
          Document = new FileInfo("invoice.pdf"),
          Model = "retab-small",
      }
  );

  Console.WriteLine(result.Output.Text);
  ```

  ```java Java theme={null}
  import com.retab.RetabClient;

  public final class Example {
    public static void main(String[] args) throws Exception {
      RetabClient client = new RetabClient(System.getenv("RETAB_API_KEY"));

      var result = client.parses().create(null, "retab-1.5", null, "Extract the invoice fields", null, null);
      System.out.println(result);
    }
  }
  ```
</CodeGroup>

Both clients provide the same core functionality for processing documents, running workflows, and managing resources, with the flexibility to match your application's concurrency model.

## Pagination

Many top-level resources have support for bulk fetches via list API methods. For instance, you can list extraction links, list email addresses, and list logs. These list API methods share a common structure, taking at least these four parameters: limit, order, after, and before.

Retab utilizes pagination via the after and before parameters. Both parameters take an existing object ID value and return objects in either descending or ascending order by creation time.

## Rate Limits

Retab implements rate limiting to ensure stable service for all users. The API uses a rolling window rate limit with the following configuration:

* **300 requests** per **60-second** window
* Applies across the following API endpoints:
  * `POST /v1/extractions`
  * `POST /v1/parses`

When you exceed the rate limit, the API will return a `429 Too Many Requests` response. The response headers will include:

```bash theme={null}
Status 429 - {'detail': 'Rate limit exceeded. Please try again later.'}
```

For high-volume applications, we can provide a dedicated plan. [Contact us](mailto:contact@retab.com) for more information.

## Consensus

You can leverage the consensus feature to improve the accuracy of the extraction. The consensus feature is a way to aggregate the results of multiple LLMs to improve the accuracy of the extraction.

The consensus principle is simple: Multiple runs should give the same result, if the result is not the same, the LLM is not confident about the result so neither should you.
We compute a consensus score for each field.

Some additional `_consensus_score` fields are added to the likelihoods object, they are computed as the average of the consensus scores within some context.

<CodeGroup>
  ```python Python theme={null}
  import json
  from retab import Retab
  from retab.types.extractions import ExtractionRequest

  with open("booking_confirmation_json_schema.json", "r") as f:
      json_schema = json.load(f)

  client = Retab()

  response = client.extractions.create(
      document="booking_confirmation.jpg",
      model="retab-micro",
      json_schema=json_schema,
      n_consensus=10,  # >1 combines results from multiple LLM calls
  )

  ```

  ```typescript TypeScript theme={null}
  import { Retab } from '@retab/node';
  import { readFileSync } from 'fs';

  const jsonSchema = JSON.parse(
      readFileSync('booking_confirmation_json_schema.json', 'utf-8')
  );

  const client = new Retab({ apiKey: process.env.RETAB_API_KEY });

  const response = await client.extractions.create(
      'booking_confirmation.jpg',
      jsonSchema,
      'retab-micro',
      undefined,
      10  // This will run and combine the results of 10 calls to the same LLM
  );
  ```

  ```go Go theme={null}
  package main

  import (
  	"context"
  	"encoding/json"
  	"fmt"
  	"log"
  	"os"

  	retab "github.com/retab-dev/retab/clients/go"
  )

  func main() {
  	ctx := context.Background()

  	client, err := retab.NewClient("")
  	if err != nil {
  		log.Fatal(err)
  	}

  	schemaBytes, err := os.ReadFile("booking_confirmation_json_schema.json")
  	if err != nil {
  		log.Fatal(err)
  	}
  	var jsonSchema map[string]any
  	if err := json.Unmarshal(schemaBytes, &jsonSchema); err != nil {
  		log.Fatal(err)
  	}

  	model := "retab-micro"
  	nConsensus := 10
  	response, err := client.Extractions.Create(ctx, &retab.ExtractionsCreateParams{
  		Document:   "booking_confirmation.jpg",
  		Model:      &model,
  		JSONSchema: jsonSchema,
  		NConsensus: &nConsensus, // This will run and combine the results of 10 calls to the same LLM
  	})
  	if err != nil {
  		log.Fatal(err)
  	}
  	fmt.Println(response)
  }
  ```

  ```ruby Ruby theme={null}
  require 'retab'
  require 'json'

  json_schema = JSON.parse(File.read('booking_confirmation_json_schema.json'))

  client = Retab::Client.new

  response = client.extractions.create(
    document: 'booking_confirmation.jpg',
    model: 'retab-micro',
    json_schema: json_schema,
    n_consensus: 10, # >1 combines results from multiple LLM calls
  )
  ```

  ```php PHP theme={null}
  <?php
  require 'vendor/autoload.php';

  use Retab\Client;

  $jsonSchema = json_decode(file_get_contents('booking_confirmation_json_schema.json'), true);

  $client = new Client();

  $response = $client->extractions()->create(
      document: 'booking_confirmation.jpg',
      jsonSchema: $jsonSchema,
      model: 'retab-micro',
      nConsensus: 10, // >1 combines results from multiple LLM calls
  );
  ```

  ```rust Rust theme={null}
  use retab::resources::extractions::CreateParams;
  use retab::Retab;
  use std::collections::HashMap;
  use std::fs;

  #[tokio::main]
  async fn main() -> Result<(), Box<dyn std::error::Error>> {
      let client = Retab::new(std::env::var("RETAB_API_KEY")?);

      let schema_bytes = fs::read("booking_confirmation_json_schema.json")?;
      let json_schema: HashMap<String, serde_json::Value> = serde_json::from_slice(&schema_bytes)?;

      let mut params = CreateParams::new("booking_confirmation.jpg", json_schema);
      params.body.model = Some("retab-micro".into());
      params.body.n_consensus = Some(10); // >1 combines results from multiple LLM calls

      let response = client.extractions().create(params).await?;
      println!("{:?}", response);
      Ok(())
  }
  ```

  ```csharp C# theme={null}
  using System;
  using System.Collections.Generic;
  using System.IO;
  using System.Threading.Tasks;
  using Retab;
  using RetabClient = Retab.Retab;
  using Newtonsoft.Json;

  var jsonSchema = JsonConvert.DeserializeObject<Dictionary<string, object>>(
      await System.IO.File.ReadAllTextAsync("booking_confirmation_json_schema.json")
  )!;

  var client = new RetabClient("YOUR_API_KEY");

  var response = await client.Extractions.CreateAsync(
      new ExtractionsCreateOptions
      {
          Document = new FileInfo("booking_confirmation.jpg"),
          Model = "retab-micro",
          JsonSchema = jsonSchema,
          NConsensus = 10, // >1 combines results from multiple LLM calls
      }
  );

  Console.WriteLine(response.Output);
  ```

  ```JSON Extracted JSON theme={null}
  {
    "booking_id": null,
    "payment": {
      "total_price": 1500,
      "currency": "EUR"
    },
    "client": {
      "company_name": "Acme Corporation",
      "VAT_number": "GB123456789",
      "city": "Manchester",
      "postal_code": "M1 4WP",
      "country": "GB",
      "code": null,
      "email": "client@acme.com"
    },
    "shipments": [
      {
        "shipment_id": "BC-67890",
        "sender": {
          "company_name": "Acme Corporation",
          "address": {
            "city": "Manchester",
            "postal_code": "M1 4WP",
            "country": "GB",
            "line1": "456 Oak Avenue",
            "line2": "Floor 3"
          },
          "phone_number": "+44 20 7946 0958",
          "email_address": "client@acme.com",
          "pickup_datetime": {
            "date": "2023-05-02",
            "start_time": "08:00:00",
            "end_time": "12:00:00"
          },
          "observations": "The transport involves safety protocols, possibly for hazardous goods."
        },
        "recipient": {
          "company_name": "Beta Industries",
          "address": {
            "city": "Munich",
            "postal_code": "80331",
            "country": "DE",
            "line1": "Uncertain",
            "line2": "Suite 500"
          },
          "phone_number": "+49 89 12345",
          "email_address": "contact@beta-ind.com",
          "delivery_datetime": {
            "date": "2023-05-03",
            "start_time": "10:00:00",
            "end_time": "16:00:00"
          },
          "observations": "Uncertain"
        },
        "goods": {
          "packing": {
            "units": 10,
            "packing_type": "pallet",
            "supplementary_parcels": null,
            "pallets_on_ground": null,
            "number_eur_pallet": null,
            "observation": "Uncertain"
          },
          "dimensions": {
            "loading_meters": null,
            "volume": null
          },
          "weight": null,
          "temperature_infos": {
            "min_temperature": null,
            "max_temperature": null,
            "category": "Frozen"
          },
          "dangerous_goods_infos": [
            {
              "weight": 2000,
              "UN_code": null,
              "UN_label": "Uncertain",
              "UN_class": "3",
              "UN_packing_group": "II",
              "ADR_tunnel_code": "B/E"
            }
          ]
        },
        "transport_constraints": {
          "vehicle_type": "Uncertain",
          "body_type": "Refrigerated",
          "tail_lift": false,
          "crane": null
        }
      }
    ]
  }
  ```

  ```JSON Consensus scores theme={null}
  {
    "booking_id": 1.0,
    "payment": {
      "total_price": 0.88889,
      "currency": 0.8,
      "_consensus_score": 0.84445
    },
    "client": {
      "company_name": 0.9,
      "VAT_number": 0.8579535180399872,
      "city": 0.3,
      "postal_code": 0.3,
      "country": 0.3,
      "code": 1.0,
      "email": 0.1,
      "_consensus_score": 0.53685
    },
    "_consensus_score": 0.72476,
    "shipments": [
      {
        "_consensus_score": 0.51772,
        "shipment_id": 0.5,
        "sender": {
          "company_name": 0.7,
          "address": {
            "city": 0.5,
            "postal_code": 0.7360869868340911,
            "country": 0.5,
            "line1": 0.7935465965677979,
            "line2": 0.6270515867719917,
            "_consensus_score": 0.63134
          },
          "phone_number": 0.9208203821621724,
          "email_address": 0.4,
          "pickup_datetime": {
            "date": 0.2,
            "start_time": 0.2,
            "end_time": 0.2,
            "_consensus_score": 0.2
          },
          "observations": 0.5278386154074294,
          "_consensus_score": 0.56333
        },
        "recipient": {
          "company_name": 0.6,
          "address": {
            "city": 0.7,
            "postal_code": 0.8527968987667864,
            "country": 0.7,
            "line1": 0.15743862396133873,
            "line2": 0.5952886900789938,
            "_consensus_score": 0.6011
          },
          "_consensus_score": 0.36685,
          "phone_number": 0.1,
          "email_address": 0.1,
          "delivery_datetime": {
            "date": 0.6,
            "start_time": 0.7,
            "end_time": 0.7,
            "_consensus_score": 0.66667
          },
          "observations": 0.13333486600852992
        },
        "goods": {
          "packing": {
            "units": 0.33333,
            "_consensus_score": 0.47583,
            "packing_type": 0.3,
            "supplementary_parcels": 0.77778,
            "pallets_on_ground": 0.77778,
            "number_eur_pallet": 0.55556,
            "observation": 0.11051665874425773
          },
          "_consensus_score": 0.58476,
          "dimensions": {
            "loading_meters": 1.0,
            "_consensus_score": 0.88889,
            "volume": 0.77778
          },
          "weight": 0.77778,
          "temperature_infos": {
            "min_temperature": 0.66667,
            "_consensus_score": 0.51111,
            "max_temperature": 0.66667,
            "category": 0.2
          },
          "dangerous_goods_infos": [
            {
              "_consensus_score": 0.27017,
              "weight": 0.1,
              "UN_code": 0.33333,
              "UN_label": 0.1876726334617529,
              "UN_class": 0.5,
              "UN_packing_group": 0.4,
              "ADR_tunnel_code": 0.1
            }
          ]
        },
        "transport_constraints": {
          "vehicle_type": 0.1945964952773603,
          "_consensus_score": 0.57365,
          "body_type": 0.2,
          "tail_lift": 0.9,
          "crane": 1.0
        }
      }
    ]
  }
  ```

  ```java Java theme={null}
  import com.retab.RetabClient;

  public final class Example {
    public static void main(String[] args) throws Exception {
      RetabClient client = new RetabClient(System.getenv("RETAB_API_KEY"));

      var result = client.extractions().create(null, null, "retab-1.5", "Extract the invoice fields", 10L, null, null, null, null, null, null);
      System.out.println(result);
    }
  }
  ```
</CodeGroup>
