npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sudowealth/schwab-api

v2.1.1

Published

TypeScript client for Charles Schwab API with OAuth support, market data, trading functionality, and complete type safety

Readme

Schwab API Client

TypeScript client for Charles Schwab API with OAuth support, market data, trading functionality, and complete type safety.

Unofficial Library

This is an unofficial, community-developed TypeScript client library for interacting with Schwab APIs. It has not been approved, endorsed, or certified by Charles Schwab. It is provided as-is, and its functionality may be incomplete or unstable. Use at your own risk, especially when dealing with financial data or transactions.

Getting Started

To use the Schwab API, you'll need to register for a developer account:

  1. Visit Schwab Developer Portal
  2. Sign up for a developer account
  3. Create an application to obtain your client ID and secret
  4. Review the API documentation and usage limits

Features

  • OAuth Helper: Client-credentials OAuth flow with automatic token handling
  • Request Pipeline: Middleware system for auth, rate limits, and retries
  • Type Safety: Complete TypeScript definitions for all API endpoints
  • Zod Validation: Runtime schema validation for API responses
  • Market Data: Real-time quotes, price history, options chains, market hours, and movers
  • Trading: Account management, order placement, transaction history, and user preferences

Installation

Available on npm:

npm install @sudowealth/schwab-api

Quick Start

Prerequisites:

  1. You must have a Schwab developer account. You can register at @https://developer.schwab.com/register.
  2. Create an application at @https://developer.schwab.com/dashboard/apps.
  3. In your application settings, provide your callback URL.
  4. Obtain the Client ID (App Key) and Client Secret from your application page. These will be used as environment variables (e.g., SCHWAB_CLIENT_ID and SCHWAB_CLIENT_SECRET).

The quickest way to get started is by using createSchwabAuth along with createApiClient. The examples below use the OAuth 2.0 Code Flow.

Basic Setup

import { createSchwabAuth, createApiClient } from '@sudowealth/schwab-api'

// Create the auth manager (Enhanced OAuth client)
const auth = createSchwabAuth({
	oauthConfig: {
		clientId: process.env.SCHWAB_CLIENT_ID!,
		clientSecret: process.env.SCHWAB_CLIENT_SECRET!,
		redirectUri: 'https://example.com/callback',
		// Optional: provide persistence callbacks
		// save: async (tokens) => { ... },
		// load: async () => { ... },
	},
})

// Generate login URL
console.log('Visit:', auth.getAuthorizationUrl().authUrl)

// Exchange auth code for tokens (when user returns)
const tokens = await auth.exchangeCode('<authorization-code>')

// Optionally persist tokens here...

// Create API client using the auth manager
const schwab = createApiClient({ auth })

Market Data

// Get real-time quotes
const quotes = await schwab.marketData.quotes.getQuotes({
	queryParams: {
		symbols: 'AAPL,MSFT,GOOGL',
		fields: 'quote,fundamental',
	},
})

// Get price history
const history = await schwab.marketData.priceHistory.getPriceHistory({
	queryParams: {
		symbol: 'AAPL',
		periodType: 'day',
		period: 10,
		frequencyType: 'minute',
		frequency: 1,
	},
})

// Get options chain
const options = await schwab.marketData.options.getOptionChain({
	queryParams: {
		symbol: 'AAPL',
		contractType: 'CALL',
		strikeCount: 10,
	},
})

// Get market hours
const hours = await schwab.marketData.marketHours.getMarketHours({
	queryParams: {
		markets: ['equity', 'option'],
	},
})

// Get movers
const movers = await schwab.marketData.movers.getMovers({
	pathParams: { symbol_id: '$SPX' },
	queryParams: { sort: 'up', frequency: 0 },
})

// Search instruments
const instruments = await schwab.marketData.instruments.getInstruments({
	queryParams: {
		symbol: 'AAPL',
		projection: 'symbol-search',
	},
})

Trading

// Get accounts
const accounts = await schwab.trader.accounts.getAccounts()

// Get account details
const account = await schwab.trader.accounts.getAccountByNumber({
	pathParams: { accountNumber: 'your-account-hash' },
	queryParams: { fields: 'positions' },
})

// Get orders
const orders = await schwab.trader.orders.getOrdersByAccount({
	pathParams: { accountNumber: 'your-account-hash' },
	queryParams: { maxResults: 50 },
})

// Place an order
const orderResponse = await schwab.trader.orders.placeOrderForAccount({
	pathParams: { accountNumber: 'your-account-hash' },
	body: {
		orderType: 'MARKET',
		session: 'NORMAL',
		duration: 'DAY',
		orderStrategyType: 'SINGLE',
		orderLegCollection: [
			{
				instruction: 'BUY',
				quantity: 10,
				instrument: {
					symbol: 'AAPL',
					assetType: 'EQUITY',
				},
			},
		],
	},
})

// Get transactions
const transactions = await schwab.trader.transactions.getTransactions({
	pathParams: { accountNumber: 'your-account-hash' },
	queryParams: {
		types: 'TRADE',
		startDate: '2024-01-01',
		endDate: '2024-12-31',
	},
})

// Get user preferences
const preferences = await schwab.trader.userPreference.getUserPreference()

Advanced Configuration with Middleware

import { createSchwabAuth, createApiClient } from '@sudowealth/schwab-api'

const auth = createSchwabAuth({
	oauthConfig: {
		clientId: process.env.SCHWAB_CLIENT_ID!,
		clientSecret: process.env.SCHWAB_CLIENT_SECRET!,
		redirectUri: 'https://example.com/callback',
	},
})

// Customize middleware via options
const schwab = createApiClient({
	auth,
	middleware: {
		rateLimit: { maxRequests: 120, windowMs: 60_000 },
		retry: { maxAttempts: 3, baseDelayMs: 1000 },
	},
})

