# We lead an external extraction benchmark (but run your own evals)

> A competitor commissioned a long-table extraction benchmark, and scored Datalab at 34% recall. We rebuilt our extraction and now have the top recall (99.1%) and lead on precision too.  Still, you should take all vendor benchmarks with a grain of salt and run your own evals.

- Canonical: https://www.datalab.to/blog/trusting-vendor-benchmarks
- Published: 2026-07-14
- Authors: Vik Paruchuri

Recently, a competitor of ours (Reducto) commissioned **LongExtractionBench**, a benchmark for pulling structured data out of long, dense tables. Reducto wrote the grading methodology, and when it launched they were the top performer. They chose recall as their headline metric, and scored Datalab at **33.8%**. Recall here is defined as the share of a document's rows a tool actually captures.

This benchmark tested an edge case that we weren't well set up to handle. Like most vendor benchmarks, it was specifically designed for marketing - Reducto was intended to win, and structurally it is very hard for anyone else to match their score. The task, extracting thousands of rows from dense tables, is a valid extraction task, but only matters for a small fraction of customers.

Nonetheless, we took this as a challenge. We rebuilt our long-table extraction from the ground up - and today, on the public benchmark set Reducto released (50 documents), **Datalab has the highest recall, ahead of Reducto itself.**

For several reasons, we wouldn't read too much into this benchmark, though (more below) - we'd encourage you not to trust vendor benchmarks (including ours!), and to run your own evals.

## We now lead on recall and precision

Here's where things stand today on the born-digital set:

<figure style="margin:32px 0">
  <svg viewBox="0 0 680 340" width="100%" style="max-width:680px;font-family:var(--font-mono)" role="img" aria-label="Recall, precision, and leaf accuracy: Datalab vs Reducto">
    <!-- gridlines (0-100 scale) -->
    <line x1="40" y1="70" x2="642" y2="70" style="stroke:var(--color-rule);stroke-width:1"/>
    <line x1="40" y1="175" x2="642" y2="175" style="stroke:var(--color-rule);stroke-width:1"/>
    <line x1="40" y1="280" x2="642" y2="280" style="stroke:var(--color-rule-strong);stroke-width:1"/>
    <text x="32" y="74" text-anchor="end" style="fill:var(--color-ink-4);font-size:10px">100</text>
    <text x="32" y="179" text-anchor="end" style="fill:var(--color-ink-4);font-size:10px">50</text>
    <text x="32" y="284" text-anchor="end" style="fill:var(--color-ink-4);font-size:10px">0</text>
    <!-- legend -->
    <rect x="238" y="26" width="13" height="13" style="fill:var(--color-accent)"/>
    <text x="258" y="37" style="fill:var(--color-ink-2);font-size:12px">Datalab</text>
    <rect x="345" y="26" width="13" height="13" style="fill:var(--color-ink)"/>
    <text x="365" y="37" style="fill:var(--color-ink-2);font-size:12px">Reducto</text>
    <!-- Recall -->
    <rect x="98" y="72" width="46" height="208" style="fill:var(--color-accent)"/>
    <rect x="156" y="74" width="46" height="206" style="fill:var(--color-ink)"/>
    <text x="121" y="64" text-anchor="middle" style="fill:var(--color-accent);font-size:15px;font-weight:600">99.1%</text>
    <text x="179" y="66" text-anchor="middle" style="fill:var(--color-ink-2);font-size:15px;font-weight:600">98.3%</text>
    <text x="150" y="301" text-anchor="middle" style="fill:var(--color-ink-2);font-size:14px;font-weight:600">Recall</text>
    <text x="150" y="320" text-anchor="middle" style="fill:var(--color-accent);font-size:12px">Datalab +0.8</text>
    <!-- Precision -->
    <rect x="288" y="70" width="46" height="210" style="fill:var(--color-accent)"/>
    <rect x="346" y="71" width="46" height="209" style="fill:var(--color-ink)"/>
    <text x="311" y="62" text-anchor="middle" style="fill:var(--color-accent);font-size:15px;font-weight:600">99.8%</text>
    <text x="369" y="63" text-anchor="middle" style="fill:var(--color-ink-2);font-size:15px;font-weight:600">99.7%</text>
    <text x="340" y="301" text-anchor="middle" style="fill:var(--color-ink-2);font-size:14px;font-weight:600">Precision</text>
    <text x="340" y="320" text-anchor="middle" style="fill:var(--color-accent);font-size:12px">Datalab +0.1</text>
    <!-- Leaf accuracy -->
    <rect x="478" y="72" width="46" height="208" style="fill:var(--color-accent)"/>
    <rect x="536" y="70" width="46" height="210" style="fill:var(--color-ink)"/>
    <text x="501" y="64" text-anchor="middle" style="fill:var(--color-accent);font-size:15px;font-weight:600">99.1</text>
    <text x="559" y="62" text-anchor="middle" style="fill:var(--color-ink-2);font-size:15px;font-weight:600">99.8</text>
    <text x="530" y="301" text-anchor="middle" style="fill:var(--color-ink-2);font-size:14px;font-weight:600">Leaf accuracy</text>
    <text x="530" y="320" text-anchor="middle" style="fill:var(--color-ink-3);font-size:12px">Reducto +0.7</text>
  </svg>
  <figcaption style="font-family:var(--font-mono);font-size:0.7rem;letter-spacing:0.08em;text-transform:uppercase;color:var(--color-ink-3);margin-top:12px">Datalab vs Reducto on 50 born-digital docs — our re-run, same corpus and grader (0–100 scale). Datalab leads recall and precision; Reducto edges leaf.</figcaption>
