🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@aetherwealth/sdk

Package Overview
Dependencies
Maintainers
2
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@aetherwealth/sdk

Official typed TypeScript SDK for Aether Wealth journals, accounts, analytics, alerts, and forex and crypto market data.

latest
npmnpm
Version
0.2.13
Version published
Weekly downloads
1.2K
300.66%
Maintainers
2
Weekly downloads
 
Created
Source

@aetherwealth/sdk

Typed TypeScript client for the Aether Wealth public API. Programmatic access to your trading journal, accounts, analytics, alerts, market data, and diary over the public REST surface (/api/public/v1/…).

Keep your API key secret. Authentication uses an API key for the public API (aw_live_…) sent as a bearer token. It is a server-side secret: treat it like a password — never commit it or ship it to an untrusted client.

Install

npm install @aetherwealth/sdk

Quickstart

import { AetherClient } from '@aetherwealth/sdk'

const client = new AetherClient({
  auth: { type: 'apiKey', apiKey: process.env.AETHER_API_KEY! },
  // baseUrl defaults to the production API (https://api.aetherwealth.ai).
  // Set it only to target staging or a local dev backend.
})

// Every call is authenticated with your API key.
const { data: openTrades } = await client.trades.list({ status: 'OPEN' })
const stats = await client.stats.summary({ pair: 'EURUSD' })

Authentication

Each request sends your secret API key as Authorization: Bearer <apiKey>. The key scopes every operation to its owner — you never pass userId in a request body. auth is a discriminated union (one member today, apiKey) so future auth modes can be added without breaking existing callers.

new AetherClient({
  baseUrl,
  auth: { type: 'apiKey', apiKey },
  fetchImpl,             // optional — inject a custom fetch (tests, proxies)
  userAgent,             // optional
  timeoutMs: 30_000,     // optional — per-request timeout (default 30s; 0 disables)
  validateResponses: false, // optional — opt-in Zod validation of responses
  dangerouslyAllowBrowser: false, // optional — see "Browser use" below
  onRequest, onResponse, // optional diagnostics hooks
})

The diagnostics hooks receive only { method, url, status, ms } — your API key travels solely on the Authorization header and never appears in a hook payload, a thrown error, or the request URL.

Browser use is blocked by default

The API key is a server-side secret. Constructing the client in a browser-like environment (window.document present) throws — a key shipped to a browser is visible in the bundle and DevTools and must be treated as public. Call the API from your server. If you are certain you are in a trusted non-browser runtime that happens to define window, set dangerouslyAllowBrowser: true (mirrors the OpenAI SDK).

Resources

ResourceMethods
client.tradeslist · get · create · update · close · delete · pages · listAll
client.accountslist · create · update · delete · trades · tradesAll
client.statssummary
client.alertslist · createPrice · createTrendline · update · delete · listIndicator · createIndicator · updateIndicator · deleteIndicator
client.marketconfig · calendar · macroSeries · macro · subscribe
client.diarylist · get · upsert · delete · pages · listAll

List methods return { data, pagination }; single-item methods return the object directly. All response fields are camelCase.

Pagination

pages() and listAll() async-iterate a paginated resource, auto-advancing page until the last page (or an empty page), with an infinite-loop backstop:

// One page at a time (keeps pagination metadata):
for await (const page of client.trades.pages({ status: 'CLOSED', limit: 100 })) {
  console.log(page.pagination.page, page.data.length)
}

// Every item, flattened across all pages:
for await (const trade of client.trades.listAll({ pair: 'EURUSD' })) {
  handle(trade)
}

// Also: client.diary.pages/listAll and client.accounts.tradesAll(accountId)

Iteration starts at query.page if given, else page 1.

Live market stream

client.market.subscribe mints a short-lived ticket from aether-backend and opens one reconnecting WebSocket for up to 25 symbols. The user API key needs market:read; it is sent only on the HTTPS ticket request, never in the WebSocket URL.

const stop = client.market.subscribe(
  { symbols: ['EURUSD', 'GBPUSD'] },
  {
    onUpdate(update) {
      console.log(update.symbol, update.candle.close)
    },
    onError(error) {
      console.error(error.message)
    },
    onStatus(status) {
      if (status.state === 'terminal') {
        console.error('stream stopped permanently', status.error)
      }
    },
  },
)

// Idempotent; also available through the optional AbortSignal input.
stop()

The V1 feed is fixed to 1m. candle.close is the latest observed price, not an executable bid/ask quote. Network failures, 5xx ticket responses, transient 408/425/429 responses, missed heartbeat pongs, and transient socket closes reconnect automatically with a fresh ticket; a 429 waits at least as long as Retry-After. The backend's planned 4001 reauthentication close is also transient and remints the ticket.

