Every endpoint has two entry points: a resource method on the connector, which sends and returns the DTO, and the request class behind it, which you send yourself. Both take the same arguments, except the concurrency that summaries() and bans() add. Every argument that identifies a user is a SteamId; the shapes coming back are documented in Data objects.

Badges list what a request throws on its own. On top of that, any request can raise SteamRateLimitException, SteamConnectionException or SteamApiException from the connector, and every request that needs a key can raise InvalidApiKeyException, or ApiKeyNotConfiguredException when the config carries no key at all — see Exceptions.

Both layers are namespaced by the Steam interface they belong to — one resource per interface, and one request class per endpoint:

  • Directorysrc/Http
    • DirectoryRequests
      • DirectoryIPlayerService
        • GetBadgesRequest.php
        • GetCommunityBadgeProgressRequest.php
        • GetOwnedGamesRequest.php
        • GetRecentlyPlayedGamesRequest.php
        • GetSteamLevelRequest.php
      • DirectoryISteamApps
        • GetSdrConfigRequest.php
        • GetServersAtAddressRequest.php
        • UpToDateCheckRequest.php
      • DirectoryISteamNews
        • GetNewsForAppRequest.php
      • DirectoryISteamUser
        • GetFriendListRequest.php
        • GetPlayerBansRequest.php
        • GetPlayerSummariesRequest.php
        • GetUserGroupListRequest.php
        • ResolveVanityUrlRequest.php
      • DirectoryISteamUserStats
        • GetGlobalAchievementPercentagesForAppRequest.php
        • GetNumberOfCurrentPlayersRequest.php
        • GetPlayerAchievementsRequest.php
        • GetSchemaForGameRequest.php
        • GetUserStatsForGameRequest.php
      • DirectoryISteamWebAPIUtil
        • GetServerInfoRequest.php
        • GetSupportedApiListRequest.php
    • DirectoryResources
      • AppsResource.php
      • NewsResource.php
      • PlayersResource.php
      • StatsResource.php
      • UsersResource.php
      • WebApiResource.php
Request Resource method Returns
GetBadgesRequest players()->badges() PlayerBadges
GetCommunityBadgeProgressRequest players()->communityBadgeProgress() list<CommunityBadgeQuest>
GetOwnedGamesRequest players()->ownedGames() list<OwnedGame>
GetRecentlyPlayedGamesRequest players()->recentlyPlayedGames() RecentlyPlayedGames
GetSteamLevelRequest players()->steamLevel() int
GetSdrConfigRequest apps()->sdrConfig() SdrConfig
GetServersAtAddressRequest apps()->serversAtAddress() list<GameServer>
UpToDateCheckRequest apps()->upToDateCheck() AppVersionCheck
GetNewsForAppRequest news()->appNews() AppNews
GetFriendListRequest users()->friends() list<Friend>
GetPlayerBansRequest users()->bans() list<PlayerBan>
GetPlayerSummariesRequest users()->summaries() list<PlayerSummary>
GetUserGroupListRequest users()->groups() list<UserGroup>
ResolveVanityUrlRequest users()->resolveVanityUrl() SteamId
GetGlobalAchievementPercentagesForAppRequest stats()->globalAchievements() list<GlobalAchievement>
GetNumberOfCurrentPlayersRequest stats()->currentPlayers() int
GetPlayerAchievementsRequest stats()->achievements() PlayerAchievements
GetSchemaForGameRequest stats()->schema() GameSchema
GetUserStatsForGameRequest stats()->userStats() UserStats
GetServerInfoRequest webApi()->serverTime() DateTimeImmutable
GetSupportedApiListRequest webApi()->supportedApis() list<WebApiInterface>
$connector->players()->badges(SteamId $steamId)
new GetBadgesRequest($steamId)

throws ProfileNotPublicException

Every badge a player has earned, plus the experience behind their community level. Badges that come from trading cards name the app they belong to; the rest — event and milestone badges — leave appId null, which is what separates the two kinds.