</figure>

A few notes:

- **Datalab and Reducto are a fresh, same-corpus re-run.** We ran both on the same 50 born-digital documents with the official grader, with cache-busting so neither system could serve a stored answer. All three metrics land within a point — these systems are very close.
- **Datalab doesn't win every metric.** We lead recall (99.1 vs 98.3) and precision (99.8 vs 99.7); Reducto edges cell-level (leaf) accuracy by ~0.7 (99.8 vs 99.1). We report all three rather than just the one we win.
- **On recall, Datalab also leads the rest of the field** — Extend 92.7%, LlamaParse 77.5%, Gemini 3.1 Pro 48.6% (Reducto's published figures on the full 225-document set, which includes documents we can't see; close to what we measure, but not the identical corpus).

## The task it measures

LongExtractionBench tests one specific thing: taking a document with a very long, very dense table — up to ~15,000 rows over hundreds of pages — and extracting **every single row** into a JSON schema.

Most of the failures in this mode are **recall** failures. It's relatively easy to map table rows to the schema, but doing it perfectly across thousands of rows is challenging. It's even harder if some rows should be included, but not others.

<figure style="margin:32px 0">
  <div style="display:flex;align-items:stretch;gap:14px;font-family:var(--font-mono)">
    <div style="flex:1;min-width:0;border:var(--hairline) solid var(--color-rule-strong)">
      <div style="padding:10px 14px;border-bottom:var(--hairline) solid var(--color-rule);background:var(--color-surface);font-size:0.66rem;letter-spacing:0.06em;text-transform:uppercase;color:var(--color-ink-3)">Source — one long, dense table</div>
      <table style="width:100%;border-collapse:collapse;font-size:0.72rem;color:var(--color-ink-2)">
        <thead>
          <tr style="text-align:left;color:var(--color-ink-3)">
            <th style="padding:7px 10px;border-bottom:var(--hairline) solid var(--color-rule-strong)">Timestamp</th>
            <th style="padding:7px 10px;border-bottom:var(--hairline) solid var(--color-rule-strong)">Channel</th>
            <th style="padding:7px 10px;border-bottom:var(--hairline) solid var(--color-rule-strong)">Reading</th>
            <th style="padding:7px 10px;border-bottom:var(--hairline) solid var(--color-rule-strong)">Unit</th>
            <th style="padding:7px 10px;border-bottom:var(--hairline) solid var(--color-rule-strong)">Flag</th>
          </tr>
        </thead>
        <tbody>
          <tr style="background:var(--color-accent-soft)">
            <td style="padding:6px 10px">09:14:00</td><td style="padding:6px 10px">CH-07</td><td style="padding:6px 10px">0.482</td><td style="padding:6px 10px">V</td><td style="padding:6px 10px">ok</td>
          </tr>
          <tr>
            <td style="padding:6px 10px">09:14:01</td><td style="padding:6px 10px">CH-07</td><td style="padding:6px 10px">0.483</td><td style="padding:6px 10px">V</td><td style="padding:6px 10px">ok</td>
          </tr>
          <tr>
            <td style="padding:6px 10px">09:14:02</td><td style="padding:6px 10px">CH-07</td><td style="padding:6px 10px">0.481</td><td style="padding:6px 10px">V</td><td style="padding:6px 10px">ok</td>
          </tr>
          <tr>
            <td colspan="5" style="padding:8px 10px;color:var(--color-ink-4)">⋮&nbsp;&nbsp;14,208 rows total</td>
          </tr>
        </tbody>
      </table>
    </div>
    <div style="display:flex;align-items:center;color:var(--color-ink-4);font-size:1.5rem;flex:0 0 auto">→</div>
    <div style="flex:1;min-width:0;border:var(--hairline) solid var(--color-rule-strong)">
      <div style="padding:10px 14px;border-bottom:var(--hairline) solid var(--color-rule);background:var(--color-surface);font-size:0.66rem;letter-spacing:0.06em;text-transform:uppercase;color:var(--color-ink-3)">Extracted — one object per row</div>
      <pre style="margin:0;padding:12px 14px;font-size:0.72rem;line-height:1.55;color:var(--color-ink-2);overflow-x:auto"><span style="color:var(--color-ink-4)">[</span>
  &#123;
    <span style="color:var(--color-accent)">"timestamp"</span>: <span style="color:var(--color-ink)">"09:14:00"</span>,
    <span style="color:var(--color-accent)">"channel"</span>: <span style="color:var(--color-ink)">"CH-07"</span>,
    <span style="color:var(--color-accent)">"reading"</span>: <span style="color:var(--color-ok)">0.482</span>,
    <span style="color:var(--color-accent)">"unit"</span>: <span style="color:var(--color-ink)">"V"</span>,
    <span style="color:var(--color-accent)">"flag"</span>: <span style="color:var(--color-ink)">"ok"</span>
  &#125;,
  <span style="color:var(--color-ink-4)">… 14,207 more objects</span>
<span style="color:var(--color-ink-4)">]</span></pre>
    </div>
  </div>
  <figcaption style="font-family:var(--font-mono);font-size:0.7rem;letter-spacing:0.08em;text-transform:uppercase;color:var(--color-ink-3);margin-top:12px">Every row of the table becomes one object in the schema. Recall is the share of all 14,208 rows you actually get back.</figcaption>
