Search by

beziworld / laravel-krs

kamcw

Laravel client for the Polish National Court Register (KRS) open API: company extracts, representation, shareholders, status and the daily change bulletin

Package info

github.com/Beziworld/laravel-krs

Homepage

Issues

pkg:composer/beziworld/laravel-krs

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

v1.0.0 2026-11-04 16:49 UTC

This package is auto-updated.

Last update: 2026-11-04 16:52:39 UTC


README

tests Latest Version License

Laravel client for the open API of the Polish National Court Register (Krajowy Rejestr Sądowy, KRS), published by the Ministry of Justice. Read company data straight from the register:

  • current extract (odpis aktualny) and full extract with the history of entries (odpis pełny)
  • name, legal form, NIP, REGON, address, seat, registration date, share capital, PKD activities
  • management board with functions and the method of representation, supervisory board, procurators
  • shareholders and partners with their shares, limited partners, sole shareholder
  • status: liquidation, bankruptcy, restructuring, suspended activity, removal from the register
  • the daily and hourly change bulletin as a LazyCollection, a watcher with a KrsEntryChanged event
  • ExistsInKrs validation rule, krs:show and krs:watch Artisan commands
  • automatic register detection (entrepreneurs P, then associations S), caching, retries of read-only calls

No API key, no registration and no contract are needed.

Polska wersja dokumentacji

Installation

composer require beziworld/laravel-krs
php artisan vendor:publish --tag=krs-config   # optional

Everything works without configuration. Optional .env settings:

KRS_CACHE_TTL=86400    # seconds; 0 disables caching
KRS_CACHE_STORE=       # cache store, empty = default store
KRS_RETRIES=2          # retries after connection errors and HTTP 5xx
KRS_TIMEOUT=30
KRS_WATCH=0000635012,0000019193   # numbers checked by krs:watch
KRS_URL=https://api-krs.ms.gov.pl/

Company data

use Beziworld\Krs\Facades\Krs;

$company = Krs::current('635012');   // leading zeros optional, "KRS 0000635012" works too

$company->krs;                 // "0000635012"
$company->register;            // Register::Entrepreneurs (P) or Register::Associations (S)
$company->name;                // "ALLEGRO SPÓŁKA Z OGRANICZONĄ ODPOWIEDZIALNOŚCIĄ"
$company->legalForm;           // "SPÓŁKA Z OGRANICZONĄ ODPOWIEDZIALNOŚCIĄ"
$company->nip;                 // "5252674798"
$company->regon;               // "365331553" (14-digit REGON padded with zeros is shortened)
$company->registeredAt;        // CarbonImmutable 2016-09-02
$company->address;             // Address: street, houseNumber, flatNumber, postalCode, city...
(string) $company->address;    // "UL. WIERZBIĘCICE 1B, 61-569 POZNAŃ"
$company->seat->voivodeship;   // "WIELKOPOLSKIE"
$company->shareCapital;        // Money: amount "40000000.00" (decimal string), currency "PLN"
$company->mainActivity;        // Pkd: code "63.12.Z", description
$company->pkdCodes();          // ["63.12.Z", "63.11.Z", "62.09.Z", "73.1", ...]
$company->eDeliveryAddress;    // "AE:PL-82834-96805-BTBTU-28"
$company->raw;                 // decoded API response, for fields not mapped above

Krs::current() searches the register of entrepreneurs first and then the register of associations, foundations and other organisations. Pass the register to make a single request: Krs::current('30897', 'S') or Krs::current('30897', Register::Associations). Krs::find() returns null instead of throwing NotFound.

Values are returned exactly as published by the register, in upper case.

Representation and procurators

$company->representation->body;     // "ZARZĄD"
$company->representationMethod();   // "DO SKŁADANIA OŚWIADCZEŃ W IMIENIU SPÓŁKI WYMAGANE JEST WSPÓŁDZIAŁANIE DWÓCH ..."

foreach ($company->representatives() as $member) {
    $member->name();       // "M***** P**** K*******"
    $member->role;         // "PREZES ZARZĄDU"
    $member->suspended;    // false
    $member->isMasked();   // true
}

$company->supervisoryBodies;        // list<Representation>, e.g. "RADA NADZORCZA"
$company->procurators[0]->role;     // "PROKURA ŁĄCZNA Z DRUGIM PROKURENTEM ALBO CZŁONKIEM ZARZĄDU"

The open API pseudonymises natural persons: names and PESEL numbers come with asterisks, for example J**** K*****. Legal persons (corporate board members, shareholders, trustees) are published in full with their KRS and REGON numbers.

Shareholders and partners

foreach ($company->shareholders as $shareholder) {
    $shareholder->name();              // "ALLEGRO.EU SA"
    $shareholder->shares;              // "800.000 UDZIAŁÓW O ŁĄCZNEJ WARTOŚCI 40.000.000 ZŁ"
    $shareholder->shareCount();        // 800000
    $shareholder->ownsAllShares;       // true
    $shareholder->party->krs;          // KRS of a corporate shareholder, when published
    $shareholder->limitedPartner;      // limited partnerships: true for a komandytariusz
    $shareholder->limitedLiabilitySum; // "2000,00 PLN"
}

Shareholders of a limited liability company, the sole shareholder of a joint-stock company and the partners of a general, limited or professional partnership are all returned as Shareholder.

Status

