--- name: medicines-register description: Download public Estonia Medicines Register CSV/XML datasets for products, packages, substances, dosage forms, administration routes, and ATC codes. module: ravimid.mjs execution: code --- # Medicines Register Downloads ## Vetted module (use this first) `ravimid.mjs` ships in https://kodanikukratt.ee/kratt-agent.tar.gz (plain ESM, Node 22+) and is re-verified against the live source: ```js import { show } from "./kratt.mjs"; import { medicines, medicinePackages, medicineDownloads } from "./ravimid.mjs"; const r = await medicines({ name: "CIPRALEX" }); show({ kokku: r.totals, note: r.note }); show(r.rows); const p = await medicinePackages({ atc: "N02BE01", supply: true }); show({ kokku: p.totals }); show(p.rows.filter((x) => x.supplyIssue).slice(0, 5)); // tarneraskused show(medicineDownloads()); // the whole export catalogue ``` - **Withdrawn products sit in the same file as current ones.** 19,406 of the 64,691 package rows read "Müügiloa kehtivus lõppenud", and the human-medicines export holds end dates in 2017 next to the 6,244 rows dated 01.01.2050 (counted live 2026-08-01). `medicines()` computes `authorised` per row and splits `totals` into `authorised`/`expired`, and leaves expired rows out unless `{includeExpired:true}` — **never say a medicine is available without checking that flag**. `authorisedUntil: null` on an authorised row means open-ended; 01.01.2050 is the register's placeholder for that. - Five exports are plain static files, verified 2026-08-01, so the module fetches them directly and skips the `__VIEWSTATE` handshake. The classifiers (`toimeained`, `atc`, `ravimvormid`, `manustamisviisid`, `pakendid.xml`) have **no** static URL and still need the postback — `medicineDownloads()` reports `direct` per file rather than pretending they are alike. Never hard-code a `__VIEWSTATE`. - **Two different columns are both headed "Soodustused".** In the medicines export it is the reimbursement **rates** ("100% 90% 50%"), one per legal ground; in the packages export it is a list of related **package codes**. The module returns them as `reimbursement` and `reimbursedPackages` so one cannot be read as the other. Which rate a person actually gets depends on their diagnosis and is Tervisekassa's decision. - `maxPriceEur` is the **piirhind** — the ceiling Tervisekassa reimburses against, not what a pharmacy charges. Most packs have none (2,011 of 64,691). - `{supply:true}` switches to the only export carrying **tarneraskus**, the shortage the holder has notified. Without it `supplyIssue` is undefined, which is not evidence there is no shortage. - `spcUrl`/`pilUrl` are the official product characteristics and the patient leaflet (real PDFs, checked live). Link the leaflet rather than summarising dosing. - **This is regulatory data, not prescribing advice**: it says what is authorised, never what anyone should take. The endpoints below are the fallback if a module call throws. ## Use when - You need medicinal-product or package records, authorization status, substances, dosage forms, administration routes, or ATC codes. - You need a complete bulk extract rather than manual search results. ## Avoid when - You need annual market or pharmacy indicators; these exports carry product-level records, not aggregates. ## Endpoint - Public downloads: https://www.ravimiregister.ee/publichomepage.aspx?pv=PublicDownloads ## Workflow 1. Start a cookie-preserving HTTP session and GET the downloads page. 2. Parse all hidden form fields, including `__VIEWSTATE`, `__VIEWSTATEGENERATOR`, and `__EVENTVALIDATION`. 3. POST them to the same URL with `__EVENTTARGET` set to the required control and an empty `__EVENTARGUMENT`. 4. Parse the returned CSV with UTF-8 BOM and semicolon delimiters, or parse XML against the downloadable schema. ## Export controls - Packages CSV: `ctl10$packagesCsvDownload` - Detailed packages CSV: `ctl10$packagesDetailedCsvDownload` - Detailed packages CSV v2: `ctl10$packagesSpecialCsvDownload` - Packages XML: `ctl10$packagesXmlDownload` - Human medicines: `ctl10$humMedDownload` - Veterinary medicines: `ctl10$vetMedDownload` - Active substances CSV: `ctl10$downloadActiveSubstancesCsv` - Dosage forms CSV: `ctl10$downloadMedFormsCsv` - Administration routes CSV: `ctl10$downloadRoutesOfAdministrationCsv` - Human/veterinary ATC CSV: `ctl10$humAtcDownload`, `ctl10$vetAtcDownload` ## Access reality - Public ASP.NET WebForms exports with no login, verified 2026-07-14. - The packages postback redirects to `https://www.ravimiregister.ee/Data/XML/pakendid.csv` and returns a UTF-8 semicolon CSV with product, package, authorization, pricing, and medicine-information fields. ## Output schema expectations - Preserve package/product identifiers, names, ATC code, active substance, strength, form, prescription status, authorization holder/status dates, and source URL when present. - Keep authorization-ended, unauthorized, human, and veterinary records distinguishable. ## Limits and caveats - **These are bulk exports, not a query API, so we cannot read them with plain requests.** The packages CSV alone is over 20 MB (21.4 MB on 2026-08-02) and the classifiers come back only from the postback above, so a question about one medicine means downloading a whole file and filtering it in code. Without a runtime, say the extract was not retrieved rather than quoting rows from a truncated download. - Do not hard-code `__VIEWSTATE` or other hidden values; fetch them for each session. - The server uses `application/octet-stream` for CSV. Validate the BOM/header and parse content rather than relying on MIME type. - This is medical-regulatory data, not prescribing advice. ## Verification hooks - Require a successful downloads page containing the requested postback control. - For packages CSV, require a UTF-8 BOM and a header beginning `Pakendi liik;Ravimi liik;Pakendi kood;Pakendi nimetus;ATC kood`. ## Module API medicines({name, atc, substance, vet, includeExpired}) -> {source, page, filter, totals, rows, note} medicinePackages({name, atc, code, supply}) -> {source, page, filter, totals, rows, note}