michaeldrennen / schwab-api-php
A PHP library to connect to the Charles Schwab API.
Installs: 157
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 1
Forks: 2
Open Issues: 0
pkg:composer/michaeldrennen/schwab-api-php
Requires
- php: ^8.2
- guzzlehttp/guzzle: ^7.9
- illuminate/support: ^11.0
Requires (Dev)
- chrome-php/chrome: ^1.11
- phpunit/phpunit: ^11.4
- symfony/var-dumper: ^7.0
README
A comprehensive PHP library for interacting with the Charles Schwab Trader API. This library provides a clean, object-oriented interface to access account information, manage orders, retrieve transactions, and more.
Table of Contents
- Features
- Requirements
- Installation
- Configuration
- Quick Start
- Authentication Flow
- API Reference
- Error Handling
- Security
- Testing
- Contributing
- License
Features
- ✅ Complete API Coverage - All Schwab Trader API endpoints implemented
- ✅ Type-Safe - Full PHP 8.2+ type declarations
- ✅ Modern PHP - Uses traits, strict types, and best practices
- ✅ OAuth 2.0 - Built-in authentication and token management
- ✅ Comprehensive Error Handling - Detailed exceptions with context
- ✅ Well Documented - Extensive PHPDoc comments throughout
- ✅ Secure - Credential masking and secure defaults
- ✅ Tested - PHPUnit test suite included
Supported Operations
- Accounts: Get account numbers, balances, positions
- Orders: Place, modify, cancel, preview orders
- Transactions: Retrieve transaction history
- Market Data: Quotes, price history, option chains, market hours
- Trading: Equity and options trading support
- User Preferences: Access user settings
Requirements
- PHP 8.2 or higher
- Composer
- A Schwab Developer account with API credentials
Installation
Install via Composer:
composer require michaeldrennen/schwab-api-php
Configuration
1. Get API Credentials
- Visit Schwab Developer Portal
- Create an application and note your:
- API Key (Client ID)
- API Secret (Client Secret)
- Callback URL (Redirect URI)
2. Environment Setup
Copy the example environment file:
cp .env.example .env
Edit .env or phpunit.xml with your credentials:
SCHWAB_API_KEY=your_api_key_here SCHWAB_API_SECRET=your_api_secret_here SCHWAB_CALLBACK_URI=https://your-domain.com/callback
Quick Start
<?php require 'vendor/autoload.php'; use MichaelDrennen\SchwabAPI\SchwabAPI; // Initialize the API client $api = new SchwabAPI( apiKey: $_ENV['SCHWAB_API_KEY'], apiSecret: $_ENV['SCHWAB_API_SECRET'], apiCallbackUrl: $_ENV['SCHWAB_CALLBACK_URI'], authenticationCode: $authCode, // From OAuth callback accessToken: null, refreshToken: null, debug: false ); // Get account numbers $accountNumbers = $api->accountNumbers(); // Get account details with positions $accounts = $api->accounts(positions: true); // Place a buy order $responseCode = $api->placeBuyOrder( hashValueOfAccountNumber: $encryptedAccountNumber, symbol: 'AAPL', quantity: 10 );
Authentication Flow
The Schwab API uses OAuth 2.0 authentication. Here's the complete flow:
Step 1: Redirect User to Authorization
$api = new SchwabAPI( apiKey: $apiKey, apiSecret: $apiSecret, apiCallbackUrl: $callbackUrl, authenticationCode: 'temporary', // Placeholder debug: false ); // Redirect user to this URL $authorizeUrl = $api->getAuthorizeUrl(); header("Location: $authorizeUrl");
Step 2: Handle Callback
After user authorization, Schwab redirects to your callback URL with a code parameter:
// Your callback URL: https://your-domain.com/callback?code=ABC123...&session=XYZ789... $code = $_GET['code']; $session = $_GET['session']; $api = new SchwabAPI( apiKey: $apiKey, apiSecret: $apiSecret, apiCallbackUrl: $callbackUrl, authenticationCode: $code, debug: false ); // Exchange code for tokens $api->requestToken(); // Store these tokens securely $accessToken = $api->getAccessToken(); $refreshToken = $api->getRefreshToken(); $expiresIn = $api->getExpiresIn(); // Seconds until expiration (typically 1800)
Step 3: Use Access Token
$api = new SchwabAPI( apiKey: $apiKey, apiSecret: $apiSecret, apiCallbackUrl: $callbackUrl, authenticationCode: null, accessToken: $storedAccessToken, refreshToken: $storedRefreshToken, debug: false ); // Make API calls $accounts = $api->accounts();
Step 4: Refresh Token
Access tokens expire after 30 minutes. Refresh them before expiration:
// Refresh the access token $api->requestToken(doRefreshToken: true); // Get new tokens $newAccessToken = $api->getAccessToken(); $newRefreshToken = $api->getRefreshToken();
API Reference
Account Operations
Get Account Numbers
Returns a list of account numbers with their encrypted hash values:
$accountNumbers = $api->accountNumbers(); // Returns: [['accountNumber' => '12345', 'hashValue' => 'ABC123...']]
Get All Accounts
// Get accounts with balances only $accounts = $api->accounts(); // Get accounts with positions $accounts = $api->accounts(positions: true);
Get Specific Account
$account = $api->account( hashValueOfAccountNumber: $encryptedAccountHash, fields: ['positions'] );
Get Account by Number (Helper)
// Get indexed by account number $accountsByNumber = $api->accountsByNumber(); // Get specific account by number $account = $api->accountByNumber(accountNumber: 12345678);
Get Long Equity Positions
$positions = $api->getLongEquityPositions( hashValueOfAccountNumber: $encryptedAccountHash );
Order Management
Get All Orders
use Carbon\Carbon; // Get all orders across all accounts $orders = $api->orders( fromTime: Carbon::now()->subDays(30), toTime: Carbon::now(), maxResults: 100, status: 'FILLED' );
Valid status values:
AWAITING_PARENT_ORDER,AWAITING_CONDITION,AWAITING_STOP_CONDITIONAWAITING_MANUAL_REVIEW,ACCEPTED,AWAITING_UR_OUTPENDING_ACTIVATION,QUEUED,WORKING,REJECTEDPENDING_CANCEL,CANCELED,PENDING_REPLACE,REPLACEDFILLED,EXPIRED,NEW,AWAITING_RELEASE_TIMEPENDING_ACKNOWLEDGEMENT,PENDING_RECALL,UNKNOWN
Get Orders for Specific Account
$orders = $api->ordersForAccount( hashValueOfAccountNumber: $encryptedAccountHash, maxResults: 50, fromTime: Carbon::now()->subWeek(), toTime: Carbon::now(), status: 'WORKING' );
Get Specific Order
$order = $api->orderForAccount( hashValueOfAccountNumber: $encryptedAccountHash, orderId: 12345 );
Place Buy Order
$responseCode = $api->placeBuyOrder( hashValueOfAccountNumber: $encryptedAccountHash, symbol: 'AAPL', quantity: 10 ); // Returns: 201 (Created)
Place Sell Order
$responseCode = $api->placeSellOrder( hashValueOfAccountNumber: $encryptedAccountHash, symbol: 'AAPL', quantity: 10 );
Preview Order
Preview an order before placing it to see projected impact:
$preview = $api->previewOrder( hashValueOfAccountNumber: $encryptedAccountHash, orderPayload: $orderJson ); // Returns projected balances, commissions, etc.
Replace Order
Modify an existing order:
$responseCode = $api->replaceOrder( hashValueOfAccountNumber: $encryptedAccountHash, orderId: 12345, orderPayload: $newOrderJson ); // Returns: 200 (OK)
Cancel Order
$responseCode = $api->cancelOrder( hashValueOfAccountNumber: $encryptedAccountHash, orderId: 12345 ); // Returns: 200 (OK)
Transaction History
Get Transactions
use Carbon\Carbon; $transactions = $api->transactions( hashValueOfAccountNumber: $encryptedAccountHash, startDate: Carbon::now()->subMonths(3), endDate: Carbon::now(), symbol: 'AAPL', // Optional: filter by symbol types: 'TRADE' // Optional: filter by transaction type );
Get Specific Transaction
$transaction = $api->transaction( hashValueOfAccountNumber: $encryptedAccountHash, transactionId: 'TXN123456' );
Market Data
The library also includes market data endpoints (already implemented in your codebase):
- Quotes: Real-time and delayed quotes
- Price History: Historical price data
- Option Chains: Option chain data
- Option Expiration: Option expiration dates
- Market Hours: Trading hours for different markets
- Movers: Top market movers
- Instruments: Security instrument lookup
User Preferences
$preferences = $api->userPreference();
Error Handling
The library provides detailed error handling with custom exceptions:
use MichaelDrennen\SchwabAPI\Exceptions\RequestException; try { $accounts = $api->accounts(); } catch (RequestException $e) { // Get error message echo $e->getMessage(); // Get HTTP status code echo $e->getCode(); // Get response body echo $e->getResponseBody(); // Get previous exception $previous = $e->getPrevious(); } catch (\InvalidArgumentException $e) { // Handle validation errors echo "Invalid parameters: " . $e->getMessage(); } catch (\RuntimeException $e) { // Handle JSON decode errors echo "JSON error: " . $e->getMessage(); }
Common Exceptions
RequestException- API request failures (4xx, 5xx errors)InvalidArgumentException- Invalid constructor parametersRuntimeException- JSON decode failures\Exception- General errors (token refresh, validation)
Security
Credential Protection
The library implements several security features:
- Credential Masking: The
__toString()method masks sensitive data - No Hardcoded Secrets: Use environment variables
- Secure Defaults: Debug mode disabled by default
Best Practices
// ❌ DON'T: Log the entire API object error_log(print_r($api, true)); // Credentials would be masked but still risky // ✅ DO: Log only what you need error_log("Account count: " . count($accounts)); // ✅ DO: Store tokens securely // Use encrypted database storage or secure key management $encryptedToken = encryptToken($api->getAccessToken()); storeSecurely($encryptedToken); // ✅ DO: Use HTTPS for callbacks $callbackUrl = 'https://your-domain.com/callback'; // Never HTTP
Testing
Run the test suite:
# Copy and configure phpunit.xml with your test credentials cp phpunit.xml.dist phpunit.xml # Run tests ./vendor/bin/phpunit
Note: Some tests require valid API credentials and may interact with the live API.
Architecture
The library uses a trait-based architecture for organization:
SchwabAPI (Main Class)
├── RequestTrait (HTTP methods)
├── AccountRequests (Account operations)
├── OrderRequests (Order management)
├── TransactionRequests (Transaction history)
├── QuotesRequests (Quote data)
├── PriceHistoryRequests (Historical data)
├── OptionChainsRequests (Options data)
├── OptionExpirationChainRequests (Option expiration)
├── MoversRequests (Market movers)
├── MarketHoursRequests (Trading hours)
├── InstrumentsRequests (Instrument lookup)
└── UserPreferenceRequests (User preferences)
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Changelog
Version 2.0.0 (Latest)
- ✅ Complete API coverage for Schwab Trader API
- ✅ Added transaction management
- ✅ Added order preview, replace, and cancel operations
- ✅ Upgraded to Laravel 11 support
- ✅ Enhanced error handling with specific exceptions
- ✅ Added comprehensive parameter validation
- ✅ Improved security with credential masking
- ✅ Modernized code (removed alternative syntax)
- ✅ Added support for PUT and DELETE HTTP methods
Resources
License
This project is licensed under the MIT License - see the LICENSE file for details.
Disclaimer
This library is not affiliated with, officially maintained by, or endorsed by Charles Schwab & Co., Inc. Use at your own risk. Always test with small amounts in a paper trading account before using in production.
Trading stocks and options involves risk. This library is provided "as-is" without warranty of any kind.
Support
For issues, questions, or contributions:
- Issues: GitHub Issues
- Email: michaeldrennen74@gmail.com
Made with ❤️ by Michael Drennen