Documentation

Methodology

How this dataset is sourced, processed, and made safe to query — and where its limits are.

What this is

This is an independent, unofficial aggregation of public transparency-reporting data, not affiliated with any platform, company, or government. It brings two bodies of reporting into one queryable schema:

  • EU DSA transparency reports
    Periodic content-moderation statistics that providers publish under the EU Digital Services Act (DSA) — the recurring transparency reports required by DSA Articles 15, 24 and 42 (the reporting duties for intermediary services, online platforms, and very large platforms respectively), not the separate real-time DSA Transparency Database of individual Statements of Reasons. Filers use a shared, tiered EU template: each provider completes only the reporting sections its tier requires (this dataset indexes those sections internally as t3–t11). It covers the Very Large Online Platforms & Search Engines (VLOPs / VLOSEs) — those the European Commission has formally designated for their EU reach — which report more often and in more detail, alongside dozens of smaller platforms. See the DSA Reports dashboard for live counts and the covered period.
  • Google government removal requests
    Google's reported counts of government requests to remove content, by country, product, reason, and reporting period — Google's own long-running voluntary transparency report, not a DSA filing. Explore it on the Google Removals dashboard.

Where each platform publishes its underlying report is catalogued on the Report locations page, with links to archived copies of the source files.

Where the data comes from

Every figure originates with the platform or company that published it — these are their reported numbers, reproduced without altering the reported values, not estimates produced by this project. The contribution here is aggregation and structuring: collecting the individually-published reports, extracting their tables, and normalising them into one schema so they can be compared and queried. The reported values themselves are never edited; the transformations are structural — extraction, flagging the aggregate total rows, normalising labels across languages, and dropping rows that cannot be parsed — and are described below.

How a report becomes queryable

Each source report's tables are extracted and loaded into a database that the API opens read-only (defence in depth) and reaches only through an interface that can read but never write — arranged as a star schema: one table of figures per DSA report table (indexed internally as t3–t11), surrounded by shared lookup tables (services, categories, sections, indicators, scopes, surfaces, reporting periods). Repeated labels are stored once and referenced by number, so the same category or platform name lives in a single place. Browse the resulting tables and every queryable field on the Schema page.

The nine report tables decode as: t3 Member-State orders · t4 notices (incl. trusted flaggers) · t5 / t6 own-initiative actions (illegal content / terms-of-service) · t7 appeals & recidivism · t8 use of automated means · t9 human resources · t10 average monthly active recipients · t11 qualitative descriptions.

When a report doesn't fit the template

Most platforms ship the fixed 11-section workbook, and the loader places each sheet by the section number in its name. A few file a format variant — the same underlying template content, but with the sheets renamed or renumbered. These are mapped through an explicit per-platform table from each sheet to its canonical section, so the figures still land in the right report table: LINE condenses the template into five unnumbered sheets (mapped to sections 1, 3, 4, 5 and 11), while Discord omits the own-initiative-on-illegal-content, human-resources and active-recipients sections and renumbers the rest — so its sheets are matched by name (its “Own Initiative TC”, for example, is the terms-of-service own-initiative table, section 6, not section 5). A document that is not the harmonised template at all is not forced into it, because mapping it would invent structure the source does not have: heise's forum report is a short free-form Article 15 summary with German free-text categories, and WordPress.com publishes a different report (DMCA, government and IRU requests). Those are kept as archived source files rather than loaded into t3–t11.

Avoiding double-counting

The DSA template places an aggregate total row right next to the breakdown rows it sums (for example a grand-total “All the entries” category beside the per-category counts, a “Total number” scope beside the upheld/reversed outcomes, or an “All” surface that sums the per-surface rows such as Core and Ads). A naïve SUM over such a column counts the parts and their own total, inflating the figure. The loader flags every aggregate row with an is_total marker and exposes it as a filterable dimension, so a query can deliberately pick a single grain — totals only, or breakdown only — and never sum a total together with its components. The dashboard's headline numbers use the reported totals; its breakdown charts use the component rows. Where a figure still carries a caveat, the API returns a warnings note that the dashboard surfaces alongside the result.