$company->isActive();             // not removed, not in liquidation, not bankrupt, activity not suspended
$company->isInLiquidation();      // $company->liquidation: opening, representationMethod, liquidators
$company->isBankrupt();           // $company->bankruptcy: court, caseNumber, declaredOn, trustees
$company->isInRestructuring();    // $company->restructuring: "PRZYMUSOWA RESTRUKTURYZACJA"
$company->isSuspended();          // $company->suspendedSince
$company->dissolution;            // "ROZWIĄZANIE"
$company->publicBenefit;          // public benefit organisation (OPP)

Full extract and removed entities

$extract = Krs::full('106150');

$extract->entries;                  // list<RegistryEntry>: number, description, date, caseNumber, court, finalOn
$extract->lastEntry()->description; // "WYKREŚLENIE Z KRAJOWEGO REJESTRU SĄDOWEGO"
$extract->isDeleted();              // true
$extract->company;                  // state in force, or the last state before removal
$extract->company->deletedAt;       // 2022-08-12

For an entity removed from the register the API answers the current extract with HTTP 204, so Krs::current() throws DeletedEntity; the full extract still returns its history.

Change bulletin and watching counterparties

// Entities with entries made on a day: unique ten-digit numbers, fetched lazily
Krs::changedOn('2026-11-02')->take(10)->all();

// Hourly bulletin for 10:00-11:00 (Polish time)
Krs::changedOn(now(), fromHour: 10, toHour: 11);

// Several days, one request per day while iterating
Krs::changedBetween('2026-11-01', '2026-11-07')->each(fn (string $krs) => ...);

The watcher compares a list of numbers with the bulletin, forgets their cached extracts and dispatches Beziworld\Krs\Events\KrsEntryChanged for each changed entity. One request covers any number of watched entities.

use Beziworld\Krs\Events\KrsEntryChanged;
use Beziworld\Krs\Facades\Krs;
use Beziworld\Krs\KrsWatcher;

// AppServiceProvider::boot(): watch the counterparties stored in the database
KrsWatcher::watchUsing(fn () => Contractor::whereNotNull('krs')->pluck('krs'));

Event::listen(function (KrsEntryChanged $event): void {
    $company = Krs::current($event->krs);   // fresh data, the cache was forgotten
    // update the contractor, notify the account manager...
});
// routes/console.php
Schedule::command('krs:watch')->dailyAt('06:00');               // yesterday's bulletin
Schedule::command('krs:watch --previous-hour')->hourlyAt(5);    // near real time

Validation

use Beziworld\Krs\Enums\Register;
use Beziworld\Krs\Rules\ExistsInKrs;

$request->validate([
    'krs' => ['required', new ExistsInKrs()],                         // entered and not removed
    'supplier_krs' => ['required', (new ExistsInKrs())->active()],    // also not in liquidation or bankruptcy
    'foundation_krs' => ['required', (new ExistsInKrs())->in(Register::Associations)->strict()],
]);

ExistsInKrs passes when the API cannot be reached, so an outage does not block your forms; strict() fails the validation instead. For a format-only check without an HTTP call use the krs rule of laravel-polish-validation.

Artisan

php artisan krs:show 635012
php artisan krs:show 30897 --register=S
php artisan krs:show 106150 --full --entries=5
php artisan krs:watch 635012 19193 --date=2026-11-02
php artisan krs:watch --previous-hour

Caching and limits

Extracts and bulletins of past days are cached for KRS_CACHE_TTL seconds (one day by default). The bulletin of the current day is never cached because it grows during the day, and krs:watch forgets cached extracts of changed entities, so a long TTL stays accurate when the watcher runs. Krs::forget($krs) clears one entity by hand.

The API documents no rate limit and returns no rate-limit headers; be gentle with bulk lookups and rely on the cache. Connection failures and HTTP 5xx answers are retried KRS_RETRIES times.

Errors

Situation Result
No entity with this number in the searched register(s) (HTTP 404) NotFound (krs, registers); find() returns null
Entity removed from the register (HTTP 204 for the current extract) DeletedEntity; use full()
Malformed KRS number InvalidKrsNumber, before any request
HTTP 400, 5xx after retries, invalid JSON ApiException (httpStatus, path, isServerError(), isRateLimited())
Connection failure or timeout after retries ApiException wrapping the ConnectionException
Invalid KRS_URL InvalidConfiguration
Unknown register, invalid bulletin day or hours InvalidArgumentException

All package exceptions extend Beziworld\Krs\Exceptions\KrsException.

Related packages

  • laravel-company-data: GUS REGON (BIR), VAT white list and VIES. Use it for sole traders (CEIDG) that are not in KRS, for the VAT status and bank accounts of a NIP.
  • laravel-polish-validation: format validation of KRS, NIP, REGON, PESEL and more.

Development

composer install
composer ci   # pint --test, phpstan (level 8), pest

Tests use responses captured from the live API (tests/Fixtures); no request leaves the test suite.

Author

Created and maintained by Kamil Ćwiertnia, beziworld.eu.

Need an integration with a Polish public register or business API? Get in touch through beziworld.eu.

Other packages by the author

laravel-company-data · laravel-polish-validation · laravel-uldk · laravel-nbp

License

MIT. See LICENSE.md. The data comes from the open API of the National Court Register published by the Polish Ministry of Justice (Ministerstwo Sprawiedliwości). This package is not affiliated with or endorsed by the Ministry of Justice.