beziworld / laravel-krs
Laravel client for the Polish National Court Register (KRS) open API: company extracts, representation, shareholders, status and the daily change bulletin
Requires
- php: ^8.3
- ext-json: *
- guzzlehttp/guzzle: ^7.8
- illuminate/console: ^12.0|^13.0
- illuminate/contracts: ^12.0|^13.0
- illuminate/events: ^12.0|^13.0
- illuminate/http: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.18
- orchestra/testbench: ^10.0|^11.0
- pestphp/pest: ^3.7|^4.0
- pestphp/pest-plugin-laravel: ^3.0|^4.0
- phpstan/phpstan-strict-rules: ^2.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-04 16:52:39 UTC
README
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 aKrsEntryChangedevent ExistsInKrsvalidation rule,krs:showandkrs:watchArtisan 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.
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.
- E-mail: biuro@beziworld.eu
- Issues: GitHub issues
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.