$badges = $connector->players()->badges($steamId);
echo $badges->playerLevel, ' — ', $badges->xpNeededToLevelUp, ' XP to go', PHP_EOL;
foreach ($badges->badges as $badge) {
echo $badge->badgeId, ' level ', $badge->level, PHP_EOL;
}

badgeId is not unique on its own: trading card badges repeat the same ID across different apps and are told apart by appId, so the result stays a list rather than a map.

$connector->players()->communityBadgeProgress(SteamId $steamId)
new GetCommunityBadgeProgressRequest($steamId)

throws ProfileNotPublicException

The quests behind the Steam community badge, each with the flag saying whether the account finished it. Steam publishes no schema mapping questId to a name, so the IDs stay opaque.

$quests = $connector->players()->communityBadgeProgress($steamId);
foreach ($quests as $quest) {
echo $quest->questId, $quest->completed ? ' — done' : ' — pending', PHP_EOL;
}

Steam’s badgeid filter is not exposed. 0 behaves as a wildcard, and every other ID answers with the same empty payload Steam sends for a hidden profile and for a SteamID64 that belongs to no account — a valid filter would be indistinguishable from the failure that raises the exception.

$connector->players()->ownedGames(
SteamId $steamId,
list<int> $appIdsFilter = [],
bool $includeAppInfo = false,
bool $includePlayedFreeGames = false,
)
new GetOwnedGamesRequest($steamId, $appIdsFilter, $includeAppInfo, $includePlayedFreeGames)

throws ProfileNotPublicException

The games a player owns. appIdsFilter narrows the result to specific app IDs; includeAppInfo adds names and icons; includePlayedFreeGames includes free games the player has launched.

$library = $connector->players()->ownedGames(
steamId: $steamId,
appIdsFilter: [381210],
includeAppInfo: true,
);
$connector->players()->recentlyPlayedGames(SteamId $steamId, ?int $count = null)
new GetRecentlyPlayedGamesRequest($steamId, $count)

throws ProfileNotPublicException

What a player launched in the last two weeks. count caps how many games come back, while totalCount on the result stays the full figure — that is how you tell a truncated list from a complete one.

$recent = $connector->players()->recentlyPlayedGames(steamId: $steamId, count: 2);
foreach ($recent->games as $game) {
echo $game->name, ' — ', $game->playtimeTwoWeeks, ' min', PHP_EOL;
}
$connector->players()->steamLevel(SteamId $steamId)
new GetSteamLevelRequest($steamId)

throws ProfileNotPublicException

The player’s Steam community level, as a plain int rather than a DTO. Hiding game details leaves it readable — only a profile Steam withholds altogether comes back empty, and that is what raises the exception.

$level = $connector->players()->steamLevel($steamId);
$connector->apps()->sdrConfig(int $appId)
new GetSdrConfigRequest($appId)

throws AppNotFoundException

The Steam Datagram Relay map a game connects through: every point of presence, where it stands, and the IPv4 addresses and ports its relays answer on. A game without a map of its own gets Valve’s default one, and app ID 0 returns the whole network. Steam serves it anonymously, so the SDK strips the configured key from the query, and a connector built without one can send it.

$config = $connector->apps()->sdrConfig(730);
foreach ($config->pointsOfPresence['waw']->relays as $relay) {
echo $relay->ipv4, ':', $relay->minPort, '-', $relay->maxPort, PHP_EOL;
}
$connector->apps()->serversAtAddress(string $address)
new GetServersAtAddressRequest($address)

throws InvalidServerAddressException

Every game server Steam knows at an IPv4 address, or only the one behind ip:queryport. An address with no servers returns an empty list. Steam serves it anonymously, so the SDK strips the configured key from the query, and a connector built without one can send it.

foreach ($connector->apps()->serversAtAddress('108.181.62.21') as $server) {
echo $server->address, ' runs app ', $server->appId, PHP_EOL;
}
$connector->apps()->upToDateCheck(int $appId, int $version)
new UpToDateCheckRequest($appId, $version)

throws AppVersionUnavailableException

Whether a game server’s build is still the one Steam accepts, and which version to update to if not. Steam serves it anonymously, so the SDK strips the configured key from the query, and a connector built without one can send it.

$check = $connector->apps()->upToDateCheck(440, $installedVersion);
if (! $check->isUpToDate) {
echo 'Update to ', $check->requiredVersion, ': ', $check->message, PHP_EOL;
}
$connector->news()->appNews(
int $appId,
?int $count = null,
?int $maxLength = null,
?DateTimeInterface $endDate = null,
list<string> $feeds = [],
list<string> $tags = [],
)
new GetNewsForAppRequest($appId, $count, $maxLength, $endDate, $feeds, $tags)

throws AppNewsUnavailableException

A game’s news, newest first: Steam’s own announcements next to blog and press feeds. count sets the page size, 20 by default, and 0 returns nothing; feeds and tags filter by feed name and tag. Without maxLength, or with 0, each body comes in its feed’s own markup, BBCode or HTML; any positive value flattens it, even when it fits, and cuts a longer one to an excerpt ending in ..., keeping HTML links and bold. Steam serves it anonymously, so the SDK strips the configured key from the query, and a connector built without one can send it. Every 403 covers two causes — an app ID Steam does not know, or an app whose news it does not publish, such as Spacewar — because both answer identically.

The SDK ships no pager. Pass the last item’s publishedAt back as endDate:

use Fkrzski\SteamApiSdk\Dto\NewsItem;
$seen = [];
$endDate = null;
do {
$page = $connector->news()->appNews(440, count: 50, endDate: $endDate);
$fresh = array_filter($page->items, fn (NewsItem $item): bool => ! isset($seen[$item->id]));
foreach ($fresh as $item) {
$seen[$item->id] = true;
$endDate = $item->publishedAt;
echo $item->publishedAt->format('Y-m-d'), ' ', $item->title, PHP_EOL;
}
} while ($fresh !== []);

endDate is inclusive, so every page opens with the last item of the one before, and the loop drops it by ID. Stepping a second back instead would lose posts published in that same second.

$connector->users()->friends(SteamId $steamId, ?FriendRelationship $relationship = null)
new GetFriendListRequest($steamId, $relationship)

throws ProfileNotPublicException

The player’s friends. Pass a FriendRelationship to filter; omit it for all relationships. A private friend list is refused with a 401, not an empty payload.

use Fkrzski\SteamApiSdk\Enums\FriendRelationship;
$friends = $connector->users()->friends($steamId, FriendRelationship::Friend);
$connector->users()->bans(list<SteamId> $steamIds, int $concurrency = 5)
new GetPlayerBansRequest($steamIds)

throws InvalidArgumentException throws TooManySteamIdsException

VAC, game, community and economy ban status. The request takes 1 to 100 players; bans() takes any number and splits a longer list. Unlike the other batch endpoint this one needs no public profile — bans come back for private profiles too.

$bans = $connector->users()->bans([$steamId]);
foreach ($bans as $ban) {
echo $ban->steamId, ' — VAC bans: ', $ban->numberOfVacBans, PHP_EOL;
}
$connector->users()->summaries(list<SteamId> $steamIds, int $concurrency = 5)
new GetPlayerSummariesRequest($steamIds)

throws InvalidArgumentException throws TooManySteamIdsException

Public profile summaries. The request takes 1 to 100 players and checks both limits in the constructor, so a bad batch costs no quota; summaries() takes any number and splits a longer list.

$summaries = $connector->users()->summaries([$steamId]);
foreach ($summaries as $summary) {
echo $summary->personaName, ' — ', $summary->profileUrl, PHP_EOL;
}
$connector->users()->groups(SteamId $steamId)
new GetUserGroupListRequest($steamId)

throws ProfileNotPublicException

The Steam groups a player belongs to. Only group IDs come back — Steam exposes no group metadata on this endpoint.

$groups = $connector->users()->groups($steamId);
foreach ($groups as $group) {
echo $group->gid, PHP_EOL;
}
$connector->users()->resolveVanityUrl(string $vanityName)
new ResolveVanityUrlRequest($vanityName)

throws SteamUserNotFoundException

