windy-network/client-php

Official PHP client SDK for the Windy Network API

Installs: 8

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/windy-network/client-php

v1.0.2 2026-02-08 17:37 UTC

This package is auto-updated.

Last update: 2026-02-08 17:39:19 UTC


README

Official PHP SDK for the Windy Network API. Provides typed access to crypto, stocks, forex, options, and calendar market data with built-in authentication, retries, and real-time streaming.

Requirements

  • PHP 8.1+
  • Composer

Installation

composer require windy-network/client-php

Quick Start

use WindyNetwork\WindyClient;

$client = WindyClient::create('your-api-key');

// Crypto rates
$btc = $client->crypto->getRate('BTC', 'USD');
echo "BTC/USD: ${$btc->rate}";

// Stock quotes
$aapl = $client->stocks->getQuote('AAPL');
echo "AAPL: ${$aapl->price}";

// Forex rates
$eurusd = $client->forex->getRate('EUR', 'USD');
echo "EUR/USD: {$eurusd->rate}";

Configuration

use WindyNetwork\Config;
use WindyNetwork\WindyClient;

$client = WindyClient::create(new Config(
    apiKey: 'your-api-key',
    baseUrl: 'https://api.windy.network',
    timeout: 30,
    retries: 3,
    debug: true,
));

Authentication

API Key

$client = WindyClient::create('your-api-key');

Basic Auth

use WindyNetwork\Config;
use WindyNetwork\Enums\AuthType;

$client = WindyClient::create(new Config(
    authType: AuthType::Basic,
    username: 'user',
    password: 'pass',
));

OAuth 2.0

use WindyNetwork\Config;
use WindyNetwork\Enums\AuthType;

$client = WindyClient::create(new Config(
    authType: AuthType::OAuth,
    clientId: 'your-client-id',
    clientSecret: 'your-client-secret',
));

// Client credentials grant
$auth = $client->getAuthProvider();
$token = $auth->clientCredentialsGrant(['crypto:read', 'stock:read']);

// Authorization code grant with PKCE
use WindyNetwork\Auth\OAuthClient;

$codeVerifier = OAuthClient::generateCodeVerifier();
$codeChallenge = OAuthClient::generateCodeChallenge($codeVerifier);

$authUrl = $auth->getAuthorizationUrl([
    'state' => bin2hex(random_bytes(16)),
    'code_challenge' => $codeChallenge,
    'code_challenge_method' => 'S256',
]);

// After redirect:
$token = $auth->exchangeCode($code, $redirectUri, $codeVerifier);

API Endpoints

System

$health = $client->system->health();
$time = $client->system->time();
$version = $client->system->version();

Crypto

use WindyNetwork\Enums\Timeframe;

// Rates
$rate = $client->crypto->getRate('BTC', 'USD');
$rates = $client->crypto->getRates('basecoin');

// OHLC candlestick data
$ohlc = $client->crypto->getOHLC('BTC', 'USD', Timeframe::H1, ['limit' => 24]);

// Technical indicators
$rsi = $client->crypto->getRSI('BTC', 'USD', Timeframe::D1, ['period' => 14]);
$sma = $client->crypto->getSMA('BTC', 'USD', Timeframe::D1, ['period' => 20]);
$ema = $client->crypto->getEMA('BTC', 'USD', Timeframe::D1, ['period' => 12]);
$macd = $client->crypto->getMACD('BTC', 'USD', Timeframe::D1);
$bb = $client->crypto->getBollingerBands('BTC', 'USD', Timeframe::D1);

// Tickers & orderbook
$ticker = $client->crypto->getTicker('binance', 'BTC', 'USD');
$orderbook = $client->crypto->getOrderbook('BTC', 'USD');

// Market data
$gainers = $client->crypto->getGainers();
$losers = $client->crypto->getLosers();
$summary = $client->crypto->getMarketSummary();

// Assets & exchanges
$assets = $client->crypto->getAssets();
$exchanges = $client->crypto->getExchanges();

Stocks

$quote = $client->stocks->getQuote('AAPL');
$quotes = $client->stocks->getQuotes(['AAPL', 'GOOGL', 'MSFT']);
$ohlc = $client->stocks->getOHLC('AAPL', Timeframe::D1, ['limit' => 30]);

Forex

$rate = $client->forex->getRate('EUR', 'USD');
$rates = $client->forex->getRates();
$ohlc = $client->forex->getOHLC('EUR', 'USD', Timeframe::H1, ['limit' => 24]);

Options

