A framework-agnostic PHP SDK for the Steam Web API, built on top of Saloon v4. Every request returns a strongly-typed, readonly DTO — you never touch raw JSON, and a malformed profile fails with a domain exception instead of a silent null. It powers the player profiles on Dead by Stats.

  • Fluent resources — $connector->players()->ownedGames($id), no request classes to import.
  • Strongly typed — PHP 8.5, PHPStan max, 100% type coverage.
  • Readonly DTOs — immutable objects with DateTimeImmutable, never framework date wrappers.
  • Domain exceptions — one hierarchy rooted at SteamApiException for every failure.
  • Rate limit baked in — the 100 000 requests/day quota is enforced per API key and resets at 00:00 UTC; rateLimits swaps in your own.
  • Timeouts and retries — set once on SteamConfig; only a 5xx or no answer at all is retried, since every attempt spends quota.
  • Hooks without the key — onRequest(), onResponse() and onFailure() for logs and metrics, and debug() masks the key as key=***.
  • Your own requests — a method the SDK does not ship is one Saloon request away, sent with the key, the budget and SDK exceptions.
  • Test fakes — SteamFake fakes every endpoint through the resource chain, with a factory per DTO and the failures Steam really sends.
  • Zero framework coupling — plain Saloon over any Sender you hand it; a Laravel bridge ships separately.
  • PHP 8.5+
  • Saloon 4+
  1. Pull the package in:

    Terminal window
    composer require fkrzski/php-steam-api-sdk
  2. Keep your Steam Web API key out of the source tree, or skip it if the anonymous endpoints are all you need:

    .env
    STEAM_API_KEY=your-steam-web-api-key
  3. Build a connector from it, and reuse that one instance — it carries the key and the daily budget:

    steam.php
    use Fkrzski\SteamApiSdk\SteamConfig;
    use Fkrzski\SteamApiSdk\SteamConnector;
    $connector = new SteamConnector(new SteamConfig(
    apiKey: $_ENV['STEAM_API_KEY'],
    ));
use Fkrzski\SteamApiSdk\ValueObjects\SteamId;
$summaries = $connector->users()->summaries([SteamId::fromSteamId64('76561198000000000')]);
echo $summaries[0]->personaName;

Every endpoint hangs off a resource named after its Steam interface — players(), users(), stats(), apps(), news(), webApi() — and returns the readonly DTO straight away.

The SDK follows SemVer from 1.0.0. A minor or patch release keeps:

  • SteamConfig and its named arguments, and on SteamConnector the constructor, the resources with their methods, and onRequest(), onResponse() and onFailure().
  • The request classes’ constructors and the DTO each one returns.
  • What you read off DTOs, hook payloads and exceptions: properties and their types, the exception hierarchy and codes, and the Steam method that opens a message.
  • SteamId, the enums with fromApiValue(), and the HasLanguage, SendsNoApiKey and SendsOptionalApiKey contracts.
  • The SteamApiException constructor, for exceptions of your own.
  • The exception each Steam answer maps to, and what a sender has to honour. Following Valve when it changes an answer is a fix.
  • SteamFake, SteamResponse and the factories under Testing.

Any release may change:

  • Anything marked @internal: DTO constructors and fromArray(), the hook payload and SteamSender constructors, and the exceptions’ named constructors such as fromLimit(). Build DTOs and failures in tests with the testing helpers; PHPStan with bleeding edge reports a call from your code.
  • Resource constructors; reach a resource through the connector.
  • An exception’s message after the method prefix.
  • Saloon’s lifecycle methods the connector and requests implement, such as boot(), resolveEndpoint() and getRequestException().
  • Limit names and the keys in a rate limit store, so a release may restart the counters once.

Saloon’s own methods on the connector — send(), pool(), withMockClient(), getExceededLimit() and the rest — follow Saloon’s versioning, and the SDK keeps what it documents on top of them. A new major of Saloon or its rate limit plugin comes in an SDK major, a higher minimum PHP in a minor.