</figure>

## A note on vendor benchmarks

LongExtractionBench is **not a general test of document extraction.** It's built around a single edge case — thousands of near-identical rows in dense tables — that most workloads never hit, and that happens to be a Reducto strength. Most documents aren't 15,000-row tables; most extraction jobs are a few dozen fields off an invoice, a form, a contract, or a report, where reading order, layout, handwriting, and language matter far more than raw row throughput.

The leader also scores 99+%, which is a red flag that either the benchmark has been fit to the specific vendor (tailored to their strengths), or is relatively easy.  Scores on more realistic benchmarks tend to max out at lower scores, with headroom for improvement.

Here are some factors to pay attention to in vendor benchmarks:

<figure style="margin:32px 0">
  <div style="display:flex;flex-direction:column;gap:10px;font-family:var(--font-mono);font-size:0.82rem">
    <div style="display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border:var(--hairline) solid var(--color-rule);border-left:3px solid var(--color-accent)">
      <span style="color:var(--color-ink)">The corpus</span>
      <span style="color:var(--color-ink-3)">who chose the documents — and do they play to your strength?</span>
    </div>
    <div style="display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border:var(--hairline) solid var(--color-rule);border-left:3px solid var(--color-accent)">
      <span style="color:var(--color-ink)">The metric</span>
      <span style="color:var(--color-ink-3)">which number gets to be "the one that matters"?</span>
    </div>
    <div style="display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border:var(--hairline) solid var(--color-rule);border-left:3px solid var(--color-accent)">
      <span style="color:var(--color-ink)">The field</span>
      <span style="color:var(--color-ink-3)">competitors are run once, by the publisher, at settings you can't inspect</span>
    </div>
    <div style="display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border:var(--hairline) solid var(--color-rule);border-left:3px solid var(--color-accent)">
      <span style="color:var(--color-ink)">Home field</span>
      <span style="color:var(--color-ink-3)">the publisher can tune their own system against the eval; nobody else can</span>
    </div>
  </div>
  <figcaption style="font-family:var(--font-mono);font-size:0.7rem;letter-spacing:0.08em;text-transform:uppercase;color:var(--color-ink-3);margin-top:12px">Four ways any self-published benchmark tilts toward whoever built it.</figcaption>
</figure>

Right in the benchmark's limitations section: _"Reducto commissioned this benchmark. Reducto is also one of the systems under test and the top performer on these documents."_ The same party that ranks first also chose the task, commissioned the set, and wrote the grading methodology. This is true for any vendor benchmark, including our own.

So take our recall win for exactly what it is: strong evidence on one hard, narrow task, on a benchmark we didn't design. Here's how we recommend you benchmark document extraction systems:

1. **Collect 20–50 documents that look like your real workload**
2. **Define what "correct" means for you.** Every row of a table? A handful of header fields? Reading order? Non-English text? The answer is workload-specific, and it's rarely a single composite score.
3. **Run every candidate the same way**, on the same inputs, and read the actual failures — not just the aggregate.

We try to be as objective as possible with our own benchmarking - use external benchmarks where possible, if we have internal benchmarks make them fair and representative of difficult real-world tasks. But you should still run evals on your own documents. We built [Forge Evals](/blog/introducing-forge-evals) for exactly this, and our [playground](/playground) is easy to do some quick testing with. Also, feel free to reach out to us if you have custom eval needs. Use what we offer, use a competitor's tools too, and trust what you measure over what any of us publish.