Important Notes

Token Management

The auth client provides a unified interface for OAuth operations:

  • getAuthorizationUrl(): Generate URL for user login
  • exchangeCode(code): Exchange authorization code for tokens
  • refresh(refreshToken): Refresh expired access tokens

Refresh Token Expiration

Important: Schwab refresh tokens have a hard 7-day expiration limit that cannot be extended. This is a security measure enforced by Schwab's API servers.

When a refresh token expires:

  • The refresh() method will throw a SchwabAuthError with code TOKEN_EXPIRED
  • The user must complete a full re-authentication flow through Schwab's login page
  • There is no way to refresh tokens indefinitely without user interaction

Handling Token Expiration

try {
	const newTokens = await auth.refresh(oldRefreshToken)
	// Update stored tokens
} catch (error) {
	if (error instanceof SchwabAuthError && error.code === 'TOKEN_EXPIRED') {
		// Redirect user to re-authenticate
		const { authUrl } = auth.getAuthorizationUrl()
		window.location.href = authUrl
	}
}

API Structure

The API client is organized into logical namespaces:

  • marketData: Real-time and historical market data

    • quotes: Real-time quotes and fundamentals
    • priceHistory: Historical price data and charts
    • options: Options chains and pricing
    • marketHours: Trading hours for different markets
    • movers: Top gaining/losing securities
    • instruments: Security search and lookup
  • trader: Account and trading operations

    • accounts: Account information and positions
    • orders: Order management and execution
    • transactions: Transaction history and details
    • userPreference: User settings and preferences

Security Best Practices

Token Storage

⚠️ NEVER store tokens in plain text. Always encrypt sensitive data before storage.

// ❌ BAD - Insecure plain text storage
const insecureStorage = {
	save: async (tokens) => {
		await fs.writeFile('tokens.json', JSON.stringify(tokens))
	},
	load: async () => {
		const data = await fs.readFile('tokens.json', 'utf-8')
		return JSON.parse(data)
	},
}

// ✅ GOOD - Encrypted storage example
import crypto from 'crypto'

const secureStorage = {
	save: async (tokens) => {
		// Use a secure key management system in production
		const key = process.env.ENCRYPTION_KEY
		const iv = crypto.randomBytes(16)
		const cipher = crypto.createCipheriv(
			'aes-256-gcm',
			Buffer.from(key, 'hex'),
			iv,
		)

		let encrypted = cipher.update(JSON.stringify(tokens), 'utf8', 'hex')
		encrypted += cipher.final('hex')

		const authTag = cipher.getAuthTag()

		await secureStore.set('tokens', {
			encrypted,
			iv: iv.toString('hex'),
			authTag: authTag.toString('hex'),
		})
	},
	load: async () => {
		const data = await secureStore.get('tokens')
		if (!data) return null

		const key = process.env.ENCRYPTION_KEY
		const decipher = crypto.createDecipheriv(
			'aes-256-gcm',
			Buffer.from(key, 'hex'),
			Buffer.from(data.iv, 'hex'),
		)

		decipher.setAuthTag(Buffer.from(data.authTag, 'hex'))

		let decrypted = decipher.update(data.encrypted, 'hex', 'utf8')
		decrypted += decipher.final('utf8')

		return JSON.parse(decrypted)
	},
}

Credential Management

  • Never commit credentials: Keep .env files in .gitignore
  • Use environment variables: Store sensitive data in environment variables or secure vaults
  • Rotate credentials regularly: Implement a credential rotation policy
  • Principle of least privilege: Only grant the minimum required permissions
# .env (never commit this file)
SCHWAB_CLIENT_ID=your-client-id
SCHWAB_CLIENT_SECRET=your-client-secret
ENCRYPTION_KEY=your-256-bit-hex-key

Security Checklist

  • [ ] Use HTTPS for all API communications
  • [ ] Encrypt tokens before storing them
  • [ ] Never log tokens or sensitive data
  • [ ] Implement proper error handling that doesn't leak information
  • [ ] Use secure key management (AWS KMS, Azure Key Vault, etc.)
  • [ ] Monitor for suspicious activity
  • [ ] Implement request signing if available
  • [ ] Keep dependencies up to date

Common Security Mistakes to Avoid

  1. Logging Sensitive Data

    // ❌ NEVER log tokens
    console.log('Access token:', tokens.access_token)
    
    // ✅ Log only non-sensitive metadata
    console.log('Token refreshed successfully')
  2. Storing Secrets in Code

    // ❌ NEVER hardcode secrets
    const clientSecret = 'abc123-secret-key'
    
    // ✅ Use environment variables
    const clientSecret = process.env.SCHWAB_CLIENT_SECRET
  3. Exposing Error Details

    // ❌ Don't expose internal details
    catch (error) {
      res.json({ error: error.stack })
    }
    
    // ✅ Return generic error messages
    catch (error) {
      console.error('Internal error:', error) // Log internally
      res.json({ error: 'Authentication failed' }) // Generic response
    }

Error Handling

import { SchwabApiError, SchwabAuthError } from '@sudowealth/schwab-api'

try {
	await schwab.trader.accounts.getAccounts()
} catch (error) {
	if (error instanceof SchwabAuthError) {
		if (error.code === 'TOKEN_EXPIRED') {
			// Handle expired tokens
		}
	} else if (error instanceof SchwabApiError) {
		// Handle API errors
		console.error('API Error:', error.message)
	}
}

Development

  • Clone the repository
  • Install dependencies: npm install
  • Build: npm run build
  • Lint: npm run lint
  • Type check: npm run typecheck
  • Format: npm run format
  • Validate all: npm run validate

Installing Beta Versions

To install the latest beta release:

npm install @sudowealth/schwab-api@beta

License

MIT