{
  "openapi": "3.2.0",
  "$self": "https://traderegister.fi/api/openapi.json",
  "info": {
    "title": "TradeRegister.fi API",
    "summary": "Free JSON API for company search and company profiles, powered by Coragrid.",
    "description": "Three read-only endpoints mirror the traderegister.fi website: a prefix search over company names and business IDs, one company by its permanent ID, and that company's current officers with their sources. Responses use the same permanent IDs and field names as the Coragrid public API (https://api.coragrid.com/openapi.json), so moving to a Coragrid credential later needs no data migration.\n\nNo key is required. Requests are rate limited per client address; over the limit the API answers 429 with a Retry-After header, and https://traderegister.fi/api states the current allowance. Browser requests from any origin are allowed for GET. Errors are RFC 9457 problem documents with a stable code. Show \"Powered by Coragrid\" wherever you display the data.",
    "termsOfService": "https://traderegister.fi/about",
    "contact": {
      "name": "TradeRegister.fi",
      "url": "https://traderegister.fi/api"
    },
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://api.traderegister.fi",
      "name": "production",
      "description": "The free API. Every path appends to this URL."
    }
  ],
  "security": [],
  "tags": [
    {
      "name": "Companies",
      "description": "Company search, profiles and current officers"
    }
  ],
  "paths": {
    "/v1/search": {
      "get": {
        "operationId": "searchCompanies",
        "summary": "Search companies by the start of a name or a business ID",
        "description": "Matches the start of a company name or a business ID, in registry name order with no relevance score. Results carry the profile fields but no identifiers; read the company for those. Pages continue through next_cursor.",
        "tags": ["Companies"],
        "parameters": [
          { "$ref": "#/components/parameters/Query" },
          { "$ref": "#/components/parameters/AddressCountry" },
          { "$ref": "#/components/parameters/Jurisdiction" },
          { "$ref": "#/components/parameters/Limit" },
          { "$ref": "#/components/parameters/Cursor" }
        ],
        "responses": {
          "200": {
            "description": "One page of matching companies.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/SearchPage" },
                "example": {
                  "object": "list",
                  "query": "example",
                  "address_countries": ["FI"],
                  "data": [
                    {
                      "id": "co_014d2pf2dbsqqg28t5cy4tqkff",
                      "object": "company",
                      "lifecycle": { "status": "active" },
                      "name": "Example Robotics Oy",
                      "country_code": "FI",
                      "company_status": "active",
                      "company_type": "private_limited_company"
                    }
                  ],
                  "next_cursor": null,
                  "attribution": "Powered by Coragrid"
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/InvalidRequest" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "502": { "$ref": "#/components/responses/UpstreamError" },
          "503": { "$ref": "#/components/responses/UpstreamUnavailable" }
        }
      }
    },
    "/v1/companies/{company_id}": {
      "get": {
        "operationId": "getCompany",
        "summary": "One company by permanent ID",
        "description": "Redirected records return the requested handle with the canonical ID in lifecycle.canonical_id; split records list lifecycle.successor_ids. Unknown and restricted values are absent, never filled in.",
        "tags": ["Companies"],
        "parameters": [{ "$ref": "#/components/parameters/CompanyId" }],
        "responses": {
          "200": {
            "description": "The company.",
            "headers": {
              "Cache-Control": {
                "description": "Always no-store: reading the company may queue a register check, so a stored answer would go stale.",
                "schema": { "type": "string", "const": "no-store" }
              },
              "Retry-After": {
                "description": "Only while register_check.status is pending: seconds to wait before reading the company again for the check's result.",
                "schema": { "type": "integer", "minimum": 1 }
              }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/CompanyEnvelope" },
                "example": {
                  "data": {
                    "id": "co_014d2pf2dbsqqg28t5cy4tqkff",
                    "object": "company",
                    "lifecycle": { "status": "active" },
                    "name": "Example Robotics Oy",
                    "summary": "Finnish industrial robotics for flexible manufacturing cells.",
                    "country_code": "FI",
                    "company_status": "active",
                    "company_type": "private_limited_company",
                    "legal_form": "Osakeyhtiö",
                    "branch": false,
                    "as_of_date": "2026-07-15",
                    "identifiers": [
                      {
                        "scheme": "national_registry_number",
                        "value": "1234567-8",
                        "country_scope": "FI"
                      }
                    ],
                    "register_check": {
                      "status": "completed",
                      "outcome": "verified",
                      "checked_at": "2026-07-15T08:30:00Z"
                    }
                  },
                  "attribution": "Powered by Coragrid"
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/InvalidRequest" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "502": { "$ref": "#/components/responses/UpstreamError" },
          "503": { "$ref": "#/components/responses/UpstreamUnavailable" }
        }
      }
    },
    "/v1/companies/{company_id}/officers": {
      "get": {
        "operationId": "listCompanyOfficers",
        "summary": "Current officers of one company, with their sources",
        "description": "Each officer names the publishing system, its URL when known and the observation time. A redirected company ID resolves to its canonical record; both IDs are returned.",
        "tags": ["Companies"],
        "parameters": [{ "$ref": "#/components/parameters/CompanyId" }],
        "responses": {
          "200": {
            "description": "The current officers.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/OfficerPage" },
                "example": {
                  "object": "company_officer_list",
                  "requested_company_id": "co_014d2pf2dbsqqg28t5cy4tqkff",
                  "company_id": "co_014d2pf2dbsqqg28t5cy4tqkff",
                  "data": [
                    {
                      "object": "company_officer",
                      "name": "Anna Virtanen",
                      "roles": ["Chief executive officer"],
                      "appointed_on": "2024-01-15",
                      "source": {
                        "system": "official_company_register",
                        "url": "https://registry.example/officers/anna-virtanen",
                        "title": "Current officers",
                        "published_at": "2026-07-14T09:30:00Z",
                        "observed_at": "2026-07-15T07:00:00Z"
                      }
                    }
                  ],
                  "has_more": false,
                  "attribution": "Powered by Coragrid"
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/InvalidRequest" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "502": { "$ref": "#/components/responses/UpstreamError" },
          "503": { "$ref": "#/components/responses/UpstreamUnavailable" }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "Query": {
        "name": "q",
        "in": "query",
        "required": true,
        "description": "The start of a company name or a business ID, 3 to 64 characters once whitespace is collapsed.",
        "schema": {
          "type": "string",
          "minLength": 3,
          "maxLength": 64,
          "example": "example"
        }
      },
      "AddressCountry": {
        "name": "address_country",
        "in": "query",
        "description": "ISO 3166-1 alpha-2 address country, such as FI, in either case, matched against country_code. Omit it or pass all for every country.",
        "schema": {
          "type": "string",
          "pattern": "^([A-Za-z]{2}|[Aa][Ll][Ll])$",
          "example": "FI"
        }
      },
      "Jurisdiction": {
        "name": "jurisdiction",
        "in": "query",
        "description": "Registration jurisdiction, ISO 3166-1 alpha-2 or ISO 3166-2 such as CA or CA-QC, matched against the registry identifiers; a country code also matches its subdivisions. Omit or pass all for every jurisdiction.",
        "schema": {
          "type": "string",
          "pattern": "^([A-Za-z]{2}(-[A-Za-z0-9]{1,3})?|[Aa][Ll][Ll])$",
          "example": "CA"
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "description": "Results per page.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 25,
          "default": 10
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "description": "The next_cursor value of the previous page. Cursors expire; on 400 start again from the first page.",
        "schema": { "type": "string" }
      },
      "CompanyId": {
        "name": "company_id",
        "in": "path",
        "required": true,
        "description": "A permanent Coragrid company ID.",
        "schema": { "$ref": "#/components/schemas/CompanyId" }
      }
    },
    "headers": {
      "RetryAfter": {
        "description": "Seconds to wait before retrying.",
        "schema": { "type": "integer", "minimum": 1 }
      }
    },
    "responses": {
      "InvalidRequest": {
        "description": "A parameter is missing or malformed, or a cursor has expired; detail names it.",
        "content": {
          "application/problem+json": {
            "schema": { "$ref": "#/components/schemas/Problem" },
            "example": {
              "type": "https://traderegister.fi/problems/invalid-request",
              "title": "Invalid request",
              "status": 400,
              "detail": "Type at least 3 characters of a company name or a business ID.",
              "instance": "https://api.traderegister.fi/v1/search",
              "request_id": "req_014d2pf2dbsqqg28t5cy4tqkff",
              "code": "invalid_request",
              "retryable": false
            }
          }
        }
      },
      "NotFound": {
        "description": "No company is visible under that ID.",
        "content": {
          "application/problem+json": {
            "schema": { "$ref": "#/components/schemas/Problem" },
            "example": {
              "type": "https://traderegister.fi/problems/not-found",
              "title": "Resource not found",
              "status": 404,
              "detail": "No company is visible under that ID.",
              "instance": "https://api.traderegister.fi/v1/companies/co_00000000000000000000000009",
              "request_id": "req_014d2pf2dbsqqg28t5cy4tqkff",
              "code": "not_found",
              "retryable": false
            }
          }
        }
      },
      "RateLimited": {
        "description": "Over the per-client rate limit; retry after the number of seconds in Retry-After.",
        "headers": {
          "Retry-After": { "$ref": "#/components/headers/RetryAfter" }
        },
        "content": {
          "application/problem+json": {
            "schema": { "$ref": "#/components/schemas/Problem" },
            "example": {
              "type": "https://traderegister.fi/problems/rate-limited",
              "title": "Too many requests",
              "status": 429,
              "detail": "The free API allows a bounded number of requests per minute per client; retry after 1 seconds.",
              "instance": "https://api.traderegister.fi/v1/search",
              "request_id": "req_014d2pf2dbsqqg28t5cy4tqkff",
              "code": "rate_limited",
              "retryable": true
            }
          }
        }
      },
      "UpstreamError": {
        "description": "The company data service rejected the request; retryable.",
        "content": {
          "application/problem+json": {
            "schema": { "$ref": "#/components/schemas/Problem" },
            "example": {
              "type": "https://traderegister.fi/problems/upstream-error",
              "title": "Upstream data service error",
              "status": 502,
              "detail": "The company data service rejected the request.",
              "instance": "https://api.traderegister.fi/v1/search",
              "request_id": "req_014d2pf2dbsqqg28t5cy4tqkff",
              "code": "upstream_error",
              "retryable": true
            }
          }
        }
      },
      "UpstreamUnavailable": {
        "description": "Company data is temporarily unavailable (upstream_unavailable) or the company data service is busy (upstream_busy, with Retry-After); retryable.",
        "headers": {
          "Retry-After": { "$ref": "#/components/headers/RetryAfter" }
        },
        "content": {
          "application/problem+json": {
            "schema": { "$ref": "#/components/schemas/Problem" },
            "example": {
              "type": "https://traderegister.fi/problems/upstream-busy",
              "title": "Company data temporarily unavailable",
              "status": 503,
              "detail": "The company data service is busy; retry shortly.",
              "instance": "https://api.traderegister.fi/v1/search",
              "request_id": "req_014d2pf2dbsqqg28t5cy4tqkff",
              "code": "upstream_busy",
              "retryable": true
            }
          }
        }
      }
    },
    "schemas": {
      "CompanyId": {
        "type": "string",
        "description": "A permanent Coragrid company ID. Names and registry numbers change and repeat; the ID does not.",
        "pattern": "^co_[0-7][0-9a-hj-km-np-tv-z]{25}$",
        "example": "co_014d2pf2dbsqqg28t5cy4tqkff"
      },
      "CompanyLifecycle": {
        "type": "object",
        "description": "Whether the record is current. A redirected record names its canonical record; a split record names its successors.",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "active",
              "redirected",
              "split",
              "tombstoned",
              "restricted"
            ]
          },
          "canonical_id": {
            "$ref": "#/components/schemas/CompanyId",
            "description": "The current record of a redirected one."
          },
          "successor_ids": {
            "type": "array",
            "description": "The records a split one continues as.",
            "items": { "$ref": "#/components/schemas/CompanyId" }
          }
        },
        "required": ["status"]
      },
      "CompanyIdentifier": {
        "type": "object",
        "properties": {
          "scheme": {
            "type": "string",
            "description": "Canonical scheme as the API returns it: national_registry_number, lei, eu_vat_number, vat_number or tin. A Finnish business ID is national_registry_number scoped to FI.",
            "example": "national_registry_number"
          },
          "value": {
            "type": "string",
            "description": "The identifier as the register publishes it.",
            "example": "1234567-8"
          },
          "country_scope": {
            "type": "string",
            "description": "Registration jurisdiction that scopes the value, ISO 3166-1 alpha-2 or ISO 3166-2. Absent for globally unique schemes such as lei.",
            "example": "FI"
          },
          "role": {
            "type": "string",
            "enum": ["root", "alias"],
            "description": "The registration's role: root for the number the company record was created from, exactly one per company; alias for every other registration. Absent, like the other registration fields, when no registration is recorded."
          },
          "status": {
            "type": "string",
            "enum": ["active", "ended", "suspended", "unknown"],
            "description": "The registration's status, mapped from the authority's own word. An ended registration is a former number, never a current one; a suspended one is still current."
          },
          "valid_from": {
            "type": "string",
            "pattern": "^[0-9]{4}(-[0-9]{2}(-[0-9]{2})?)?$",
            "description": "The registration date as the source states it, at the source's precision: YYYY, YYYY-MM or YYYY-MM-DD. Absent when the source states none; never an observation time.",
            "example": "2016-04"
          },
          "valid_to": {
            "type": "string",
            "pattern": "^[0-9]{4}(-[0-9]{2}(-[0-9]{2})?)?$",
            "description": "The deregistration date, likewise.",
            "example": "2019"
          },
          "source": {
            "type": "string",
            "description": "The source system that stated the registration."
          }
        },
        "required": ["scheme", "value"]
      },
      "CompanyClassification": {
        "type": "object",
        "description": "A current activity classification in its source scheme and version. Codes preserve source punctuation and leading zeros; no crosswalk is inferred.",
        "properties": {
          "scheme": { "type": "string", "example": "nace_2008" },
          "code": { "type": "string", "example": "28.99" },
          "label": { "type": "string" },
          "is_primary": { "type": "boolean" }
        },
        "required": ["scheme", "code", "is_primary"]
      },
      "Company": {
        "type": "object",
        "description": "A company as the registers describe it. Fields the registers do not publish are absent.",
        "properties": {
          "id": { "$ref": "#/components/schemas/CompanyId" },
          "object": { "type": "string", "const": "company" },
          "lifecycle": { "$ref": "#/components/schemas/CompanyLifecycle" },
          "name": { "type": "string" },
          "summary": { "type": "string" },
          "country_code": {
            "type": "string",
            "description": "Address country, ISO 3166-1 alpha-2. The registration jurisdiction is the country_scope of the first scoped entry in identifiers; the two can differ.",
            "example": "FI"
          },
          "company_status": { "type": "string", "example": "active" },
          "company_type": {
            "type": "string",
            "example": "private_limited_company"
          },
          "legal_form": {
            "type": "string",
            "description": "The legal form exactly as the register publishes it, in the register's language, such as Osakeyhtiö or Canada Business Corporations Act; a few registers publish a code. company_type is the register's own type code or label, so its values differ per register.",
            "example": "Osakeyhtiö"
          },
          "branch": {
            "type": "boolean",
            "description": "The register's branch classification as it states it: true when the register classifies the record as a branch, false when it classifies it as not a branch, absent when the register's form is not classified. Each register defines a branch its own way; the flag is not normalized."
          },
          "industry": { "type": "string" },
          "business_model": { "type": "string" },
          "product_category": { "type": "string" },
          "operating_geography": { "type": "string" },
          "as_of_date": {
            "type": "string",
            "format": "date",
            "description": "When the register was last observed."
          },
          "classifications": {
            "type": "array",
            "description": "Current activity classifications, primary first, then scheme and code. Absent when none are available. Schemes are not limited to a fixed list.",
            "items": { "$ref": "#/components/schemas/CompanyClassification" }
          },
          "identifiers": {
            "type": "array",
            "description": "The registry identifiers the sources list, strongest first: national registry number, LEI, EU VAT number, VAT number, TIN, then every other registration of the company, current ones first: a former number, a number carried over by a merge, a host-jurisdiction registration. The first entry with a country_scope names the registration jurisdiction. An entry whose status is ended is a former number, never a current one. On the company resource only, never on search results.",
            "items": { "$ref": "#/components/schemas/CompanyIdentifier" }
          },
          "register_check": { "$ref": "#/components/schemas/RegisterCheck" }
        },
        "required": ["id", "object", "lifecycle"]
      },
      "RegisterCheck": {
        "type": "object",
        "description": "The check of the company with its official register. On the company resource only, never on search results. Reading the company may queue one check per company and result lifetime and is safe to repeat; while the check is pending, read the company again for its result.",
        "properties": {
          "status": {
            "type": "string",
            "description": "pending: queued, running or waiting on the register's rate limit. completed: the register answered. failed: the last check got no answer from the register. not_verifiable: the record is not active or its register cannot be checked from here. unavailable: the check could not be read in time.",
            "enum": [
              "pending",
              "completed",
              "failed",
              "not_verifiable",
              "unavailable"
            ]
          },
          "outcome": {
            "type": ["string", "null"],
            "description": "verified or not_found once completed, unavailable once failed, otherwise null.",
            "enum": ["verified", "not_found", "unavailable", null]
          },
          "checked_at": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "When the register answered or the check was given up, UTC; null for the other statuses."
          }
        },
        "required": ["status", "outcome", "checked_at"]
      },
      "SearchPage": {
        "type": "object",
        "properties": {
          "object": { "type": "string", "const": "list" },
          "query": {
            "type": "string",
            "description": "The query as searched, with whitespace collapsed."
          },
          "address_countries": {
            "type": "array",
            "description": "The address_country filter in effect; empty when every country was searched.",
            "items": { "type": "string", "example": "FI" }
          },
          "jurisdiction": {
            "type": "string",
            "description": "The jurisdiction filter in effect; absent when every jurisdiction was searched.",
            "example": "CA"
          },
          "data": {
            "type": "array",
            "description": "Matching companies in registry name order, without identifiers or relevance scores.",
            "items": { "$ref": "#/components/schemas/Company" }
          },
          "next_cursor": {
            "type": ["string", "null"],
            "description": "Pass as cursor for the next page; null on the last page."
          },
          "attribution": {
            "type": "string",
            "const": "Powered by Coragrid"
          }
        },
        "required": [
          "object",
          "query",
          "address_countries",
          "data",
          "next_cursor",
          "attribution"
        ]
      },
      "CompanyEnvelope": {
        "type": "object",
        "properties": {
          "data": { "$ref": "#/components/schemas/Company" },
          "attribution": {
            "type": "string",
            "const": "Powered by Coragrid"
          }
        },
        "required": ["data", "attribution"]
      },
      "OfficerSource": {
        "type": "object",
        "description": "Where the officer record was published and when it was observed.",
        "properties": {
          "system": {
            "type": "string",
            "description": "The publishing system.",
            "example": "official_company_register"
          },
          "url": { "type": "string", "format": "uri" },
          "title": { "type": "string" },
          "published_at": { "type": "string", "format": "date-time" },
          "observed_at": { "type": "string", "format": "date-time" }
        },
        "required": ["system"]
      },
      "Officer": {
        "type": "object",
        "properties": {
          "object": { "type": "string", "const": "company_officer" },
          "name": { "type": "string" },
          "roles": {
            "type": "array",
            "description": "Role titles as the register states them; absent when it states none.",
            "items": { "type": "string" }
          },
          "appointed_on": { "type": "string", "format": "date" },
          "resigned_on": { "type": "string", "format": "date" },
          "source": { "$ref": "#/components/schemas/OfficerSource" }
        },
        "required": ["object", "name", "source"]
      },
      "OfficerPage": {
        "type": "object",
        "properties": {
          "object": { "type": "string", "const": "company_officer_list" },
          "requested_company_id": {
            "$ref": "#/components/schemas/CompanyId",
            "description": "The ID in the request, kept when it redirects to another."
          },
          "company_id": {
            "$ref": "#/components/schemas/CompanyId",
            "description": "The canonical company whose officers these are."
          },
          "data": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/Officer" }
          },
          "has_more": {
            "type": "boolean",
            "description": "Whether the register lists more current officers than returned."
          },
          "attribution": {
            "type": "string",
            "const": "Powered by Coragrid"
          }
        },
        "required": [
          "object",
          "requested_company_id",
          "company_id",
          "data",
          "has_more",
          "attribution"
        ]
      },
      "Problem": {
        "type": "object",
        "description": "An RFC 9457 problem document with the same fields as Coragrid's. Request IDs are the site's own.",
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "description": "https://traderegister.fi/problems/ followed by the code with hyphens."
          },
          "title": { "type": "string" },
          "status": { "type": "integer", "minimum": 400, "maximum": 599 },
          "detail": { "type": "string" },
          "instance": {
            "type": "string",
            "format": "uri",
            "description": "The absolute public URL of the request."
          },
          "request_id": {
            "type": "string",
            "pattern": "^req_[0-7][0-9a-hj-km-np-tv-z]{25}$"
          },
          "code": {
            "type": "string",
            "enum": [
              "invalid_request",
              "not_found",
              "rate_limited",
              "upstream_error",
              "upstream_busy",
              "upstream_unavailable"
            ]
          },
          "retryable": {
            "type": "boolean",
            "description": "Whether the same request may succeed later."
          }
        },
        "required": [
          "type",
          "title",
          "status",
          "detail",
          "instance",
          "request_id",
          "code",
          "retryable"
        ]
      }
    }
  }
}
