
Company News
AWS Security Hub Adds Socket for Supply Chain Security
Socket is now in the AWS Security Hub Extended plan. Adopt it through AWS, apply committed spend, and block malicious open source packages.
@aetherwealth/sdk
Advanced tools
Official typed TypeScript SDK for Aether Wealth journals, accounts, analytics, alerts, and forex and crypto market data.
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.
npm install @aetherwealth/sdk
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' })
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.
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).
| Resource | Methods |
|---|---|
client.trades | list · get · create · update · close · delete · pages · listAll |
client.accounts | list · create · update · delete · trades · tradesAll |
client.stats | summary |
client.alerts | list · createPrice · createTrendline · update · delete · listIndicator · createIndicator · updateIndicator · deleteIndicator |
client.market | config · calendar · macroSeries · macro · subscribe |
client.diary | list · get · upsert · delete · pages · listAll |
List methods return { data, pagination }; single-item methods return the
object directly. All response fields are camelCase.
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.
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.
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).
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.
withRetryonly retries transient failures (AetherRateLimitError,AetherNetworkError,AetherTimeoutError); retrying a mutation is only safe with a stable key.
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.
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
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.
MIT. See the LICENSE file included with this package. Service use is governed
by the Aether Wealth Terms of Service.
FAQs
Official typed TypeScript SDK for Aether Wealth journals, accounts, analytics, alerts, and forex and crypto market data.
The npm package @aetherwealth/sdk receives a total of 1,175 weekly downloads. As such, @aetherwealth/sdk popularity was classified as popular.
We found that @aetherwealth/sdk demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?

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.

Company News
Socket is now in the AWS Security Hub Extended plan. Adopt it through AWS, apply committed spend, and block malicious open source packages.

Research
/Security News
Popular npm packages keyv and cacheable compromised.

Security News
A misconfiguration gave three Anthropic models internet access, and one, believing it was in a simulation, shipped a credential-stealing package to PyPI.