Other ticket 4xx responses, the backend's explicit market_stream_unavailable response, unscoped non-retryable gateway errors, and policy/auth socket closes stop that subscription. They call onError and emit an onStatus event whose state is terminal; no later reconnect occurs. Retryable gateway errors reconnect. A non-retryable error scoped to symbol is reported as AetherMarketStreamGatewayError while the socket stays alive for unaffected symbols. Local and whole-stream gateway policy failures use AetherMarketStreamTerminalError; ticket HTTP failures keep their typed AetherApiError subclass.

One AetherClient may have at most five active market.subscribe() calls, with up to 25 symbols per call. A sixth call throws AetherMarketStreamTerminalError with code MARKET_STREAM_LIMIT. Calling the returned unsubscribe function—or reaching a terminal failure—immediately frees that slot.

The package's conditional Node export supplies the ws transport. Runtimes resolving the universal entry, including edge bundlers, use their native WebSocket, keeping that entry free of Node-only built-ins.

Timeouts & cancellation

Every request is bounded by timeoutMs (default 30s), overridable per call. A timeout aborts the request and rejects with AetherTimeoutError (a subclass of AetherNetworkError, so it's retryable and caught by existing network-error handlers). You can also pass your own AbortSignal — the request aborts when either the timeout or your signal fires:

// Client-wide default (30s) or a per-request override via the low-level request():
const slow = new AetherClient({ baseUrl, auth, timeoutMs: 60_000 })
await client.request('/api/public/v1/trades/list', { method: 'POST', body: {}, timeoutMs: 5_000 })

// Caller cancellation (e.g. a UI "cancel" button or request budget):
const controller = new AbortController()
setTimeout(() => controller.abort(new Error('cancelled')), 1_000)
await client.request('/api/public/v1/stats/stats', { method: 'POST', body: {}, signal: controller.signal })

A timeout throws AetherTimeoutError; a caller-initiated abort surfaces your signal's own reason (it is not treated as a transient error to retry).

Idempotency & safe retries

Each create — trades.create, accounts.create, alerts.createPrice, alerts.createTrendline, alerts.createIndicator — sends an Idempotency-Key header so a create that the server committed but whose response you never saw won't duplicate on a re-send. If you don't pass one, the SDK generates a fresh key per call:

await client.trades.create(input) // auto Idempotency-Key, protects this one call

An auto per-call key protects a single invocation. It does not make a create retry-safe: a new key each attempt means the backend can't dedup. To retry a create safely, pass a stable key so every attempt targets the same record:

import { withRetry } from '@aetherwealth/sdk'

const idempotencyKey = crypto.randomUUID() // generated ONCE, outside the retry
const trade = await withRetry(
  () => client.trades.create(input, { idempotencyKey }),
  { maxAttempts: 3 },
)

Do not generate the key inside the retried closure — each attempt would get a different key and dedup would be lost. withRetry only retries transient failures (AetherRateLimitError, AetherNetworkError, AetherTimeoutError); retrying a mutation is only safe with a stable key.

Errors

Non-2xx responses (and { success: false } envelopes) throw a typed AetherApiError subclass so you can branch on the failure mode:

import {
  AetherRateLimitError,
  AetherNotFoundError,
  AetherAuthError,
  AetherTimeoutError,
} from '@aetherwealth/sdk'

try {
  await client.trades.get(id)
} catch (err) {
  if (err instanceof AetherNotFoundError) { /* 404 */ }
  else if (err instanceof AetherRateLimitError) { /* 429 — err.retryAfterSeconds */ }
  else if (err instanceof AetherAuthError) { /* 401 — invalid or missing API key */ }
  else if (err instanceof AetherTimeoutError) { /* client timeout — err.timeoutMs / err.elapsedMs */ }
  else throw err
}

AetherTimeoutError and AetherNetworkError (its parent) never reached the server; they carry no HTTP status. All HTTP failures are AetherApiError subclasses.

Optional runtime validation

Compile-time types come from the SDK. Runtime validation is off by default for performance and additive-field tolerance (a backend that adds a field won't break older SDK consumers). Turn it on two ways:

Whole-client — every resource validates its response with a Zod parser and throws a ZodError on shape drift (unknown fields are still tolerated):

const client = new AetherClient({ baseUrl, auth, validateResponses: true })

Per-call — import a parser and validate a single response:

import { parseTrade } from '@aetherwealth/sdk'
const trade = parseTrade(await client.trades.get(id)) // throws on shape drift

Not covered

AI chat conversations remain tRPC-only and are not part of this SDK. API-key management is likewise out of scope: an API key can't provision other keys.

License

MIT. See the LICENSE file included with this package. Service use is governed by the Aether Wealth Terms of Service.

Keywords

aether wealth

FAQs

Package last updated on 03 Aug 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts