Migration guide: 4.x → 5.0
Version 5 adopts Alpaca's latest Trading and Market Data OpenAPI documents. The upstream documents removed public endpoints and replaced several anonymous generated models with named schemas, so this is a major SDK release.
Still using 3.x? Complete the 3.x → 4.0 migration first. The migration guide index describes the supported upgrade paths.
Install the new major:
npm install @alpacahq/alpaca-trade-api@^5
Automated migration helper
The alpaca-v4-to-v5.js codemod applies safe
generated-contract renames and adds review diagnostics for semantic changes:
# TypeScript
npx jscodeshift \
-t ./node_modules/@alpacahq/alpaca-trade-api/codemods/alpaca-v4-to-v5.js \
--parser=tsx --extensions=ts,tsx,mts,cts src
# JavaScript
npx jscodeshift \
-t ./node_modules/@alpacahq/alpaca-trade-api/codemods/alpaca-v4-to-v5.js \
--parser=babel --extensions=js,jsx,mjs,cjs src
Run it on a clean version-control branch and review the resulting diff and
jscodeshift report. The transform does not silently rewrite removed APIs, SSE
control flow, REORG/REO semantics, or Trading dividend flag comparisons.
See the codemod reference for its exact scope.
Applications that receive an Alpaca client through dependency injection can
register its exact lexical name with --instanceName=client (comma-separate
multiple names). Each requested name must resolve to exactly one lexical binding
in a source file. This opt-in includes function parameters and stable aliases,
but ignores reassigned, shadowed, or otherwise ambiguous bindings. A variable
merely named alpaca is not trusted without the option or a proven SDK
constructor.
JavaScript users must also manually audit response consumers because untyped data flow cannot always prove that a property came from this SDK. In particular, search for and review:
CorporateAnnouncementdate string operations and the removedcorporateActionsId/expirationDatefields;Assets.easyToBorrow, replacing it with an appropriateborrowStatuscomparison;- truthiness checks on Trading dividend
foreign/specialstring flags.
TypeScript reports removed and structurally incompatible fields through compiler
diagnostics, but it does not reject truthiness checks on the valid string
union "true" | "false". JavaScript and TypeScript users must both audit
Trading dividend foreign / special checks. The codemod deliberately does
not match property names globally because that would modify unrelated
application objects. It follows proven model types, chained type aliases, and
stable value aliases instead.
Required non-nullable primitive arrays now receive the same defensive
deserialization as model arrays: an upstream null or missing value becomes
[] instead of leaking null through a non-nullable TypeScript field. This
only changes malformed responses that contradict their OpenAPI requirement.
Removed upstream endpoints
The Market Data specification no longer publishes the crypto perpetual-futures or index-value endpoints. Version 5 therefore removes:
alpaca.marketData.cryptoPerpetualFutures(also available through thealpaca.dataalias in v4) andmarketData.CryptoPerpetualFuturesApi;alpaca.marketData.indices(also available throughalpaca.data) andmarketData.IndexApi;getIndexValues,iterateIndexValues, andcollectIndexValuesBySymbol;- generated
indexValues/indexLatestValuesand theirRawsiblings; - generated
cryptoPerpLatestBars,cryptoPerpLatestFuturesPricing,cryptoPerpLatestOrderbooks,cryptoPerpLatestQuotes,cryptoPerpLatestTrades, and theirRawsiblings; - the canonical
IndexValue,toIndexValue, andtoIndexValuesBySymbolexports; - the generated
marketData.IndexValuemodel and itsIndexValueFromJSON,IndexValueFromJSONTyped,IndexValueToJSON,IndexValueToJSONTyped, andinstanceOfIndexValueruntime helpers; - the generated
CryptoPerpFuturesPricing,CryptoPerpLatestFuturesPricingResp,CryptoPerpLoc,IndexLatestValuesResp, andIndexValuesRespmodels and theirFromJSON,FromJSONTyped,ToJSON,ToJSONTyped, andinstanceOf*runtime helpers.
These endpoints were removed upstream rather than relocated; continuing to call them on version 4 results in server errors.
The codemod marks direct and destructured uses when it can prove they came from
alpaca.marketData, the alpaca.data alias, or marketDataShapes. Resolve each
marker manually; unrelated objects with the same property names are left alone.
Generated trading model renames
Order creation now uses the named CreateOrderRequest schema:
await alpaca.trading.orders.postOrder({
createOrderRequest: {
symbol: "AAPL",
qty: "1",
side: "buy",
type: "market",
timeInForce: "day",
},
});
Replace PostOrderRequest, PostOrderRequestTakeProfit, and
PostOrderRequestStopLoss model references with CreateOrderRequest,
CreateOrderRequestTakeProfit, and CreateOrderRequestStopLoss. The ergonomic
order builders (market, limit, bracket, and the other order helpers) keep
their existing call signatures.
Do not confuse the removed body model with the generated operation wrapper:
trading.PostOrderRequest still exists as the argument type for
orders.postOrder(...), and now contains a createOrderRequest property whose
value is the renamed body model.
If you explicitly imported the generated operation wrapper, replace the v4
PostOrderOperationRequest type with v5 PostOrderRequest, and replace its
postOrderRequest property with createOrderRequest. In other words:
- v4 wrapper:
PostOrderOperationRequest, containingpostOrderRequest: PostOrderRequest; - v5 wrapper:
PostOrderRequest, containingcreateOrderRequest: CreateOrderRequest.
Response models also received stable upstream names:
GetOptionsContracts200Response→OptionContractsResponse;GetV2CorporateActionsAnnouncements200ResponseInnerandGetV2CorporateActionsAnnouncementsId200Response→CorporateAnnouncement;PositionClosedReponse→PositionClosedResponse.
The position-close rename only corrects the generated TypeScript name; the
closeAllPositions() response payload and runtime behavior are unchanged. The
codemod updates the model name and its generated JSON conversion and type-guard
helpers.
CorporateAnnouncement also adopts the current upstream field contract; this
is not only a type rename:
declarationDate,exDate,payableDate, andrecordDateare nowDatevalues instead of strings;caTypeis now theCorporateActionCaTypeunion rather than an arbitrary string;- use
corporateActionIdinstead of the removedcorporateActionsId; expirationDatewas removed, whileeffectiveDate?: Datewas added.
Update string-only date handling such as slice() or direct serialization. To
send a date elsewhere, format it explicitly (for example,
announcement.effectiveDate?.toISOString()). Although announcement responses
preserve unknown wire fields for forward compatibility, raw snake-case fields
are not replacements for the removed camel-case properties.
Other generated contract changes
getV2CorporateActionsAnnouncements({ caTypes })now takes aCorporateActionCaType[](for example,["Dividend"]) instead of one untyped string. The codemod splits comma-delimited literals such as"Dividend,Merger"into["Dividend", "Merger"]and canonicalizes known casing; dynamic, empty, or unknown values are left with a review TODO.Assets.easyToBorrowwas removed upstream; useborrowStatus.OptionContract.ppindis now required.PortfolioHistory.baseValueand entries inequity,profitLoss, andprofitLossPctcan benull;cashflowis now typed as a map of numeric arrays.- The
foreignandspecialfields on cash-dividend activity models are typed as the wire strings"true"/"false"rather than booleans, matching what the API already returns. groupIdis now optional on options activity models; handleundefinedwhen grouping related activities.ActivityV2DetailNTAis now a union of the concrete activity-detail models instead of an unstructured interface.GetTokenizationRequestsIssuerEnumwas replaced by the sharedTokenizationIssuermodel.
String-valued dividend flags
Version 5 corrects the generated declarations for Trading activity details to
match the existing API wire format; it does not convert these flags from
booleans at runtime. Both non-empty strings are truthy in JavaScript, so do not
use if (details.foreign), if (details.special), or
Boolean(details.foreign). Compare the value explicitly after narrowing the
activity detail:
TypeScript permits those truthiness checks because both values are valid strings; a clean type-check does not make the checks safe.
const isForeign = details.foreign === "true";
const isSpecial = details.special === "true";
This applies to Trading cash-dividend and substitute-payment activity details.
Market Data corporate-action models continue to expose their foreign and
special fields as booleans.
Narrowing activity details
Version 4 exposed non-trade activity details as an unstructured interface.
Version 5 preserves each concrete detail shape in a union, so narrow it before
reading variant-specific fields. The parent activityType describes the wire
event, but TypeScript does not automatically correlate that string with the
separate details union; combine the semantic check with a generated guard:
import { trading } from "@alpacahq/alpaca-trade-api";
if (
event.activityType === "OPTRD" &&
trading.instanceOfOPTRDActivityV2(event.details)
) {
console.log(event.details.symbol);
console.log(event.details.groupId ?? "ungrouped");
}
The generated instanceOf... functions are exported for every concrete detail
model. Avoid an unchecked cast when processing externally supplied event data.
New REO activity code and clarified REORG meaning
Version 5 adopts the upstream REO activity code for reorganizations. The
previous specification described REORG generically as "Reorg CA", but already
listed WRM (worthless removal) as its only subtype and exposed a corresponding
worthless-removal detail model. The updated contract makes that distinction
explicit:
REOrepresents a reorganization (ActivityType.Reo);REORGremains the code for a worthless-removal corporate action (ActivityType.Reorg).
Update switches, filters, persisted mappings, and analytics that interpret activity-type strings according to their intent:
- use
"REO"for newly classified reorganization events; - retain
"REORG"for worthless removals, normally with subtype"WRM"; - include both when the application handles the broader family of corporate actions.
Do not mechanically rewrite persisted historical "REORG" values to "REO";
inspect their subtype and meaning. The generated ActivityType.Reorg property
still resolves to the "REORG" wire value, while ActivityType.Reo resolves to
"REO".
Added API coverage
Version 5 adds Trading API methods searchVASPs and
updateWhitelistedAddressTravelRuleInfo under trading.cryptoFunding, plus
subscribeToCorporateActionsEventsSSE under
marketData.corporateActions.
TravelRuleInfo requires both a destination (beneficiaryVaspId,
beneficiaryIsSelfHosted: true, or beneficiaryManualEntry) and an identity
(beneficiaryEntityName, or both beneficiaryGivenName and
beneficiaryFamilyName). TypeScript enforces these combinations, and runtime
serialization rejects incomplete JavaScript payloads before sending them.
Safe tokenization-mint retries
trading.tokenization.postTokenizationMint() now accepts an optional
idempotencyKey. Production callers should supply a new UUID for each logical
mint operation:
await alpaca.trading.tokenization.postTokenizationMint({
idempotencyKey: crypto.randomUUID(),
tokenizationMintRequest,
});
Repeating the same request body with the same key returns the existing mint
response instead of creating a duplicate. Reusing the key with a different
body returns HTTP 422. The API currently accepts keys up to 128 characters,
but new applications should use a UUIDv4 or UUIDv7 string (36 characters)
because Alpaca is moving toward that limit.
The SDK does not automatically retry POST requests. The idempotency key makes
an application-directed retry safe after a timeout, network error, or transient
5xx; retain the same key and body for every attempt at the same logical mint.
Generated SSE now returns a live stream
In version 4, subscribeToActivitiesSSE() was generated as
Promise<ActivityEventV2[]>. That signature was not functional for a live
text/event-stream: it buffered forever, attempted JSON-array parsing, and
inherited the ordinary request deadline.
Version 5 replaces it with Promise<SseSubscription<ActivityEventV2>>; the new
corporate-actions SSE method uses the same shape. Prefer the short facade
helpers for application code:
const events = await alpaca.trading.subscribeActivities();
try {
for await (const event of events) {
console.log(event.activityType, event.details);
}
} finally {
events.close();
}
The corresponding corporate-action helper is
alpaca.marketData.subscribeCorporateActions(). Both delegate to the raw
generated methods, which remain available as
trading.events.subscribeToActivitiesSSE() and
marketData.corporateActions.subscribeToCorporateActionsEventsSSE().
The generated *Raw siblings now return SSEApiResponse<T> rather than
JSONApiResponse<T[]>.
The second argument is now SseOptions. Plain RequestInit fields remain valid
at the top level for source compatibility, while new code can group them under
requestInit:
await alpaca.trading.subscribeActivities(
{},
{
signal: controller.signal,
requestInit: {
headers: { "X-Correlation-ID": correlationId },
},
},
);
Nested requestInit values override duplicate top-level RequestInit fields,
except for signal: when both locations provide an AbortSignal, either signal
cancels the subscription. Prefer supplying one signal unless intentionally
combining independent cancellation sources.
The former InitOverrideFunction form is not supported for SSE; use
requestInit or middleware pre instead. SSE runs middleware pre and
onError, but not post, because cloning a long-lived response can buffer or
stall its body.
Live subscriptions reconnect by default and resume with Last-Event-ID; finite
until / untilId queries do not reconnect. The initial open is limited to two
retries by default; established live streams continue reconnecting unless you
set maxAttempts / maxElapsedMs. Replays may include the last event again, so
deduplicate side effects by event id. Pass an AbortSignal, reconnect: false,
or reconnect limits when you need explicit lifecycle bounds. For a successful
200/204 connection, the configured timeoutMs ends after validated response
headers and does not bound the live body. For non-2xx responses, it remains
active while the SDK reads the bounded error body for a typed ApiError.
connectTimeoutMs overrides this deadline per subscription, and 0 disables
it. Opt into idleTimeoutMs or maxDurationMs to limit the live body.
Subscriptions are single-consumer; call close() if you open one but never
start iteration.
Fetch-based SSE accepts the same key/secret or OAuth credentials as REST.
Unlike WebSocket streams, it can use an OAuth-only client. Version 5 also
accepts a Promise or function-backed accessToken on the Alpaca facade; a
provider is evaluated before every REST request and SSE reconnect so expiring
tokens can be refreshed. HTTP 401 and 403 responses are terminal and do not
cause a reconnect loop.