Swedish Company Financials & Annual Reports (årsredovisningar) avatar

Swedish Company Financials & Annual Reports (årsredovisningar)

Pricing

from $0.95 / 1,000 company records

Go to Apify Store
Swedish Company Financials & Annual Reports (årsredovisningar)

Swedish Company Financials & Annual Reports (årsredovisningar)

Multi-year financials for any Swedish aktiebolag — nettoomsättning, årets resultat, rörelseresultat, summa tillgångar, eget kapital, medelantal anställda — keyed on organisationsnummer, parsed from the official Bolagsverket annual reports (iXBRL).

Pricing

from $0.95 / 1,000 company records

Rating

0.0

(0)

Developer

Karl Sundström

Karl Sundström

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Categories

Share

Multi-year financials for any Swedish aktiebolag — nettoomsättning, årets resultat, rörelseresultat, summa tillgångar, eget kapital, medelantal anställda — keyed on organisationsnummer, parsed from the company's official Bolagsverket annual report filings (iXBRL / Inline XBRL).

Most Swedish company actors stop at the registry key-value layer. This one adds the financial statement layer — the part buyers actually pay for. The figures are delivered parsed and normalised, not as a document: one flat JSON record per company, with a multi-year series and derived ratios.

No API key needed. The annual reports are read from Bolagsverket's public "Värdefulla datamängder" archive. The optional identity enrichment is the only part that uses credentials, and it is off by default.

What it does

Give it one or more organisation numbers. For each company the actor:

  1. locates the company's digitally filed årsredovisningar in Bolagsverket's public bulk archive (arsredovisningar/<year>/<week>_<part>.zip), reading only the ZIP central directory and the members it needs over HTTP range requests — tens of KB per archive instead of the archive's ~93 MB;
  2. extracts the filing's iXBRL document (a filing may also contain a separate, text-only revisionsberättelse; the actor selects the document that carries the numbers);
  3. parses the financial statement line items against the Swedish BFN K2/K3 taxonomy, handling both taxonomy generations with one mapping;
  4. assembles one record per company: latest-year figures, the multi-year series the filing itself carries, and named derived ratios.

One dataset item = one company. Companies with no filing on record still produce an honest record with data_status: "no_filing" and null figures — a "no data" answer is a real answer, not a failure — and those records are not charged.

Input

FieldTypeDefaultMeaning
organisationNumbersstring[]—Swedish org numbers, NNNNNN-NNNN (hyphen optional). Required in practice.
archiveKeysstring[]—Pin exact weekly archives, e.g. arsredovisningar/2025/47_4.zip. Fast, deterministic, few KB.
scanModeall | newestallHow to choose archives when archiveKeys is empty.
maxArchivesToScanint50Only for scanMode: "newest".
fiscalYearsstring[]—Only return these fiscal years.
maxYearsPerCompanyint5Cap the multi-year series. 0 = no cap.
includeSeriesbooltrueEmit financials_by_year (the multi-year series).
downloadDocumentsbooltrueParse the filing. Off = filing metadata only, no figures.
includePersonsboolfalseCEO/board signatories. Personal data — see below.
reportDocumentRefbooltrueInclude archive key / object name / document URL per filing.
enrichWithApiboolfalseFill legal form, registration date, address, SNI via the Bolagsverket API (needs credentials).
concurrencyint6Archives read in parallel.
maxRunSecondsint210Soft budget for the archive scan; keeps a run inside a 5-minute slot.
maxDownloadMbnumber4096Hard cap on bytes downloaded per run.
allowFullArchiveDownloadbooltrueAllow a full-archive fallback if a server ignores range requests.

The prefilled input pins one small archive and one real company, so a default run finishes in seconds without credentials or a large download.

Finding a company's filing

The public archive has no company index — it is organised by filing week, and the organisation number appears only inside the ZIP member names. So the actor searches archives newest-first and stops as soon as each company has enough fiscal years:

  • scanMode: "all" (default) searches every archive's central directory by range request. Complete coverage; measured ~26 s for a company whose filings sit in older archives (it stops early once satisfied).
  • archiveKeys skips the search entirely — use it when you know the filing period, or on the Apify scheduler where predictable run time matters.
  • scanMode: "newest" trades coverage for the cheapest possible run.

Output

One record per company. Money is SEK; every field copied verbatim from a filing is suffixed _reported, everything we computed is suffixed _derived.

