# OpenAlex Romania: scholarly works and institutions > The Romanian slice of OpenAlex, the free open catalogue of the global research system (built by OurResearch, successor to Microsoft Academic Graph). Institutions located in Romania, and every scholarly work OpenAlex indexes with at least one Romanian-affiliated authorship — article, book chapter, preprint, dataset. Authorships are kept relational rather than nested: work_authorships is one row per (work, author, Romanian institution), so a reader can ask "who are Romania's most-cited researchers" or "which field is Romania publishing most in" directly in SQL. REFERENCED LIVE, NOT COPIED: all three tables are published as views over OpenAlex's own Parquet snapshot on the AWS Open Data bucket s3://openalex, so no row is stored in this directory's lake and every query reads OpenAlex directly — a citation count OpenAlex has since revised, or a correction to a title or a retraction flag, shows up in the next query with no re-ingest here. That also means there is no history beyond the one query each view was published with, no row counts or column statistics of our own, and a query needs network access to s3://openalex (public, no credentials). The bucket has no partition on country, so filtering works/work_authorships to Romania costs a genuine column scan of the works archive — this connector publishes the complete archive's view by default (since=all), which costs nothing to publish under federation; a query against the resulting view pays whatever scan the query itself needs. - Dataset id: `openalex-ro` - Update cadence: weekly - Upstream source: https://openalex.org - Tags: research, science, scholarly, academia, openalex, publications, romania - Current version: ingest `01M5GSRHR2T9X2A4ZJ159GK0HA`, snapshot 2026-10-22, generated 2026-10-22T03:16:38.923Z ## Licence and attribution - Licence: Creative Commons Zero v1.0 Universal (public domain dedication) (`CC0-1.0`) - Licence text: https://github.com/ourresearch/openalex-docs/blob/main/license.md - Required attribution: Data source: OpenAlex (https://openalex.org). - Notes: OpenAlex's own licence page states plainly: "OpenAlex data is made available under the CC0 license. That means it's in the public domain, and free to use in any way you like," adding that attribution "is appreciated where it's convenient, but it's not at all necessary." Verified directly against github.com/ourresearch/openalex-docs/blob/main/license.md on 2026-09-02. The one carve-out OpenAlex documents is its legacy "MAG Format" export, released under ODC-BY because it inherits Microsoft Academic Graph's own licence — this connector does not use that export, only the current Parquet snapshot, so ODC-BY does not apply here. IMPORTANT CAVEATS FOR A REUSER, none of which change the licence but all of which change how far to trust a value. (1) OpenAlex itself aggregates many upstream sources (Crossref, PubMed, ORCID, ROR, institutional repositories, publisher metadata) and inherits their coverage gaps and occasional errors — a missing DOI or an unresolved affiliation is usually an upstream gap, not evidence the work does not exist. (2) Affiliation-to-institution matching is automated: a work with institutions_distinct_count = 0 has an author who wrote something OpenAlex could not resolve to a canonical institution, and is correctly excluded here rather than guessed at. (3) cited_by_count, fwci and works_count grow over time as OpenAlex indexes new citing works — and because works/institutions are published as VIEWS over OpenAlex's live bucket (not copied), a later query naturally sees the larger, current number with no re-ingest of ours involved: two queries run minutes apart can legitimately disagree, and the later one is the more current value, not a data-quality problem. (4) What is published here is a filtered, reshaped VIEW over OpenAlex's own data — the Romanian slice, presented as relational tables — not a stored copy and not a verbatim republication of the OpenAlex snapshot; the SELECT that shapes it is published in full as each table's view definition. Reproduce the attribution line verbatim anywhere you republish this data. ## Manifest - Start here for how this directory works: https://data.bunnytech.app/llms.txt - This dataset: https://data-directory.fsn1.your-objectstorage.com/public/openalex-ro/manifest.json - All datasets: https://data-directory.fsn1.your-objectstorage.com/public/index.json - Human page: https://data.bunnytech.app/datasets/openalex-ro The manifest is the contract: file URLs, row counts, column types, min/max statistics and example queries all live there. Re-read it to discover a new version. ## Fastest path — Parquet exports Immutable, physically sorted snapshot exports. No catalog fetch, best row-group pruning, safe to cache forever. Register one view per table: ```sql INSTALL httpfs; LOAD httpfs; -- institutions: queried live at the source, never copied here — no parquet export. -- works: queried live at the source, never copied here — no parquet export. -- work_authorships: queried live at the source, never copied here — no parquet export. ``` Example queries: -- Romanian scholarly output by year: publication_year leads works.sortBy, which describes the reading order the view was designed around — this table is federated (materialize: "view"), so nothing is physically sorted by us and the order buys no pruning of ours; whatever pruning happens is whatever OpenAlex's own Parquet writer left behind. ```sql CREATE OR REPLACE SECRET "openalex_pds" (TYPE s3, SCOPE 's3://openalex', PROVIDER 'config', REGION 'us-east-1'); ATTACH IF NOT EXISTS ':memory:' AS "openalex_ro"; CREATE SCHEMA IF NOT EXISTS "openalex_ro"."main"; CREATE OR REPLACE VIEW "openalex_ro"."main"."works" AS SELECT id AS work_id, title, doi, ids['pmid'] AS pmid, publication_date, publication_year, "type" AS work_type, language, authors_count, len(list_filter(authorships, a -> list_contains(a.countries, 'RO'))) AS ro_authors_count, countries_distinct_count, institutions_distinct_count, open_access.is_oa AS is_oa, open_access.oa_status AS oa_status, primary_topic.id AS primary_topic_id, primary_topic.display_name AS primary_topic_name, primary_topic.subfield.display_name AS primary_subfield_name, primary_topic.field.display_name AS primary_field_name, primary_topic.domain.display_name AS primary_domain_name, cited_by_count, fwci, referenced_works_count, is_retracted, updated_date, DATE '2026-10-22' AS first_seen_date, DATE '2026-10-22' AS last_seen_date, TIMESTAMPTZ '2026-10-22T03:16:34.178Z' AS ingested_at FROM read_parquet('s3://openalex/data/parquet/works/**/*.parquet', hive_partitioning = true, union_by_name = true) WHERE EXISTS (SELECT 1 FROM unnest(authorships) AS a(auth) WHERE list_contains(auth.countries, 'RO')); SELECT publication_year, count(*) AS works FROM openalex_ro.main.works WHERE publication_year BETWEEN 2015 AND 2025 GROUP BY publication_year ORDER BY publication_year; ``` -- Romania's most-cited researchers: work_authorships.sortBy leads with author_id for exactly a query shaped like this, but the table is federated (materialize: "view") — the order is advisory, describing the reading order a query should use, not a physical sort this platform performed. cited_by_count is read live from OpenAlex at query time, so it reflects whatever OpenAlex currently counts, not the value at any past ingest. ```sql CREATE OR REPLACE SECRET "openalex_pds" (TYPE s3, SCOPE 's3://openalex', PROVIDER 'config', REGION 'us-east-1'); ATTACH IF NOT EXISTS ':memory:' AS "openalex_ro"; CREATE SCHEMA IF NOT EXISTS "openalex_ro"."main"; CREATE OR REPLACE VIEW "openalex_ro"."main"."work_authorships" AS SELECT work_id, row_number() OVER ( PARTITION BY work_id ORDER BY author_position, author_id, institution_id, author_name ) AS authorship_seq, author_id, author_name, author_orcid, author_position, is_corresponding, institution_id, institution_name, institution_ror, raw_affiliation_string, updated_date, DATE '2026-10-22' AS first_seen_date, DATE '2026-10-22' AS last_seen_date, TIMESTAMPTZ '2026-10-22T03:16:34.178Z' AS ingested_at FROM ( SELECT id AS work_id, auth.author.id AS author_id, auth.author.display_name AS author_name, auth.author.orcid AS author_orcid, auth.author_position AS author_position, auth.is_corresponding AS is_corresponding, inst.id AS institution_id, inst.display_name AS institution_name, inst.ror AS institution_ror, auth.raw_affiliation_strings[1] AS raw_affiliation_string, updated_date FROM read_parquet('s3://openalex/data/parquet/works/**/*.parquet', hive_partitioning = true, union_by_name = true), unnest(authorships) AS a(auth), unnest(auth.institutions) AS i(inst) WHERE inst.country_code = 'RO' ) flattened; CREATE OR REPLACE VIEW "openalex_ro"."main"."works" AS SELECT id AS work_id, title, doi, ids['pmid'] AS pmid, publication_date, publication_year, "type" AS work_type, language, authors_count, len(list_filter(authorships, a -> list_contains(a.countries, 'RO'))) AS ro_authors_count, countries_distinct_count, institutions_distinct_count, open_access.is_oa AS is_oa, open_access.oa_status AS oa_status, primary_topic.id AS primary_topic_id, primary_topic.display_name AS primary_topic_name, primary_topic.subfield.display_name AS primary_subfield_name, primary_topic.field.display_name AS primary_field_name, primary_topic.domain.display_name AS primary_domain_name, cited_by_count, fwci, referenced_works_count, is_retracted, updated_date, DATE '2026-10-22' AS first_seen_date, DATE '2026-10-22' AS last_seen_date, TIMESTAMPTZ '2026-10-22T03:16:34.178Z' AS ingested_at FROM read_parquet('s3://openalex/data/parquet/works/**/*.parquet', hive_partitioning = true, union_by_name = true) WHERE EXISTS (SELECT 1 FROM unnest(authorships) AS a(auth) WHERE list_contains(auth.countries, 'RO')); SELECT wa.author_name, wa.author_orcid, count(*) AS papers, sum(w.cited_by_count) AS citations FROM openalex_ro.main.work_authorships wa JOIN openalex_ro.main.works w USING (work_id) GROUP BY wa.author_id, wa.author_name, wa.author_orcid ORDER BY citations DESC LIMIT 20; ``` ## Live lake — DuckLake catalog with time travel Every table in this dataset is queried live at its source, so nothing was copied into a DuckLake and the catalog published beside it holds no tables. Use the setup printed with the example queries above, or the `view.sql` of the table you want from `manifest.json` — that is the query, in full, against the upstream source. ## Wholesale — S3 access For bulk downloads, read the bucket prefix directly over S3 with httpfs: ```sql INSTALL httpfs; LOAD httpfs; CREATE SECRET hetzner ( TYPE s3, ENDPOINT 'fsn1.your-objectstorage.com', URL_STYLE 'path', KEY_ID '', SECRET '' ); SELECT * FROM read_parquet('s3://data-directory/public/openalex-ro/v/01M5GSRHR2T9X2A4ZJ159GK0HA/**/*.parquet'); ``` ## Schema ### institutions One row per OpenAlex institution located in Romania — 319 of them as measured on 2026-09-02: universities, research institutes, hospitals, companies and museums that have published research OpenAlex indexes. The dimension works.ro_authors_count and work_authorships.institution_id join to. FEDERATED (materialize: "view" in index.ts): this table is a live view over OpenAlex's own institutions Parquet, not a copy — a rename or reclassification OpenAlex makes shows up in the next query with no re-ingest here. - 0 rows · 0 B · write mode `upsert` - Physically sorted by: `name_lc` - Primary key: `institution_id` - Text search columns: `display_name` - Files: 0 Columns: - `institution_id` VARCHAR NOT NULL — OpenAlex institution id, e.g. "https://openalex.org/I138164181". Used exactly as OpenAlex publishes it (a resolvable URL) so it matches work_authorships.institution_id with no transformation. - `ror` VARCHAR — Research Organization Registry identifier, OpenAlex’s preferred cross-database key for institutions. NULL for the handful of institutions with no ROR match. - `display_name` VARCHAR NOT NULL — Institution name as OpenAlex publishes it. - `name_lc` VARCHAR NOT NULL — display_name lower-cased and accent-folded, for case- and accent-insensitive search. Searching this column instead of lower(display_name) is what lets a prefix match use parquet statistics. - `institution_type` VARCHAR — OpenAlex’s classification of the institution: education, healthcare, company, nonprofit, government, facility, archive, other. - `country_code` VARCHAR NOT NULL — ISO 3166-1 alpha-2 country code. Always "RO" in this dataset — kept as a column rather than assumed, so a query written against this table still works if it is ever widened. - `homepage_url` VARCHAR — The institution’s own homepage, where OpenAlex has one. - `works_count` INTEGER — How many works OpenAlex attributes to this institution GLOBALLY — not limited to the works this dataset ships. A rough size signal, not a count you can join and re-derive. - `cited_by_count` INTEGER — Total citations across every work OpenAlex attributes to this institution, globally. - `city` VARCHAR — City OpenAlex geocoded the institution to. - `region` VARCHAR — Romanian county (județ) or region OpenAlex geocoded the institution to. - `latitude` DOUBLE [degrees] — Institution latitude, decimal degrees. - `longitude` DOUBLE [degrees] — Institution longitude, decimal degrees. - `first_seen_date` DATE NOT NULL — The day (UTC) the published view definition was built. This table is federated (materialize: "view" in index.ts), so it is NOT a seen-window any more: a view has no memory of previous runs, so first_seen_date and last_seen_date carry the same constant on every row. While this table was copied into the lake these two columns really did widen across runs — kept, with this description, rather than dropped, so a query written against the copied table still resolves. - `last_seen_date` DATE NOT NULL — The same constant as first_seen_date (UTC), for the same reason: this table is federated and a view cannot remember a previous run. An institution OpenAlex stops listing is simply absent from the view — it is not retained under a stale last-seen value the way the copied, upserted version of this table retained it. - `ingested_at` TIMESTAMP NOT NULL — When the run that PUBLISHED THIS VIEW started (UTC). This table is federated — no row was ever copied — so this is one constant for every row, and it dates the view definition rather than the data: the rows themselves are read from OpenAlex live, at the moment you query them. ### works One row per scholarly work — article, book chapter, dataset, preprint — with at least one Romanian-affiliated authorship, read live from the OpenAlex works snapshot. Bibliographic facts only: no nested authorship detail (see work_authorships for that) and no full abstract. ro_authors_count says how many of the authorships were Romanian-affiliated; authors_count is the work’s total author count including foreign co-authors. FEDERATED (materialize: "view" in index.ts): this table is a view over OpenAlex’s Parquet, filtered to whichever since/until window the last publish used (see updated_date and the connector’s since/until params) — cited_by_count, fwci and every other value are read fresh at query time, not frozen at some past ingest. - 0 rows · 0 B · write mode `upsert` - Physically sorted by: `publication_year`, `work_id` - Primary key: `work_id` - Files: 0 Columns: - `work_id` VARCHAR NOT NULL — OpenAlex work id, e.g. "https://openalex.org/W2129290015". Joins to work_authorships.work_id. - `title` TEXT — Work title, as OpenAlex has it. NULL for the small number of records OpenAlex has not yet titled. - `doi` VARCHAR — Digital Object Identifier, as a resolvable https://doi.org/ URL. - `pmid` VARCHAR — PubMed identifier, for works indexed in PubMed. NULL otherwise. - `publication_date` DATE — The work’s publication date, as OpenAlex resolved it. OpenAlex’s own docs format this one as a bare ISO 8601 date with no time-of-day or zone at all — contrast updated_date below, which OpenAlex explicitly documents as UTC. So this is not a UTC day: it is the calendar day of publication, exactly as the publisher or indexer stated it, unconverted. - `publication_year` INTEGER — Calendar year of publication_date, kept as its own column because "papers per year" is the single most common aggregation over this table. - `work_type` VARCHAR — OpenAlex’s work-type classification: article, book-chapter, preprint, dataset, dissertation, review, and about a dozen more. - `language` VARCHAR — BCP-47 language code OpenAlex detected for the work. - `authors_count` INTEGER — Total number of authorships on this work, Romanian and foreign combined. - `ro_authors_count` INTEGER NOT NULL — How many of this work’s authorships carry a Romanian institutional affiliation — computed by this connector, not published by OpenAlex directly, because it is exactly the count that motivated including the work in this dataset in the first place. Declared INTEGER, but computed with DuckDB’s len(), which returns BIGINT — this table is federated (materialize: "view" in index.ts) and a view is not CAST to its declared type the way a lake copy is, so a reader who runs DESCRIBE against the published view sees this column as BIGINT (checked directly, not assumed — see test/ingest.integration.test.ts). Harmless for ordinary comparisons and arithmetic, but a real divergence from the declared schema worth knowing about before relying on the exact type. - `countries_distinct_count` INTEGER — Number of distinct countries among this work’s resolved institutional affiliations, as OpenAlex computes it. 0 for a work with no resolved institution at all. - `institutions_distinct_count` INTEGER — Number of distinct institutions among this work’s resolved affiliations, as OpenAlex computes it. - `is_oa` BOOLEAN — Whether OpenAlex found an open-access copy of this work. - `oa_status` VARCHAR — The open-access route OpenAlex found, when is_oa is true: gold, green, hybrid, bronze, diamond. "closed" when none was found. - `primary_topic_id` VARCHAR — OpenAlex id of the work’s primary topic. - `primary_topic_name` VARCHAR — Name of the work’s primary topic, OpenAlex’s most granular subject classification. - `primary_subfield_name` VARCHAR — Name of the subfield the primary topic belongs to. - `primary_field_name` VARCHAR — Name of the field the primary topic belongs to. - `primary_domain_name` VARCHAR — Name of the broadest domain the primary topic belongs to. - `cited_by_count` INTEGER — How many later works cite this one, as OpenAlex currently counts it. Grows over time; not a fixed fact about the work. - `fwci` DOUBLE — Field-Weighted Citation Impact: citations relative to the average for works of the same type, field and publication year. 1.0 is exactly average. NULL when OpenAlex has not computed it (typically works with too few citing years so far). - `referenced_works_count` INTEGER — How many works this one cites, per OpenAlex’s reference list extraction. - `is_retracted` BOOLEAN NOT NULL — Whether this work has been retracted, per OpenAlex’s retraction tracking (via Retraction Watch and Crossref). - `updated_date` DATE NOT NULL — The day (UTC) OpenAlex itself last wrote this work’s record — not a publication date. OpenAlex’s own docs state this one explicitly: "the last time anything in this Work object changed, expressed as an ISO 8601 date string (in UTC)." This is the column a narrowed ingest filters on (see the connector’s since/until params); for a federated view (materialize: "view" in index.ts) that filter is baked into the published view as a fixed WHERE clause, not re-evaluated per run. - `first_seen_date` DATE NOT NULL — The day (UTC) the published view definition was built. This table is federated, so it is NOT a seen-window: a view has no memory of previous runs, so first_seen_date and last_seen_date carry the same constant on every row. While this table was copied into the lake these two columns really did widen across runs; they are kept, with this description, rather than dropped, so a query written against the copied table still resolves. - `last_seen_date` DATE NOT NULL — The same constant as first_seen_date (UTC), for the same reason: this table is federated and a view cannot remember a previous run. A work that vanishes from a later since/until window is simply absent from the next published view — it is not retained under a stale last-seen value the way the copied, upserted version of this table retained it. - `ingested_at` TIMESTAMP NOT NULL — When the run that PUBLISHED THIS VIEW started (UTC). This table is federated — no row was ever copied — so this is one constant for every row, and it dates the view definition rather than the data: cited_by_count, fwci and every other value are read from OpenAlex live, at the moment you query them, not as of this timestamp. ### work_authorships One row per (work, author, Romanian institution) — the flattened form of the authorships[].institutions[] structure OpenAlex nests inside every work, kept to the Romanian side of each authorship. This is where an author’s name, ORCID and Romanian institution live; join to works on work_id for the bibliographic facts, and to institutions on institution_id for the institution’s own details. FEDERATED (materialize: "view" in index.ts): flattened by the published view definition itself, over the same since/until window as works — nothing here is stored or accumulated across runs. - 0 rows · 0 B · write mode `upsert` - Physically sorted by: `author_id`, `work_id` - Primary key: `work_id`, `authorship_seq` - Files: 0 Columns: - `work_id` VARCHAR NOT NULL — The work this authorship belongs to; joins to works.work_id. - `authorship_seq` INTEGER NOT NULL — A tiebreaker ordinal, 1-based within each work, assigned in a stable read order — NOT an OpenAlex field. It exists because author_id can be NULL (see below) and because OpenAlex’s own authorship list occasionally repeats the same author for the same institution verbatim; both make (work_id, author_id, institution_id) alone unreliable as a key. Not meaningful on its own — use it only to make a row addressable. - `author_id` VARCHAR — OpenAlex author id. Stable across a person’s works, so GROUP BY author_id is how you count one researcher’s output in this dataset. NULL for an author OpenAlex has not yet disambiguated to a canonical id — seen on recently-added works — in which case author_name is the only identifying information available. - `author_name` VARCHAR — Author display name, as OpenAlex resolved it for this authorship. - `author_orcid` VARCHAR — The author’s ORCID, where OpenAlex has matched one. - `author_position` VARCHAR — "first", "middle" or "last" — the author’s position in the work’s byline, as OpenAlex reports it. - `is_corresponding` BOOLEAN — Whether OpenAlex marked this author as the corresponding author for the work. - `institution_id` VARCHAR NOT NULL — The Romanian institution this authorship is affiliated with; joins to institutions.institution_id. - `institution_name` VARCHAR — Institution name at the time OpenAlex resolved this affiliation — may differ slightly in wording from institutions.display_name if the institution has since been renamed. - `institution_ror` VARCHAR — ROR identifier of the institution. - `raw_affiliation_string` TEXT — The first raw affiliation string OpenAlex parsed for this authorship, exactly as it appeared on the paper — before any institution matching. Useful for spotting a mismatch; an authorship can carry several raw strings (co-affiliations, department plus university) and this keeps only the first. - `updated_date` DATE NOT NULL — The day (UTC) OpenAlex last wrote the parent work’s record — the same value as works.updated_date (also UTC; see that column, and OpenAlex’s own docs, for the exact wording) for this authorship’s work_id. For a federated view (materialize: "view" in index.ts), the since/until window this filters on is baked into the published view, not re-evaluated per run. - `first_seen_date` DATE NOT NULL — The day (UTC) the published view definition was built. This table is federated, so it is NOT a seen-window: a view has no memory of previous runs, so first_seen_date and last_seen_date carry the same constant on every row. While this table was copied into the lake these two columns really did widen across runs; they are kept, with this description, rather than dropped, so a query written against the copied table still resolves. - `last_seen_date` DATE NOT NULL — The same constant as first_seen_date (UTC), for the same reason: this table is federated and a view cannot remember a previous run. A row that vanishes from a later since/until window is simply absent from the next published view — it is not retained under a stale last-seen value the way the copied, upserted version of this table retained it. - `ingested_at` TIMESTAMP NOT NULL — When the run that PUBLISHED THIS VIEW started (UTC). This table is federated — no row was ever copied — so this is one constant for every row, and it dates the view definition rather than the data: the rows themselves are read from OpenAlex live, at the moment you query them. ## Performance notes - There are no indexes in a lakehouse. The physical sort order plus Parquet row-group min/max statistics ARE the index. Predicates on the leading sort columns prune whole row groups; everything else is a full column scan. - `institutions` prunes on `name_lc`. - `works` prunes on `publication_year` → `work_id`. - `work_authorships` prunes on `author_id` → `work_id`. - `ILIKE '%needle%'` cannot prune: it decompresses that whole column. Prefer prefix predicates on the lowercased search companion columns where they exist. - Every file URL under `/v//` is immutable. Cache it as aggressively as you like. - Files are never rewritten in place — a new ingest writes a new version path. Re-read the manifest to pick up updates rather than polling the file URLs. - Pull only the columns you need. Parquet is columnar and the reader range-GETs what it reads. ## Do not - Do not scrape https://data.bunnytech.app HTML. Every number on those pages comes from the manifest above. - Do not hit the upstream source (https://openalex.org) in bulk. This mirror exists so you do not have to. - Do not read DuckLake internal data files with a plain Parquet reader. They come with positional delete files, and ignoring those silently returns deleted rows. Use the ducklake extension, or use the published exports above. - Do not republish without the attribution line, and do not imply endorsement.