All posts

The Same Number, Three Formats: Auditing Data Across Sources Without Losing Precision

Cross-checking a figure across an HTML press release, a CSV dataset, and a structured filing usually means writing a different scraper for each format — Gyrence's Fetch endpoint detects and parses all of them the same way, so the diff compares real values instead of scraper noise.

The Same Number, Three Formats: Auditing Data Across Sources Without Losing Precision

The Same Number, Three Formats: Auditing Data Across Sources Without Losing Precision

A distribution amount, a revenue figure, an inventory count — the kind of number that shows up in more than one place on the public web, published by more than one party, in more than one format. A company might post it in a press release (HTML). A regulator or data vendor might publish the same figure in a structured dataset (a CSV or Parquet file). The authoritative version might live inside a structured filing (XBRL). If you're building anything that reconciles those — an audit tool, a compliance check, an agent that flags when a company's own page disagrees with the regulatory record — you need all three numbers pulled out cleanly and compared as real values, not as three different flavors of scraped text.

The naive version of this is a scraper that treats every URL as a web page: fetch it, strip the tags, hope for markdown. That works fine on the HTML press release. It falls apart on the other two — a CSV file run through an HTML-to-text converter comes back as a wall of comma-separated characters with no row or column structure, and a Parquet file (a binary, columnar format) doesn't survive being decoded as text at all. You end up with three sources that are supposedly reporting the same number, and no reliable way to compare them because two of the three were never actually parsed — just mangled.

How Gyrence's Fetch endpoint handles this

Gyrence's /api/v1/fetch endpoint decides what a URL actually is before it decides how to read it. The detection is layered, and each layer only runs if the one before it doesn't produce an answer:

  1. Raw byte signatures first. The first bytes of the response are checked against known binary signatures — the same technique your operating system uses to recognize a file as a PDF or a ZIP archive regardless of its file name. This step is authoritative: if the bytes say Parquet, the response is parsed as Parquet even if the server's declared content type says something else entirely.
  2. The server's declared content type, if the byte check didn't match anything.
  3. The URL's file extension (.csv, .json, .xml, and so on), if the content type is missing or unhelpful.
  4. A light inspection of the decoded text — does it start with { or <? — as a last resort.

Whatever format is identified, every response from Fetch carries a kind field telling you exactly what was parsed (html, csv, json, xbrl, parquet, arrow, and eleven others), plus a via field showing whether the page was retrieved with a plain HTTP request or escalated to a full headless browser. Nothing gets silently treated as HTML by default.

Why that matters for a reconciliation pipeline specifically

For an auditing use case, the differences between formats aren't cosmetic — they're the difference between a comparison you can trust and one you can't:

  • Comma-separated and tab-separated files come back with the header row inferred and each row parsed as a typed record, not a string you have to split yourself.
  • JSON and newline-delimited JSON are returned as real parsed objects, never re-stringified — and for newline-delimited files, a malformed row is isolated to that line instead of failing the entire file.
  • Parquet and Arrow — binary, columnar formats commonly used for large structured datasets — are parsed into typed rows with their schema intact. This is the one that matters most for an auditor: when a column holds a decimal value too large to represent exactly in the numeric type JavaScript uses under the hood, Gyrence flags it with an explicit approximationRisk marker instead of silently rounding it. If the entire point of the exercise is confirming that two numbers match exactly, a silent rounding error is worse than no data at all.
  • XBRL and inline XBRL — the structured tagging format regulatory filings use to make individual figures machine-readable — are parsed with precision preserved as the exact string from the source document, again avoiding floating-point coercion of numbers that need to stay exact.

And when a source simply can't be read — a bot-protection wall, a file format nothing in the pipeline understands — Fetch returns a structured error rather than a response that looks successful but contains garbage. For a pipeline whose whole job is telling you when two numbers disagree, that distinction matters: "this source failed to load" and "these two sources genuinely disagree" need to be two different signals, not the same silent failure.

What this looks like in practice

An agent or script built for this doesn't need a bespoke parser per source. It calls the same Fetch endpoint against the HTML press release, the CSV or Parquet dataset, and the XBRL filing, reads the kind field to confirm what it got back, and compares the extracted values directly — with the precision guarantees intact on the numbers that actually mattered enough to check in the first place. The same endpoint is available over plain REST or as an MCP tool, so an agent framework can call it the same way it calls any other tool, without a separate integration per data format.

Full content-kind reference and response shapes are at gyrence.com/docs.