PHPackages                             marceloeatworld/runpod-serverless-php - 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. marceloeatworld/runpod-serverless-php

ActiveLibrary[API Development](/categories/api)

marceloeatworld/runpod-serverless-php
=====================================

\#1 PHP client for the RunPod Serverless API, compatible with Laravel and native PHP, built on Saloon v4

v2.0.0(3mo ago)03.1k↓80.8%MITPHPPHP ^8.2

Since Nov 20Pushed 3mo ago1 watchersCompare

[ Source](https://github.com/marceloeatworld/runpod-serverless-php)[ Packagist](https://packagist.org/packages/marceloeatworld/runpod-serverless-php)[ RSS](/packages/marceloeatworld-runpod-serverless-php/feed)WikiDiscussions main Synced 2d ago

READMEChangelog (2)Dependencies (4)Versions (3)Used By (0)

RunPod Serverless PHP Client
============================

[](#runpod-serverless-php-client)

[![Latest Version on Packagist](https://camo.githubusercontent.com/a38cb9de16f4bae848d5a3869baee80db4a9d9e010419dd22cecaae22999576c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6d617263656c6f656174776f726c642f72756e706f642d7365727665726c6573732d7068702e737667)](https://packagist.org/packages/marceloeatworld/runpod-serverless-php)[![PHP Version](https://camo.githubusercontent.com/9661b7c4d41d445846d705a82e9bda9d54f60fb5fb050ecb4bd3d669695f0aec/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f6d617263656c6f656174776f726c642f72756e706f642d7365727665726c6573732d7068702e737667)](https://packagist.org/packages/marceloeatworld/runpod-serverless-php)[![License](https://camo.githubusercontent.com/8e069265fc331971fadcdcf9109ef775d651644f10b09e63d92e542e9803ff81/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6d617263656c6f656174776f726c642f72756e706f642d7365727665726c6573732d7068702e737667)](https://packagist.org/packages/marceloeatworld/runpod-serverless-php)

\#1 PHP client for the [RunPod Serverless API](https://docs.runpod.io/serverless/overview), compatible with Laravel and native PHP, built on [Saloon v4](https://docs.saloon.dev).

---

Table of Contents
-----------------

[](#table-of-contents)

- [Requirements](#requirements)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Job Lifecycle](#job-lifecycle)
- [API Reference](#api-reference)
    - [Submitting Jobs](#submitting-jobs)
    - [Checking Job Status](#checking-job-status)
    - [Streaming Results](#streaming-results)
    - [Cancelling Jobs](#cancelling-jobs)
    - [Retrying Failed Jobs](#retrying-failed-jobs)
    - [Endpoint Health](#endpoint-health)
    - [Purging the Queue](#purging-the-queue)
- [RunPodResponse](#runpodresponse)
    - [Status Checks](#status-checks)
    - [Data Accessors](#data-accessors)
    - [JSON Serialization](#json-serialization)
- [Advanced Configuration](#advanced-configuration)
    - [Webhooks](#webhooks)
    - [Execution Policies](#execution-policies)
    - [S3 Storage Integration](#s3-storage-integration)
    - [Combining Options](#combining-options)
- [Error Handling](#error-handling)
- [Rate Limits](#rate-limits)
- [Result Retention](#result-retention)
- [Laravel Integration](#laravel-integration)
- [Support &amp; Security](#support--security)
- [License](#license)

---

Requirements
------------

[](#requirements)

- **PHP 8.2** or higher
- **Composer**

Saloon v4 is installed automatically as a dependency.

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

[](#installation)

```
composer require marceloeatworld/runpod-serverless-php
```

Quick Start
-----------

[](#quick-start)

```
use MarceloEatWorld\RunPod\RunPod;

// 1. Create the client with your API key
$runpod = new RunPod('your-api-key');

// 2. Target a specific endpoint
$endpoint = $runpod->endpoint('your-endpoint-id');

// 3. Submit a job
$result = $endpoint->run(['prompt' => 'A beautiful landscape']);

echo "Job submitted: " . $result->id; // e.g. "cb68890e-436f-4234-..."
echo "Status: " . $result->status;     // "IN_QUEUE"
```

> Your API key is available at [runpod.io/console/user/settings](https://www.runpod.io/console/user/settings). Your endpoint ID is the alphanumeric string visible in your endpoint's URL on the RunPod dashboard.

---

Job Lifecycle
-------------

[](#job-lifecycle)

Every RunPod job goes through a state machine:

```
                          +---> COMPLETED
                          |
IN_QUEUE ---> IN_PROGRESS +---> FAILED
   |              |       |
   |              |       +---> TIMED_OUT (executionTimeout exceeded)
   |              |
   +---> TIMED_OUT (TTL expired before pickup)
   |
   +---> CANCELLED (manual cancel)

```

StatusDescription`IN_QUEUE`Waiting for an available worker`IN_PROGRESS`Actively being processed`COMPLETED`Finished successfully, output available`FAILED`Worker returned an error`CANCELLED`Manually stopped via `cancel()``TIMED_OUT`Expired (TTL in queue or executionTimeout during processing)---

API Reference
-------------

[](#api-reference)

### Submitting Jobs

[](#submitting-jobs)

#### Async: `run(array $input)`

[](#async-runarray-input)

Submits a job and returns immediately. You then poll `status()` or use a webhook.

```
$result = $endpoint->run(['prompt' => 'A futuristic city']);

echo $result->id;     // "cb68890e-436f-4234-..."
echo $result->status; // "IN_QUEUE"
```

**Payload limit:** 10 MB.

#### Sync: `runSync(array $input)`

[](#sync-runsyncarray-input)

Submits a job and waits for completion. Best for fast tasks (&lt; 90 seconds).

```
$result = $endpoint->runSync(['prompt' => 'Hello world']);

if ($result->isCompleted()) {
    $output = $result->getOutput();
}
```

> If the job takes longer than ~90 seconds, `runSync` returns with status `IN_PROGRESS`. You must then fall back to polling `status()`.

**Payload limit:** 20 MB.

#### Full Polling Example

[](#full-polling-example)

```
$result = $endpoint->run(['prompt' => 'Generate something']);

// Poll until terminal state
while ($result->isInQueue() || $result->isInProgress()) {
    sleep(2); // Wait 2 seconds between polls
    $result = $endpoint->status($result->id);
}

// Handle terminal states
if ($result->isCompleted()) {
    $output = $result->getOutput();
    echo "Done! Worker: " . $result->getWorkerId();
    echo "Execution time: " . $result->getExecutionTime() . " ms";
    echo "Queue delay: " . $result->getDelayTime() . " ms";
} elseif ($result->isFailed()) {
    echo "Error: " . $result->getError();
} elseif ($result->isTimedOut()) {
    echo "Timed out, retrying...";
    $result = $endpoint->retry($result->id);
} elseif ($result->isCancelled()) {
    echo "Job was cancelled";
}
```

---

### Checking Job Status

[](#checking-job-status)

#### `status(string $jobId)`

[](#statusstring-jobid)

Retrieve the current state and results of a job.

```
$status = $endpoint->status('cb68890e-436f-4234-...');

echo $status->status;            // "COMPLETED"
echo $status->getOutput();       // The worker's output
echo $status->getExecutionTime(); // 2297 (ms)
echo $status->getDelayTime();    // 2188 (ms)
echo $status->getWorkerId();     // "smjcwth8e5sqvv"
```

---

### Streaming Results

[](#streaming-results)

#### `stream(string $jobId)`

[](#streamstring-jobid)

Retrieve incremental results from a streaming job. The worker must support streaming.

```
$result = $endpoint->run(['prompt' => 'Write a story']);

// Wait a bit for the worker to start producing chunks
sleep(5);

$stream = $endpoint->stream($result->id);
$chunks = $stream->getStream(); // Array of stream chunks

foreach ($chunks as $chunk) {
    echo $chunk['output'];
}
```

> Streaming in RunPod is **poll-based**, not chunked transfer encoding. Each chunk is limited to 1 MB.

---

### Cancelling Jobs

[](#cancelling-jobs)

#### `cancel(string $jobId)`

[](#cancelstring-jobid)

Cancel a queued or running job.

```
$result = $endpoint->run(['prompt' => 'Something expensive']);

// Changed my mind
$cancelled = $endpoint->cancel($result->id);
```

---

### Retrying Failed Jobs

[](#retrying-failed-jobs)

#### `retry(string $jobId)`

[](#retrystring-jobid)

Requeue a failed or timed-out job. RunPod re-uses the same job ID and original input.

```
$status = $endpoint->status($jobId);

if ($status->isFailed() || $status->isTimedOut()) {
    $retry = $endpoint->retry($jobId);
    echo "Retrying: " . $retry->id;     // Same job ID
    echo "Status: " . $retry->status;   // "IN_QUEUE"
}
```

---

### Endpoint Health

[](#endpoint-health)

#### `health()`

[](#health)

Get worker pool and job pipeline statistics.

```
$health = $endpoint->health();

// The raw data contains:
// {
//   "jobs": { "completed": 367, "failed": 6, "inProgress": 0, "inQueue": 0, "retried": 0 },
//   "workers": { "idle": 1, "initializing": 0, "ready": 1, "running": 0, "throttled": 0, "unhealthy": 0 }
// }

$data = $health->data;
echo "Workers ready: " . $data['workers']['ready'];
echo "Jobs in queue: " . $data['jobs']['inQueue'];
echo "Jobs failed: " . $data['jobs']['failed'];
```

---

### Purging the Queue

[](#purging-the-queue)

#### `purgeQueue()`

[](#purgequeue)

Remove all pending jobs from the queue. Running jobs are not affected.

```
$endpoint->purgeQueue();
```

> Use with caution. This is irreversible and has a strict rate limit (2 calls per 10 seconds).

---

RunPodResponse
--------------

[](#runpodresponse)

Every method returns a `RunPodResponse` object wrapping the raw API JSON response.

### Status Checks

[](#status-checks)

```
$response->isCompleted();  // COMPLETED
$response->isInQueue();    // IN_QUEUE
$response->isInProgress(); // IN_PROGRESS
$response->isFailed();     // FAILED
$response->isCancelled();  // CANCELLED
$response->isTimedOut();   // TIMED_OUT
```

### Data Accessors

[](#data-accessors)

MethodReturn TypeDescription`$response->id``?string`Unique job identifier`$response->status``?string`Current job status`$response->data``array`Complete raw API response`->getOutput()``mixed`Worker's output (when `COMPLETED`)`->getError()``mixed`Error details (when `FAILED`)`->getMetrics()``?array`Execution metrics`->getExecutionTime()``?int`Active processing time in ms`->getDelayTime()``?int`Time spent waiting in queue in ms`->getWorkerId()``?string`ID of the worker that processed the job`->getStream()``?array`Array of stream chunks (from `stream()`)### JSON Serialization

[](#json-serialization)

`RunPodResponse` implements `JsonSerializable`, so you can pass it directly to `json_encode()` or return it from a Laravel controller:

```
// Plain PHP
echo json_encode($response);

// Laravel
return response()->json($response);
```

---

Advanced Configuration
----------------------

[](#advanced-configuration)

The fluent methods `withWebhook()`, `withPolicy()`, and `withS3Config()` configure options on the endpoint resource. They are chainable and apply to the next `run()` or `runSync()` call.

> **Note:** These options are **sticky** on the resource instance. If you call `withWebhook()` once, subsequent `run()` calls on the same instance will continue sending that webhook. Create a new endpoint instance if you need different config.

### Webhooks

[](#webhooks)

Instead of polling `status()`, you can provide a webhook URL. RunPod will POST the complete response JSON to your URL when the job finishes.

```
$result = $endpoint
    ->withWebhook('https://your-site.com/api/runpod/callback')
    ->run(['prompt' => 'Your prompt']);

// No need to poll - RunPod will call your webhook
echo "Job submitted: " . $result->id;
```

Webhook behavior:

- RunPod POSTs the full response JSON on completion
- Your endpoint must return HTTP 200
- On failure, RunPod retries **2 more times** with a **10 second delay** between retries

### Execution Policies

[](#execution-policies)

Control job timeout and priority behavior.

```
$result = $endpoint
    ->withPolicy([
        'executionTimeout' => 900000,  // 15 min - max active runtime (ms)
        'lowPriority' => false,        // true = won't trigger worker autoscaling
        'ttl' => 3600000,              // 1 hour - total job lifespan from submission (ms)
    ])
    ->run(['prompt' => 'Your prompt']);
```

ParameterDefaultRangeDescription`executionTimeout`600,000 (10 min)5s - 7 daysMax time a job can actively run on a worker`ttl`86,400,000 (24h)10s - 7 daysTotal lifespan from submission (includes queue wait)`lowPriority``false`-If `true`, the job won't trigger autoscaling of new workers> **`executionTimeout` vs `ttl`:** TTL counts from when the job is *submitted* (including queue time). executionTimeout counts from when a worker *starts processing* the job. If TTL expires while a job is running, it's immediately removed.

### S3 Storage Integration

[](#s3-storage-integration)

For large payloads exceeding the 10/20 MB limits, use S3 integration to pass data via object storage.

```
$result = $endpoint
    ->withS3Config([
        'accessId' => 'your-access-key-id',
        'accessSecret' => 'your-secret-access-key',
        'bucketName' => 'your-bucket-name',
        'endpointUrl' => 'https://your-s3-endpoint.com',
    ])
    ->run(['prompt' => 'Your prompt']);
```

### Combining Options

[](#combining-options)

All fluent methods are chainable:

```
$result = $endpoint
    ->withWebhook('https://your-site.com/callback')
    ->withPolicy(['executionTimeout' => 120000, 'ttl' => 600000])
    ->withS3Config(['accessId' => '...', 'accessSecret' => '...', 'bucketName' => '...', 'endpointUrl' => '...'])
    ->run(['prompt' => 'Your prompt']);
```

---

Error Handling
--------------

[](#error-handling)

This client uses Saloon's `AlwaysThrowOnErrors` trait. Any HTTP 4xx/5xx response automatically throws an exception. Connection-level errors (DNS, timeout) are also thrown.

```
use Saloon\Exceptions\Request\RequestException;
use Saloon\Exceptions\Request\FatalRequestException;
use Saloon\Exceptions\Request\ClientException;
use Saloon\Exceptions\Request\ServerException;

try {
    $result = $endpoint->run(['prompt' => 'test']);
} catch (FatalRequestException $e) {
    // Connection-level errors: DNS failure, TLS error, timeout
    echo "Connection failed: " . $e->getMessage();
} catch (ClientException $e) {
    // 4xx errors
    $status = $e->getResponse()->status();
    match ($status) {
        401 => 'Invalid API key',
        404 => 'Endpoint not found or job TTL expired',
        429 => 'Rate limit exceeded - implement backoff',
        default => 'Client error: ' . $status,
    };
} catch (ServerException $e) {
    // 5xx errors
    echo "RunPod server error: " . $e->getResponse()->status();
} catch (RequestException $e) {
    // Catch-all for any other HTTP error
    echo "Request failed: " . $e->getResponse()->status();
}
```

Exception hierarchy:

```
SaloonException
  FatalRequestException        (connection errors - always thrown)
  RequestException             (HTTP errors)
    ServerException (5xx)
      InternalServerErrorException (500)
      ServiceUnavailableException  (503)
      GatewayTimeoutException      (504)
    ClientException (4xx)
      UnauthorizedException        (401)
      ForbiddenException           (403)
      NotFoundException            (404)
      UnprocessableEntityException (422)
      TooManyRequestsException     (429)

```

---

Rate Limits
-----------

[](#rate-limits)

RunPod enforces per-endpoint rate limits:

EndpointMax per 10sMax Concurrent`/run`1,000200`/runsync`2,000400`/status`2,000400`/stream`2,000400`/cancel`10020`/purge-queue`2-Exceeding these limits returns HTTP 429. Implement exponential backoff with jitter when retrying.

---

Result Retention
----------------

[](#result-retention)

RunPod automatically deletes job results after a retention period:

ModeRetention After CompletionAsync (`run`)**30 minutes**Sync (`runSync`)**1 minute** (5 minutes max)Fetch your results within these windows, or use webhooks to receive results immediately.

---

Laravel Integration
-------------------

[](#laravel-integration)

### 1. Configuration

[](#1-configuration)

Add to `config/services.php`:

```
'runpod' => [
    'api_key' => env('RUNPOD_API_KEY'),
],
```

Add to your `.env`:

```
RUNPOD_API_KEY=your-api-key-here

```

### 2. Service Provider

[](#2-service-provider)

Register as a singleton in `AppServiceProvider` (or a dedicated provider):

```
use MarceloEatWorld\RunPod\RunPod;

public function register(): void
{
    $this->app->singleton(RunPod::class, function () {
        return new RunPod(config('services.runpod.api_key'));
    });
}
```

### 3. Usage in Controllers

[](#3-usage-in-controllers)

```
use MarceloEatWorld\RunPod\RunPod;
use Illuminate\Http\Request;

class AIController extends Controller
{
    public function generate(RunPod $runpod, Request $request)
    {
        $endpoint = $runpod->endpoint('your-endpoint-id');
        $result = $endpoint->run($request->validated());

        return response()->json([
            'job_id' => $result->id,
            'status' => $result->status,
        ]);
    }

    public function status(RunPod $runpod, string $jobId)
    {
        $endpoint = $runpod->endpoint('your-endpoint-id');
        $status = $endpoint->status($jobId);

        return response()->json($status); // Uses JsonSerializable
    }
}
```

### 4. Usage in Jobs / Queues

[](#4-usage-in-jobs--queues)

```
use MarceloEatWorld\RunPod\RunPod;

class ProcessAITask implements ShouldQueue
{
    public function __construct(
        private string $endpointId,
        private array $input,
    ) {}

    public function handle(RunPod $runpod): void
    {
        $endpoint = $runpod->endpoint($this->endpointId);
        $result = $endpoint->runSync($this->input);

        if ($result->isCompleted()) {
            // Store output...
        }
    }
}
```

---

Support &amp; Security
----------------------

[](#support--security)

For security issues, please email .

License
-------

[](#license)

MIT License - see [LICENSE](LICENSE)

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance82

Actively maintained with recent releases

Popularity19

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

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 ~494 days

Total

2

Last Release

96d ago

Major Versions

v1.0.0 → v2.0.02026-03-29

PHP version history (2 changes)v1.0.0PHP ^8.1.0

v2.0.0PHP ^8.2

### Community

Maintainers

![](https://www.gravatar.com/avatar/b50b46eb9dc22e81943c192896b4c697c18a2d30bc914948c46a3106637a59f9?d=identicon)[marceloeatworld](/maintainers/marceloeatworld)

---

Top Contributors

[![marceloeatworld](https://avatars.githubusercontent.com/u/20625497?v=4)](https://github.com/marceloeatworld "marceloeatworld (4 commits)")

---

Tags

aiapi-clientgpumachine-learningphpphp-sdkrunpodsaloonsdkserverlessphpapi clientserverlessrunpod

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/marceloeatworld-runpod-serverless-php/health.svg)

```
[![Health](https://phpackages.com/badges/marceloeatworld-runpod-serverless-php/health.svg)](https://phpackages.com/packages/marceloeatworld-runpod-serverless-php)
```

###  Alternatives

[marceloeatworld/falai-php

\#1 PHP client for the fal.ai serverless AI platform, compatible with Laravel and native PHP, built on Saloon v4

106.1k](/packages/marceloeatworld-falai-php)[fabian-beiner/todoist-php-api-library

A PHP client library that provides a native interface to the official Todoist REST API.

4812.3k](/packages/fabian-beiner-todoist-php-api-library)[sandorian/moneybird-api-php

Moneybird API client for PHP

148.2k](/packages/sandorian-moneybird-api-php)

PHPackages © 2026

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