Once the package is installed and configured,
the Steam facade is the only entry point. Three things carry the rest: the
SteamId value object every endpoint takes, the resources that send them, and the
readonly DTOs they return.
The SteamId value object
Section titled “The SteamId value object”SteamId — from the underlying SDK — is the only identifier the facade accepts;
no raw strings. Build one from a verified 64-bit ID, or parse untrusted input:
use Fkrzski\SteamApiSdk\ValueObjects\SteamId;
// Throws InvalidSteamIdException on anything but a 17-digit numeric ID.$id = SteamId::fromSteamId64('76561198000000000');use Fkrzski\SteamApiSdk\ValueObjects\SteamId;
// Returns null for anything that is not a SteamID64 or /profiles/<id> URL.$id = SteamId::tryFromInput('https://steamcommunity.com/profiles/76561198000000000');Read the value with $id->value (or (string) $id) and compare two IDs with
$id->equals($other). It is the same object the
AsSteamId cast stores on your models.
Calling the facade
Section titled “Calling the facade”Each endpoint sits on the resource for its Steam interface, which builds the request, sends it through the shared connector, and decodes the response into typed DTOs — objects, never arrays:
use Fkrzski\LaravelSteamApiSdk\Facades\Steam;
$summaries = Steam::users()->summaries([$id]);$bans = Steam::users()->bans([$id]);$friends = Steam::users()->friends($id);$groups = Steam::users()->groups($id);$resolvedId = Steam::users()->resolveVanityUrl('gabelogannewell');$library = Steam::players()->ownedGames($id, appIdsFilter: [381210]);$recentGames = Steam::players()->recentlyPlayedGames($id);$level = Steam::players()->steamLevel($id);$badges = Steam::players()->badges($id);$quests = Steam::players()->communityBadgeProgress($id);$stats = Steam::stats()->userStats($id, appId: 381210);$achievements = Steam::stats()->achievements($id, appId: 381210);$playing = Steam::stats()->currentPlayers(appId: 381210);$globalStats = Steam::stats()->globalAchievements(gameId: 381210);$gameSchema = Steam::stats()->schema(appId: 381210);$versionCheck = Steam::apps()->upToDateCheck(appId: 440, version: $installedVersion);$servers = Steam::apps()->serversAtAddress('108.181.62.21');$relayNetwork = Steam::apps()->sdrConfig(appId: 730);$news = Steam::news()->appNews(appId: 440);$newsFeed = Steam::newsFeed(appId: 440);$serverTime = Steam::webApi()->serverTime();$webApis = Steam::webApi()->supportedApis();The resources are the base SDK’s own, handed back as the connector builds them, while
newsFeed(), a lazy collection paging through appNews(), is the bridge’s. The
API reference lists what each returns; the DTO
properties and enums live in Data objects.
supportedApis() sends the key when one is configured and goes out without it otherwise, so it
lists the methods your key can call on top of the anonymous ones. Steam answers a rejected key
with a 403, which the base SDK raises as InvalidApiKeyException.
Concurrent requests
Section titled “Concurrent requests”Use pool() to fan out several requests at once instead of sending them serially:
use Fkrzski\LaravelSteamApiSdk\Facades\Steam;use Fkrzski\SteamApiSdk\Http\Requests\IPlayerService\GetOwnedGamesRequest;use Fkrzski\SteamApiSdk\Http\Requests\ISteamUser\GetPlayerSummariesRequest;use Saloon\Http\Response;
Steam::pool( requests: [ new GetOwnedGamesRequest($id, [381210]), new GetPlayerSummariesRequest([$id]), ], concurrency: 2, responseHandler: fn (Response $response) => $response->dto(),)->send()->wait();Pools work with raw Saloon requests, so you decode each Response yourself with
->dto() inside the handler. A failed request goes to exceptionHandler instead, one Steam
reports in a 200 payload included, such as a hidden games list.
Escape hatch
Section titled “Escape hatch”For a custom request the resources don’t cover, reach for the connector directly:
use Fkrzski\LaravelSteamApiSdk\Facades\Steam;
$connector = Steam::connector(); // the underlying SteamConnector$response = Steam::send($request); // send any Saloon Request, get the raw Responsesend() raises what the resources raise, inside the call: a GetOwnedGamesRequest for a hidden
profile throws there, though Steam answers it with a 200. Steam::connector()->debug() dumps
every request after it with the key masked as key=*** — see
Debugging. A hook added on the connector dies with it, so
listen to the events instead. Steam::connector()->sender() is the
base SDK’s SteamSender wrapper, so Guzzle middleware goes on a
sender of your own.
Exceptions
Section titled “Exceptions”The connector runs Saloon’s AlwaysThrowOnErrors and maps every failure onto an
SDK exception, so a non-2xx response never reaches you as a DTO. All of them
extend SteamApiException, so one catch handles the lot:
SteamApiException (root, extends RuntimeException)├── ApiKeyNotConfiguredException No key configured for a request that needs one.├── AppNewsUnavailableException App whose news Steam does not publish, or an unknown app.├── AppNotFoundException App ID Steam does not know.├── AppVersionUnavailableException No server version to check, or an unknown app.├── InvalidApiKeyException Steam rejected the key, or received none.├── InvalidServerAddressException Address is neither IPv4 nor IPv4:queryport.├── InvalidSteamIdException Malformed SteamID64.├── ProfileNotPublicException Profile, games list, or stats are private.├── StatsUnavailableException No stats for that game, or the profile hides them.├── SteamConnectionException Steam never answered: timeout, DNS, TLS, refused.├── SteamRateLimitException A local limit ran out, or Steam answered 429; exposes the Limit.├── SteamUserNotFoundException Vanity URL unresolved or profile missing.├── TooManySteamIdsException More than 100 IDs in a batch request.├── FakeNotInstalledException Facade assertion without Steam::fake(). (Laravel bridge)├── FakeOutsideTestsException Steam::fake() outside tests. (Laravel bridge)├── InvalidSteamHttpOptionException Timeout, retry or sender value the connector rejects. (Laravel bridge)├── InvalidSteamLanguageException Language code Steam does not accept. (Laravel bridge)├── InvalidSteamLoggingOptionException Log channel or flag the config cannot use. (Laravel bridge)└── InvalidSteamRateLimitOptionException Store, limit or cool-down the config cannot use. (Laravel bridge)Catch the leaf you care about, or the root. The API reference notes which endpoint raises which.
A message built from a Steam response opens with the Steam method:
GetNumberOfCurrentPlayers: No Steam app found for app ID 1. Such an exception, and
SteamConnectionException, carries no previous one, so a report() callback finds no Saloon
exception in the chain — read Steam’s status from getCode() and the payload from response.
A status no leaf claims — a 5xx once the
retries are spent, an
unexpected 4xx — arrives as SteamApiException itself, carrying the response and Steam’s
status as the code.
The six marked as the bridge’s own (Fkrzski\LaravelSteamApiSdk\Exceptions) signal a
misconfiguration, or a test wired up wrong, rather than a failed call — see
Configuration. The rest live in
Fkrzski\SteamApiSdk\Exceptions.
Rendering
Section titled “Rendering”An exception that reaches Laravel’s handler unhandled renders as the status it maps to, rather than a blanket 500:
| Exception | Status |
|---|---|
SteamUserNotFoundException |
404 |
AppNotFoundException |
404 |
AppNewsUnavailableException |
404 — Steam answers a 403, though the client asked for nothing forbidden |
AppVersionUnavailableException |
404 — Steam answers an unknown app and one without server versions alike |
StatsUnavailableException |
404 — the cause is ambiguous, so the status says nothing about which |
ProfileNotPublicException |
403 |
InvalidServerAddressException |
422 — the address is input, typically a route parameter |
SteamRateLimitException |
429, with Retry-After counting down what is left of the limit’s window, or of the cool-down after Steam’s own 429 |
SteamConnectionException, and SteamApiException for a 5xx |
503, the body saying only Service Unavailable |
InvalidApiKeyException, TooManySteamIdsException, ApiKeyNotConfiguredException, InvalidSteamLanguageException, InvalidSteamHttpOptionException, InvalidSteamRateLimitOptionException, InvalidSteamLoggingOptionException, FakeOutsideTestsException |
500 |
The 404, 403 and 422 bodies carry the exception’s message, which opens with the Steam method.
InvalidSteamIdException and FakeNotInstalledException are left alone: an ID that
fails to parse is already a 404 from the route binding,
and a missing fake only happens in a test.
Every row above the 500 one is turned off together by
steam-api.exceptions.render;
the 500 group is not, and never carries Steam’s own status.