{
"organisationsnummer": "559514-2257",
"company_name": "…",
"company_name_source": "filing",
"legal_form": null,
"registration_date": null,
"address": null,
"sni_codes": null,
"fiscal_year": "2025",
"period_start": "2025-01-08",
"period_end": "2025-08-31",
"revenue_sek_reported": 0,
"profit_loss_sek_reported": 198196,
"operating_profit_sek_reported": -1813,
"profit_before_tax_sek_reported": 198196,
"result_after_financial_sek_reported": 198196,
"total_assets_sek_reported": 1183196,
"equity_sek_reported": 223196,
"employees_reported": 0,
"soliditet_reported": 0.19,
"kassalikviditet_reported": null,
"profit_margin_derived": null,
"operating_margin_derived": null,
"equity_ratio_derived": 0.1886,
"revenue_growth_derived": null,
"equity_growth_derived": null,
"financials_by_year": [
{
"fiscal_year": "2025",
"period_start": "2025-01-08",
"period_end": "2025-08-31",
"revenue_sek_reported": 0,
"profit_loss_sek_reported": 198196,
"total_assets_sek_reported": 1183196,
"equity_sek_reported": 223196,
"employees_reported": 0
}
],
"years_available": 1,
"data_status": "ok",
"has_filing": true,
"missing_fields": ["kassalikviditet_reported"],
"taxonomy_generation": "gen-base/2021-10-31+ar-base",
"taxonomy_framework": "K2",
"facts_used": 41,
"dedupe_conflicts": 2,
"notes": [],
"report_archive_key": "arsredovisningar/2025/47_4.zip",
"report_object_name": "5595142257_2025-08-31.zip",
"report_document_name": "d9a6fbbc-….xhtml",
"report_format": "ixbrl-xhtml",
"report_period_ends": "2025-08-31",
"report_document_url": "https://vardefulla-datamangder.bolagsverket.se/arsredovisningar-bulkfiler/arsredovisningar/2025/47_4.zip",
"reports": [ { "period_end": "…", "archive_key": "…", "object_name": "…", "document_names": ["…"] } ],
"persons": null,
"source": "bolagsverket",
"source_url": "https://vardefulla-datamangder.bolagsverket.se/arsredovisningar-bulkfiler/arsredovisningar/2025/47_4.zip",
"fetched_at": "2026-09-27T19:20:00Z",
"actor_version": "0.1.0"
}

Field conventions

  • _reported = copied verbatim from the filing. _derived = computed by the actor, with the formula below. No invented values, ever.
  • data_status is ok (figures parsed), metadata_only (downloadDocuments: false), parse_failed (filing found, no numbers read), or no_filing (nothing on record).
  • employees_reported is the filing's medelantal anställda (average number of employees), not a headcount on a given day.
  • Nulls are honest. A field the filing does not carry is null, and it is listed in missing_fields. Measured coverage over 1,021 real filings: equity 99.8 %, total assets 99.5 %, net profit 99.3 %, revenue 94.5 % (holding and service companies often report no net revenue), employees 63.9 % (no-staff companies omit it).
  • legal_form / registration_date / address / sni_codes are registry fields the iXBRL filing does not carry. They are null unless enrichWithApi: true with credentials supplied.

Derived ratios (_derived)

FieldFormulaNull when
profit_margin_derivedprofit_loss_sek_reported / revenue_sek_reportedrevenue missing or 0
operating_margin_derivedoperating_profit_sek_reported / revenue_sek_reportedrevenue missing or 0
equity_ratio_derivedequity_sek_reported / total_assets_sek_reported (soliditet)assets missing or 0
revenue_growth_derived`(rev_t − rev_{t−1}) /rev_{t−1}
equity_growth_derived`(eq_t − eq_{t−1}) /eq_{t−1}

Growth compares the two newest fiscal years in financials_by_year. Ratios are rounded to 4 decimals. soliditet_reported / kassalikviditet_reported are the values the filing itself publishes, so they can be compared against our derived ratio.

Multi-year series

A Swedish annual report contains a flerårsöversikt — typically ~5 fiscal years of net revenue/result and 2–5 years of balance items. The actor emits that whole series in financials_by_year at no extra cost, merged across every filing it found for the company (newest filing wins on conflicts). One download can therefore produce several company-years.

