Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Companies House Bundle Laravel Package

wearescytale/companies-house-bundle

Symfony bundle for integrating the UK Companies House API into PHP applications. Provides configuration and services to search companies and retrieve profiles, officers, and filing data, reducing boilerplate around authentication and API responses. Useful for lookup and due-diligence features.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony bundle, not a Laravel package — there is no service provider, no facade, and no config/companies_house.php to publish. Treat it as a thin object you wire up yourself.

  1. Install:
    composer require wearescytale/companies-house-bundle
    
  2. Get an API key from the Companies House Developer Hub and add it to .env:
    COMPANIES_HOUSE_KEY=your-key-here
    
    Add it to config/services.php:
    'companies_house' => ['key' => env('COMPANIES_HOUSE_KEY')],
    
  3. Look here first in vendor/wearescytale/companies-house-bundle/:
    • DependencyInjection/*Extension.php — shows the real config keys the bundle expects.
    • Resources/config/services.yml (or services.php) — tells you the service IDs and constructor arguments.
    • The client class itself — this is the only thing you actually want from the bundle.
  4. Register it manually in a service provider:
    // app/Providers/CompaniesHouseServiceProvider.php
    public function register(): void
    {
        $this->app->singleton(\WeAreScytale\CompaniesHouseBundle\CompaniesHouseClient::class, function () {
            return new \WeAreScytale\CompaniesHouseBundle\CompaniesHouseClient(
                config('services.companies_house.key')
            );
        });
    }
    
    (Verify the exact FQCN and constructor signature against vendor/ — it will differ from this sketch.)
  5. First use case: company profile lookup by number, e.g. in a controller or artisan command:
    $company = app(CompaniesHouseClient::class)->getCompany('09446231');
    

Do not assume composer require is enough. Until you've opened the bundle's DI extension, you don't know its config shape.

Implementation Patterns

Wrap it behind your own interface immediately. With 2 stars and 0 dependents, the bundle is a liability. Define an interface and adapt the bundle to it so you can swap it for raw Http::withBasicAuth() later without touching call sites:

interface CompanyRegistry
{
    public function profile(string $number): ?CompanyData;
}

Caching decorator. Companies House data changes rarely. Cache profiles for 24h and search results for a few minutes:

public function profile(string $number): ?CompanyData
{
    return Cache::remember("ch:profile:{$number}", now()->addDay(), function () use ($number) {
        return $this->inner->profile($number);
    });
}

Queued enrichment. Officers, filing history, and PSC data are separate endpoints. Fan them out into jobs with throttle middleware rather than chaining synchronous HTTP calls in a request:

public function middleware(): array
{
    return [(new ThrottlesRequests(500, 5))->by('companies-house')];
}

Validation rule for company numbers. Normalize before hitting the API — uppercase, strip whitespace. Numbers are 8 characters, optionally prefixed with jurisdiction codes (SC, NI, OC, SO, FC, NC, etc.):

'company_number' => ['required', 'string', 'regex:/^[A-Z]{0,2}\d{6,8}$/'],

Bind a fake in tests. Because the bundle's client is a concrete class, either bind your interface to a fake implementation or use Http::fake() if the bundle uses Laravel-visible Guzzle underneath (it likely uses Symfony HttpClient, so Http::fake() may not intercept it — prefer the interface).

Separate "search" from "lookup". Search costs rate-limit budget and returns unstable ranking; lookups by number are cheap and deterministic. Build UIs around lookup, and reserve search for an explicit "find company" form.

Gotchas and Tips

  • Symfony config trees don't exist in Laravel. The bundle expects config/packages/companies_house.yaml. You must either feed the client its key manually (above) or instantiate the bundle's own config object — there is no automatic bridge.
  • The API key is the Basic-auth username, with an empty password: base64($key . ':'). It is not a bearer token and not a query parameter. Most "401 Unauthorized" reports come from getting this wrong.
  • Rate limit is real: 600 requests per 5 minutes per key. Bulk-importing companies without a throttle will get you 429s, and repeated abuse can get the key suspended. Use Laravel's RateLimiter or job middleware.
  • Composer conflicts are likely. The bundle pins symfony/* component versions. If it was published against Symfony 5 and you're on Laravel 11 (Symfony 7 components), composer require may fail or force downgrades. Run composer why-not before committing.
  • 404 means "no such company", not "error". Distinguish 404 (return null / a typed NotFound result) from 401 (bad key) and 429 (throttled). Letting a 404 throw an exception makes the common path ugly.
  • Response shapes are associative arrays. Expect inconsistent field presence — e.g. registered_office_address fields are individually optional. Validate with DTOs and ?? null rather than assuming keys.
  • Debugging: log the full request URL and response status from inside a decorating wrapper; the bundle likely swallows errors into generic exceptions. If you can't see the raw body, decorate the client and dump $response->getContent().
  • Extension points: the only safe one is decoration. Register your own class that wraps the bundle's client and bind it in the container. Avoid subclassing the client — its internals are unstable and undocumented.
  • Abandonment risk is high. Low stars, zero dependents, no Laravel integration. Budget a day to write a 40-line raw client using Http::withBasicAuth($key, '') as a fallback. Honestly, for most Laravel apps that is the better first move — the bundle is only worth it if you're already in a Symfony codebase.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle