Download the PHP package goal-api/sdk without Composer
On this page you can find all versions of the php package goal-api/sdk. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Package sdk
Short Description PHP SDK for the GOAL API: football fixtures, live scores, standings, stats and odds.
License MIT
Homepage https://goal-api.com
Informations about the package sdk
goal-api/sdk
PHP SDK for the GOAL API: football fixtures, live scores, standings, player stats, odds and real-time match updates over WebSocket.
No framework dependency and no Guzzle. PHP 8.1+ with the curl, json and openssl extensions, so it installs into a project that already pins a conflicting HTTP client. The live WebSocket client needs php-cli, since PHP-FPM cannot hold a socket open.
Quick start
Get a key at goal-api.com/signup.
Share one client: curl connection reuse and the rate-limit snapshot are per-instance. In Laravel or Symfony, register it as a singleton.
Client options
Named arguments, so you only pass what you're changing:
Retries use exponential backoff with full jitter and always honour a server-sent
Retry-After.
Using your own HTTP stack
If your app mandates Guzzle, a PSR-18 client, or an instrumented transport, pass an
$httpHandler and the SDK routes every request through it instead of curl:
Response header keys must be lowercased. This is the same seam the SDK's own tests use.
Endpoints
Grouped by resource. Params are a plain array; nulls and empty strings are dropped, so you
can build one unconditionally. Full reference in ENDPOINTS.md.
Enum values are constants: GoalApi::MATCH_STATUSES, ::PLAYER_TYPES, ::PLAYER_STATS,
::HALVES.
Every method returns the raw decoded envelope, so pagination and source stay reachable:
One exception: $goal->status
The five /public/* endpoints don't use the ['success', 'data'] envelope. They return
bare objects, so read them directly with no ['data']:
They also paginate with page/limit instead of limit/offset, so paginate() does not
apply to coverageLeagues.
Pagination
paginate() is a generator, so memory stays flat however large the collection:
Default pageSize is 100, the limit ceiling on most endpoints.
Errors
Everything thrown extends GoalApiException. Branch only where you'd actually behave
differently:
Types: ValidationException, AuthenticationException, PermissionException,
PlanUpgradeRequiredException, NotFoundException, ConflictException,
RateLimitException, ServiceUnavailableException, ServerException,
ConnectionException, TimeoutException.
$error->errorCodeholds the API's code, e.g.VALIDATION_ERROR. It is named that rather thancodebecauseException::$codeis an int in PHP and already carries the HTTP status.Two error shapes
The API answers with one of two bodies, and the SDK normalises both:
| Gateway (auth, routing, rate limits) | Football service (most endpoints) | |
|---|---|---|
| text | message |
error |
code |
yes | yes |
category |
yes | no |
correlationId |
yes | no |
details |
object | array, on validation errors |
So $error->getMessage() and $error->errorCode are always populated, and $error->correlationId is
only set on gateway errors. Quote it in a support ticket when you have it.
Rate limits
Live updates
The socket is at
wss://api.goal-api.com/ws, not/v1/ws. Only nginx'slocation ^~ /wscarries theUpgradeheaders;/v1/wsis proxied as ordinary HTTP and answers 200 instead of upgrading. The SDK derives the right URL for you.Two services authenticate: the gateway authorises the upgrade from the header or
?wsToken=, then websocket-service needs an{"type": "auth", ...}frame as the very first message. The SDK sends it, and treatsauth_successas the point the connection is usable.
subscribeis capped per plan and the cap can be 0.auth_successreportsmaxSubscriptions; if it is 0 the socket works but nomatch_updatewill ever arrive. See the known server issue inENDPOINTS.md.
There are two ways in, and which one you want depends on where the socket lives.
In a php-cli worker
$goal->live() is a blocking WebSocket client, written on PHP streams with no
dependencies. It handles the handshake, auth, keepalives and reconnection.
Or drive it yourself, which is what you want inside an existing loop:
receive() returns null when the timeout expires with nothing to read, so a quiet feed
never blocks your loop. Handlers registered with on() fire from both receive() and
run(). Subscriptions are replayed after a reconnect, because the server does not
remember a dropped connection's.
This needs php-cli. It holds a socket open for as long as you want updates, which is
exactly what PHP-FPM cannot do — the worker is tied to a request and gets recycled out
from under the connection. The constructor throws under any SAPI other than cli,
phpdbg or embed; pass allowAnySapi: true only if you know yours can hold a
long-lived socket (Swoole, RoadRunner, a custom embed).
Options: autoReconnect, maxReconnectAttempts, pingInterval, authTimeout,
readTimeout, connectTimeout, streamContext, connectToken, url.
From a browser
If the live data is headed for a frontend anyway, skip the worker. Your backend holds the API key and hands the browser a short-lived, single-use token, so the key never reaches the client:
The token is consumed on first connect, so mint one per connection.
The server caps client messages at 60/minute and concurrent subscriptions by plan. Only
resource: "match" is supported. Server message types: match_update, auth_success,
status, pong, server_shutdown, error, subscribe_response,
unsubscribe_response, get_subscriptions_response. LiveClient adds open and close
for the transport itself, and * receives everything.
Webhooks
Verify against the raw body: php://input, not $_POST. A decoded-and-re-encoded
array has different bytes and will never match.
In Laravel, disable CSRF for the route and use $request->getContent() for the raw body.
Timestamps outside 300s are rejected as replays. Override with tolerance:.
Escape hatch
For an endpoint this SDK doesn't wrap yet:
Examples
| File | Shows |
|---|---|
examples/basic.php |
Status, live fixtures, standings, pagination |
examples/live-worker.php |
A php-cli worker on the live socket |
examples/live-token.php |
Minting a browser token for live data, with the JS to use it |
examples/webhook-receiver.php |
Verifying a webhook against php://input |
examples/bulk-export.php |
Walking every page of a collection to CSV |
Testing
The live tests skip themselves without a key. Endpoint-by-endpoint coverage of the API
lives in tools/sweep.py in the SDK workspace.
Other languages
Four more first-party clients over the same API, with the same resource groups, the same retry and pagination behaviour and the same error types. All five release in lockstep, so a version number means the same surface everywhere.
| Language | Package | Install |
|---|---|---|
| JavaScript / TypeScript | @goalapi/sdk |
npm install @goalapi/sdk |
| Python | goal-api |
pip install goal-api |
| Go | goal-api-go |
go get github.com/goal-api/goal-api-go |
| Dart / Flutter | goal_api |
dart pub add goal_api |
Each one is its own repository and carries the same ENDPOINTS.md, the
API contract derived from the running service.
Licence
MIT. See LICENSE.
No runtime dependencies: curl and json are PHP extensions, not composer packages, which is
what keeps vendor/ at one package. See
THIRD_PARTY_NOTICES.md.
Security issues: SECURITY.md.
All versions of sdk with dependencies
ext-curl Version *
ext-json Version *
ext-hash Version *
ext-openssl Version *