Correctness notes

  • Duplicate facts are deduped by (concept, period), keeping the most precise copy: the flerårsöversikt repeats values rounded to thousands (decimals="-3" scale="3") while the statement carries the exact figure (decimals="INF"). The exact one wins, and the number of disagreements is reported as dedupe_conflicts.
  • Both taxonomy generations are handled by one mapping. K2 and K3 use identical concept names and differ only in extension namespaces, so matching is on the concept's local name; the generation is detected from the namespace URI (se-gen-base/2017-09-30 vs …/2021-10-31, with or without the se-ar-base wrapper) and reported in taxonomy_generation / taxonomy_framework.
  • No dimensions are used in K2/K3 filings, so no dimension handling is needed.
  • A filing holding two documents (årsredovisning + revisionsberättelse) is resolved by selecting the document with the numbers and merging numeric facts; the text-only auditor copy is skipped.

Pricing

Actor start $0.05 + $0.0015 per record ($1.50 / 1,000 companies). Records with no filing are free.

This deliberately undercuts every financials-bearing competitor found on Apify on 2026-09-27 while sitting well above cost (the source archive is public and free):

Competitor (verified 2026-09-27)Ratevs ours
regdata/poland-krs-financial-scraper (closest category analogue)$0.06 / statement + $0.01 start~40× higher
scrapeworks/danish-annual-reports-cvrfrom $5.00 / 1,000 results3.3× higher
stealth_mode/proff-bussiness-search-scraper$3.00 / 1,0002× higher
logiover/allabolag-scraper$1.99 / 1,0001.3× higher
our own vhsgreed/allabolag-fresh-details (registry only, no statements)$0.20 / 1,0007.5× lower — the financials layer and the series are what justify the step up

See PLAN.md → Pricing for the full justification and unit economics.

Credentials (optional)

Only needed for enrichWithApi: true. Runtime environment variables, never committed:

BOLAGSVERKET_CLIENT_ID
BOLAGSVERKET_CLIENT_SECRET

OAuth2 client-credentials; the actor fetches a bearer token and re-uses it until expiry. Without them, enrichWithApi is skipped and the identity fields stay null — the financials path does not need any credentials.

Responsible use

  • This actor reads Bolagsverket's official public archive and API, not a scraped website. Respect their terms and rate limits in your own runs.
  • We publish a tool; you are the operator and are responsible for how you run it and for complying with the source's terms and with GDPR in your own use.
  • We read only the ZIP members needed and cap downloads per run; archive requests are made at modest concurrency. Keep it that way if you modify it.
  • Personal data: the default output contains no personal data — only company data. includePersons: true adds the CEO/board signatories that appear in the public filing; that is personal data and you must have a lawful basis before enabling it (the actor records its stated basis as GDPR Art. 6(1)(f), legitimate interest in already-published public-register data, minimised to name + role). Beneficial owners (verkliga huvudmän) are never returned.
  • Do not republish personal data unlawfully.

Notes / limitations

  • Only digitally filed annual reports exist in the corpus. Digital filing was voluntary until recently, so coverage for older years and small companies is incomplete; it becomes mandatory for fiscal years beginning after 2025-12-31 (~587,000 aktiebolag), which steadily closes the gap.
  • No company-name search. The archive has no name index, so companyNames is not wired (it is logged and ignored). Supply organisation numbers. See PLAN.md open question 7.
  • The bulk archive is the only source: if Bolagsverket is down or changes the key layout, the actor logs and returns no_filing rather than failing.
  • ESEF/IFRS reports (listed groups) were not found in the sampled corpus and are out of scope for v0.1.0 — the mapping targets BFN K2/K3.
  • Raw reports are also free in bulk (…/arsredovisningar-bulkfiler: 1,435 ZIPs, ~133 GB). The value here is the parsing, multi-year assembly, dedupe, cross-generation mapping and query layer — not access to the documents.

Verification

The iXBRL parser was validated 2,115/2,115 numeric facts against the independent ixbrlparse library (0 value mismatches) on 14 real filings, and is re-verified on every release against real filings pulled from the live archive (tests/test_smoke_live.py). Run the offline suite with:

pip install -r requirements.txt
python -m pytest # offline, no credentials
RUN_LIVE_SMOKE=1 python -m pytest tests/test_smoke_live.py # real archive, no credentials