Reading across languages

Non-VLOP platforms may file the harmonised template in any official EU language, so the same template row can arrive as different text (Decisions upheld / Décisions confirmées / Bestätigte Entscheidungen). Each row keeps its original-language label for display, but is also stamped with a language-neutral standard label — a canonical key — matched through a translation map (a “crosswalk”) that aligns same-structure reports to an English reference and drops anything ambiguous. Queries can group or filter on the canonical key to compare like-for-like across languages, while the original text is still shown — though coverage is not yet uniform across every language (see Known limitations).

How you query it

There is no SQL interface. A query is a structured request — it names a table, a boolean filter of field/operation/value conditions, and optional grouping, aggregates, sorting, and limits. The server validates every field and operation against that table's fixed registry and compiles it into a single pre-checked database lookup (a parameterised SELECT), where your input is always treated as data, never run as a command. Queries are also bounded — a capped result size, request-size and complexity limits, and a per-key rate limit — so one request can't exhaust the service. Cross-table “composite” queries join two or more tables on shared dimensions through the same validated boundary. The natural-language “Ask” box, where enabled, sends your typed question (with the dataset's schema) to a third-party LLM (Anthropic's Claude) to draft a structured query, which is then validated like any other before it runs — that LLM call is the only time the service sends your input to an outside party on its own. The one other outbound path is opt-in and yours to point: an optional callback_url you attach to a query, which delivers your own result to a URL you choose. Try it on the DSA Reports dashboard, or read the API reference and our Privacy note.

Reproducibility & citation

The database is a static, read-only snapshot. Each snapshot carries an immutable dataset version — a fingerprint of its contents, so it changes whenever the data does — sent as an ETag and an X-Dataset-Version header, and every fact row exposes the report_id of its source filing. A figure is pinned by three things together — the dataset version, the exact query, and the report_id(s) it draws from — so cite all three and the rows can be re-fetched (the version and report_id alone identify a source filing, not a particular number). Results come back in a fully deterministic order whenever you sort or paginate, so a saved pull is byte-reproducible. The snapshot's build date and covered period appear in the “Cite as” line on each dashboard and in the headers of every export, and a row's report_id identifies its source filing (platform, period and tier); for non-VLOP platforms, where that filing is published is listed by platform in the Report locations catalogue.

Attribution: attribute the figure to the platform that reported it; cite this site only as the access tool.

Example: YouTube, EU DSA transparency report (H2 2025), accessed via the Transparency Report API — dataset version 7f3c1a2b9d04, report_id 42, retrieved 2026-06-26. The exact dataset version and covered period for the live snapshot appear in the “Cite as” line on the DSA Reports and Google Removals dashboards.

Known limitations

  • Snapshot, not live
    Figures reflect the reports available when the snapshot was built; later corrections or newly-filed reports appear only in a subsequent snapshot (with a new dataset version).
  • Source quality varies
    Platforms file with differing rigour and formatting. Mis-parsed junk rows (header cells, blanks, stray values leaked by some extracts) are dropped during loading rather than guessed at — as are template rows the cross-language crosswalk can't unambiguously map. The exact counts of dropped rows and crosswalk coverage are not yet published, so overall completeness is not quantified.
  • Some labels stay un-normalised
    Greek extracts have a column-shift in the source data, so most Greek indicator/scope labels are left correct-but-unmapped rather than risk a wrong mapping; category labels are not crosswalked across languages yet.
  • Demonstration service
    This is a portfolio/demo project. It is accurate to its sources, but it is not an official record — always confirm against the platform's own published report (linked from Report locations) for anything consequential.

Sources & code

Built entirely from public reporting. The full pipeline — extraction, normalisation, the crosswalk, and the query layer — is open source at github.com/krMaynard/transparency-report-api. Start exploring: DSA Reports · Google Removals · Schema.