$chain = $client->options->getChain('AAPL', ['expiration' => '2025-01-17']);

Calendar

$status = $client->calendar->getStatus();
$sessions = $client->calendar->getForexSessions();
$exchanges = $client->calendar->getExchanges();
$holidays = $client->calendar->getMarketHolidays(['market' => 'US']);

User (Authenticated)

// Alerts
$alerts = $client->user->getAlerts();
$alert = $client->user->createAlert([...]);
$client->user->deleteAlert($alertId);

// Watchlists
$watchlists = $client->user->getWatchlists();
$watchlist = $client->user->createWatchlist([...]);

// Portfolio
$portfolios = $client->user->getPortfolios();
$positions = $client->user->getPositions($portfolioId);

Real-Time Streaming

Server-Sent Events (SSE)

$sse = $client->sse();

$sse->on('connect', fn() => echo "Connected\n");
$sse->on('error', fn(\Throwable $e) => echo "Error: {$e->getMessage()}\n");

$channels = [
    'crypto:rate:basecoin:BTC:USD',
    'crypto:rate:basecoin:ETH:USD',
];

$sse->connect($channels, function ($message) {
    echo "[{$message->channel}] " . json_encode($message->data) . "\n";
});

HTTP Polling

$poller = $client->polling(['interval' => 5]);

$poller->connect(['crypto:rate:basecoin:BTC:USD'], function ($message) {
    echo json_encode($message->data) . "\n";
});

WebSocket

Requires the optional textalk/websocket package:

composer require textalk/websocket
$ws = $client->websocket();

$ws->connect(['crypto:rate:basecoin:BTC:USD'], function ($message) {
    echo json_encode($message->data) . "\n";
});

Error Handling

use WindyNetwork\Exceptions\AuthenticationException;
use WindyNetwork\Exceptions\RateLimitException;
use WindyNetwork\Exceptions\ValidationException;
use WindyNetwork\Exceptions\ApiException;
use WindyNetwork\Exceptions\WindyException;

try {
    $rate = $client->crypto->getRate('BTC', 'USD');
} catch (AuthenticationException $e) {
    // Invalid API key (401)
} catch (RateLimitException $e) {
    // Too many requests (429)
    sleep($e->retryAfter);
} catch (ValidationException $e) {
    // Bad request (400)
    $details = $e->details;
} catch (ApiException $e) {
    // Other HTTP errors
    echo "Status: {$e->statusCode}, Request ID: {$e->requestId}";
} catch (WindyException $e) {
    // SDK-level errors
}

// Check if an error is retryable
if (WindyException::isRetryable($e)) {
    // Retry the request
}

Rate Limiting

Rate limit info is available after each request:

$rateLimit = $client->getHttpClient()->getRateLimit();
if ($rateLimit) {
    echo "Remaining: {$rateLimit->remaining}/{$rateLimit->limit}";
}

The SDK automatically retries on 429 responses with exponential backoff.

Laravel Integration

Setup

composer require windy-network/client-php
php artisan vendor:publish --tag=windy-config

Add to your .env:

WINDY_API_KEY=your-api-key

Usage

// Dependency injection
class CryptoController extends Controller
{
    public function __construct(
        private readonly WindyClient $windy,
    ) {}

    public function rate(string $base, string $quote)
    {
        return response()->json(
            $this->windy->crypto->getRate($base, $quote)
        );
    }
}

// Facade
use WindyNetwork\Laravel\WindyFacade as Windy;

$rate = Windy::crypto->getRate('BTC', 'USD');

// Helper
$client = app(\WindyNetwork\WindyClient::class);

Symfony Integration

Configuration

# config/packages/windy.yaml
windy:
    api_key: '%env(WINDY_API_KEY)%'
    base_url: 'https://api.windy.network'
    timeout: 30
    retries: 3

Usage

class CryptoController extends AbstractController
{
    public function __construct(
        private readonly WindyClient $windy,
    ) {}

    #[Route('/rate/{base}/{quote}')]
    public function rate(string $base, string $quote): JsonResponse
    {
        $rate = $this->windy->crypto->getRate($base, $quote);
        return $this->json($rate);
    }
}

Testing

# Unit tests
vendor/bin/phpunit --testsuite=Unit

# Integration tests (requires API key)
WINDY_API_KEY=your-key vendor/bin/phpunit --testsuite=Integration

# Static analysis
vendor/bin/phpstan analyse

# Code style
vendor/bin/php-cs-fixer fix --dry-run

Examples

See the examples/ directory:

License

MIT