PHPackages                             scrappy-hu/laravel - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. scrappy-hu/laravel

ActiveLibrary[API Development](/categories/api)

scrappy-hu/laravel
==================

Official Scrappy SDK for Laravel — submit scrape jobs and verify webhooks against api.scrappy.hu.

v0.1.1(2mo ago)03MITPHPPHP ^8.1

Since May 7Pushed 2mo agoCompare

[ Source](https://github.com/scrappy-hu/laravel)[ Packagist](https://packagist.org/packages/scrappy-hu/laravel)[ Docs](https://scrappy.hu)[ RSS](/packages/scrappy-hu-laravel/feed)WikiDiscussions main Synced 3w ago

READMEChangelogDependencies (5)Versions (3)Used By (0)

Scrappy SDK for Laravel
=======================

[](#scrappy-sdk-for-laravel)

Submit web-scraping jobs and verify webhooks against [api.scrappy.hu](https://scrappy.hu) from any Laravel 9 / 10 / 11 / 12 app.

Requires PHP 8.1+ (the SDK uses `readonly` properties for typed response objects, which Laravel 9 + PHP 8.0 doesn't support — bump PHP to 8.1+ and you're fine on any Laravel 9.x).

```
composer require scrappy-hu/laravel
```

Installation
------------

[](#installation)

```
composer require scrappy-hu/laravel
php artisan vendor:publish --tag=scrappy-config
```

Then in `.env`:

```
SCRAPPY_API_KEY=sk_live_...

```

Generate the key from .

That's it — the package's `ScrappyServiceProvider` is auto-registered via Laravel's package discovery, and the `Scrappy` facade is wired up.

Submit a job
------------

[](#submit-a-job)

```
use Scrappy\Facades\Scrappy;

$job = Scrappy::jobs()->create([
    'url' => 'https://example.com/products/widget',
    'options' => [
        'extract' => ['title', 'description', 'tables'],
    ],
    'webhook_url' => route('scrappy.webhook'),
    'metadata' => ['order_id' => $order->id],
]);

// store $job->webhookSecret somewhere keyed by $job->id —
// you'll need it to verify the inbound webhook.
DB::table('scrappy_jobs')->insert([
    'id' => $job->id,
    'order_id' => $order->id,
    'webhook_secret' => $job->webhookSecret,
    'created_at' => now(),
]);

return ['job_id' => $job->id, 'status' => $job->status];
```

Read a job
----------

[](#read-a-job)

```
$job = Scrappy::jobs()->get($jobId);

if ($job->status === 'completed') {
    $title = $job->result?->title;
    $html = $job->result?->html;
}
```

Verify a webhook
----------------

[](#verify-a-webhook)

```
use Illuminate\Http\Request;
use Scrappy\Facades\Scrappy;

Route::post('/scrappy/webhook', function (Request $request) {
    $jobId = $request->header('X-Scrappy-Job-Id');
    $stored = DB::table('scrappy_jobs')->where('id', $jobId)->first();
    if (! $stored) {
        abort(404);
    }

    $verified = Scrappy::webhooks()->verify(
        rawBody: $request->getContent(),
        header: $request->header('X-Scrappy-Signature'),
        secret: $stored->webhook_secret,
    );
    if (! $verified) {
        abort(401, 'Invalid signature');
    }

    $event = $request->json()->all();
    if ($event['event'] === 'job.completed') {
        // … process the result
    }

    return response()->noContent();
});
```

**Critical**: pass the **raw** request body (`$request->getContent()`). Re-serialising via `json_encode($request->json())` reorders / spaces keys differently and breaks the HMAC.

Test your webhook receiver
--------------------------

[](#test-your-webhook-receiver)

Before going live, fire a test event from the SDK to verify your endpoint is reachable + signature verification works end-to-end:

```
$result = Scrappy::webhooks()->test('https://your-app.example.com/scrappy/webhook');
// $result['delivered'] === true
// $result['response_status'] === 200
```

Account snapshot
----------------

[](#account-snapshot)

```
$snap = Scrappy::me()->get();

echo $snap->planName();             // 'Pro'
echo $snap->monthlyUsed();          // 1234
echo $snap->monthlyRemaining();     // 8766
```

Errors
------

[](#errors)

Every non-2xx response throws a typed exception you can pattern-match:

```
use Scrappy\Exceptions\{
    ScrappyException,
    AuthenticationException,
    RateLimitException,
    QuotaExceededException,
    ValidationException,
    NotFoundException,
};

try {
    Scrappy::jobs()->create(['url' => 'https://example.com']);
} catch (RateLimitException $e) {
    sleep($e->retryAfterSeconds());
    // retry…
} catch (QuotaExceededException $e) {
    // surface upgrade CTA to the user
    return redirect($e->upgradeUrl());
} catch (ValidationException $e) {
    foreach ($e->fieldErrors() as $field => $errors) {
        // render errors next to the form field
    }
} catch (AuthenticationException) {
    abort(500, 'Scrappy api key is missing or invalid');
} catch (ScrappyException $e) {
    Log::error('scrappy', [
        'code' => $e->errorCode(),
        'status' => $e->statusCode(),
        'payload' => $e->payload(),
    ]);
    throw $e;
}
```

Configuration
-------------

[](#configuration)

`config/scrappy.php` (after `vendor:publish`):

KeyEnv varDefaultNotes`api_key``SCRAPPY_API_KEY`—Required.`timeout``SCRAPPY_TIMEOUT``30`Per-call HTTP timeout (seconds).`webhook_secret``SCRAPPY_WEBHOOK_SECRET`—Optional default secret for `verify()`.`replay_window_seconds`—`300`Reject signatures older than this.The base URL is intentionally not configurable from Laravel — every Laravel-driven instance points at `https://api.scrappy.hu`. Plain-PHP users can still override it via the `Scrappy` constructor for tests or unusual self-hosted setups.

Plain PHP usage
---------------

[](#plain-php-usage)

The package works outside Laravel too — instantiate the client directly:

```
$scrappy = new \Scrappy\Scrappy(
    apiKey: getenv('SCRAPPY_API_KEY'),
);
$job = $scrappy->jobs()->create(['url' => 'https://example.com']);
```

Testing your own code
---------------------

[](#testing-your-own-code)

The SDK throws on api errors instead of returning bad data, which makes mocking straightforward:

```
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;

$handler = new MockHandler([
    new Response(201, [], json_encode([
        'job_id' => 'test-id',
        'status' => 'queued',
        'created_at' => '2026-05-08T10:00:00Z',
    ])),
]);
$scrappy = new \Scrappy\Scrappy('sk_live_test', 'https://api.scrappy.hu');
// (For full mocking inject a Guzzle client into Scrappy\Http\Client — see tests/.)
```

API reference
-------------

[](#api-reference)

Full reference + interactive API explorer:

-  — long-form reference
-  — interactive (Scalar)
-  — OpenAPI 3.1 spec (machine-readable)

Versioning
----------

[](#versioning)

This SDK follows semver. Breaking changes go in major versions; new methods + bug fixes go in minors / patches. Tracked at .

License
-------

[](#license)

[MIT](LICENSE).

###  Health Score

33

—

LowBetter than 72% of packages

Maintenance85

Actively maintained with recent releases

Popularity3

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity34

Early-stage or recently created project

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

78d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/4653410?v=4)[Kevin Krizsánszki](/maintainers/hletick)[@hletick](https://github.com/hletick)

---

Tags

apilaravelwebhooksscrapingplaywrightscrappy

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/scrappy-hu-laravel/health.svg)

```
[![Health](https://phpackages.com/badges/scrappy-hu-laravel/health.svg)](https://phpackages.com/packages/scrappy-hu-laravel)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3345.3M347](/packages/psalm-plugin-laravel)[simplestats-io/laravel-client

Server-side analytics for Laravel that follows the full funnel from visit to registration to payment, attributed to the channel that drove it. Revenue, MRR, churn and ad-spend profit (ROAS/CAC) per channel. GDPR compliant, ad-blocker proof.

5022.6k](/packages/simplestats-io-laravel-client)[defstudio/telegraph

A laravel facade to interact with Telegram Bots

813336.8k3](/packages/defstudio-telegraph)[spatie/laravel-export

Create a static site bundle from a Laravel app

674146.0k6](/packages/spatie-laravel-export)[api-platform/laravel

API Platform support for Laravel

58174.6k17](/packages/api-platform-laravel)[eslazarev/wildberries-sdk

Wildberries OpenAPI clients (generated).

293.1k](/packages/eslazarev-wildberries-sdk)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
