Finnish Business Information System (PRH)

Document information
FieldValue
Canonical URL/docs/11_partner_services/20_finnish-business-information-system
Version (published date)2026-08-14
Tagspartners, prh, ytj, bis, finland, reference-data, restapi-generic

Summary

The PRH open data service exposes the Finnish Business Information System (BIS, Finnish: YTJ) as read-only JSON HTTP APIs. You can look up companies on the Finnish Trade Register by Business ID (Y-tunnus), name, location, company form, or TOL 2008 line of business, and you can watch Trade Register notifications. Tealfabric has exercised the v3 BIS GET interface against restapi-generic-1.1.0 (operation: receive, method: GET).

Basic company and notification lookups are free and do not use an API key. Financial statements beyond the small IXBRL open-data subset are a one-time purchase from PRH’s Virre Information Service. Tealfabric does not resell that data or collect PRH fees.

Why use it with Tealfabric

Tealfabric stores your customers, entities, and processes. It does not maintain the Finnish Trade Register. Use BIS when a workflow needs an authoritative Finnish company record:

  • Confirm a Business ID and current name during onboarding
  • Copy registered office, company form, and tax-register membership onto an entity
  • Detect bankruptcy, restructuring, or liquidation (companySituations)
  • Watch Trade Register notifications for customers you already store
  • Enrich a CRM or DataPool catalog of Finnish legal entities

Typical pattern: keep the entity in Tealfabric; call BIS when a step, WebApp, or agent needs register facts. Persist what you need in DataPool or entity-information so later steps do not hit PRH on every form load.

What the APIs provide

All three open-data surfaces are GET-only JSON (except the IXBRL statement body, which is XML). There is no create/update/delete. No Authorization header. Data refresh is daily. License: Creative Commons Attribution 4.0. Name PRH as the source. Do not use PRH or YTJ logos, and do not present your WebApp as a PRH service.

SurfaceBase URLWhat you get
Finnish Business Information Systemhttps://avoindata.prh.fi/opendata-ytj-api/v3Names, Business ID, EUID, addresses, company form, TOL 2008, websites, tax-register memberships, Trade Register status, situations
Registered notificationshttps://avoindata.prh.fi/opendata-registerednotices-api/v3Notifications registered since 7 November 2014 (date, subject, company basics)
Digital financial statements (IXBRL)https://avoindata.prh.fi/opendata-xbrl-api/v3Profit-and-loss and balance-sheet periods filed in IXBRL (~5% of all statements)

Useful BIS (/companies) query patterns:

NeedHow
One companyGET /companies?businessId=0116297-6
Name searchGET /companies?name=Valio
Industry + new registrationsGET /companies?mainBusinessLine=62&registrationDateStart=2026-01-01&page=1
Town / post codelocation= or postCode=
Company formcompanyForm=OY (codes from the Trade Register list on prh.fi)
Next pagepage= (1-based). More than 100 hits are paged; omit page or an out-of-range page returns page 1

Response shape: { totalResults, companies: [...] }. Each company includes businessId.value, names, addresses, companyForms, registeredEntries, tradeRegisterStatus, companySituations, mainBusinessLine, website, and lastModified.

restapi-generic-1.1.0 receive wraps a JSON object as a one-element data array. Unwrap companies from that envelope in step code (see below).

Not in the open data: private traders (toiminimi), municipalities, wellbeing services counties, tax partnerships, email addresses, and phone numbers. Occasional human lookups are easier in the YTJ company search than through the API.

Interactive schemas:

Platform compatibility

We have run BIS v3 through Tealfabric’s generic REST connector. The combination that works for free company search:

Integration settingValue
Connectorrestapi-generic-1.1.0
urlhttps://avoindata.prh.fi
path/opendata-ytj-api/v3/companies
methodGET
api_key / auth_header_nameleave empty
acceptapplication/json
Process step operationreceive

Keep this integration GET / receive only. Do not set method to POST. On restapi-generic-1.1.0, a write verb remaps receive to send and changes the response envelope.

Paging and filters belong in the query argument (page, businessId, name, …), not in a request body.

Create separate read integrations when you also call notices or IXBRL (different path). Leave executable_by_ai_agents off until you explicitly want Trace AI to call BIS. Put the OpenAPI URL in the integration description so the agent sees the real query surface.

Do not point restapi-generic-1.1.0 at GET /all_companies. That endpoint returns a ZIP of the full register, not JSON.

Connector details: Generic REST API connector.

Connect from a process step

Replace YOUR_INTEGRATION_ID with the BIS integration. The examples look up one Business ID, then page a filtered list.

const integrationId = "YOUR_INTEGRATION_ID";

function failMessage(result: Record<string, unknown>, fallback: string): string {
  const err = result.error;
  if (typeof err === "string") return err;
  if (err && typeof err === "object") {
    return String((err as Record<string, unknown>).message ?? fallback);
  }
  return fallback;
}

function unwrapCompanies(result: Record<string, unknown>): {
  companies: unknown[];
  totalResults: number;
} {
  const envelope = (result.data ?? {}) as Record<string, unknown>;
  let root: unknown = envelope.data ?? envelope;
  if (Array.isArray(root) && root.length === 1) {
    root = root[0];
  }
  const obj =
    root && typeof root === "object" && !Array.isArray(root)
      ? (root as Record<string, unknown>)
      : {};
  const companies = Array.isArray(obj.companies) ? obj.companies : [];
  return { companies, totalResults: Number(obj.totalResults ?? companies.length) };
}

const lookup = (await integration.executeSync(integrationId, {
  operation: "receive",
  query: { businessId: "0116297-6" },
})) as Record<string, unknown>;

if (!lookup?.success) {
  throw new Error(failMessage(lookup, "PRH BIS receive failed"));
}

const { companies: hits } = unwrapCompanies(lookup);
const company = hits[0] ?? null;

const PAGE_SIZE_HINT = 100;
const collected: unknown[] = [];
let page = 1;
let totalResults = Number.POSITIVE_INFINITY;

while (collected.length < totalResults) {
  const result = (await integration.executeSync(integrationId, {
    operation: "receive",
    query: {
      mainBusinessLine: "62",
      registrationDateStart: "2026-01-01",
      page: String(page),
    },
  })) as Record<string, unknown>;

  if (!result?.success) {
    throw new Error(failMessage(result, "PRH BIS receive failed"));
  }

  const batch = unwrapCompanies(result);
  totalResults = batch.totalResults;
  if (batch.companies.length === 0) {
    break;
  }
  collected.push(...batch.companies);
  // Out-of-range `page` on BIS returns page 1 again — stop on a short page or full count.
  if (batch.companies.length < PAGE_SIZE_HINT || collected.length >= totalResults) {
    break;
  }
  page += 1;
}

return {
  success: true,
  data: {
    company,
    industry_companies: collected,
    industry_companies_count: collected.length,
  },
};

Notification lookup by Business ID (separate integration whose path is /opendata-registerednotices-api/v3):

const result = await integration.executeSync(noticesIntegrationId, {
  operation: "receive",
  path: "0116297-6",
});

Space calls when paging. PRH returns HTTP 429 when you send too many requests; back off and retry.

Example use cases with Tealfabric

Onboard a Finnish customer. A WebApp collects a Business ID. A process step receives businessId=…, checks tradeRegisterStatus and companySituations, then writes businessId.value, current names (type current version), and street address onto the entities row / entity-information.

Keep a CRM in sync. A scheduled ProcessFlow pages /companies with registrationDateStart (or a DataPool watermark) and upserts new OY/OYJ records into your CRM connector. Filter out non-operational statuses in step code before create.

Watch register events. Nightly receive on the notices integration with noticeRegistrationDateStart / noticeRegistrationDateEnd (and optional entryCode such as KONALK / SANALK). Match businessId to existing entities and raise a notification or Trace AI brief when a customer enters bankruptcy or restructuring.

Industry lead list. Filter mainBusinessLine (TOL 2008) plus location or postCode, store the page in DataPool, and drive an outreach WebApp. Do not cache the entire register through /companies; use date windows.

Credit file (purchase). Use free BIS for identity and status. For P&L and balance sheet of a specific period, either call the IXBRL open-data API when that company filed digitally, or buy the statement from Virre (one-time, per document). Keep purchased PDFs in the tenant document store; do not expect restapi-generic-1.1.0 to complete a Virre checkout.

Agent lookup. Enable executable_by_ai_agents on the BIS read integration only. Description: https://avoindata.prh.fi/opendata-ytj-api/v3/schema?lang=en. Agents can then answer “what is the Trade Register status for this Y-tunnus?” without write access.

Pricing and accounts

NeedCost modelWhere
Company search, bulk JSON of current companies, registered notificationsFree, no accountPRH open data
IXBRL digital P&L / balance sheet (~5% of filings)Free on the open-data XBRL APIXBRL schema
Other financial statements (most filings, typically PDF)One-time purchase per statementVirre financial statements search
Contractual Virre / BIS application services (representatives, PDFs of articles, higher-volume interfaces)PRH contract; setup fee and per-search prices published by PRHApplication services

Open-data prices and Virre fees are set by PRH. Treat this table as orientation. In Tealfabric you only store an integration for the HTTP APIs you call; purchased Virre documents are files you upload after checkout.

Limits to keep in mind

  • GET / receive only. Query parameters, no body.
  • BIS pages at 100 companies; notices pages at 50. Use page.
  • HTTP 429 is expected under burst traffic. Delay between pages.
  • /financial (IXBRL body) returns XML. Use a separate integration with accept application/xml (or application/xml, text/xml, */*), or stick to JSON /financials for period lists.
  • Do not call PRH from the browser. Call it from a process step or a server-side WebApp callback.
  • Attribute PRH. CC BY 4.0. No PRH/YTJ branding on your surfaces.

See also