Resolves a vanity slug — the <name> in steamcommunity.com/id/<name> — to a SteamId. Working from a full URL? Run it through SteamId::extractVanityName() first.

$steamId = $connector->users()->resolveVanityUrl('gabelogannewell');

GetGlobalAchievementPercentagesForAppRequest

Section titled “GetGlobalAchievementPercentagesForAppRequest”
$connector->stats()->globalAchievements(int $gameId)
new GetGlobalAchievementPercentagesForAppRequest($gameId)

throws StatsUnavailableException

How much of the playerbase has unlocked each achievement in a game, ordered from the most common down. Steam serves it anonymously, so the SDK strips the configured key from the query, a connector built without one can send it, and no key error can land here. Every 403 is about the game, and covers two causes — a game carrying no achievements, or a game ID Steam does not know — because both answer identically.

$achievements = $connector->stats()->globalAchievements(381210);
foreach ($achievements as $achievement) {
echo $achievement->apiName, ' — ', $achievement->percent, '%', PHP_EOL;
}
$connector->stats()->currentPlayers(int $appId)
new GetNumberOfCurrentPlayersRequest($appId)

throws AppNotFoundException

How many people are playing a game right now, as a plain int rather than a DTO. Steam serves it anonymously, so the SDK strips the configured key from the query, and a connector built without one can send it.

$players = $connector->stats()->currentPlayers(381210);
$connector->stats()->achievements(SteamId $steamId, int $appId, ?Language $language = null)
new GetPlayerAchievementsRequest($steamId, $appId, $language)

throws StatsUnavailableException

A player’s achievements for one game, each with its unlock state and time. The exception covers two causes — no achievements on the app, or a hidden profile — because Steam reports them identically.

use Fkrzski\SteamApiSdk\Enums\Language;
$result = $connector->stats()->achievements(
steamId: $steamId,
appId: 381210,
language: Language::English,
);
foreach ($result->achievements as $achievement) {
echo $achievement->apiName, ' — ', $achievement->achieved ? 'unlocked' : 'locked', PHP_EOL;
}
$connector->stats()->schema(int $appId, ?Language $language = null)
new GetSchemaForGameRequest($appId, $language)

throws AppNotFoundException

Every stat and achievement a game publishes, with the display names and descriptions a player’s own achievements are read against. language localises them; see the Language enum.

use Fkrzski\SteamApiSdk\Enums\Language;
$schema = $connector->stats()->schema(appId: 381210, language: Language::Polish);
foreach ($schema->achievements as $achievement) {
echo $achievement->apiName, ' — ', $achievement->name, PHP_EOL;
}
$connector->stats()->userStats(SteamId $steamId, int $appId, ?Language $language = null)
new GetUserStatsForGameRequest($steamId, $appId, $language)

throws ProfileNotPublicException

A player’s stats and achievement flags for one game. language localises achievement metadata; see the Language enum.

$stats = $connector->stats()->userStats(steamId: $steamId, appId: 381210);
foreach ($stats->stats as $stat) {
echo $stat->name, ' = ', $stat->value, PHP_EOL;
}
$connector->webApi()->serverTime()
new GetServerInfoRequest

Steam’s clock, as a DateTimeImmutable in UTC rather than a DTO. Steam serves it anonymously, so the SDK strips the configured key from the query, and a connector built without one can send it. The servertimestring Steam sends beside it is left out: it is US Pacific local time with no offset.

$skew = $connector->webApi()->serverTime()->getTimestamp() - time();
$connector->webApi()->supportedApis()
new GetSupportedApiListRequest

throws InvalidApiKeyException

Every Web API method Steam lets the caller see, grouped by interface. The key is optional: the configured one goes along and adds the methods it can call, and a connector built without one gets the anonymous list. Steam refuses a bad key here with a JSON 403 rather than its usual HTML, and the request still raises it as the exception.

foreach ($connector->webApi()->supportedApis() as $interface) {
foreach ($interface->methods as $method) {
echo $interface->name, '/', $method->name, '/v', $method->version, PHP_EOL;
}
}