tigusigalpa / kucoin-php
An idiomatic PHP client for KuCoin's UTA and Classic REST/WebSocket APIs, with optional Laravel 10-13 integration.
Requires
- php: ^8.1
- psr/http-client: ^1.0
- psr/http-factory: ^1.0
- psr/log: ^3.0
Requires (Dev)
- guzzlehttp/guzzle: ^7.0
- guzzlehttp/psr7: ^2.0
- illuminate/contracts: ^10.0|^11.0|^12.0|^13.0
- illuminate/support: ^10.0|^11.0|^12.0|^13.0
- phpunit/phpunit: ^10.0
- symfony/yaml: ^6.0|^7.0
Suggests
- guzzlehttp/guzzle: Default PSR-18 HTTP client binding if you don't already have one
- illuminate/support: Required only for the optional Laravel service provider/facade
This package is auto-updated.
Last update: 2026-08-09 13:43:42 UTC
README
A PHP client for KuCoin's UTA (Unified Trading Account) and Classic (Spot/Margin/Futures) REST and WebSocket APIs, written from scratch against KuCoin's current docs-new documentation — not a wrapper around the official Universal SDK. PSR-18/PSR-17 based (no hardcoded Guzzle), with optional Laravel 10–13 integration.
Package: a matching Go SDK is available at tigusigalpa/kucoin-go.
Status
This is an early, honest checkpoint, not a finished library. Only UTA public market data (plus the one endpoint in that group that turns out to require auth — see below) is implemented and tested so far. Everything else described in this project's roadmap — UTA Account/Orders/Positions/Leverage/Transfers, Classic Spot/Margin/Futures trading, and every WebSocket channel — is not yet implemented. See docs/ENDPOINTS.md for the exact, generated list of what's covered, with a direct KuCoin documentation link per method.
| Phase | Scope | Status |
|---|---|---|
| 1 — reliable core | Shared HTTP/auth/error/retry infra; UTA market/account/orders; Classic Spot market/orders; Classic Futures market/order/position; UTA + Classic WS core channels | Partial — shared infra + UTA Market done; the rest is not started |
| 2 — trading breadth | Classic Margin; advanced Spot/Margin orders; UTA positions/leverage; more WS channels | Not started |
| 3 — specialty domains | Account/funding/subaccounts/deposits/withdrawals, Earn, VIP Lending, Convert, Broker, Affiliate, Copy Trading | Not started |
What's inside (so far)
- PHP 8.1+,
declare(strict_types=1)everywhere, readonly constructor properties, a backedTradeTypeenum - No hardcoded HTTP client —
Clienttakes any PSR-18ClientInterface+ PSR-17 factories;guzzlehttp/guzzleisrequire-dev/suggestonly, never a hard dependency - Strings for every price/quantity/PnL/fee field — no float rounding errors
- Independent HMAC-SHA256 signer (
KC-API-SIGNand the HMAC-signedKC-API-PASSPHRASE), verified against known-answer fixture vectors shared with kucoin-go - A shared
Http\RequestExecutor: response envelope decoding,Http\ResponseMeta(HTTP status, KuCoin business code/message, request ID,gw-ratelimit-*,x-in-time/x-out-time), and a typed exception hierarchy - A conservative retry policy — GET requests only, exponential backoff with jitter, bounded max-elapsed time; POST/DELETE (place/cancel/amend orders, transfers, withdrawals) are never auto-retried
- Optional Laravel 10–13 integration (
Laravel\KucoinServiceProvider,Laravel\Facades\Kucoin) — kept entirely separate from the core; the core never calls a Laravel helper - A manifest-driven docs/ENDPOINTS.md, regenerated by
php bin/gendocs.phpfrom resources/endpoints.yaml; CI fails if they drift apart
Install
composer require tigusigalpa/kucoin-php
Plain PHP apps also need a PSR-18 client + PSR-17 factories — e.g.:
composer require guzzlehttp/guzzle guzzlehttp/psr7
Laravel setup
The service provider and Kucoin facade are auto-discovered. If guzzlehttp/guzzle + guzzlehttp/psr7 are installed, a default PSR-18/17 binding is wired up automatically; otherwise the container throws a clear error naming what to install or bind yourself.
php artisan vendor:publish --tag=kucoin-config
KUCOIN_API_KEY=your-api-key KUCOIN_API_SECRET=your-api-secret KUCOIN_API_PASSPHRASE=your-passphrase KUCOIN_API_KEY_VERSION=3
use Tigusigalpa\Kucoin\Client; use Tigusigalpa\Kucoin\Uta\Market\TradeType; class MarketController { public function __construct(private readonly Client $kucoin) {} public function index() { return $this->kucoin->uta->market->getTickers(TradeType::Spot->value, 'BTC-USDT'); } }
Because services are exposed as typed public readonly properties ($client->uta->market) rather than facade methods, prefer constructor injection of Client over the facade; the facade mainly helps for quick artisan tinker usage via Kucoin::getFacadeRoot().
Plain PHP (no Laravel)
use GuzzleHttp\Client as GuzzleClient; use GuzzleHttp\Psr7\HttpFactory; use Tigusigalpa\Kucoin\Client; use Tigusigalpa\Kucoin\Uta\Market\TradeType; $factory = new HttpFactory(); $client = new Client( httpClient: new GuzzleClient(), requestFactory: $factory, streamFactory: $factory, ); $tickers = $client->uta->market->getTickers(TradeType::Spot->value, 'BTC-USDT'); echo $tickers['list'][0]['lastPrice'];
Runnable example (verified against the live API — read-only, no credentials): examples/uta_market.php.
Credentials and security
Read KUCOIN_API_KEY/KUCOIN_API_SECRET/KUCOIN_API_PASSPHRASE from environment variables or Laravel config — never hardcode them:
use Tigusigalpa\Kucoin\Config\Credentials; $credentials = new Credentials( apiKey: getenv('KUCOIN_API_KEY'), apiSecret: getenv('KUCOIN_API_SECRET'), apiPassphrase: getenv('KUCOIN_API_PASSPHRASE'), apiKeyVersion: getenv('KUCOIN_API_KEY_VERSION'), // per-key metadata, not a constant );
- Create keys with the minimum permission needed (read-only for market/account calls).
- Restrict the key to specific IPs where KuCoin allows it.
- This library never logs
KC-API-KEY,KC-API-SIGN,KC-API-PASSPHRASE, or full private request bodies. - It never places live trading, transfer, or withdrawal calls in its own tests, examples, or CI.
- A published
config/kucoin.phpreads secrets fromenv()— it never contains actual secret values.
See SECURITY.md to report a vulnerability.
UTA versus Classic
KuCoin exposes two distinct account models with different permissions, hosts, and data shapes. This SDK keeps them as explicit, separate properties — $client->uta today, $client->classic once implemented — rather than merging them into one type, since doing so would misrepresent which fields/permissions actually apply.
REST examples
Market data (public, no credentials):
$tickers = $client->uta->market->getTickers(TradeType::Spot->value, 'BTC-USDT');
The one authenticated exception in this service — getOrderBook looked public in KuCoin's docs but a live test confirmed it requires credentials:
$book = $client->uta->market->getOrderBook(TradeType::Spot->value, 'BTC-USDT', limit: 20);
Error and rate-limit inspection:
use Tigusigalpa\Kucoin\Exception\RateLimitException; use Tigusigalpa\Kucoin\Exception\KucoinException; try { $client->uta->market->getOrderBook(TradeType::Spot->value, 'BTC-USDT'); } catch (RateLimitException $e) { // back off } catch (KucoinException $e) { echo $e->httpStatus, ' ', $e->kucoinCode, ' ', $e->getMessage(); } $meta = $client->uta->executor->getLastResponseMeta(); echo $meta->rateLimitRemaining, '/', $meta->rateLimitLimit;
Authenticated account/order examples aren't shown here because that domain isn't implemented yet — see Status above.
WebSocket examples
Not yet implemented. No WebSocket client exists in this checkpoint. Token acquisition, subscription ACKs, heartbeat, reconnect/re-subscribe, and typed message dispatch are all planned for a later phase — see CONTRIBUTING.md.
Configuration
config/kucoin.php key |
Env var | Default |
|---|---|---|
api_key |
KUCOIN_API_KEY |
'' |
api_secret |
KUCOIN_API_SECRET |
'' |
api_passphrase |
KUCOIN_API_PASSPHRASE |
'' |
api_key_version |
KUCOIN_API_KEY_VERSION |
'' |
uta_base_url |
KUCOIN_UTA_BASE_URL |
https://api.kucoin.com |
site_type |
KUCOIN_SITE_TYPE |
global |
enable_ns |
KUCOIN_ENABLE_NS |
false |
Outside Laravel, pass the equivalent constructor arguments to Client/Config\RetryPolicy directly — see src/Client.php.
Endpoint reference
Full, generated coverage map with direct KuCoin doc links: docs/ENDPOINTS.md. Source manifest: resources/endpoints.yaml. Official docs map: KuCoin llms.txt.
Laravel section
- Auto-discovery: no manual provider/facade registration needed on Laravel 10–13.
- Publishing config:
php artisan vendor:publish --tag=kucoin-config. - Dependency injection: constructor-inject
Tigusigalpa\Kucoin\Client— the recommended pattern, since services are typed properties, not facade methods. - Config caching:
config/kucoin.phponly readsenv()at merge time (standard Laravel config-cache-safe pattern) — safe withphp artisan config:cache. - Worker lifecycle: the
Clientsingleton is safe to reuse across queue-worker jobs or Octane requests today (every call is a stateless REST request). This will need re-evaluation once a WebSocket client exists — see the note in src/Laravel/KucoinServiceProvider.php.
Testing and development
composer install vendor/bin/phpunit # or: composer test php bin/gendocs.php # or: composer gendocs — regenerate docs/ENDPOINTS.md after editing the manifest
Unit tests run fully offline against a mocked Guzzle/PSR-18 transport (MockHandler) — no network access or credentials required. No test, example, or CI job in this repository places a live trading, transfer, or withdrawal call.
Compatibility and migration
Pre-1.0: breaking changes may happen between minor versions while Phase 1 is being built out; see CHANGELOG.md. This library is not a drop-in replacement for the official KuCoin Universal SDK — method names, types, and error handling are intentionally different.
Security, risk and legal notice
This is an unofficial, community-maintained client. It is provided as is, with no warranty. Nothing here is financial advice. You are solely responsible for complying with the laws and regulations of your jurisdiction and for the safety of your API credentials and funds. Always test against a small amount / read-only permissions before trusting new code with a funded account.
Contributing and roadmap
See CONTRIBUTING.md for the workflow and the phase-by-phase roadmap.
License
MIT. See LICENSE.
Author
Igor Sazonov — @tigusigalpa — sovletig@gmail.com
Links
Not affiliated with KuCoin. This is an early checkpoint — verify coverage in docs/ENDPOINTS.md before relying on any endpoint.