From 9370096e07100973d8c76f768541d1389bf79718 Mon Sep 17 00:00:00 2001 From: Luca Mitas Date: Mon, 20 Apr 2026 12:13:29 -0300 Subject: [PATCH 01/29] feat: adds initial documentation for js sdk --- sdk/js/authentication.mdx | 120 ++++++++++++ sdk/js/client.mdx | 270 +++++++++++++++++++++++++++ sdk/js/funds/candles.mdx | 184 +++++++++++++++++++ sdk/js/funds/index.mdx | 14 ++ sdk/js/index.mdx | 40 ++++ sdk/js/installation.mdx | 98 ++++++++++ sdk/js/markets/index.mdx | 14 ++ sdk/js/markets/status.mdx | 176 ++++++++++++++++++ sdk/js/options/chain.mdx | 269 +++++++++++++++++++++++++++ sdk/js/options/expirations.mdx | 148 +++++++++++++++ sdk/js/options/index.mdx | 14 ++ sdk/js/options/lookup.mdx | 111 +++++++++++ sdk/js/options/quotes.mdx | 136 ++++++++++++++ sdk/js/settings.mdx | 326 +++++++++++++++++++++++++++++++++ sdk/js/stocks/candles.mdx | 222 ++++++++++++++++++++++ sdk/js/stocks/earnings.mdx | 166 +++++++++++++++++ sdk/js/stocks/index.mdx | 14 ++ sdk/js/stocks/news.mdx | 140 ++++++++++++++ sdk/js/stocks/prices.mdx | 224 ++++++++++++++++++++++ sdk/js/stocks/quotes.mdx | 198 ++++++++++++++++++++ 20 files changed, 2884 insertions(+) create mode 100644 sdk/js/authentication.mdx create mode 100644 sdk/js/client.mdx create mode 100644 sdk/js/funds/candles.mdx create mode 100644 sdk/js/funds/index.mdx create mode 100644 sdk/js/index.mdx create mode 100644 sdk/js/installation.mdx create mode 100644 sdk/js/markets/index.mdx create mode 100644 sdk/js/markets/status.mdx create mode 100644 sdk/js/options/chain.mdx create mode 100644 sdk/js/options/expirations.mdx create mode 100644 sdk/js/options/index.mdx create mode 100644 sdk/js/options/lookup.mdx create mode 100644 sdk/js/options/quotes.mdx create mode 100644 sdk/js/settings.mdx create mode 100644 sdk/js/stocks/candles.mdx create mode 100644 sdk/js/stocks/earnings.mdx create mode 100644 sdk/js/stocks/index.mdx create mode 100644 sdk/js/stocks/news.mdx create mode 100644 sdk/js/stocks/prices.mdx create mode 100644 sdk/js/stocks/quotes.mdx diff --git a/sdk/js/authentication.mdx b/sdk/js/authentication.mdx new file mode 100644 index 0000000..10ade49 --- /dev/null +++ b/sdk/js/authentication.mdx @@ -0,0 +1,120 @@ +--- +title: Authentication +sidebar_position: 2 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +The Market Data API uses a **Bearer Token** for authentication. The token is required for each request you make to the API. Your token should have been e-mailed to you when you first signed up for an account. If you do not have a token or have lost your sign-up email, request a new token from the [Market Data Dashboard](https://www.marketdata.app/dashboard/). + +There are three ways to set your token when using the JavaScript SDK: + +1. Set it from an environment variable _(recommended for production)_ +2. Load it from a `.env` file _(recommended for local development)_ +3. Pass it directly when creating the [client](/sdk/js/client) + +On startup, the SDK will look for the `MARKETDATA_TOKEN` environment variable. If found, it will use the token for all requests. The SDK also loads a `.env` file automatically via `dotenv` if one is present in the working directory. + +:::tip +When your code is running in a production environment, we recommend using an environment variable to ensure your token is not stored with your code. This is the most secure way to set your token. +::: + +## How To Set-Up The Environment Variable + +Set the token in the environment variable `MARKETDATA_TOKEN`. Alternatively, you can pass it directly when creating the client, but please be aware that this is not secure and could pose a risk if your code is shared. + +### Set The Environment Variable In The Console + + + + +This command should be run in the terminal. It sets the environment variable for the current session only. If you open a new terminal window or restart your computer, the environment variable will not persist. + +```bash +export MARKETDATA_TOKEN="your_api_token" +``` + +#### Verify the Variable + +To verify that the `MARKETDATA_TOKEN` environment variable has been set properly, run `echo $MARKETDATA_TOKEN`. If set correctly, this prints the token value; if unset, the output is blank. + +#### Make The Variable Persistent + +Add the `export` line to your shell's profile script (`~/.bash_profile`, `~/.bashrc`, `~/.zshrc`, etc.), then either restart your terminal or run `source ~/.bashrc` (adjusting for your shell) to apply. + + + + +This command should be run in the Command Prompt. `setx` sets the variable permanently, but the new value is not available in the current Command Prompt session — you will need to open a new session. + +```bash +setx MARKETDATA_TOKEN "your_api_token" +``` + +#### Verify The Variable + +Open a new Command Prompt and run `echo %MARKETDATA_TOKEN%`. If the output prints the token, the variable is set correctly. If it prints `%MARKETDATA_TOKEN%` unchanged, the variable is not set. + + + + +### Using a .env File + +The SDK automatically loads a `.env` file from your working directory at import time (via [`dotenv`](https://github.com/motdotla/dotenv)). Create a file named `.env` in your project root: + +```env title=".env" +MARKETDATA_TOKEN=your_api_token +``` + +:::warning +Add `.env` to your `.gitignore` so the token is not committed to source control. +::: + +### Make A Test Request + +Use the following code to verify that your authentication is working by making a test request to `SPY` or any other symbol that requires authentication. Do not use `AAPL` to test your authentication because `AAPL` is a free test symbol and will return data even if you are not authenticated properly. + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +// No need to pass a token here — the SDK reads it from +// the MARKETDATA_TOKEN environment variable automatically. +const client = new MarketDataClient(); + +const result = await client.stocks.quotes("SPY"); + +result.match( + (quotes) => console.log(quotes), + (error) => console.error(`Error: ${error.message}`), +); +``` + +If your token is set correctly, you should see the output of the test request. If you see an error, double-check that you have set the token correctly. + +## How To Create a Client and Assign a Token Directly + +If you decide to pass the token directly when creating the client, you can do so by passing it as a property of the configuration object. This is not recommended for production code. + +### Example Code + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const token = "your_token_here"; + +const client = new MarketDataClient({ token }); + +const result = await client.stocks.quotes("SPY"); + +result.match( + (quotes) => console.log(quotes), + (error) => console.error(`Error: ${error.message}`), +); +``` + +## Next Steps + +After successful authentication, you will be able to begin making requests to the Market Data API. We recommend reading the brief overview of how the [client](/sdk/js/client) works, so you can get familiar with interacting with the SDK. + +You may also want to configure [Settings](/sdk/js/settings) to customize output format, date format, and other universal parameters. diff --git a/sdk/js/client.mdx b/sdk/js/client.mdx new file mode 100644 index 0000000..02a0f5d --- /dev/null +++ b/sdk/js/client.mdx @@ -0,0 +1,270 @@ +--- +title: Client +sidebar_position: 3 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +The Market Data JavaScript Client handles API requests, response parsing, rate-limit tracking, retries, and logging. The SDK supports stocks, options, funds, and market status data. + +### Get Started Quickly with the MarketDataClient + +1. Review the [documentation on authentication](/sdk/js/authentication) to learn how to set your API token. +2. Create a [`MarketDataClient`](#MarketDataClient) instance and use it to make requests to the Market Data API. +3. Make a test request and review the console output. The SDK includes logging capabilities to help you debug requests. +4. Check the [rate limit](#RateLimits) in the client to track credit usage and how many API credits remain. +5. Configure [Settings](/sdk/js/settings) to customize output format, date format, and other universal parameters. + + +## MarketDataClient + +```typescript +class MarketDataClient { + constructor(config?: MarketDataConfig); +} + +interface MarketDataConfig { + token?: string; + baseUrl?: string; + apiVersion?: string; + maxRetries?: number; + retryInitialWait?: number; + retryMaxWait?: number; + retryFactor?: number; + debug?: boolean; + logger?: Logger; +} +``` + +[MarketDataClient](#MarketDataClient) is the main client class for interacting with the Market Data API. It provides access to all resources (stocks, options, funds, markets) and handles authentication, rate limiting, and request management. + +#### Properties + +- `token` (string, optional): The authentication token for API requests. See [authentication documentation](/sdk/js/authentication) for details. +- `rateLimits` ([UserRateLimits](#RateLimits), optional): Current rate limit information. Populated after the first successful API request. +- `baseUrl` (string): The base URL for API requests (default: `https://api.marketdata.app`). +- `apiVersion` (string): The API version to use (default: `v1`). +- `headers` (Record<string, string>): HTTP headers including `Authorization` and `User-Agent`. +- `logger` ([Logger](#Logger)): The logger instance used for diagnostic output. +- `settings` ([MarketDataSettings](/sdk/js/settings)): Resolved configuration including env-var defaults. See [Settings](/sdk/js/settings) for details. + +#### Resources + +- `stocks` ([StocksResource](/sdk/js/stocks)): Access to stocks endpoints (prices, quotes, candles, earnings, news) +- `options` ([OptionsResource](/sdk/js/options)): Access to options endpoints (chain, expirations, quotes, lookup) +- `funds` ([FundsResource](/sdk/js/funds)): Access to funds endpoints (candles) +- `markets` ([MarketsResource](/sdk/js/markets)): Access to markets endpoints (status) + + +### constructor + +```typescript +new MarketDataClient(config?: MarketDataConfig) +``` + +Creates and configures a new `MarketDataClient` instance. This initializes the client with the provided token (or reads it from the `MARKETDATA_TOKEN` environment variable), sets up HTTP headers, and prepares the resource namespaces. + +#### Parameters + +- `config` ([MarketDataConfig](#MarketDataClient), optional) + + Configuration object. All properties are optional: + + - `token` (string): The authentication token. Falls back to `MARKETDATA_TOKEN` environment variable if not provided. + - `baseUrl` (string): Override the API base URL. Defaults to `https://api.marketdata.app`. + - `apiVersion` (string): Override the API version. Defaults to `v1`. + - `maxRetries` (number): Maximum retry attempts for retriable errors. Defaults to `3`. + - `retryInitialWait` (number): Initial wait in seconds before the first retry. Defaults to `0.5`. + - `retryMaxWait` (number): Maximum wait in seconds between retries. Defaults to `10`. + - `retryFactor` (number): Exponential backoff factor. Defaults to `2`. + - `debug` (boolean): If `true`, sets the default logger level to `DEBUG`. Defaults to `false`. + - `logger` ([Logger](#Logger)): A custom logger instance. If omitted, the SDK uses its built-in `DefaultLogger`. + +#### Returns + +- `MarketDataClient` + + A new `MarketDataClient` instance ready to make API requests. + +#### Notes + +- The client sets a `User-Agent` header of the form `marketdata-js-{version}` (e.g. `marketdata-js-0.0.1`). +- All authenticated requests include an `Authorization: Bearer {token}` header. +- The client reuses a single underlying `fetch` client, which benefits from Node's global connection pooling. +- Configuration properties can also be provided via environment variables — see [Settings](/sdk/js/settings) for the full list and their resolution order. + +#### Example + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +// Token will be read from MARKETDATA_TOKEN environment variable +const client = new MarketDataClient(); + +// Or provide the token explicitly +const clientWithToken = new MarketDataClient({ token: "your_token_here" }); + +// Enable debug logging for troubleshooting +const debugClient = new MarketDataClient({ debug: true }); + +// Provide a custom logger +import { DefaultLogger, LogLevel } from "marketdata-sdk-js"; +const logger = new DefaultLogger(LogLevel.WARN); +const quietClient = new MarketDataClient({ logger }); +``` + + +## The Result Pattern + +Every resource method returns a [`MarketDataResult`](#MarketDataResult) — a `ResultAsync` from the [neverthrow](https://github.com/supermacro/neverthrow) library. Errors are **not thrown**; they are represented as failed results. + +This means your code never needs a `try`/`catch` around SDK calls. You handle success and failure explicitly. + +### Handling Results + + + + +The idiomatic way. Pattern-match on success and error with two callbacks. + +```typescript +const result = await client.stocks.prices("AAPL"); + +result.match( + (prices) => console.log("Success:", prices), + (error) => console.error("Failed:", error.message), +); +``` + + + + + +Branch on result state and access `.value` or `.error` directly. + +```typescript +const result = await client.stocks.prices("AAPL"); + +if (result.isOk()) { + console.log("Prices:", result.value); +} else { + console.error("Error:", result.error.message); +} +``` + + + + + +Convert to a thrown exception if you prefer `try`/`catch`. + +```typescript +try { + const result = await client.stocks.prices("AAPL"); + const prices = result._unsafeUnwrap(); + console.log(prices); +} catch (error) { + console.error("Failed:", error); +} +``` + + + + + +Chain operations on the success value without unwrapping. + +```typescript +const result = await client.stocks.prices("AAPL") + .map((prices) => prices.filter((p) => p.mid > 100)); + +if (result.isOk()) { + console.log("Filtered:", result.value); +} +``` + + + + + +### MarketDataResult + +```typescript +interface MarketDataResult extends ResultAsync { + save(filename?: string): Promise; + blob(): Promise; +} +``` + +Every resource method returns a `MarketDataResult`. In addition to the standard `ResultAsync` interface from neverthrow, it exposes two convenience methods: + +- `save(filename?: string)`: Write the response to a file. Returns a `Promise` resolving to the filename. Format is inferred from the extension when possible. +- `blob()`: Materialise the response as a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob). Useful for downloading CSV responses. + + +## Accessing Rate Limits + +The client tracks rate limits from the API by reading the `X-Api-Ratelimit-*` headers on every response. The `client.rateLimits` property is populated after the first successful API call. + +```typescript +const client = new MarketDataClient(); + +// Make a request to populate rate limits +await client.stocks.prices("AAPL"); + +// Access current rate limits +if (client.rateLimits) { + console.log(`Limit: ${client.rateLimits.requestsLimit}`); + console.log(`Remaining: ${client.rateLimits.requestsRemaining}`); + console.log(`Consumed: ${client.rateLimits.requestsConsumed}`); + console.log(`Reset at: ${new Date(client.rateLimits.requestsReset * 1000)}`); +} +``` + +#### UserRateLimits + +```typescript +interface UserRateLimits { + requestsLimit: number; // Total API credits allowed + requestsRemaining: number; // API credits remaining + requestsConsumed: number; // API credits consumed + requestsReset: number; // Unix timestamp when the limit resets +} +``` + +**Note:** Rate limits are tracked via the following response headers: + +- `x-api-ratelimit-limit`: Total API credits allowed +- `x-api-ratelimit-remaining`: Number of API credits remaining +- `x-api-ratelimit-consumed`: Number of API credits consumed +- `x-api-ratelimit-reset`: Unix timestamp when the rate limit resets + +If rate limits have been fetched and `requestsRemaining` is `0`, the next resource call will fail fast with a `RateLimitError` rather than hitting the API. + + +## Logging + +The SDK includes a built-in logger that outputs diagnostic information. You can pass a custom logger or use the default. + +```typescript +import { MarketDataClient, DefaultLogger, LogLevel } from "marketdata-sdk-js"; + +// Default logger (INFO level) +const client1 = new MarketDataClient(); + +// Debug logging (more verbose — shows request URLs, response timings, token suffix) +const client2 = new MarketDataClient({ debug: true }); + +// Custom log level +const logger = new DefaultLogger(LogLevel.WARN); +const client3 = new MarketDataClient({ logger }); +``` + +**Log levels** (in order of verbosity): `DEBUG`, `INFO`, `WARN`, `ERROR`. + +The default logger obfuscates tokens in log output, showing only the last 4 characters. + +## Configuration + +The SDK supports flexible configuration of universal parameters through environment variables, constructor arguments, and per-method overrides. See the [Settings](/sdk/js/settings) documentation for complete details on all available configuration options and how they interact. diff --git a/sdk/js/funds/candles.mdx b/sdk/js/funds/candles.mdx new file mode 100644 index 0000000..010df50 --- /dev/null +++ b/sdk/js/funds/candles.mdx @@ -0,0 +1,184 @@ +--- +title: Candles +sidebar_position: 1 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Retrieve historical OHLC (open/high/low/close) candles for any supported mutual fund symbol. + +## Making Requests + +Use the `candles()` method on the `funds` resource to fetch fund candles. + +:::info +Mutual fund candles are daily-resolution only (unlike stock candles). They do not include a `volume` field. +::: + +| Output Format | Result Payload | Description | +|---------------|-----------------------------------------------------|-----------------------------------------------------| +| **internal** (default) | `FundsCandle[]` or `FundsCandleHuman[]` | Array of decoded candle records, one per bar. | +| **json** | Raw JSON object | The compressed, array-keyed response object. | +| **csv** | `Blob` | CSV payload. Use `.save(filename)` to write it. | + + +## candles + +```typescript +// Positional form +candles

( + symbol: string, + params?: P, +): MarketDataResult + +// Object form +candles

( + params: P & { symbol: string }, +): MarketDataResult +``` + +Fetches historical daily candles for a single fund symbol. + +#### Parameters + +- `symbol` (string) + + The fund symbol (e.g. `"VFINX"`). + +- `resolution` (string, optional, default `"D"`) + + The candle resolution. Funds support daily-and-up only: `"D"`, `"daily"`, `"1D"`, `"W"`, `"weekly"`, `"1W"`, `"M"`, `"monthly"`, `"1M"`, `"Y"`, `"yearly"`, `"1Y"`, or any integer prefix of those units. + +- `from` (string | Date, optional) + + Start of the date range. + +- `to` (string | Date, optional) + + End of the date range. + +- `countback` (number, optional) + + Fetch `N` candles before `to` (mutually exclusive with `from`). + +- [`outputFormat`](/sdk/js/settings#output-format) (optional): The format of the returned data. Alias: `format`. +- [`dateFormat`](/sdk/js/settings#date-format) (optional): Date format. Alias: `dateformat`. +- [`columns`](/sdk/js/settings#columns) (optional): Columns to include. +- [`addHeaders`](/sdk/js/settings#headers) (optional): Whether to include headers in CSV output. Alias: `headers`. +- [`useHumanReadable`](/sdk/js/settings#human-readable) (optional): Use human-readable field names. Alias: `human`. +- [`mode`](/sdk/js/settings#data-mode) (optional): The data mode to use. + +#### Returns + +- [`MarketDataResult`](/sdk/js/client#MarketDataResult) + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +// VFINX is available without authentication (free test symbol). +const result = await client.funds.candles("VFINX", { countback: 30 }); + +result.match( + (candles) => { + for (const c of candles) { + console.log(`t=${c.t} o=${c.o} h=${c.h} l=${c.l} c=${c.c}`); + } + }, + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.funds.candles("VFINX", { + resolution: "M", + from: "2020-01-01", + to: "2024-12-31", +}); + +result.match( + (candles) => console.log(`${candles.length} monthly bars`), + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.funds.candles("VFINX", { + countback: 10, + human: true, +}); + +result.match( + (candles) => { + for (const c of candles) { + console.log(`Date=${c.Date} Open=${c.Open} Close=${c.Close}`); + } + }, + (error) => console.error(error.message), +); +``` + + + + + +## FundsCandle + +```typescript +interface FundsCandle { + s?: string; + t: number; // timestamp + o: number; // open + h: number; // high + l: number; // low + c: number; // close +} +``` + +`FundsCandle` uses the API's short, machine-readable field names. + +#### Properties + +- `s` (string, optional): Status indicator. +- `t` (number): Unix timestamp of the bar. +- `o` (number): Opening NAV. +- `h` (number): High NAV. +- `l` (number): Low NAV. +- `c` (number): Closing NAV. + + +## FundsCandleHuman + +```typescript +interface FundsCandleHuman { + s?: string; + Date: number; + Open: number; + High: number; + Low: number; + Close: number; +} +``` + +`FundsCandleHuman` is returned when `human: true` is set. diff --git a/sdk/js/funds/index.mdx b/sdk/js/funds/index.mdx new file mode 100644 index 0000000..0e22372 --- /dev/null +++ b/sdk/js/funds/index.mdx @@ -0,0 +1,14 @@ +--- +title: Funds +slug: /js/funds +sidebar_position: 30 +--- + +The JavaScript SDK from Market Data provides methods designed to streamline your use of the following Funds endpoints. These intuitive methods provide a seamless interface for accessing mutual fund data. + +## Funds Endpoints + +import DocCardList from "@theme/DocCardList"; +import { useCurrentSidebarCategory } from "@docusaurus/theme-common"; + + diff --git a/sdk/js/index.mdx b/sdk/js/index.mdx new file mode 100644 index 0000000..e8e32e6 --- /dev/null +++ b/sdk/js/index.mdx @@ -0,0 +1,40 @@ +--- +title: JavaScript SDK +sidebar_position: 4 +slug: /js +sidebar_custom_props: + badge: n +--- + +Welcome to the Market Data JavaScript SDK documentation. This SDK allows you to integrate Market Data services into your Node.js and TypeScript applications. It ships typed responses, runtime schema validation, and a functional error-handling pattern. + +:::info +The JavaScript SDK is in early development (version `0.0.1`). The API surface and return types may change before 1.0. +::: + +## Open Source + +The SDK is open source and available on GitHub. Feel free to contribute to the project, report bugs, or request new features. + +- [JavaScript SDK GitHub](https://github.com/MarketDataApp/sdk-js/) + +## Documentation + +The best source for documentation on the SDK is right here. This documentation is the most up-to-date and accurate source of information on the SDK. + +## Using the SDK + +This SDK is designed to help you get up and running with Market Data's APIs as quickly as possible, providing you with all the tools you need to access real-time stock and options prices, historical data, and much more. + +### Getting Started + +1. [Install the SDK](/sdk/js/installation) into a Node.js or TypeScript project. +2. Set up your [authentication token](/sdk/js/authentication) to access the API. +3. Learn about the [client](/sdk/js/client) and how to make your first API requests. +4. Configure [Settings](/sdk/js/settings) to customize output format, date format, and other universal parameters. +5. Explore the available endpoints for [stocks](/sdk/js/stocks), [options](/sdk/js/options), [funds](/sdk/js/funds), and [markets](/sdk/js/markets). + +### Support + +- If you have any questions or need further assistance, please don't hesitate to open a ticket at our [helpdesk](https://www.marketdata.app/dashboard/). +- If you find a bug you may also [open an issue in our GitHub repository](https://github.com/MarketDataApp/sdk-js/issues). Please only open issues if you find a bug. Use our helpdesk for general questions or implementation assistance. diff --git a/sdk/js/installation.mdx b/sdk/js/installation.mdx new file mode 100644 index 0000000..be3aaaa --- /dev/null +++ b/sdk/js/installation.mdx @@ -0,0 +1,98 @@ +--- +title: Installation +sidebar_position: 1 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +This guide will help you install the Market Data JavaScript SDK and configure it for your needs. + +## Prerequisites + +- Node.js >= 20 +- A package manager: [pnpm](https://pnpm.io/) (used by the SDK itself), [npm](https://npmjs.com), or [yarn](https://yarnpkg.com) + +## Basic Installation + + + + +```bash +pnpm add marketdata-sdk-js +``` + + + + +```bash +npm install marketdata-sdk-js +``` + + + + +```bash +yarn add marketdata-sdk-js +``` + + + + +:::info +The SDK is in early development (version `0.0.1`) and has not yet been published to npm. Until it is, install directly from GitHub: + +```bash +pnpm add github:MarketDataApp/sdk-js +``` +::: + +## TypeScript Support + +The SDK is written in TypeScript and ships first-class type definitions. No `@types/*` package is needed. The SDK builds to both ESM and CommonJS, so it works with either module system. + +- **ESM**: `import { MarketDataClient } from "marketdata-sdk-js";` +- **CommonJS**: `const { MarketDataClient } = require("marketdata-sdk-js");` + +## Local Development Installation + +For local development, clone the repository and install from the project directory: + +```bash +# Clone the repository +git clone https://github.com/MarketDataApp/sdk-js.git +cd sdk-js + +# Install dependencies +pnpm install + +# Run the test suite (all mocked, no API calls) +pnpm test + +# Build the dual CJS+ESM bundle +pnpm build +``` + +The project uses [Corepack](https://nodejs.org/api/corepack.html) to pin the pnpm version. If `pnpm` is not available, enable Corepack first: + +```bash +corepack enable +``` + +## Core Dependencies + +The SDK includes the following core dependencies (installed automatically): + +- `dotenv`: Loads environment variables from a `.env` file +- `neverthrow`: Functional `Result` type for error handling +- `p-limit`: Concurrency pool for fan-out requests +- `p-retry`: Retry logic with exponential backoff +- `zod`: Runtime schema validation and type inference + +## Next Steps + +After installation, you'll need to: + +1. Set up your [authentication token](/sdk/js/authentication) +2. Learn about the [client](/sdk/js/client) and how to make your first API requests +3. Configure [settings](/sdk/js/settings) to customize output format, date format, and other universal parameters diff --git a/sdk/js/markets/index.mdx b/sdk/js/markets/index.mdx new file mode 100644 index 0000000..c3d6a40 --- /dev/null +++ b/sdk/js/markets/index.mdx @@ -0,0 +1,14 @@ +--- +title: Markets +slug: /js/markets +sidebar_position: 40 +--- + +The JavaScript SDK from Market Data provides methods designed to streamline your use of the following Markets endpoints. These intuitive methods provide a seamless interface for accessing market status information. + +## Markets Endpoints + +import DocCardList from "@theme/DocCardList"; +import { useCurrentSidebarCategory } from "@docusaurus/theme-common"; + + diff --git a/sdk/js/markets/status.mdx b/sdk/js/markets/status.mdx new file mode 100644 index 0000000..d8b87a6 --- /dev/null +++ b/sdk/js/markets/status.mdx @@ -0,0 +1,176 @@ +--- +title: Status +sidebar_position: 1 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Retrieve market status information (open/closed) for dates and countries. + +## Making Requests + +Use the `status()` method on the `markets` resource to fetch market status information. + +| Output Format | Result Payload | Description | +|---------------|--------------------------------------------------------|-----------------------------------------------------| +| **internal** (default) | `MarketStatus[]` or `MarketStatusHuman[]` | Array of decoded market-status records. | +| **json** | Raw JSON object | The compressed, array-keyed response object. | +| **csv** | `Blob` | CSV payload. Use `.save(filename)` to write it. | + + +## status + +```typescript +status

( + params?: P, +): MarketDataResult +``` + +Fetches market status information. All parameters are optional. + +#### Parameters + +- `country` (string, optional) + + Filter by country code (e.g. `"US"`, `"CA"`, `"GB"`). + +- `date` (string | Date, optional) + + Specific date to fetch status for. + +- `from` (string | Date, optional) + + Start of the date range. + +- `to` (string | Date, optional) + + End of the date range. + +- `countback` (number, optional) + + Number of days to return, counting backwards from `to` (or the current date if `to` is not provided). + +- [`outputFormat`](/sdk/js/settings#output-format) (optional): The format of the returned data. Alias: `format`. +- [`dateFormat`](/sdk/js/settings#date-format) (optional): Date format. Alias: `dateformat`. +- [`columns`](/sdk/js/settings#columns) (optional): Columns to include. +- [`addHeaders`](/sdk/js/settings#headers) (optional): Whether to include headers in CSV output. Alias: `headers`. +- [`useHumanReadable`](/sdk/js/settings#human-readable) (optional): Use human-readable field names. Alias: `human`. +- [`mode`](/sdk/js/settings#data-mode) (optional): The data mode to use. + +#### Returns + +- [`MarketDataResult`](/sdk/js/client#MarketDataResult) + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.markets.status(); + +result.match( + (days) => { + for (const d of days) { + console.log(`date=${d.date} status=${d.status}`); + } + }, + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.markets.status({ + from: "2024-01-01", + to: "2024-01-31", + country: "US", +}); + +result.match( + (days) => console.log(days), + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +// Last 7 trading-status days +const result = await client.markets.status({ countback: 7 }); + +result.match( + (days) => console.log(days), + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.markets.status({ countback: 7, human: true }); + +result.match( + (days) => { + for (const d of days) { + console.log(`Date=${d.Date} Status=${d.Status}`); + } + }, + (error) => console.error(error.message), +); +``` + + + + + +## MarketStatus + +```typescript +interface MarketStatus { + s?: string; + date: number; + status: string; +} +``` + +#### Properties + +- `s` (string, optional): Status indicator. +- `date` (number): Unix timestamp of the trading day. +- `status` (string): Market status (e.g. `"open"`, `"closed"`). + + +## MarketStatusHuman + +```typescript +interface MarketStatusHuman { + s?: string; + Date: number; + Status: string; +} +``` + +`MarketStatusHuman` is returned when `human: true` is set. diff --git a/sdk/js/options/chain.mdx b/sdk/js/options/chain.mdx new file mode 100644 index 0000000..d77d522 --- /dev/null +++ b/sdk/js/options/chain.mdx @@ -0,0 +1,269 @@ +--- +title: Chain +sidebar_position: 1 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Retrieve the full option chain — every call and put contract — for any supported underlying symbol, with extensive filtering options. + +## Making Requests + +Use the `chain()` method on the `options` resource to fetch an option chain. The response includes Greeks (delta, gamma, theta, vega, IV), intrinsic/extrinsic value, and underlying price alongside the quote fields. + +| Output Format | Result Payload | Description | +|---------------|-----------------------------------------------------|---------------------------------------------------------| +| **internal** (default) | `OptionsChain[]` or `OptionsChainHuman[]` | Array of decoded option-contract records. | +| **json** | Raw JSON object | The compressed, array-keyed response object. | +| **csv** | `Blob` | CSV payload. Use `.save(filename)` to write it. | + + +## chain + +```typescript +// Positional form +chain

( + symbol: string, + params?: P, +): MarketDataResult + +// Object form +chain

( + params: P & { symbol: string }, +): MarketDataResult +``` + +Fetches the option chain for a single underlying symbol. + +#### Parameters + +- `symbol` (string) + + The underlying stock symbol (e.g. `"AAPL"`). + +- `date` (string | Date, optional) + + Fetch the chain as-of a specific historical date (end-of-day snapshot). + +- `expiration` (string | Date, optional) + + Filter by specific expiration date. + +- `days_to_expiration` (number, optional) + + Filter by number of days to expiration. + +- `from` / `to` (string | Date, optional) + + Range filter for expiration dates. + +- `month` (number, optional, 1–12), `year` (number, optional) + + Filter by expiration month and/or year. + +- `weekly` / `monthly` / `quarterly` (boolean, optional) + + Filter by expiration cycle type. + +- `strike` (number | string, optional) + + Filter by specific strike price. + +- `delta` (number, optional) + + Filter by target delta value. + +- `strike_limit` (number, optional) + + Limit the response to the N strikes nearest the underlying price. + +- `range` (string, optional) + + Filter by in-the-money / out-of-the-money range (e.g. `"itm"`, `"otm"`). + +- `min_bid` / `max_bid` / `min_ask` / `max_ask` (number, optional) + + Filter by quote price thresholds. + +- `max_bid_ask_spread` (number, optional), `max_bid_ask_spread_pct` (number, optional) + + Filter by spread thresholds. + +- `min_open_interest` (number, optional), `min_volume` (number, optional) + + Filter by liquidity thresholds. + +- `nonstandard` (boolean, optional) + + Include or exclude non-standard contracts. + +- `side` (`"call"` | `"put"` | `"both"`, optional) + + Filter by option side. + +- `am` / `pm` (boolean, optional) + + Filter by AM-settled or PM-settled contracts. + +- [`outputFormat`](/sdk/js/settings#output-format) (optional): The format of the returned data. Alias: `format`. +- [`dateFormat`](/sdk/js/settings#date-format) (optional): Date format. Alias: `dateformat`. +- [`columns`](/sdk/js/settings#columns) (optional): Columns to include. +- [`addHeaders`](/sdk/js/settings#headers) (optional): Whether to include headers in CSV output. Alias: `headers`. +- [`useHumanReadable`](/sdk/js/settings#human-readable) (optional): Use human-readable field names. Alias: `human`. +- [`mode`](/sdk/js/settings#data-mode) (optional): The data mode to use. + +#### Returns + +- [`MarketDataResult`](/sdk/js/client#MarketDataResult) + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.options.chain("AAPL"); + +result.match( + (contracts) => console.log(`Got ${contracts.length} contracts`), + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +// Calls only, narrow strike window, high open interest +const result = await client.options.chain("AAPL", { + side: "call", + strike_limit: 10, + min_open_interest: 100, +}); + +result.match( + (contracts) => { + for (const c of contracts) { + console.log( + `${c.optionSymbol} strike=${c.strike} mid=${c.mid} delta=${c.delta} oi=${c.openInterest}` + ); + } + }, + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.options.chain("AAPL", { + date: "2024-01-15", + expiration: "2024-02-16", + side: "call", +}); + +result.match( + (contracts) => console.log(contracts), + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.options.chain("AAPL", { + side: "put", + human: true, +}); + +result.match( + (contracts) => { + for (const c of contracts) { + console.log( + `${c.Symbol} Strike=${c.Strike} Mid=${c.Mid} Delta=${c.Delta}` + ); + } + }, + (error) => console.error(error.message), +); +``` + + + + + +## OptionsChain + +```typescript +interface OptionsChain { + s?: string; + optionSymbol: string; + underlying: string; + expiration: number; + side: string; + strike: number; + firstTraded: number; + dte: number; + updated: number; + bid: number | null; + bidSize: number | null; + mid: number | null; + ask: number | null; + askSize: number | null; + last: number | null; + openInterest: number | null; + volume: number | null; + inTheMoney: boolean; + intrinsicValue: number; + extrinsicValue: number; + underlyingPrice: number; + iv: number | null; + delta: number | null; + gamma: number | null; + theta: number | null; + vega: number | null; +} +``` + +#### Properties + +- `optionSymbol` (string): OCC-format option symbol (e.g. `"AAPL271217C00250000"`). +- `underlying` (string): The underlying stock symbol. +- `expiration` (number): Unix timestamp of the expiration date. +- `side` (string): `"call"` or `"put"`. +- `strike` (number): The strike price. +- `firstTraded` (number): Unix timestamp when the contract first traded. +- `dte` (number): Days to expiration. +- `updated` (number): Unix timestamp when the quote was last updated. +- `bid`, `ask`, `mid`, `last` (number | null): Quote prices. +- `bidSize`, `askSize` (number | null): Quote sizes. +- `openInterest`, `volume` (number | null): Liquidity metrics. +- `inTheMoney` (boolean): Whether the contract is currently in the money. +- `intrinsicValue`, `extrinsicValue` (number): Value decomposition. +- `underlyingPrice` (number): Price of the underlying at quote time. +- `iv`, `delta`, `gamma`, `theta`, `vega` (number | null): Implied volatility and Greeks. + + +## OptionsChainHuman + +When `human: true` is set, field names are returned in `Title_Case`: `Symbol`, `Underlying`, `Expiration_Date`, `Option_Side`, `Strike`, `Days_To_Expiration`, `Bid`, `Ask`, `Mid`, `Open_Interest`, `Volume`, `In_The_Money`, `IV`, `Delta`, `Gamma`, `Theta`, `Vega`, etc. diff --git a/sdk/js/options/expirations.mdx b/sdk/js/options/expirations.mdx new file mode 100644 index 0000000..f374864 --- /dev/null +++ b/sdk/js/options/expirations.mdx @@ -0,0 +1,148 @@ +--- +title: Expirations +sidebar_position: 3 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Retrieve the list of current or historical option expiration dates for a given underlying symbol. + +## Making Requests + +Use the `expirations()` method on the `options` resource to fetch available expiration dates. + +| Output Format | Result Payload | Description | +|---------------|--------------------------------------------------------------------|-----------------------------------------------------| +| **internal** (default) | `OptionsExpirationsResponse` or `OptionsExpirationsHumanResponse` | Object with the list of expirations. | +| **json** | Raw JSON object | The raw response as returned by the API. | + + +## expirations + +```typescript +// Positional form +expirations

( + symbol: string, + params?: P, +): MarketDataResult + +// Object form +expirations

( + params: P & { symbol: string }, +): MarketDataResult +``` + +Fetches the list of expirations for a single underlying symbol. + +#### Parameters + +- `symbol` (string) + + The underlying stock symbol. + +- `strike` (number, optional) + + Filter to expirations that have at least one contract at the given strike. + +- `date` (string | Date, optional) + + Fetch expirations as-of a specific historical date. + +- [`outputFormat`](/sdk/js/settings#output-format) (optional): The format of the returned data. Alias: `format`. +- [`dateFormat`](/sdk/js/settings#date-format) (optional): Date format. Alias: `dateformat`. +- [`columns`](/sdk/js/settings#columns) (optional): Columns to include. +- [`useHumanReadable`](/sdk/js/settings#human-readable) (optional): Use human-readable field names. Alias: `human`. + +#### Returns + +- [`MarketDataResult`](/sdk/js/client#MarketDataResult) + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.options.expirations("AAPL"); + +result.match( + (data) => { + console.log(`Updated: ${data.updated}`); + for (const exp of data.expirations) { + console.log(exp); + } + }, + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +// Only expirations that trade the $250 strike +const result = await client.options.expirations("AAPL", { strike: 250 }); + +result.match( + (data) => console.log(data.expirations), + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.options.expirations("AAPL", { human: true }); + +result.match( + (data) => console.log(data.Expirations), + (error) => console.error(error.message), +); +``` + + + + + +## OptionsExpirationsResponse + +```typescript +interface OptionsExpirationsResponse { + s: string; + expirations: string[]; + updated: number; +} +``` + +#### Properties + +- `s` (string): Status indicator. +- `expirations` (string[]): List of expiration dates in `YYYY-MM-DD` format. +- `updated` (number): Unix timestamp when the data was last updated. + + +## OptionsExpirationsHumanResponse + +```typescript +interface OptionsExpirationsHumanResponse { + s?: string; + Expirations: string[]; + Date: number; +} +``` + +`OptionsExpirationsHumanResponse` is returned when `human: true` is set. diff --git a/sdk/js/options/index.mdx b/sdk/js/options/index.mdx new file mode 100644 index 0000000..6169c7e --- /dev/null +++ b/sdk/js/options/index.mdx @@ -0,0 +1,14 @@ +--- +title: Options +slug: /js/options +sidebar_position: 20 +--- + +The JavaScript SDK from Market Data provides methods designed to streamline your use of the following Options endpoints. These intuitive methods provide a seamless interface for accessing options data including chains, expirations, quotes, and lookup functionality. + +## Options Endpoints + +import DocCardList from "@theme/DocCardList"; +import { useCurrentSidebarCategory } from "@docusaurus/theme-common"; + + diff --git a/sdk/js/options/lookup.mdx b/sdk/js/options/lookup.mdx new file mode 100644 index 0000000..a5cddaa --- /dev/null +++ b/sdk/js/options/lookup.mdx @@ -0,0 +1,111 @@ +--- +title: Lookup +sidebar_position: 4 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Convert a human-readable option description (e.g. `"AAPL 7/28/2023 200 Call"`) into its standard OCC option symbol. + +## Making Requests + +Use the `lookup()` method on the `options` resource to resolve an OCC symbol from a natural-language description. The URL-encoded lookup string is handled for you by the SDK. + + +## lookup + +```typescript +// Positional form +lookup

( + lookupStr: string, + params?: P, +): MarketDataResult + +// Object form +lookup

( + params: P & { lookup: string }, +): MarketDataResult +``` + +Resolves a human-readable option description to its OCC symbol. + +#### Parameters + +- `lookupStr` (string) + + A natural-language description of the option contract (e.g. `"AAPL 7/28/2023 200 Call"`, `"TSLA Jan 2025 300 Put"`). + +- [`outputFormat`](/sdk/js/settings#output-format) (optional): The format of the returned data. Alias: `format`. +- [`useHumanReadable`](/sdk/js/settings#human-readable) (optional): Use human-readable field names. Alias: `human`. + +#### Returns + +- [`MarketDataResult`](/sdk/js/client#MarketDataResult) + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.options.lookup("AAPL 7/28/2023 200 Call"); + +result.match( + (data) => console.log(data.optionSymbol), // "AAPL230728C00200000" + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.options.lookup("AAPL 7/28/2023 200 Call", { + human: true, +}); + +result.match( + (data) => console.log(data.Symbol), + (error) => console.error(error.message), +); +``` + + + + + +## OptionsLookupResponse + +```typescript +interface OptionsLookupResponse { + s: string; + optionSymbol: string; + updated?: number; +} +``` + +#### Properties + +- `s` (string): Status indicator. +- `optionSymbol` (string): The OCC-format option symbol. +- `updated` (number, optional): Unix timestamp when the lookup resolved. + + +## OptionsLookupHumanResponse + +```typescript +interface OptionsLookupHumanResponse { + s?: string; + Symbol: string; +} +``` + +`OptionsLookupHumanResponse` is returned when `human: true` is set. diff --git a/sdk/js/options/quotes.mdx b/sdk/js/options/quotes.mdx new file mode 100644 index 0000000..a0338cd --- /dev/null +++ b/sdk/js/options/quotes.mdx @@ -0,0 +1,136 @@ +--- +title: Quotes +sidebar_position: 2 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Retrieve quotes for one or more specific option contracts, including full Greeks. + +## Making Requests + +Use the `quotes()` method on the `options` resource to fetch quotes for individual option contracts identified by their OCC symbol. When multiple symbols are provided, the SDK fans out concurrently and merges the results. + +| Output Format | Result Payload | Description | +|---------------|---------------------------------------------------------|---------------------------------------------------------| +| **internal** (default) | `OptionsQuotes[]` or `OptionsQuotesHuman[]` | Array of decoded quote records, one per contract. | +| **json** | Raw JSON object | The compressed, array-keyed response object. | +| **csv** | `Blob` | CSV payload. Use `.save(filename)` to write it. | + + +## quotes + +```typescript +// Positional form +quotes

( + symbols: string | string[], + params?: P, +): MarketDataResult + +// Object form +quotes

( + params: P & { symbols: string | string[] }, +): MarketDataResult +``` + +Fetches quotes for one or more option contracts. + +#### Parameters + +- `symbols` (string | string[]) + + A single OCC-format option symbol (e.g. `"AAPL271217C00250000"`) or an array of symbols. When you pass an array, the SDK fetches each contract concurrently and merges the results. + +- [`outputFormat`](/sdk/js/settings#output-format) (optional): The format of the returned data. Alias: `format`. +- [`dateFormat`](/sdk/js/settings#date-format) (optional): Date format. Alias: `dateformat`. +- [`columns`](/sdk/js/settings#columns) (optional): Columns to include. +- [`addHeaders`](/sdk/js/settings#headers) (optional): Whether to include headers in CSV output. Alias: `headers`. +- [`useHumanReadable`](/sdk/js/settings#human-readable) (optional): Use human-readable field names. Alias: `human`. +- [`mode`](/sdk/js/settings#data-mode) (optional): The data mode to use. + +Additional endpoint-specific parameters like `from`, `to`, and `date` are passed through to the REST API. See [REST API Options Quotes](/api/options/quotes) for the full list. + +#### Returns + +- [`MarketDataResult`](/sdk/js/client#MarketDataResult) + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.options.quotes("AAPL271217C00250000"); + +result.match( + (quotes) => { + const q = quotes[0]; + console.log( + `${q.optionSymbol}: bid=${q.bid} ask=${q.ask} delta=${q.delta}` + ); + }, + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.options.quotes([ + "AAPL271217C00250000", + "AAPL271217P00250000", + "AAPL271217C00300000", +]); + +result.match( + (quotes) => { + for (const q of quotes) { + console.log(`${q.optionSymbol}: mid=${q.mid}`); + } + }, + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.options.quotes("AAPL271217C00250000", { + human: true, +}); + +result.match( + (quotes) => console.log(quotes[0]), + (error) => console.error(error.message), +); +``` + + + + + +## OptionsQuotes + +`OptionsQuotes` uses the same field shape as [`OptionsChain`](/sdk/js/options/chain#OptionsChain): `optionSymbol`, `underlying`, `expiration`, `side`, `strike`, `bid`, `ask`, `mid`, `last`, `openInterest`, `volume`, `inTheMoney`, `intrinsicValue`, `extrinsicValue`, `iv`, `delta`, `gamma`, `theta`, `vega`, and so on. + +See [OptionsChain](/sdk/js/options/chain#OptionsChain) for the complete list of fields. + + +## OptionsQuotesHuman + +`OptionsQuotesHuman` is returned when `human: true` is set. Field shape mirrors [`OptionsChainHuman`](/sdk/js/options/chain#OptionsChainHuman) — same columns, `Title_Case` names. diff --git a/sdk/js/settings.mdx b/sdk/js/settings.mdx new file mode 100644 index 0000000..9ba5da7 --- /dev/null +++ b/sdk/js/settings.mdx @@ -0,0 +1,326 @@ +--- +title: Settings +sidebar_position: 4 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +The Market Data JavaScript SDK provides flexible configuration for customizing API requests. You can configure universal parameters like output format, date format, data mode, and more through multiple methods with different priority levels. + +## Configuration Priority + +The SDK supports three ways to configure universal parameters, with a clear priority hierarchy. + +### Priority Order (Lowest to Highest) + +1. **Environment Variables** (lowest priority) + - Read from variables like `MARKETDATA_OUTPUT_FORMAT`, `MARKETDATA_DATE_FORMAT`, etc. + - A `.env` file in your working directory is loaded automatically via [`dotenv`](https://github.com/motdotla/dotenv). + - Applied globally to all API calls unless overridden. + +2. **Client Constructor Arguments** (medium priority) + - Set via the `MarketDataConfig` object passed to `new MarketDataClient({ ... })`. + - Applied to all API calls made with that client instance unless overridden. + - Only applies to config-level settings (`token`, `baseUrl`, `apiVersion`, retry options). + +3. **Direct Method Parameters** (highest priority) + - Passed directly as parameters to resource methods. + - Applied only to that specific API call. + +### How It Works + +When making an API call, the SDK merges parameters in this order: + +1. **Start with environment variables** (lowest priority) + - Values are read from `MARKETDATA_*` variables on startup. + +2. **Override with constructor arguments** (medium priority) + - Values passed to `new MarketDataClient({ ... })` replace env values for config-level settings. + +3. **Override with direct method parameters** (highest priority) + - Parameters passed directly to resource methods like `client.stocks.prices("AAPL", { human: true })` take final priority. + +### Examples + + + + +Set universal parameters via environment variables: + +```bash +export MARKETDATA_OUTPUT_FORMAT=json +export MARKETDATA_DATE_FORMAT=timestamp +export MARKETDATA_USE_HUMAN_READABLE=true +``` + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +// All calls will use the env-var defaults unless overridden. +const result = await client.stocks.prices("AAPL"); +``` + + + + + +Set client-level configuration via the constructor: + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient({ + token: "your_token_here", + baseUrl: "https://api.marketdata.app", + apiVersion: "v1", + maxRetries: 5, + retryInitialWait: 1, + retryMaxWait: 15, + retryFactor: 2, + debug: true, +}); +``` + +Constructor arguments override environment variables for the settings they cover. + + + + + +Pass parameters directly to resource methods: + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +// Override per call — these parameters apply to this request only. +const result = await client.stocks.prices("AAPL", { + human: true, + dateformat: "timestamp", + columns: ["ask", "bid", "mid"], +}); +``` + +Direct method parameters override both environment variables and constructor arguments. + + + + +## Available Configuration Options + +### Output Format + +Controls the format of the API response data. + +**Available Values:** +- `"internal"` (default): Returns decoded plain JavaScript objects (the array-keyed wire format is unpacked into one record per row). +- `"json"`: Returns the raw JSON response from the API, without unpacking. +- `"csv"`: Returns a `Blob` containing CSV data. Use `.save(filename)` on the result to write it to disk. + +The SDK also exposes an `OutputFormat` enum you can import for type safety: + +```typescript +import { OutputFormat } from "marketdata-sdk-js"; +// OutputFormat.INTERNAL, OutputFormat.JSON, OutputFormat.CSV +``` + +**Configuration Methods:** + +1. **Environment Variable:** `MARKETDATA_OUTPUT_FORMAT` (values: `internal`, `json`, `csv`) +2. **Per-call parameter:** `format: "csv"` or `outputFormat: "csv"` + +**Example:** + +```typescript +import { MarketDataClient, OutputFormat } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +// Default (internal) — returns plain JS objects +const defaultResult = await client.stocks.prices("AAPL"); + +// CSV — returns a Blob +const csvResult = await client.stocks.prices("AAPL", { outputFormat: OutputFormat.CSV }); +await csvResult.save("prices.csv"); // writes to disk +``` + +For more details, see the [API Format documentation](/api/universal-parameters/format). + +### Date Format + +Specifies the format for date and time information in responses. + +**Available Values:** +- `"timestamp"`: ISO 8601 timestamp format (e.g., `2020-12-30 16:00:00 -05:00`) +- `"unix"`: Unix timestamp in seconds (e.g., `1609362000`) +- `"spreadsheet"`: Excel spreadsheet format (e.g., `44195.66667`) + +The SDK also exposes a `DateFormat` enum: + +```typescript +import { DateFormat } from "marketdata-sdk-js"; +// DateFormat.TIMESTAMP, DateFormat.UNIX, DateFormat.SPREADSHEET +``` + +**Configuration Methods:** + +1. **Environment Variable:** `MARKETDATA_DATE_FORMAT` (values: `timestamp`, `unix`, `spreadsheet`) +2. **Per-call parameter:** `dateformat: "unix"` or `dateFormat: "unix"` + +**Example:** + +```typescript +import { MarketDataClient, DateFormat } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const candles = await client.stocks.candles("AAPL", { dateFormat: DateFormat.TIMESTAMP }); +``` + +For more details, see the [API Date Format documentation](/api/universal-parameters/date-format). + +### Columns + +Limits the response to only the columns you need, reducing data transfer and processing time. + +**Type:** `string[]` + +**Configuration Methods:** + +1. **Environment Variable:** `MARKETDATA_COLUMNS` (comma-separated list, e.g., `ask,bid`) +2. **Per-call parameter:** `columns: ["ask", "bid"]` + +**Example:** + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const quotes = await client.stocks.quotes("AAPL", { columns: ["ask", "bid"] }); +``` + +For more details, see the [API Columns documentation](/api/universal-parameters/columns). + +### Headers + +Controls whether headers are included in CSV output. + +**Type:** `boolean` + +**Default:** `true` + +**Configuration Methods:** + +1. **Environment Variable:** `MARKETDATA_ADD_HEADERS` (values: `true`, `false`) +2. **Per-call parameter:** `headers: false` or `addHeaders: false` + +**Example:** + +```typescript +import { MarketDataClient, OutputFormat } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const csv = await client.stocks.candles("AAPL", { + outputFormat: OutputFormat.CSV, + addHeaders: false, +}); +``` + +For more details, see the [API Headers documentation](/api/universal-parameters/headers). + +### Human Readable + +Uses human-readable field names in responses instead of short, machine-friendly ones. Field names are returned in `Title_Case` (e.g. `Symbol`, `Change_Price`) rather than `camelCase`. + +**Type:** `boolean` + +**Default:** `false` + +**Configuration Methods:** + +1. **Environment Variable:** `MARKETDATA_USE_HUMAN_READABLE` (values: `true`, `false`) +2. **Per-call parameter:** `human: true` or `useHumanReadable: true` + +**Example:** + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const quotes = await client.stocks.quotes("AAPL", { human: true }); + +quotes.match( + (data) => console.log(data[0].Symbol, data[0].Mid), + (err) => console.error(err.message), +); +``` + +For more details, see the [API Human Readable documentation](/api/universal-parameters/human-readable). + +### Data Mode + +Controls whether the API returns live, cached, or delayed data. + +**Available Values:** +- `"live"`: Real-time data (paid plans). +- `"cached"`: Cached data. Lower cost per request; honours an optional `maxage` freshness window. +- `"delayed"`: 15+ minute delayed data. + +The SDK also exposes a `Mode` enum: + +```typescript +import { Mode } from "marketdata-sdk-js"; +// Mode.LIVE, Mode.CACHED, Mode.DELAYED +``` + +**Configuration Methods:** + +1. **Environment Variable:** `MARKETDATA_MODE` (values: `live`, `cached`, `delayed`) +2. **Per-call parameter:** `mode: "cached"` + +**Example:** + +```typescript +import { MarketDataClient, Mode } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const quotes = await client.stocks.quotes("AAPL", { mode: Mode.CACHED }); +``` + +For more details, see the [API Data Mode documentation](/api/universal-parameters/mode). + +## Environment Variables Reference + +| Variable | Purpose | Default | +|-----------------------------------|-----------------------------------------------|---------------------------------| +| `MARKETDATA_TOKEN` | API authentication token | _(none)_ | +| `MARKETDATA_BASE_URL` | API base URL | `https://api.marketdata.app` | +| `MARKETDATA_API_VERSION` | API version | `v1` | +| `MARKETDATA_OUTPUT_FORMAT` | Default output format | `internal` | +| `MARKETDATA_DATE_FORMAT` | Default date format | _(API default)_ | +| `MARKETDATA_USE_HUMAN_READABLE` | Use human-readable field names by default | `false` | +| `MARKETDATA_ADD_HEADERS` | Include headers in CSV output | `true` | +| `MARKETDATA_MODE` | Default data mode | _(API default)_ | +| `MARKETDATA_MAX_RETRIES` | Maximum retry attempts | `3` | +| `MARKETDATA_RETRY_INITIAL_WAIT` | Initial retry wait (seconds) | `0.5` | +| `MARKETDATA_RETRY_MAX_WAIT` | Maximum retry wait (seconds) | `10` | +| `MARKETDATA_RETRY_FACTOR` | Exponential backoff factor | `2` | + +## Parameter Aliases + +For convenience, the SDK accepts both the short form used by the REST API and the camelCase TypeScript form. Both resolve to the same underlying parameter: + +| Short (REST style) | Long (camelCase) | +|----------------------|------------------------| +| `dateformat` | `dateFormat` | +| `format` | `outputFormat` | +| `headers` | `addHeaders` | +| `human` | `useHumanReadable` | diff --git a/sdk/js/stocks/candles.mdx b/sdk/js/stocks/candles.mdx new file mode 100644 index 0000000..9a7b66a --- /dev/null +++ b/sdk/js/stocks/candles.mdx @@ -0,0 +1,222 @@ +--- +title: Candles +sidebar_position: 3 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Retrieve historical OHLCV (open/high/low/close/volume) candles for any supported stock symbol. + +## Making Requests + +Use the `candles()` method on the `stocks` resource to fetch candles. The method handles large intraday date ranges automatically by splitting them into year-sized chunks and fetching them concurrently (up to 50 in-flight at once), then merging the results. + +| Output Format | Result Payload | Description | +|---------------|-----------------------------------------------------|-----------------------------------------------------| +| **internal** (default) | `StockCandle[]` or `StockCandleHuman[]` | Array of decoded candle records, one per bar. | +| **json** | Raw JSON object | The compressed, array-keyed response object. | +| **csv** | `Blob` | CSV payload. Use `.save(filename)` to write it. | + + +## candles + +```typescript +// Positional form +candles

( + symbol: string, + params?: P, +): MarketDataResult + +// Object form +candles

( + params: P & { symbol: string }, +): MarketDataResult +``` + +Fetches historical candles for a single symbol. + +#### Parameters + +- `symbol` (string) + + The stock symbol to fetch candles for. + +- `resolution` (string, optional, default `"D"`) + + The candle resolution. Common values: + - Minute-based: `"1"`, `"5"`, `"15"`, `"30"`, `"45"`, `"minutely"` + - Hour-based: `"H"`, `"1H"`, `"2H"`, `"hourly"` + - Day-and-up: `"D"`, `"daily"`, `"W"`, `"weekly"`, `"M"`, `"monthly"`, `"Y"`, `"yearly"` + +- `from` (string | Date, optional) + + Start of the date range. Accepts ISO strings, Unix seconds as a string, or a `Date`. + +- `to` (string | Date, optional) + + End of the date range. Accepts ISO strings, Unix seconds as a string, or a `Date`. + +- `countback` (number, optional) + + Fetch `N` candles before `to` (mutually exclusive with `from`). + +- `extended` (boolean, optional) + + Include extended-hours data for intraday resolutions. + +- `adjustsplits` (boolean, optional) + + Adjust historical prices for splits. + +- [`outputFormat`](/sdk/js/settings#output-format) (optional): The format of the returned data. Alias: `format`. +- [`dateFormat`](/sdk/js/settings#date-format) (optional): Date format for response timestamps. Alias: `dateformat`. +- [`columns`](/sdk/js/settings#columns) (optional): Columns to include. +- [`addHeaders`](/sdk/js/settings#headers) (optional): Whether to include headers in CSV output. Alias: `headers`. +- [`useHumanReadable`](/sdk/js/settings#human-readable) (optional): Use human-readable field names. Alias: `human`. +- [`mode`](/sdk/js/settings#data-mode) (optional): The data mode to use. + +#### Returns + +- [`MarketDataResult`](/sdk/js/client#MarketDataResult) + +#### Notes + +- For intraday resolutions (minute- or hour-based), date ranges longer than 365 days are split into yearly chunks and fetched concurrently. The individual responses are merged back into a single chronologically ordered result. +- Up to 50 chunks can be in flight simultaneously (sliding window, not batching). + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +// Default resolution is "D" (daily) +const result = await client.stocks.candles("AAPL", { countback: 30 }); + +result.match( + (candles) => { + for (const c of candles) { + console.log(`t=${c.t} o=${c.o} h=${c.h} l=${c.l} c=${c.c} v=${c.v}`); + } + }, + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +// Hourly candles over a multi-year range. +// The SDK automatically splits into year-sized chunks +// and fetches them concurrently. +const result = await client.stocks.candles("AAPL", { + resolution: "1H", + from: new Date("2023-01-01"), + to: new Date("2024-12-31"), +}); + +result.match( + (candles) => console.log(`Got ${candles.length} bars`), + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.stocks.candles("AAPL", { + resolution: "D", + countback: 10, + human: true, +}); + +result.match( + (candles) => { + for (const c of candles) { + console.log(`Date=${c.Date} Open=${c.Open} Close=${c.Close}`); + } + }, + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient, OutputFormat } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.stocks.candles("AAPL", { + resolution: "D", + countback: 100, + outputFormat: OutputFormat.CSV, +}); + +const filename = await result.save("aapl_daily.csv"); +console.log(`CSV saved to: ${filename}`); +``` + + + + + +## StockCandle + +```typescript +interface StockCandle { + s?: string; + t: number; // timestamp + o: number; // open + h: number; // high + l: number; // low + c: number; // close + v: number; // volume +} +``` + +`StockCandle` uses the API's short, machine-readable field names. + +#### Properties + +- `s` (string, optional): Status indicator. +- `t` (number): Unix timestamp of the bar. +- `o` (number): Opening price. +- `h` (number): High price. +- `l` (number): Low price. +- `c` (number): Closing price. +- `v` (number): Volume. + + +## StockCandleHuman + +```typescript +interface StockCandleHuman { + s?: string; + Date: number; + Open: number; + High: number; + Low: number; + Close: number; + Volume: number; +} +``` + +`StockCandleHuman` is returned when `human: true` is set. diff --git a/sdk/js/stocks/earnings.mdx b/sdk/js/stocks/earnings.mdx new file mode 100644 index 0000000..cc258fb --- /dev/null +++ b/sdk/js/stocks/earnings.mdx @@ -0,0 +1,166 @@ +--- +title: Earnings +sidebar_position: 4 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Retrieve historical and upcoming earnings information for any supported stock symbol. + +## Making Requests + +Use the `earnings()` method on the `stocks` resource to fetch earnings data. + +| Output Format | Result Payload | Description | +|---------------|------------------------------------------------------------|------------------------------------------------------| +| **internal** (default) | `StockEarnings[]` or `StockEarningsHuman[]` | Array of decoded earnings records. | +| **json** | Raw JSON object | The compressed, array-keyed response object. | +| **csv** | `Blob` | CSV payload. Use `.save(filename)` to write it. | + + +## earnings + +```typescript +// Positional form +earnings

( + symbol: string, + params?: P, +): MarketDataResult + +// Object form +earnings

( + params: P & { symbol: string }, +): MarketDataResult +``` + +Fetches earnings data for a single symbol. + +#### Parameters + +- `symbol` (string) + + The stock symbol to fetch earnings for. + +- [`outputFormat`](/sdk/js/settings#output-format) (optional): The format of the returned data. Alias: `format`. +- [`dateFormat`](/sdk/js/settings#date-format) (optional): Date format for response timestamps. Alias: `dateformat`. +- [`columns`](/sdk/js/settings#columns) (optional): Columns to include. +- [`addHeaders`](/sdk/js/settings#headers) (optional): Whether to include headers in CSV output. Alias: `headers`. +- [`useHumanReadable`](/sdk/js/settings#human-readable) (optional): Use human-readable field names. Alias: `human`. +- [`mode`](/sdk/js/settings#data-mode) (optional): The data mode to use. + +Additional endpoint-specific parameters (e.g. `from`, `to`, `date`) are passed through to the REST API as-is. See the [REST API Earnings documentation](/api/stocks/earnings) for the complete list. + +#### Returns + +- [`MarketDataResult`](/sdk/js/client#MarketDataResult) + +:::info +Earnings data is a premium endpoint on the Market Data API. Your plan must include earnings access for this method to return data. +::: + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.stocks.earnings("AAPL"); + +result.match( + (earnings) => { + for (const e of earnings) { + console.log( + `FY${e.fiscalYear} Q${e.fiscalQuarter}: ` + + `reported=${e.reportedEPS} estimated=${e.estimatedEPS}` + ); + } + }, + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.stocks.earnings("AAPL", { human: true }); + +result.match( + (earnings) => { + for (const e of earnings) { + console.log(`${e.Symbol} FY${e.Fiscal_Year} Q${e.Fiscal_Quarter}: ${e.Reported_EPS}`); + } + }, + (error) => console.error(error.message), +); +``` + + + + + +## StockEarnings + +```typescript +interface StockEarnings { + s?: string; + symbol: string; + fiscalYear: number; + fiscalQuarter: number; + date: number; + reportDate: number; + reportTime: string | null; + currency: string | null; + reportedEPS: number | null; + estimatedEPS: number | null; + surpriseEPS: number | null; + surpriseEPSpct: number | null; + updated: number; +} +``` + +#### Properties + +- `symbol` (string): The stock symbol. +- `fiscalYear` (number): The fiscal year of the earnings report. +- `fiscalQuarter` (number): The fiscal quarter (1–4). +- `date` (number): Unix timestamp of the earnings event. +- `reportDate` (number): Unix timestamp when the earnings were reported. +- `reportTime` (string | null): Time of day the earnings were reported (e.g. `"bmo"`, `"amc"`). +- `currency` (string | null): Currency of the EPS figures. +- `reportedEPS` (number | null): The actual reported earnings per share. +- `estimatedEPS` (number | null): The analyst consensus estimate. +- `surpriseEPS` (number | null): The difference between reported and estimated EPS. +- `surpriseEPSpct` (number | null): The surprise as a fraction of the estimate. +- `updated` (number): Unix timestamp when the record was last updated. + + +## StockEarningsHuman + +```typescript +interface StockEarningsHuman { + s?: string; + Symbol: string; + Fiscal_Year: number; + Fiscal_Quarter: number; + Date: number; + Report_Date: number; + Report_Time: string | null; + Currency: string | null; + Reported_EPS: number | null; + Estimated_EPS: number | null; + Surprise_EPS: number | null; + Surprise_EPS_Percent: number | null; + Updated: number; +} +``` + +`StockEarningsHuman` is returned when `human: true` is set. Field names use `Title_Case`. diff --git a/sdk/js/stocks/index.mdx b/sdk/js/stocks/index.mdx new file mode 100644 index 0000000..dfd8ca8 --- /dev/null +++ b/sdk/js/stocks/index.mdx @@ -0,0 +1,14 @@ +--- +title: Stocks +slug: /js/stocks +sidebar_position: 10 +--- + +The JavaScript SDK from Market Data provides methods designed to streamline your use of the following Stocks endpoints. These intuitive methods provide a seamless interface, effectively masking the intricacies involved in managing HTTP requests and responses. + +## Stocks Endpoints + +import DocCardList from "@theme/DocCardList"; +import { useCurrentSidebarCategory } from "@docusaurus/theme-common"; + + diff --git a/sdk/js/stocks/news.mdx b/sdk/js/stocks/news.mdx new file mode 100644 index 0000000..4ee5807 --- /dev/null +++ b/sdk/js/stocks/news.mdx @@ -0,0 +1,140 @@ +--- +title: News +sidebar_position: 5 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Retrieve news articles for any supported stock symbol. + +## Making Requests + +Use the `news()` method on the `stocks` resource to fetch news. + +| Output Format | Result Payload | Description | +|---------------|--------------------------------------------------|------------------------------------------------------| +| **internal** (default) | `StockNews[]` or `StockNewsHuman[]` | Array of decoded news articles. | +| **json** | Raw JSON object | The compressed, array-keyed response object. | +| **csv** | `Blob` | CSV payload. Use `.save(filename)` to write it. | + + +## news + +```typescript +// Positional form +news

( + symbol: string, + params?: P, +): MarketDataResult + +// Object form +news

( + params: P & { symbol: string }, +): MarketDataResult +``` + +Fetches news articles for a single symbol. + +#### Parameters + +- `symbol` (string) + + The stock symbol to fetch news for. + +- `from` (string | Date, optional) + + Start of the date range for news articles. + +- `to` (string | Date, optional) + + End of the date range for news articles. + +- `date` (string | Date, optional) + + Fetch news for a specific date. + +- `countback` (number, optional) + + Number of articles to return, counting backwards from `to`. + +- [`outputFormat`](/sdk/js/settings#output-format) (optional): The format of the returned data. Alias: `format`. +- [`dateFormat`](/sdk/js/settings#date-format) (optional): Date format. Alias: `dateformat`. +- [`columns`](/sdk/js/settings#columns) (optional): Columns to include. +- [`addHeaders`](/sdk/js/settings#headers) (optional): Whether to include headers in CSV output. Alias: `headers`. +- [`useHumanReadable`](/sdk/js/settings#human-readable) (optional): Use human-readable field names. Alias: `human`. +- [`mode`](/sdk/js/settings#data-mode) (optional): The data mode to use. + +#### Returns + +- [`MarketDataResult`](/sdk/js/client#MarketDataResult) + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.stocks.news("AAPL"); + +result.match( + (articles) => { + for (const a of articles) { + console.log(`${a.source}: ${a.headline}`); + } + }, + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.stocks.news("AAPL", { + from: "2024-01-01", + to: "2024-01-31", +}); + +result.match( + (articles) => console.log(`Got ${articles.length} articles`), + (error) => console.error(error.message), +); +``` + + + + + +## StockNews + +```typescript +interface StockNews { + s?: string; + symbol: string; + headline: string; + content: string; + source: string; + publicationDate: number; +} +``` + +#### Properties + +- `symbol` (string): The stock symbol. +- `headline` (string): The news headline. +- `content` (string): The article content or summary. +- `source` (string): The news source publisher. +- `publicationDate` (number): Unix timestamp when the article was published. + + +## StockNewsHuman + +`StockNewsHuman` is returned when `human: true` is set. It uses the same field names as `StockNews` for most columns, but with `Symbol` capitalized. diff --git a/sdk/js/stocks/prices.mdx b/sdk/js/stocks/prices.mdx new file mode 100644 index 0000000..eb214bd --- /dev/null +++ b/sdk/js/stocks/prices.mdx @@ -0,0 +1,224 @@ +--- +title: Prices +sidebar_position: 1 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Retrieve stock prices for any supported stock symbol. + +## Making Requests + +Use the `prices()` method on the `stocks` resource to fetch stock prices. The method returns a [`MarketDataResult`](/sdk/js/client#MarketDataResult) which resolves to decoded records by default. The output format is controlled by the `outputFormat` parameter (or `MARKETDATA_OUTPUT_FORMAT` env var): + +| Output Format | Result Payload | Description | +|---------------|--------------------------------------------------------|-------------------------------------------------------------------------| +| **internal** (default) | `StockPrice[]` or `StockPriceHuman[]` | Array of decoded price records, one per symbol. | +| **json** | Raw JSON object | The compressed, array-keyed response object as returned by the API. | +| **csv** | `Blob` | CSV payload. Use `.save(filename)` on the result to write it to disk. | + + +## prices + +```typescript +// Positional form +prices

( + symbols: string | string[], + params?: P, +): MarketDataResult + +// Object form +prices

( + params: P & { symbols: string | string[] }, +): MarketDataResult +``` + +Fetches stock prices for one or more symbols. The return type is narrowed automatically: + +- If `human: true` (or `useHumanReadable: true`) → payload is `StockPriceHuman[]` +- If `outputFormat: "csv"` → payload is `Blob` +- Otherwise → payload is `StockPrice[]` + +#### Parameters + +- `symbols` (string | string[]) + + A single symbol string or an array of symbol strings for which to fetch prices. + +- [`outputFormat`](/sdk/js/settings#output-format) (optional) + + The format of the returned data. See [Settings](/sdk/js/settings#output-format) for details. Also accepts the alias `format`. + +- [`dateFormat`](/sdk/js/settings#date-format) (optional) + + The date format to use in the response. Also accepts the alias `dateformat`. See [Settings](/sdk/js/settings#date-format) for details. + +- [`columns`](/sdk/js/settings#columns) (optional) + + Specify which columns to include in the response. See [Settings](/sdk/js/settings#columns) for details. + +- [`addHeaders`](/sdk/js/settings#headers) (optional) + + Whether to include headers in CSV output. Also accepts the alias `headers`. See [Settings](/sdk/js/settings#headers) for details. + +- [`useHumanReadable`](/sdk/js/settings#human-readable) (optional) + + Whether to use human-readable field names. Also accepts the alias `human`. See [Settings](/sdk/js/settings#human-readable) for details. + +- [`mode`](/sdk/js/settings#data-mode) (optional) + + The data mode to use (`"live"`, `"cached"`, `"delayed"`). See [Settings](/sdk/js/settings#data-mode) for details. + +#### Returns + +- [`MarketDataResult`](/sdk/js/client#MarketDataResult) + + A Result wrapping the prices data in the requested format. Handle success and failure with `.match()`, `.isOk()` / `.isErr()`, or any other neverthrow method. + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.stocks.prices("AAPL"); + +result.match( + (prices) => console.log(prices[0].mid), + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.stocks.prices(["AAPL", "MSFT", "GOOGL"]); + +result.match( + (prices) => { + for (const p of prices) { + console.log(`${p.symbol}: ${p.mid}`); + } + }, + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.stocks.prices("AAPL", { human: true }); + +result.match( + (prices) => { + for (const p of prices) { + console.log(`${p.Symbol}: mid=${p.Mid}, change=${p.Change_Percent}`); + } + }, + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient, OutputFormat } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.stocks.prices(["AAPL", "MSFT"], { + outputFormat: OutputFormat.JSON, +}); + +result.match( + (json) => console.log(json), + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient, OutputFormat } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.stocks.prices(["AAPL", "MSFT"], { + outputFormat: OutputFormat.CSV, +}); + +// Save the Blob to disk (Node.js) +const filename = await result.save("prices.csv"); +console.log(`CSV saved to: ${filename}`); +``` + + + + + +## StockPrice + +```typescript +interface StockPrice { + s?: string; + symbol: string; + mid: number; + change: number; + changepct: number; + updated: number; +} +``` + +`StockPrice` represents a single stock price, with short machine-readable field names. + +#### Properties + +- `s` (string, optional): Status indicator (`"ok"` for successful responses). +- `symbol` (string): The stock symbol. +- `mid` (number): The mid price calculated between the ask and bid. +- `change` (number): The price change. +- `changepct` (number): The fractional price change (e.g. `0.0124` for +1.24%). +- `updated` (number): Unix timestamp when the price was last updated. + + +## StockPriceHuman + +```typescript +interface StockPriceHuman { + s?: string; + Symbol: string; + Mid: number; + Change_Price: number; + Change_Percent: number; + Date: number; +} +``` + +`StockPriceHuman` represents a stock price in human-readable format with capitalized, snake-case field names. Returned when `human: true` (or `useHumanReadable: true`) is set. + +#### Properties + +- `Symbol` (string): The stock symbol. +- `Mid` (number): The mid price. +- `Change_Price` (number): The price change. +- `Change_Percent` (number): The percent change. +- `Date` (number): Unix timestamp of the update. diff --git a/sdk/js/stocks/quotes.mdx b/sdk/js/stocks/quotes.mdx new file mode 100644 index 0000000..50e1fb2 --- /dev/null +++ b/sdk/js/stocks/quotes.mdx @@ -0,0 +1,198 @@ +--- +title: Quotes +sidebar_position: 2 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Retrieve stock quotes (bid, ask, mid, last, volume, etc.) for one or more supported stock symbols. + +## Making Requests + +Use the `quotes()` method on the `stocks` resource to fetch stock quotes. The method returns a [`MarketDataResult`](/sdk/js/client#MarketDataResult) which resolves to decoded records by default. + +| Output Format | Result Payload | Description | +|---------------|----------------------------------------------------|-----------------------------------------------------| +| **internal** (default) | `StockQuote[]` or `StockQuoteHuman[]` | Array of decoded quote records, one per symbol. | +| **json** | Raw JSON object | The compressed, array-keyed response object. | +| **csv** | `Blob` | CSV payload. Use `.save(filename)` to write it. | + + +## quotes + +```typescript +// Positional form +quotes

( + symbols: string | string[], + params?: P, +): MarketDataResult + +// Object form +quotes

( + params: P & { symbols: string | string[] }, +): MarketDataResult +``` + +Fetches stock quotes for one or more symbols. + +#### Parameters + +- `symbols` (string | string[]) + + A single symbol string or an array of symbol strings for which to fetch quotes. + +- `52week` (boolean, optional) + + Include the 52-week high and low in the response. + +- `extended` (boolean, optional) + + Include extended-hours quote data. + +- [`outputFormat`](/sdk/js/settings#output-format) (optional): The format of the returned data. Alias: `format`. +- [`dateFormat`](/sdk/js/settings#date-format) (optional): The date format to use. Alias: `dateformat`. +- [`columns`](/sdk/js/settings#columns) (optional): Columns to include. +- [`addHeaders`](/sdk/js/settings#headers) (optional): Whether to include headers in CSV output. Alias: `headers`. +- [`useHumanReadable`](/sdk/js/settings#human-readable) (optional): Use human-readable field names. Alias: `human`. +- [`mode`](/sdk/js/settings#data-mode) (optional): The data mode to use. + +#### Returns + +- [`MarketDataResult`](/sdk/js/client#MarketDataResult) + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.stocks.quotes("AAPL"); + +result.match( + (quotes) => console.log(quotes[0]), + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.stocks.quotes(["AAPL", "MSFT", "GOOGL"]); + +result.match( + (quotes) => { + for (const q of quotes) { + console.log(`${q.symbol}: bid=${q.bid}, ask=${q.ask}, last=${q.last}`); + } + }, + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.stocks.quotes("AAPL", { "52week": true }); + +result.match( + (quotes) => console.log(quotes[0]), + (error) => console.error(error.message), +); +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk-js"; + +const client = new MarketDataClient(); + +const result = await client.stocks.quotes("AAPL", { human: true }); + +result.match( + (quotes) => { + const q = quotes[0]; + console.log(`${q.Symbol}: Bid=${q.Bid} / Ask=${q.Ask}, Volume=${q.Volume}`); + }, + (error) => console.error(error.message), +); +``` + + + + + +## StockQuote + +```typescript +interface StockQuote { + s?: string; + symbol: string; + ask: number; + askSize: number; + bid: number; + bidSize: number; + mid: number; + last: number; + change: number; + changepct: number; + volume: number; + updated: number; +} +``` + +`StockQuote` represents a single stock quote with short, machine-readable field names. + +#### Properties + +- `s` (string, optional): Status indicator (`"ok"` for successful responses). +- `symbol` (string): The stock symbol. +- `ask` (number): The ask price. +- `askSize` (number): The ask size. +- `bid` (number): The bid price. +- `bidSize` (number): The bid size. +- `mid` (number): The mid price. +- `last` (number): The last traded price. +- `change` (number): The price change. +- `changepct` (number): The fractional price change. +- `volume` (number): The trading volume. +- `updated` (number): Unix timestamp when the quote was last updated. + + +## StockQuoteHuman + +```typescript +interface StockQuoteHuman { + s?: string; + Symbol: string; + Ask: number; + Ask_Size: number; + Bid: number; + Bid_Size: number; + Mid: number; + Last: number; + Change_Price: number; + Change_Percent: number; + Volume: number; + Date: number; +} +``` + +`StockQuoteHuman` is returned when `human: true` is set. Field names are in `Title_Case` rather than `camelCase`. From 5869bee38014c45957e17a126cd28bcb558050d9 Mon Sep 17 00:00:00 2001 From: Luca Mitas Date: Mon, 20 Apr 2026 15:40:58 -0300 Subject: [PATCH 02/29] fix: align markdown tables in sdk/js docs --- sdk/js/funds/candles.mdx | 10 ++++----- sdk/js/markets/status.mdx | 10 ++++----- sdk/js/options/chain.mdx | 10 ++++----- sdk/js/options/expirations.mdx | 8 +++---- sdk/js/options/quotes.mdx | 10 ++++----- sdk/js/settings.mdx | 40 +++++++++++++++++----------------- sdk/js/stocks/candles.mdx | 10 ++++----- sdk/js/stocks/earnings.mdx | 10 ++++----- sdk/js/stocks/news.mdx | 10 ++++----- sdk/js/stocks/prices.mdx | 10 ++++----- sdk/js/stocks/quotes.mdx | 10 ++++----- 11 files changed, 69 insertions(+), 69 deletions(-) diff --git a/sdk/js/funds/candles.mdx b/sdk/js/funds/candles.mdx index 010df50..d5407de 100644 --- a/sdk/js/funds/candles.mdx +++ b/sdk/js/funds/candles.mdx @@ -16,11 +16,11 @@ Use the `candles()` method on the `funds` resource to fetch fund candles. Mutual fund candles are daily-resolution only (unlike stock candles). They do not include a `volume` field. ::: -| Output Format | Result Payload | Description | -|---------------|-----------------------------------------------------|-----------------------------------------------------| -| **internal** (default) | `FundsCandle[]` or `FundsCandleHuman[]` | Array of decoded candle records, one per bar. | -| **json** | Raw JSON object | The compressed, array-keyed response object. | -| **csv** | `Blob` | CSV payload. Use `.save(filename)` to write it. | +| Output Format | Result Payload | Description | +|------------------------|-----------------------------------------|-------------------------------------------------| +| **internal** (default) | `FundsCandle[]` or `FundsCandleHuman[]` | Array of decoded candle records, one per bar. | +| **json** | Raw JSON object | The compressed, array-keyed response object. | +| **csv** | `Blob` | CSV payload. Use `.save(filename)` to write it. | ## candles diff --git a/sdk/js/markets/status.mdx b/sdk/js/markets/status.mdx index d8b87a6..97dc204 100644 --- a/sdk/js/markets/status.mdx +++ b/sdk/js/markets/status.mdx @@ -12,11 +12,11 @@ Retrieve market status information (open/closed) for dates and countries. Use the `status()` method on the `markets` resource to fetch market status information. -| Output Format | Result Payload | Description | -|---------------|--------------------------------------------------------|-----------------------------------------------------| -| **internal** (default) | `MarketStatus[]` or `MarketStatusHuman[]` | Array of decoded market-status records. | -| **json** | Raw JSON object | The compressed, array-keyed response object. | -| **csv** | `Blob` | CSV payload. Use `.save(filename)` to write it. | +| Output Format | Result Payload | Description | +|------------------------|-------------------------------------------|-------------------------------------------------| +| **internal** (default) | `MarketStatus[]` or `MarketStatusHuman[]` | Array of decoded market-status records. | +| **json** | Raw JSON object | The compressed, array-keyed response object. | +| **csv** | `Blob` | CSV payload. Use `.save(filename)` to write it. | ## status diff --git a/sdk/js/options/chain.mdx b/sdk/js/options/chain.mdx index d77d522..dbd99f5 100644 --- a/sdk/js/options/chain.mdx +++ b/sdk/js/options/chain.mdx @@ -12,11 +12,11 @@ Retrieve the full option chain — every call and put contract — for any suppo Use the `chain()` method on the `options` resource to fetch an option chain. The response includes Greeks (delta, gamma, theta, vega, IV), intrinsic/extrinsic value, and underlying price alongside the quote fields. -| Output Format | Result Payload | Description | -|---------------|-----------------------------------------------------|---------------------------------------------------------| -| **internal** (default) | `OptionsChain[]` or `OptionsChainHuman[]` | Array of decoded option-contract records. | -| **json** | Raw JSON object | The compressed, array-keyed response object. | -| **csv** | `Blob` | CSV payload. Use `.save(filename)` to write it. | +| Output Format | Result Payload | Description | +|------------------------|-------------------------------------------|-------------------------------------------------| +| **internal** (default) | `OptionsChain[]` or `OptionsChainHuman[]` | Array of decoded option-contract records. | +| **json** | Raw JSON object | The compressed, array-keyed response object. | +| **csv** | `Blob` | CSV payload. Use `.save(filename)` to write it. | ## chain diff --git a/sdk/js/options/expirations.mdx b/sdk/js/options/expirations.mdx index f374864..7606878 100644 --- a/sdk/js/options/expirations.mdx +++ b/sdk/js/options/expirations.mdx @@ -12,10 +12,10 @@ Retrieve the list of current or historical option expiration dates for a given u Use the `expirations()` method on the `options` resource to fetch available expiration dates. -| Output Format | Result Payload | Description | -|---------------|--------------------------------------------------------------------|-----------------------------------------------------| -| **internal** (default) | `OptionsExpirationsResponse` or `OptionsExpirationsHumanResponse` | Object with the list of expirations. | -| **json** | Raw JSON object | The raw response as returned by the API. | +| Output Format | Result Payload | Description | +|------------------------|-------------------------------------------------------------------|------------------------------------------| +| **internal** (default) | `OptionsExpirationsResponse` or `OptionsExpirationsHumanResponse` | Object with the list of expirations. | +| **json** | Raw JSON object | The raw response as returned by the API. | ## expirations diff --git a/sdk/js/options/quotes.mdx b/sdk/js/options/quotes.mdx index a0338cd..61ae8bb 100644 --- a/sdk/js/options/quotes.mdx +++ b/sdk/js/options/quotes.mdx @@ -12,11 +12,11 @@ Retrieve quotes for one or more specific option contracts, including full Greeks Use the `quotes()` method on the `options` resource to fetch quotes for individual option contracts identified by their OCC symbol. When multiple symbols are provided, the SDK fans out concurrently and merges the results. -| Output Format | Result Payload | Description | -|---------------|---------------------------------------------------------|---------------------------------------------------------| -| **internal** (default) | `OptionsQuotes[]` or `OptionsQuotesHuman[]` | Array of decoded quote records, one per contract. | -| **json** | Raw JSON object | The compressed, array-keyed response object. | -| **csv** | `Blob` | CSV payload. Use `.save(filename)` to write it. | +| Output Format | Result Payload | Description | +|------------------------|---------------------------------------------|---------------------------------------------------| +| **internal** (default) | `OptionsQuotes[]` or `OptionsQuotesHuman[]` | Array of decoded quote records, one per contract. | +| **json** | Raw JSON object | The compressed, array-keyed response object. | +| **csv** | `Blob` | CSV payload. Use `.save(filename)` to write it. | ## quotes diff --git a/sdk/js/settings.mdx b/sdk/js/settings.mdx index 9ba5da7..7840097 100644 --- a/sdk/js/settings.mdx +++ b/sdk/js/settings.mdx @@ -299,28 +299,28 @@ For more details, see the [API Data Mode documentation](/api/universal-parameter ## Environment Variables Reference -| Variable | Purpose | Default | -|-----------------------------------|-----------------------------------------------|---------------------------------| -| `MARKETDATA_TOKEN` | API authentication token | _(none)_ | -| `MARKETDATA_BASE_URL` | API base URL | `https://api.marketdata.app` | -| `MARKETDATA_API_VERSION` | API version | `v1` | -| `MARKETDATA_OUTPUT_FORMAT` | Default output format | `internal` | -| `MARKETDATA_DATE_FORMAT` | Default date format | _(API default)_ | -| `MARKETDATA_USE_HUMAN_READABLE` | Use human-readable field names by default | `false` | -| `MARKETDATA_ADD_HEADERS` | Include headers in CSV output | `true` | -| `MARKETDATA_MODE` | Default data mode | _(API default)_ | -| `MARKETDATA_MAX_RETRIES` | Maximum retry attempts | `3` | -| `MARKETDATA_RETRY_INITIAL_WAIT` | Initial retry wait (seconds) | `0.5` | -| `MARKETDATA_RETRY_MAX_WAIT` | Maximum retry wait (seconds) | `10` | -| `MARKETDATA_RETRY_FACTOR` | Exponential backoff factor | `2` | +| Variable | Purpose | Default | +|---------------------------------|-------------------------------------------|------------------------------| +| `MARKETDATA_TOKEN` | API authentication token | _(none)_ | +| `MARKETDATA_BASE_URL` | API base URL | `https://api.marketdata.app` | +| `MARKETDATA_API_VERSION` | API version | `v1` | +| `MARKETDATA_OUTPUT_FORMAT` | Default output format | `internal` | +| `MARKETDATA_DATE_FORMAT` | Default date format | _(API default)_ | +| `MARKETDATA_USE_HUMAN_READABLE` | Use human-readable field names by default | `false` | +| `MARKETDATA_ADD_HEADERS` | Include headers in CSV output | `true` | +| `MARKETDATA_MODE` | Default data mode | _(API default)_ | +| `MARKETDATA_MAX_RETRIES` | Maximum retry attempts | `3` | +| `MARKETDATA_RETRY_INITIAL_WAIT` | Initial retry wait (seconds) | `0.5` | +| `MARKETDATA_RETRY_MAX_WAIT` | Maximum retry wait (seconds) | `10` | +| `MARKETDATA_RETRY_FACTOR` | Exponential backoff factor | `2` | ## Parameter Aliases For convenience, the SDK accepts both the short form used by the REST API and the camelCase TypeScript form. Both resolve to the same underlying parameter: -| Short (REST style) | Long (camelCase) | -|----------------------|------------------------| -| `dateformat` | `dateFormat` | -| `format` | `outputFormat` | -| `headers` | `addHeaders` | -| `human` | `useHumanReadable` | +| Short (REST style) | Long (camelCase) | +|--------------------|--------------------| +| `dateformat` | `dateFormat` | +| `format` | `outputFormat` | +| `headers` | `addHeaders` | +| `human` | `useHumanReadable` | diff --git a/sdk/js/stocks/candles.mdx b/sdk/js/stocks/candles.mdx index 9a7b66a..0f18db5 100644 --- a/sdk/js/stocks/candles.mdx +++ b/sdk/js/stocks/candles.mdx @@ -12,11 +12,11 @@ Retrieve historical OHLCV (open/high/low/close/volume) candles for any supported Use the `candles()` method on the `stocks` resource to fetch candles. The method handles large intraday date ranges automatically by splitting them into year-sized chunks and fetching them concurrently (up to 50 in-flight at once), then merging the results. -| Output Format | Result Payload | Description | -|---------------|-----------------------------------------------------|-----------------------------------------------------| -| **internal** (default) | `StockCandle[]` or `StockCandleHuman[]` | Array of decoded candle records, one per bar. | -| **json** | Raw JSON object | The compressed, array-keyed response object. | -| **csv** | `Blob` | CSV payload. Use `.save(filename)` to write it. | +| Output Format | Result Payload | Description | +|------------------------|-----------------------------------------|-------------------------------------------------| +| **internal** (default) | `StockCandle[]` or `StockCandleHuman[]` | Array of decoded candle records, one per bar. | +| **json** | Raw JSON object | The compressed, array-keyed response object. | +| **csv** | `Blob` | CSV payload. Use `.save(filename)` to write it. | ## candles diff --git a/sdk/js/stocks/earnings.mdx b/sdk/js/stocks/earnings.mdx index cc258fb..6a146c0 100644 --- a/sdk/js/stocks/earnings.mdx +++ b/sdk/js/stocks/earnings.mdx @@ -12,11 +12,11 @@ Retrieve historical and upcoming earnings information for any supported stock sy Use the `earnings()` method on the `stocks` resource to fetch earnings data. -| Output Format | Result Payload | Description | -|---------------|------------------------------------------------------------|------------------------------------------------------| -| **internal** (default) | `StockEarnings[]` or `StockEarningsHuman[]` | Array of decoded earnings records. | -| **json** | Raw JSON object | The compressed, array-keyed response object. | -| **csv** | `Blob` | CSV payload. Use `.save(filename)` to write it. | +| Output Format | Result Payload | Description | +|------------------------|---------------------------------------------|-------------------------------------------------| +| **internal** (default) | `StockEarnings[]` or `StockEarningsHuman[]` | Array of decoded earnings records. | +| **json** | Raw JSON object | The compressed, array-keyed response object. | +| **csv** | `Blob` | CSV payload. Use `.save(filename)` to write it. | ## earnings diff --git a/sdk/js/stocks/news.mdx b/sdk/js/stocks/news.mdx index 4ee5807..bf58680 100644 --- a/sdk/js/stocks/news.mdx +++ b/sdk/js/stocks/news.mdx @@ -12,11 +12,11 @@ Retrieve news articles for any supported stock symbol. Use the `news()` method on the `stocks` resource to fetch news. -| Output Format | Result Payload | Description | -|---------------|--------------------------------------------------|------------------------------------------------------| -| **internal** (default) | `StockNews[]` or `StockNewsHuman[]` | Array of decoded news articles. | -| **json** | Raw JSON object | The compressed, array-keyed response object. | -| **csv** | `Blob` | CSV payload. Use `.save(filename)` to write it. | +| Output Format | Result Payload | Description | +|------------------------|-------------------------------------|-------------------------------------------------| +| **internal** (default) | `StockNews[]` or `StockNewsHuman[]` | Array of decoded news articles. | +| **json** | Raw JSON object | The compressed, array-keyed response object. | +| **csv** | `Blob` | CSV payload. Use `.save(filename)` to write it. | ## news diff --git a/sdk/js/stocks/prices.mdx b/sdk/js/stocks/prices.mdx index eb214bd..4580aaf 100644 --- a/sdk/js/stocks/prices.mdx +++ b/sdk/js/stocks/prices.mdx @@ -12,11 +12,11 @@ Retrieve stock prices for any supported stock symbol. Use the `prices()` method on the `stocks` resource to fetch stock prices. The method returns a [`MarketDataResult`](/sdk/js/client#MarketDataResult) which resolves to decoded records by default. The output format is controlled by the `outputFormat` parameter (or `MARKETDATA_OUTPUT_FORMAT` env var): -| Output Format | Result Payload | Description | -|---------------|--------------------------------------------------------|-------------------------------------------------------------------------| -| **internal** (default) | `StockPrice[]` or `StockPriceHuman[]` | Array of decoded price records, one per symbol. | -| **json** | Raw JSON object | The compressed, array-keyed response object as returned by the API. | -| **csv** | `Blob` | CSV payload. Use `.save(filename)` on the result to write it to disk. | +| Output Format | Result Payload | Description | +|------------------------|---------------------------------------|-----------------------------------------------------------------------| +| **internal** (default) | `StockPrice[]` or `StockPriceHuman[]` | Array of decoded price records, one per symbol. | +| **json** | Raw JSON object | The compressed, array-keyed response object as returned by the API. | +| **csv** | `Blob` | CSV payload. Use `.save(filename)` on the result to write it to disk. | ## prices diff --git a/sdk/js/stocks/quotes.mdx b/sdk/js/stocks/quotes.mdx index 50e1fb2..04ae5cd 100644 --- a/sdk/js/stocks/quotes.mdx +++ b/sdk/js/stocks/quotes.mdx @@ -12,11 +12,11 @@ Retrieve stock quotes (bid, ask, mid, last, volume, etc.) for one or more suppor Use the `quotes()` method on the `stocks` resource to fetch stock quotes. The method returns a [`MarketDataResult`](/sdk/js/client#MarketDataResult) which resolves to decoded records by default. -| Output Format | Result Payload | Description | -|---------------|----------------------------------------------------|-----------------------------------------------------| -| **internal** (default) | `StockQuote[]` or `StockQuoteHuman[]` | Array of decoded quote records, one per symbol. | -| **json** | Raw JSON object | The compressed, array-keyed response object. | -| **csv** | `Blob` | CSV payload. Use `.save(filename)` to write it. | +| Output Format | Result Payload | Description | +|------------------------|---------------------------------------|-------------------------------------------------| +| **internal** (default) | `StockQuote[]` or `StockQuoteHuman[]` | Array of decoded quote records, one per symbol. | +| **json** | Raw JSON object | The compressed, array-keyed response object. | +| **csv** | `Blob` | CSV payload. Use `.save(filename)` to write it. | ## quotes From e2394bef23facaa6e52fc57f9e180be0da2bc417 Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Fri, 24 Apr 2026 08:55:21 -0300 Subject: [PATCH 03/29] docs: remove custom plan tip from plan-limits page --- account/plan-limits.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/account/plan-limits.md b/account/plan-limits.md index 1d1a4b9..6525640 100644 --- a/account/plan-limits.md +++ b/account/plan-limits.md @@ -5,10 +5,6 @@ sidebar_position: 3 Each Market Data plan comes with certain limitations to allow for shared use of our servers. We offer several plans with different price points depending on the quantity of data needed. -:::tip -Most users don't run into trouble with our limits. However, if our standard plans do not fit your use-case, please do not hesitate to speak with sales. We'd be happy to provide you with a custom plan that meets your needs. -::: - ## Standard Plans | | Free Forever | Starter | Trader | Quant | Prime Plans | From 12eb40fbf98dfdbb3722a7d51fe851ee923e8ae6 Mon Sep 17 00:00:00 2001 From: Luca Mitas Date: Fri, 24 Apr 2026 14:04:13 -0300 Subject: [PATCH 04/29] docs(sdk/js): sweep neverthrow examples to Promise + try/catch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updates every SDK-JS doc under sdk/js/ to reflect the ADR-007 reversal: public API now surfaces standard Promises. Neverthrow remains internal to the SDK — callers never see Result types. Mechanical changes (15 files): - Import path: `marketdata-sdk-js` → `marketdata-sdk`. - Type signatures: `MarketDataResult` → `MarketDataPromise`, `TypedResult` → `TypedPromise`. - Link anchors: `#MarketDataResult` → `#MarketDataPromise`. - Example blocks: every `result.match(ok, err)` / `isOk()/value/error` / `_unsafeUnwrap()` example rewritten to idiomatic `try/catch` around `await`. - Error handling sections: list the new 7 spec classes (Authentication/BadRequest/NotFound/RateLimit/Server/Network/Parse + client-side Validation) with cf-ray, status, url, timestamp context. - 404-as-no-data noted: response.no_data / hasData() patterns. client.mdx additionally documents: - `ready: Promise` for eager startup validation - `skipStartupValidation` config flag for serverless - 99s request timeout - Global 50-request concurrency pool - Utilities resource on the client - MarketDataPromise response-model methods (isJson/isCsv/isHtml/ hasData/no_data/saveToFile/save/blob) - Updated User-Agent to `marketdata-sdk-javascript/{version}` installation.mdx: `neverthrow` line reworded to note it's now an internal dep, with a link to the client error-handling section. No structural rewrites; ADRs under `sdk/js/docs/adr/` intentionally untouched (historical snapshots). Verified: grep for `isOk|isErr|ResultAsync|TypedResult|MarketDataResult| _unsafeUnwrap|.match(|marketdata-sdk-js|unwrap(` under sdk/js/ returns zero hits outside ADRs. --- sdk/js/authentication.mdx | 28 ++--- sdk/js/client.mdx | 185 ++++++++++++++++++++++----------- sdk/js/funds/candles.mdx | 76 +++++++------- sdk/js/installation.mdx | 12 +-- sdk/js/markets/status.mdx | 82 +++++++-------- sdk/js/options/chain.mdx | 106 +++++++++---------- sdk/js/options/expirations.mdx | 58 +++++------ sdk/js/options/lookup.mdx | 38 +++---- sdk/js/options/quotes.mdx | 74 +++++++------ sdk/js/settings.mdx | 36 +++---- sdk/js/stocks/candles.mdx | 90 ++++++++-------- sdk/js/stocks/earnings.mdx | 52 +++++---- sdk/js/stocks/news.mdx | 46 ++++---- sdk/js/stocks/prices.mdx | 88 ++++++++-------- sdk/js/stocks/quotes.mdx | 74 +++++++------ 15 files changed, 537 insertions(+), 508 deletions(-) diff --git a/sdk/js/authentication.mdx b/sdk/js/authentication.mdx index 10ade49..b24e10d 100644 --- a/sdk/js/authentication.mdx +++ b/sdk/js/authentication.mdx @@ -76,18 +76,18 @@ Add `.env` to your `.gitignore` so the token is not committed to source control. Use the following code to verify that your authentication is working by making a test request to `SPY` or any other symbol that requires authentication. Do not use `AAPL` to test your authentication because `AAPL` is a free test symbol and will return data even if you are not authenticated properly. ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; // No need to pass a token here — the SDK reads it from // the MARKETDATA_TOKEN environment variable automatically. const client = new MarketDataClient(); -const result = await client.stocks.quotes("SPY"); - -result.match( - (quotes) => console.log(quotes), - (error) => console.error(`Error: ${error.message}`), -); +try { + const quotes = await client.stocks.quotes("SPY"); + console.log(quotes); +} catch (error) { + console.error(`Error: ${error.message}`); +} ``` If your token is set correctly, you should see the output of the test request. If you see an error, double-check that you have set the token correctly. @@ -99,18 +99,18 @@ If you decide to pass the token directly when creating the client, you can do so ### Example Code ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const token = "your_token_here"; const client = new MarketDataClient({ token }); -const result = await client.stocks.quotes("SPY"); - -result.match( - (quotes) => console.log(quotes), - (error) => console.error(`Error: ${error.message}`), -); +try { + const quotes = await client.stocks.quotes("SPY"); + console.log(quotes); +} catch (error) { + console.error(`Error: ${error.message}`); +} ``` ## Next Steps diff --git a/sdk/js/client.mdx b/sdk/js/client.mdx index 02a0f5d..8c9f005 100644 --- a/sdk/js/client.mdx +++ b/sdk/js/client.mdx @@ -6,7 +6,7 @@ sidebar_position: 3 import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem"; -The Market Data JavaScript Client handles API requests, response parsing, rate-limit tracking, retries, and logging. The SDK supports stocks, options, funds, and market status data. +The Market Data JavaScript Client handles API requests, response parsing, rate-limit tracking, retries, and logging. The SDK supports stocks, options, funds, markets, and utility endpoints. ### Get Started Quickly with the MarketDataClient @@ -22,6 +22,7 @@ The Market Data JavaScript Client handles API requests, response parsing, rate-l ```typescript class MarketDataClient { constructor(config?: MarketDataConfig); + readonly ready: Promise; } interface MarketDataConfig { @@ -32,17 +33,19 @@ interface MarketDataConfig { retryInitialWait?: number; retryMaxWait?: number; retryFactor?: number; + skipStartupValidation?: boolean; debug?: boolean; logger?: Logger; } ``` -[MarketDataClient](#MarketDataClient) is the main client class for interacting with the Market Data API. It provides access to all resources (stocks, options, funds, markets) and handles authentication, rate limiting, and request management. +[MarketDataClient](#MarketDataClient) is the main client class for interacting with the Market Data API. It provides access to all resources (stocks, options, funds, markets, utilities) and handles authentication, rate limiting, and request management. #### Properties - `token` (string, optional): The authentication token for API requests. See [authentication documentation](/sdk/js/authentication) for details. -- `rateLimits` ([UserRateLimits](#RateLimits), optional): Current rate limit information. Populated after the first successful API request. +- `rateLimits` ([UserRateLimits](#RateLimits), optional): Current rate limit information. Populated by the startup `/user/` call (see `ready` below) or by the first successful API request when startup validation is skipped. +- `ready` (Promise<void>): Resolves when the eager `/user/` validation completes. Rejects with `AuthenticationError` on bad tokens; transient errors are logged and swallowed. Await it if you need the fail-fast behaviour on construction. - `baseUrl` (string): The base URL for API requests (default: `https://api.marketdata.app`). - `apiVersion` (string): The API version to use (default: `v1`). - `headers` (Record<string, string>): HTTP headers including `Authorization` and `User-Agent`. @@ -55,6 +58,7 @@ interface MarketDataConfig { - `options` ([OptionsResource](/sdk/js/options)): Access to options endpoints (chain, expirations, quotes, lookup) - `funds` ([FundsResource](/sdk/js/funds)): Access to funds endpoints (candles) - `markets` ([MarketsResource](/sdk/js/markets)): Access to markets endpoints (status) +- `utilities` (UtilitiesResource): Access to utility endpoints (`status`, `headers`, `user`) ### constructor @@ -63,7 +67,7 @@ interface MarketDataConfig { new MarketDataClient(config?: MarketDataConfig) ``` -Creates and configures a new `MarketDataClient` instance. This initializes the client with the provided token (or reads it from the `MARKETDATA_TOKEN` environment variable), sets up HTTP headers, and prepares the resource namespaces. +Creates and configures a new `MarketDataClient` instance. This initializes the client with the provided token (or reads it from the `MARKETDATA_TOKEN` environment variable), sets up HTTP headers, prepares the resource namespaces, and kicks off eager `/user/` validation (unless `skipStartupValidation: true`). #### Parameters @@ -78,6 +82,7 @@ Creates and configures a new `MarketDataClient` instance. This initializes the c - `retryInitialWait` (number): Initial wait in seconds before the first retry. Defaults to `0.5`. - `retryMaxWait` (number): Maximum wait in seconds between retries. Defaults to `10`. - `retryFactor` (number): Exponential backoff factor. Defaults to `2`. + - `skipStartupValidation` (boolean): If `true`, skips the eager `/user/` call the constructor makes to validate the token. Defaults to `false`. Use on serverless platforms where cold-start latency matters. - `debug` (boolean): If `true`, sets the default logger level to `DEBUG`. Defaults to `false`. - `logger` ([Logger](#Logger)): A custom logger instance. If omitted, the SDK uses its built-in `DefaultLogger`. @@ -89,15 +94,16 @@ Creates and configures a new `MarketDataClient` instance. This initializes the c #### Notes -- The client sets a `User-Agent` header of the form `marketdata-js-{version}` (e.g. `marketdata-js-0.0.1`). +- The client sets a `User-Agent` header of the form `marketdata-sdk-javascript/{version}` (e.g. `marketdata-sdk-javascript/0.0.1`). - All authenticated requests include an `Authorization: Bearer {token}` header. -- The client reuses a single underlying `fetch` client, which benefits from Node's global connection pooling. +- The client reuses a single underlying `fetch` client, which benefits from Node's global connection pooling, and enforces a global 50-request concurrency pool across every endpoint. +- Every request has a 99-second timeout; a timed-out fetch rejects with `NetworkError`. - Configuration properties can also be provided via environment variables — see [Settings](/sdk/js/settings) for the full list and their resolution order. #### Example ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; // Token will be read from MARKETDATA_TOKEN environment variable const client = new MarketDataClient(); @@ -105,115 +111,172 @@ const client = new MarketDataClient(); // Or provide the token explicitly const clientWithToken = new MarketDataClient({ token: "your_token_here" }); +// Fail fast on invalid tokens (default) — await readiness: +await client.ready; + +// Skip the startup /user/ call (e.g. on Lambda cold starts) +const fast = new MarketDataClient({ + token: "your_token_here", + skipStartupValidation: true, +}); + // Enable debug logging for troubleshooting const debugClient = new MarketDataClient({ debug: true }); // Provide a custom logger -import { DefaultLogger, LogLevel } from "marketdata-sdk-js"; +import { DefaultLogger, LogLevel } from "marketdata-sdk"; const logger = new DefaultLogger(LogLevel.WARN); const quietClient = new MarketDataClient({ logger }); ``` - -## The Result Pattern - -Every resource method returns a [`MarketDataResult`](#MarketDataResult) — a `ResultAsync` from the [neverthrow](https://github.com/supermacro/neverthrow) library. Errors are **not thrown**; they are represented as failed results. + +## Error Handling -This means your code never needs a `try`/`catch` around SDK calls. You handle success and failure explicitly. - -### Handling Results +Resource methods return a `MarketDataPromise` that resolves with the data on success and rejects with a subclass of `MarketDataClientError` on failure. Use standard JavaScript error-handling idioms. - + -The idiomatic way. Pattern-match on success and error with two callbacks. +The idiomatic approach. ```typescript -const result = await client.stocks.prices("AAPL"); +import { + MarketDataClient, + AuthenticationError, + RateLimitError, + NotFoundError, +} from "marketdata-sdk"; -result.match( - (prices) => console.log("Success:", prices), - (error) => console.error("Failed:", error.message), -); +const client = new MarketDataClient(); + +try { + const prices = await client.stocks.prices("AAPL"); + console.log("Success:", prices); +} catch (error) { + if (error instanceof AuthenticationError) { + console.error("Bad token:", error.message); + } else if (error instanceof RateLimitError) { + console.error("Rate limit exceeded"); + } else { + console.error("Failed:", error); + } +} ``` - + -Branch on result state and access `.value` or `.error` directly. +Fluent chaining without `try`/`catch`. ```typescript -const result = await client.stocks.prices("AAPL"); - -if (result.isOk()) { - console.log("Prices:", result.value); -} else { - console.error("Error:", result.error.message); -} +const prices = await client.stocks + .prices("AAPL") + .catch((error) => { + console.error("Failed:", error.message); + return []; + }); ``` - + + +```typescript +import { AuthenticationError } from "marketdata-sdk"; + +await expect(client.stocks.prices("AAPL")) + .rejects.toBeInstanceOf(AuthenticationError); +``` + + + -Convert to a thrown exception if you prefer `try`/`catch`. +Every thrown error is a subclass of `MarketDataClientError` and carries HTTP context for support tickets: ```typescript try { - const result = await client.stocks.prices("AAPL"); - const prices = result._unsafeUnwrap(); - console.log(prices); -} catch (error) { - console.error("Failed:", error); + await client.stocks.prices("$$$"); +} catch (err) { + // Base class — use `instanceof` to narrow to specific errors. + console.log(err.status_code); // 400 + console.log(err.request_id); // cf-ray header, e.g. "8a1b2c-SJC" + console.log(err.request_url); // full URL that failed + console.log(err.timestamp); // Date of the request + console.log(err.support_info); // multi-line string to paste into tickets } ``` - +### Error classes - +| Class | When | +|---|---| +| `AuthenticationError` | 401 — token missing, invalid, or expired | +| `BadRequestError` | 400 — malformed request or invalid parameters | +| `NotFoundError` | 404 — exported but not thrown by default. 404 resolves to `{s: "no_data"}` instead; see [no-data handling](#NoData). | +| `RateLimitError` | 429 — per-minute/day rate limit exceeded | +| `ServerError` | 5xx — retriable, server-side failure | +| `NetworkError` | Transport failure: DNS, connection, TLS, or 99s timeout | +| `ParseError` | Response body failed schema or JSON parsing | +| `ValidationError` | Client-side input validation failure (before the network call) | -Chain operations on the success value without unwrapping. + +### No-data responses + +When the server responds with 404 the SDK resolves the Promise with an empty array (or empty Blob for CSV) and flags the returned `MarketDataPromise` as `no_data: true`. Use `hasData()` to branch on it: ```typescript -const result = await client.stocks.prices("AAPL") - .map((prices) => prices.filter((p) => p.mid > 100)); +const pending = client.stocks.prices("UNKNOWN"); +const prices = await pending; -if (result.isOk()) { - console.log("Filtered:", result.value); +if (!(await pending.hasData())) { + console.log("No data for that symbol"); +} else { + console.log(prices); } ``` - - - - -### MarketDataResult + +### MarketDataPromise ```typescript -interface MarketDataResult extends ResultAsync { +class MarketDataPromise extends Promise { + isJson(): boolean; + isCsv(): boolean; + isHtml(): boolean; + readonly no_data: boolean; + hasData(): Promise; save(filename?: string): Promise; + saveToFile(filename?: string): Promise; blob(): Promise; } ``` -Every resource method returns a `MarketDataResult`. In addition to the standard `ResultAsync` interface from neverthrow, it exposes two convenience methods: +Every resource method returns a `MarketDataPromise`. It is a regular `Promise` — you `await` it to get the data — with a few extra response-model methods available before you unwrap it: + +- `isJson()` / `isCsv()` / `isHtml()`: reports the requested format. +- `no_data`: synchronous flag, `true` when the server returned 404. +- `hasData()`: awaits and returns whether the resolved value carries any rows. +- `save(filename?)` / `saveToFile(filename?)`: persist the response to disk. Returns a `Promise` resolving to the path written. Format is inferred from the extension when possible. +- `blob()`: materialise the response as a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob). + +Chained saves work without an intermediate `await`: -- `save(filename?: string)`: Write the response to a file. Returns a `Promise` resolving to the filename. Format is inferred from the extension when possible. -- `blob()`: Materialise the response as a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob). Useful for downloading CSV responses. +```typescript +const path = await client.stocks + .candles("AAPL", { resolution: "D", countback: 90 }) + .saveToFile("aapl-90d.csv"); +``` ## Accessing Rate Limits -The client tracks rate limits from the API by reading the `X-Api-Ratelimit-*` headers on every response. The `client.rateLimits` property is populated after the first successful API call. +The client tracks rate limits from the API by reading the `X-Api-Ratelimit-*` headers on every response. The `client.rateLimits` property is populated by the eager `/user/` call during construction (awaited via `client.ready`). ```typescript const client = new MarketDataClient(); +await client.ready; // ensures rateLimits is populated -// Make a request to populate rate limits -await client.stocks.prices("AAPL"); - -// Access current rate limits if (client.rateLimits) { console.log(`Limit: ${client.rateLimits.requestsLimit}`); console.log(`Remaining: ${client.rateLimits.requestsRemaining}`); @@ -248,7 +311,7 @@ If rate limits have been fetched and `requestsRemaining` is `0`, the next resour The SDK includes a built-in logger that outputs diagnostic information. You can pass a custom logger or use the default. ```typescript -import { MarketDataClient, DefaultLogger, LogLevel } from "marketdata-sdk-js"; +import { MarketDataClient, DefaultLogger, LogLevel } from "marketdata-sdk"; // Default logger (INFO level) const client1 = new MarketDataClient(); @@ -263,6 +326,8 @@ const client3 = new MarketDataClient({ logger }); **Log levels** (in order of verbosity): `DEBUG`, `INFO`, `WARN`, `ERROR`. +You can also set the default level via the `MARKETDATA_LOGGING_LEVEL` environment variable. + The default logger obfuscates tokens in log output, showing only the last 4 characters. ## Configuration diff --git a/sdk/js/funds/candles.mdx b/sdk/js/funds/candles.mdx index d5407de..79c81b7 100644 --- a/sdk/js/funds/candles.mdx +++ b/sdk/js/funds/candles.mdx @@ -30,12 +30,12 @@ Mutual fund candles are daily-resolution only (unlike stock candles). They do no candles

( symbol: string, params?: P, -): MarketDataResult +): MarketDataPromise // Object form candles

( params: P & { symbol: string }, -): MarketDataResult +): MarketDataPromise ``` Fetches historical daily candles for a single fund symbol. @@ -71,27 +71,25 @@ Fetches historical daily candles for a single fund symbol. #### Returns -- [`MarketDataResult`](/sdk/js/client#MarketDataResult) +- [`MarketDataPromise`](/sdk/js/client#MarketDataPromise) ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -// VFINX is available without authentication (free test symbol). -const result = await client.funds.candles("VFINX", { countback: 30 }); - -result.match( - (candles) => { - for (const c of candles) { - console.log(`t=${c.t} o=${c.o} h=${c.h} l=${c.l} c=${c.c}`); - } - }, - (error) => console.error(error.message), -); +try { + // VFINX is available without authentication (free test symbol). + const candles = await client.funds.candles("VFINX", { countback: 30 }); + for (const c of candles) { + console.log(`t=${c.t} o=${c.o} h=${c.h} l=${c.l} c=${c.c}`); + } +} catch (error) { + console.error(error); +} ``` @@ -99,20 +97,20 @@ result.match( ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.funds.candles("VFINX", { - resolution: "M", - from: "2020-01-01", - to: "2024-12-31", -}); - -result.match( - (candles) => console.log(`${candles.length} monthly bars`), - (error) => console.error(error.message), -); +try { + const candles = await client.funds.candles("VFINX", { + resolution: "M", + from: "2020-01-01", + to: "2024-12-31", + }); + console.log(`${candles.length} monthly bars`); +} catch (error) { + console.error(error); +} ``` @@ -120,23 +118,21 @@ result.match( ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.funds.candles("VFINX", { - countback: 10, - human: true, -}); - -result.match( - (candles) => { - for (const c of candles) { - console.log(`Date=${c.Date} Open=${c.Open} Close=${c.Close}`); - } - }, - (error) => console.error(error.message), -); +try { + const candles = await client.funds.candles("VFINX", { + countback: 10, + human: true, + }); + for (const c of candles) { + console.log(`Date=${c.Date} Open=${c.Open} Close=${c.Close}`); + } +} catch (error) { + console.error(error); +} ``` diff --git a/sdk/js/installation.mdx b/sdk/js/installation.mdx index be3aaaa..53c4262 100644 --- a/sdk/js/installation.mdx +++ b/sdk/js/installation.mdx @@ -19,21 +19,21 @@ This guide will help you install the Market Data JavaScript SDK and configure it ```bash -pnpm add marketdata-sdk-js +pnpm add marketdata-sdk ``` ```bash -npm install marketdata-sdk-js +npm install marketdata-sdk ``` ```bash -yarn add marketdata-sdk-js +yarn add marketdata-sdk ``` @@ -51,8 +51,8 @@ pnpm add github:MarketDataApp/sdk-js The SDK is written in TypeScript and ships first-class type definitions. No `@types/*` package is needed. The SDK builds to both ESM and CommonJS, so it works with either module system. -- **ESM**: `import { MarketDataClient } from "marketdata-sdk-js";` -- **CommonJS**: `const { MarketDataClient } = require("marketdata-sdk-js");` +- **ESM**: `import { MarketDataClient } from "marketdata-sdk";` +- **CommonJS**: `const { MarketDataClient } = require("marketdata-sdk");` ## Local Development Installation @@ -84,7 +84,7 @@ corepack enable The SDK includes the following core dependencies (installed automatically): - `dotenv`: Loads environment variables from a `.env` file -- `neverthrow`: Functional `Result` type for error handling +- `neverthrow`: Functional `Result` type used internally for error composition. The public API surfaces standard Promises — see [client error handling](/sdk/js/client#ErrorHandling). - `p-limit`: Concurrency pool for fan-out requests - `p-retry`: Retry logic with exponential backoff - `zod`: Runtime schema validation and type inference diff --git a/sdk/js/markets/status.mdx b/sdk/js/markets/status.mdx index 97dc204..0a5e5f9 100644 --- a/sdk/js/markets/status.mdx +++ b/sdk/js/markets/status.mdx @@ -24,7 +24,7 @@ Use the `status()` method on the `markets` resource to fetch market status infor ```typescript status

( params?: P, -): MarketDataResult +): MarketDataPromise ``` Fetches market status information. All parameters are optional. @@ -60,26 +60,24 @@ Fetches market status information. All parameters are optional. #### Returns -- [`MarketDataResult`](/sdk/js/client#MarketDataResult) +- [`MarketDataPromise`](/sdk/js/client#MarketDataPromise) ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.markets.status(); - -result.match( - (days) => { - for (const d of days) { - console.log(`date=${d.date} status=${d.status}`); - } - }, - (error) => console.error(error.message), -); +try { + const days = await client.markets.status(); + for (const d of days) { + console.log(`date=${d.date} status=${d.status}`); + } +} catch (error) { + console.error(error); +} ``` @@ -87,20 +85,20 @@ result.match( ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.markets.status({ - from: "2024-01-01", - to: "2024-01-31", - country: "US", -}); - -result.match( - (days) => console.log(days), - (error) => console.error(error.message), -); +try { + const days = await client.markets.status({ + from: "2024-01-01", + to: "2024-01-31", + country: "US", + }); + console.log(days); +} catch (error) { + console.error(error); +} ``` @@ -108,17 +106,17 @@ result.match( ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -// Last 7 trading-status days -const result = await client.markets.status({ countback: 7 }); - -result.match( - (days) => console.log(days), - (error) => console.error(error.message), -); +try { + // Last 7 trading-status days + const days = await client.markets.status({ countback: 7 }); + console.log(days); +} catch (error) { + console.error(error); +} ``` @@ -126,20 +124,18 @@ result.match( ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.markets.status({ countback: 7, human: true }); - -result.match( - (days) => { - for (const d of days) { - console.log(`Date=${d.Date} Status=${d.Status}`); - } - }, - (error) => console.error(error.message), -); +try { + const days = await client.markets.status({ countback: 7, human: true }); + for (const d of days) { + console.log(`Date=${d.Date} Status=${d.Status}`); + } +} catch (error) { + console.error(error); +} ``` diff --git a/sdk/js/options/chain.mdx b/sdk/js/options/chain.mdx index dbd99f5..86cdc85 100644 --- a/sdk/js/options/chain.mdx +++ b/sdk/js/options/chain.mdx @@ -26,12 +26,12 @@ Use the `chain()` method on the `options` resource to fetch an option chain. The chain

( symbol: string, params?: P, -): MarketDataResult +): MarketDataPromise // Object form chain

( params: P & { symbol: string }, -): MarketDataResult +): MarketDataPromise ``` Fetches the option chain for a single underlying symbol. @@ -115,22 +115,22 @@ Fetches the option chain for a single underlying symbol. #### Returns -- [`MarketDataResult`](/sdk/js/client#MarketDataResult) +- [`MarketDataPromise`](/sdk/js/client#MarketDataPromise) ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.options.chain("AAPL"); - -result.match( - (contracts) => console.log(`Got ${contracts.length} contracts`), - (error) => console.error(error.message), -); +try { + const contracts = await client.options.chain("AAPL"); + console.log(`Got ${contracts.length} contracts`); +} catch (error) { + console.error(error); +} ``` @@ -138,27 +138,25 @@ result.match( ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -// Calls only, narrow strike window, high open interest -const result = await client.options.chain("AAPL", { - side: "call", - strike_limit: 10, - min_open_interest: 100, -}); - -result.match( - (contracts) => { - for (const c of contracts) { - console.log( - `${c.optionSymbol} strike=${c.strike} mid=${c.mid} delta=${c.delta} oi=${c.openInterest}` - ); - } - }, - (error) => console.error(error.message), -); +try { + // Calls only, narrow strike window, high open interest + const contracts = await client.options.chain("AAPL", { + side: "call", + strike_limit: 10, + min_open_interest: 100, + }); + for (const c of contracts) { + console.log( + `${c.optionSymbol} strike=${c.strike} mid=${c.mid} delta=${c.delta} oi=${c.openInterest}` + ); + } +} catch (error) { + console.error(error); +} ``` @@ -166,20 +164,20 @@ result.match( ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.options.chain("AAPL", { - date: "2024-01-15", - expiration: "2024-02-16", - side: "call", -}); - -result.match( - (contracts) => console.log(contracts), - (error) => console.error(error.message), -); +try { + const contracts = await client.options.chain("AAPL", { + date: "2024-01-15", + expiration: "2024-02-16", + side: "call", + }); + console.log(contracts); +} catch (error) { + console.error(error); +} ``` @@ -187,25 +185,23 @@ result.match( ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.options.chain("AAPL", { - side: "put", - human: true, -}); - -result.match( - (contracts) => { - for (const c of contracts) { - console.log( - `${c.Symbol} Strike=${c.Strike} Mid=${c.Mid} Delta=${c.Delta}` - ); - } - }, - (error) => console.error(error.message), -); +try { + const contracts = await client.options.chain("AAPL", { + side: "put", + human: true, + }); + for (const c of contracts) { + console.log( + `${c.Symbol} Strike=${c.Strike} Mid=${c.Mid} Delta=${c.Delta}` + ); + } +} catch (error) { + console.error(error); +} ``` diff --git a/sdk/js/options/expirations.mdx b/sdk/js/options/expirations.mdx index 7606878..614956e 100644 --- a/sdk/js/options/expirations.mdx +++ b/sdk/js/options/expirations.mdx @@ -25,12 +25,12 @@ Use the `expirations()` method on the `options` resource to fetch available expi expirations

( symbol: string, params?: P, -): MarketDataResult +): MarketDataPromise // Object form expirations

( params: P & { symbol: string }, -): MarketDataResult +): MarketDataPromise ``` Fetches the list of expirations for a single underlying symbol. @@ -56,27 +56,25 @@ Fetches the list of expirations for a single underlying symbol. #### Returns -- [`MarketDataResult`](/sdk/js/client#MarketDataResult) +- [`MarketDataPromise`](/sdk/js/client#MarketDataPromise) ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.options.expirations("AAPL"); - -result.match( - (data) => { - console.log(`Updated: ${data.updated}`); - for (const exp of data.expirations) { - console.log(exp); - } - }, - (error) => console.error(error.message), -); +try { + const data = await client.options.expirations("AAPL"); + console.log(`Updated: ${data.updated}`); + for (const exp of data.expirations) { + console.log(exp); + } +} catch (error) { + console.error(error); +} ``` @@ -84,17 +82,17 @@ result.match( ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -// Only expirations that trade the $250 strike -const result = await client.options.expirations("AAPL", { strike: 250 }); - -result.match( - (data) => console.log(data.expirations), - (error) => console.error(error.message), -); +try { + // Only expirations that trade the $250 strike + const data = await client.options.expirations("AAPL", { strike: 250 }); + console.log(data.expirations); +} catch (error) { + console.error(error); +} ``` @@ -102,16 +100,16 @@ result.match( ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.options.expirations("AAPL", { human: true }); - -result.match( - (data) => console.log(data.Expirations), - (error) => console.error(error.message), -); +try { + const data = await client.options.expirations("AAPL", { human: true }); + console.log(data.Expirations); +} catch (error) { + console.error(error); +} ``` diff --git a/sdk/js/options/lookup.mdx b/sdk/js/options/lookup.mdx index a5cddaa..d0542f1 100644 --- a/sdk/js/options/lookup.mdx +++ b/sdk/js/options/lookup.mdx @@ -20,12 +20,12 @@ Use the `lookup()` method on the `options` resource to resolve an OCC symbol fro lookup

( lookupStr: string, params?: P, -): MarketDataResult +): MarketDataPromise // Object form lookup

( params: P & { lookup: string }, -): MarketDataResult +): MarketDataPromise ``` Resolves a human-readable option description to its OCC symbol. @@ -41,22 +41,22 @@ Resolves a human-readable option description to its OCC symbol. #### Returns -- [`MarketDataResult`](/sdk/js/client#MarketDataResult) +- [`MarketDataPromise`](/sdk/js/client#MarketDataPromise) ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.options.lookup("AAPL 7/28/2023 200 Call"); - -result.match( - (data) => console.log(data.optionSymbol), // "AAPL230728C00200000" - (error) => console.error(error.message), -); +try { + const data = await client.options.lookup("AAPL 7/28/2023 200 Call"); + console.log(data.optionSymbol); // "AAPL230728C00200000" +} catch (error) { + console.error(error); +} ``` @@ -64,18 +64,18 @@ result.match( ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.options.lookup("AAPL 7/28/2023 200 Call", { - human: true, -}); - -result.match( - (data) => console.log(data.Symbol), - (error) => console.error(error.message), -); +try { + const data = await client.options.lookup("AAPL 7/28/2023 200 Call", { + human: true, + }); + console.log(data.Symbol); +} catch (error) { + console.error(error); +} ``` diff --git a/sdk/js/options/quotes.mdx b/sdk/js/options/quotes.mdx index 61ae8bb..c82603d 100644 --- a/sdk/js/options/quotes.mdx +++ b/sdk/js/options/quotes.mdx @@ -26,12 +26,12 @@ Use the `quotes()` method on the `options` resource to fetch quotes for individu quotes

( symbols: string | string[], params?: P, -): MarketDataResult +): MarketDataPromise // Object form quotes

( params: P & { symbols: string | string[] }, -): MarketDataResult +): MarketDataPromise ``` Fetches quotes for one or more option contracts. @@ -53,27 +53,25 @@ Additional endpoint-specific parameters like `from`, `to`, and `date` are passed #### Returns -- [`MarketDataResult`](/sdk/js/client#MarketDataResult) +- [`MarketDataPromise`](/sdk/js/client#MarketDataPromise) ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.options.quotes("AAPL271217C00250000"); - -result.match( - (quotes) => { - const q = quotes[0]; - console.log( - `${q.optionSymbol}: bid=${q.bid} ask=${q.ask} delta=${q.delta}` - ); - }, - (error) => console.error(error.message), -); +try { + const quotes = await client.options.quotes("AAPL271217C00250000"); + const q = quotes[0]; + console.log( + `${q.optionSymbol}: bid=${q.bid} ask=${q.ask} delta=${q.delta}` + ); +} catch (error) { + console.error(error); +} ``` @@ -81,24 +79,22 @@ result.match( ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.options.quotes([ - "AAPL271217C00250000", - "AAPL271217P00250000", - "AAPL271217C00300000", -]); - -result.match( - (quotes) => { - for (const q of quotes) { - console.log(`${q.optionSymbol}: mid=${q.mid}`); - } - }, - (error) => console.error(error.message), -); +try { + const quotes = await client.options.quotes([ + "AAPL271217C00250000", + "AAPL271217P00250000", + "AAPL271217C00300000", + ]); + for (const q of quotes) { + console.log(`${q.optionSymbol}: mid=${q.mid}`); + } +} catch (error) { + console.error(error); +} ``` @@ -106,18 +102,18 @@ result.match( ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.options.quotes("AAPL271217C00250000", { - human: true, -}); - -result.match( - (quotes) => console.log(quotes[0]), - (error) => console.error(error.message), -); +try { + const quotes = await client.options.quotes("AAPL271217C00250000", { + human: true, + }); + console.log(quotes[0]); +} catch (error) { + console.error(error); +} ``` diff --git a/sdk/js/settings.mdx b/sdk/js/settings.mdx index 7840097..6cecfe4 100644 --- a/sdk/js/settings.mdx +++ b/sdk/js/settings.mdx @@ -55,7 +55,7 @@ export MARKETDATA_USE_HUMAN_READABLE=true ``` ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); @@ -70,7 +70,7 @@ const result = await client.stocks.prices("AAPL"); Set client-level configuration via the constructor: ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient({ token: "your_token_here", @@ -93,7 +93,7 @@ Constructor arguments override environment variables for the settings they cover Pass parameters directly to resource methods: ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); @@ -124,7 +124,7 @@ Controls the format of the API response data. The SDK also exposes an `OutputFormat` enum you can import for type safety: ```typescript -import { OutputFormat } from "marketdata-sdk-js"; +import { OutputFormat } from "marketdata-sdk"; // OutputFormat.INTERNAL, OutputFormat.JSON, OutputFormat.CSV ``` @@ -136,7 +136,7 @@ import { OutputFormat } from "marketdata-sdk-js"; **Example:** ```typescript -import { MarketDataClient, OutputFormat } from "marketdata-sdk-js"; +import { MarketDataClient, OutputFormat } from "marketdata-sdk"; const client = new MarketDataClient(); @@ -162,7 +162,7 @@ Specifies the format for date and time information in responses. The SDK also exposes a `DateFormat` enum: ```typescript -import { DateFormat } from "marketdata-sdk-js"; +import { DateFormat } from "marketdata-sdk"; // DateFormat.TIMESTAMP, DateFormat.UNIX, DateFormat.SPREADSHEET ``` @@ -174,7 +174,7 @@ import { DateFormat } from "marketdata-sdk-js"; **Example:** ```typescript -import { MarketDataClient, DateFormat } from "marketdata-sdk-js"; +import { MarketDataClient, DateFormat } from "marketdata-sdk"; const client = new MarketDataClient(); @@ -197,7 +197,7 @@ Limits the response to only the columns you need, reducing data transfer and pro **Example:** ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); @@ -222,7 +222,7 @@ Controls whether headers are included in CSV output. **Example:** ```typescript -import { MarketDataClient, OutputFormat } from "marketdata-sdk-js"; +import { MarketDataClient, OutputFormat } from "marketdata-sdk"; const client = new MarketDataClient(); @@ -250,16 +250,16 @@ Uses human-readable field names in responses instead of short, machine-friendly **Example:** ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const quotes = await client.stocks.quotes("AAPL", { human: true }); - -quotes.match( - (data) => console.log(data[0].Symbol, data[0].Mid), - (err) => console.error(err.message), -); +try { + const quotes = await client.stocks.quotes("AAPL", { human: true }); + console.log(quotes[0].Symbol, quotes[0].Mid); +} catch (error) { + console.error(error); +} ``` For more details, see the [API Human Readable documentation](/api/universal-parameters/human-readable). @@ -276,7 +276,7 @@ Controls whether the API returns live, cached, or delayed data. The SDK also exposes a `Mode` enum: ```typescript -import { Mode } from "marketdata-sdk-js"; +import { Mode } from "marketdata-sdk"; // Mode.LIVE, Mode.CACHED, Mode.DELAYED ``` @@ -288,7 +288,7 @@ import { Mode } from "marketdata-sdk-js"; **Example:** ```typescript -import { MarketDataClient, Mode } from "marketdata-sdk-js"; +import { MarketDataClient, Mode } from "marketdata-sdk"; const client = new MarketDataClient(); diff --git a/sdk/js/stocks/candles.mdx b/sdk/js/stocks/candles.mdx index 0f18db5..56974f3 100644 --- a/sdk/js/stocks/candles.mdx +++ b/sdk/js/stocks/candles.mdx @@ -26,12 +26,12 @@ Use the `candles()` method on the `stocks` resource to fetch candles. The method candles

( symbol: string, params?: P, -): MarketDataResult +): MarketDataPromise // Object form candles

( params: P & { symbol: string }, -): MarketDataResult +): MarketDataPromise ``` Fetches historical candles for a single symbol. @@ -78,7 +78,7 @@ Fetches historical candles for a single symbol. #### Returns -- [`MarketDataResult`](/sdk/js/client#MarketDataResult) +- [`MarketDataPromise`](/sdk/js/client#MarketDataPromise) #### Notes @@ -89,21 +89,19 @@ Fetches historical candles for a single symbol. ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -// Default resolution is "D" (daily) -const result = await client.stocks.candles("AAPL", { countback: 30 }); - -result.match( - (candles) => { - for (const c of candles) { - console.log(`t=${c.t} o=${c.o} h=${c.h} l=${c.l} c=${c.c} v=${c.v}`); - } - }, - (error) => console.error(error.message), -); +try { + // Default resolution is "D" (daily) + const candles = await client.stocks.candles("AAPL", { countback: 30 }); + for (const c of candles) { + console.log(`t=${c.t} o=${c.o} h=${c.h} l=${c.l} c=${c.c} v=${c.v}`); + } +} catch (error) { + console.error(error); +} ``` @@ -111,23 +109,23 @@ result.match( ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -// Hourly candles over a multi-year range. -// The SDK automatically splits into year-sized chunks -// and fetches them concurrently. -const result = await client.stocks.candles("AAPL", { - resolution: "1H", - from: new Date("2023-01-01"), - to: new Date("2024-12-31"), -}); - -result.match( - (candles) => console.log(`Got ${candles.length} bars`), - (error) => console.error(error.message), -); +try { + // Hourly candles over a multi-year range. + // The SDK automatically splits into year-sized chunks + // and fetches them concurrently. + const candles = await client.stocks.candles("AAPL", { + resolution: "1H", + from: new Date("2023-01-01"), + to: new Date("2024-12-31"), + }); + console.log(`Got ${candles.length} bars`); +} catch (error) { + console.error(error); +} ``` @@ -135,24 +133,22 @@ result.match( ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.stocks.candles("AAPL", { - resolution: "D", - countback: 10, - human: true, -}); - -result.match( - (candles) => { - for (const c of candles) { - console.log(`Date=${c.Date} Open=${c.Open} Close=${c.Close}`); - } - }, - (error) => console.error(error.message), -); +try { + const candles = await client.stocks.candles("AAPL", { + resolution: "D", + countback: 10, + human: true, + }); + for (const c of candles) { + console.log(`Date=${c.Date} Open=${c.Open} Close=${c.Close}`); + } +} catch (error) { + console.error(error); +} ``` @@ -160,17 +156,17 @@ result.match( ```typescript -import { MarketDataClient, OutputFormat } from "marketdata-sdk-js"; +import { MarketDataClient, OutputFormat } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.stocks.candles("AAPL", { +const csv = await client.stocks.candles("AAPL", { resolution: "D", countback: 100, outputFormat: OutputFormat.CSV, }); -const filename = await result.save("aapl_daily.csv"); +const filename = await csv.save("aapl_daily.csv"); console.log(`CSV saved to: ${filename}`); ``` diff --git a/sdk/js/stocks/earnings.mdx b/sdk/js/stocks/earnings.mdx index 6a146c0..529c85a 100644 --- a/sdk/js/stocks/earnings.mdx +++ b/sdk/js/stocks/earnings.mdx @@ -26,12 +26,12 @@ Use the `earnings()` method on the `stocks` resource to fetch earnings data. earnings

( symbol: string, params?: P, -): MarketDataResult +): MarketDataPromise // Object form earnings

( params: P & { symbol: string }, -): MarketDataResult +): MarketDataPromise ``` Fetches earnings data for a single symbol. @@ -53,7 +53,7 @@ Additional endpoint-specific parameters (e.g. `from`, `to`, `date`) are passed t #### Returns -- [`MarketDataResult`](/sdk/js/client#MarketDataResult) +- [`MarketDataPromise`](/sdk/js/client#MarketDataPromise) :::info Earnings data is a premium endpoint on the Market Data API. Your plan must include earnings access for this method to return data. @@ -63,23 +63,21 @@ Earnings data is a premium endpoint on the Market Data API. Your plan must inclu ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.stocks.earnings("AAPL"); - -result.match( - (earnings) => { - for (const e of earnings) { - console.log( - `FY${e.fiscalYear} Q${e.fiscalQuarter}: ` + - `reported=${e.reportedEPS} estimated=${e.estimatedEPS}` - ); - } - }, - (error) => console.error(error.message), -); +try { + const earnings = await client.stocks.earnings("AAPL"); + for (const e of earnings) { + console.log( + `FY${e.fiscalYear} Q${e.fiscalQuarter}: ` + + `reported=${e.reportedEPS} estimated=${e.estimatedEPS}` + ); + } +} catch (error) { + console.error(error); +} ``` @@ -87,20 +85,18 @@ result.match( ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.stocks.earnings("AAPL", { human: true }); - -result.match( - (earnings) => { - for (const e of earnings) { - console.log(`${e.Symbol} FY${e.Fiscal_Year} Q${e.Fiscal_Quarter}: ${e.Reported_EPS}`); - } - }, - (error) => console.error(error.message), -); +try { + const earnings = await client.stocks.earnings("AAPL", { human: true }); + for (const e of earnings) { + console.log(`${e.Symbol} FY${e.Fiscal_Year} Q${e.Fiscal_Quarter}: ${e.Reported_EPS}`); + } +} catch (error) { + console.error(error); +} ``` diff --git a/sdk/js/stocks/news.mdx b/sdk/js/stocks/news.mdx index bf58680..cf76f38 100644 --- a/sdk/js/stocks/news.mdx +++ b/sdk/js/stocks/news.mdx @@ -26,12 +26,12 @@ Use the `news()` method on the `stocks` resource to fetch news. news

( symbol: string, params?: P, -): MarketDataResult +): MarketDataPromise // Object form news

( params: P & { symbol: string }, -): MarketDataResult +): MarketDataPromise ``` Fetches news articles for a single symbol. @@ -67,26 +67,24 @@ Fetches news articles for a single symbol. #### Returns -- [`MarketDataResult`](/sdk/js/client#MarketDataResult) +- [`MarketDataPromise`](/sdk/js/client#MarketDataPromise) ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.stocks.news("AAPL"); - -result.match( - (articles) => { - for (const a of articles) { - console.log(`${a.source}: ${a.headline}`); - } - }, - (error) => console.error(error.message), -); +try { + const articles = await client.stocks.news("AAPL"); + for (const a of articles) { + console.log(`${a.source}: ${a.headline}`); + } +} catch (error) { + console.error(error); +} ``` @@ -94,19 +92,19 @@ result.match( ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.stocks.news("AAPL", { - from: "2024-01-01", - to: "2024-01-31", -}); - -result.match( - (articles) => console.log(`Got ${articles.length} articles`), - (error) => console.error(error.message), -); +try { + const articles = await client.stocks.news("AAPL", { + from: "2024-01-01", + to: "2024-01-31", + }); + console.log(`Got ${articles.length} articles`); +} catch (error) { + console.error(error); +} ``` diff --git a/sdk/js/stocks/prices.mdx b/sdk/js/stocks/prices.mdx index 4580aaf..fb8c74d 100644 --- a/sdk/js/stocks/prices.mdx +++ b/sdk/js/stocks/prices.mdx @@ -10,7 +10,7 @@ Retrieve stock prices for any supported stock symbol. ## Making Requests -Use the `prices()` method on the `stocks` resource to fetch stock prices. The method returns a [`MarketDataResult`](/sdk/js/client#MarketDataResult) which resolves to decoded records by default. The output format is controlled by the `outputFormat` parameter (or `MARKETDATA_OUTPUT_FORMAT` env var): +Use the `prices()` method on the `stocks` resource to fetch stock prices. The method returns a [`MarketDataPromise`](/sdk/js/client#MarketDataPromise) that resolves to decoded records by default, or rejects with a `MarketDataClientError` subclass on failure. The output format is controlled by the `outputFormat` parameter (or `MARKETDATA_OUTPUT_FORMAT` env var): | Output Format | Result Payload | Description | |------------------------|---------------------------------------|-----------------------------------------------------------------------| @@ -26,12 +26,12 @@ Use the `prices()` method on the `stocks` resource to fetch stock prices. The me prices

( symbols: string | string[], params?: P, -): MarketDataResult +): MarketDataPromise // Object form prices

( params: P & { symbols: string | string[] }, -): MarketDataResult +): MarketDataPromise ``` Fetches stock prices for one or more symbols. The return type is narrowed automatically: @@ -72,24 +72,24 @@ Fetches stock prices for one or more symbols. The return type is narrowed automa #### Returns -- [`MarketDataResult`](/sdk/js/client#MarketDataResult) +- [`MarketDataPromise`](/sdk/js/client#MarketDataPromise) - A Result wrapping the prices data in the requested format. Handle success and failure with `.match()`, `.isOk()` / `.isErr()`, or any other neverthrow method. + A Promise that resolves to the prices data in the requested format, or rejects with a `MarketDataClientError` subclass on failure. Handle with `await` + `try/catch`. ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.stocks.prices("AAPL"); - -result.match( - (prices) => console.log(prices[0].mid), - (error) => console.error(error.message), -); +try { + const prices = await client.stocks.prices("AAPL"); + console.log(prices[0].mid); +} catch (error) { + console.error(error); +} ``` @@ -97,20 +97,18 @@ result.match( ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.stocks.prices(["AAPL", "MSFT", "GOOGL"]); - -result.match( - (prices) => { - for (const p of prices) { - console.log(`${p.symbol}: ${p.mid}`); - } - }, - (error) => console.error(error.message), -); +try { + const prices = await client.stocks.prices(["AAPL", "MSFT", "GOOGL"]); + for (const p of prices) { + console.log(`${p.symbol}: ${p.mid}`); + } +} catch (error) { + console.error(error); +} ``` @@ -118,20 +116,18 @@ result.match( ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.stocks.prices("AAPL", { human: true }); - -result.match( - (prices) => { - for (const p of prices) { - console.log(`${p.Symbol}: mid=${p.Mid}, change=${p.Change_Percent}`); - } - }, - (error) => console.error(error.message), -); +try { + const prices = await client.stocks.prices("AAPL", { human: true }); + for (const p of prices) { + console.log(`${p.Symbol}: mid=${p.Mid}, change=${p.Change_Percent}`); + } +} catch (error) { + console.error(error); +} ``` @@ -139,18 +135,18 @@ result.match( ```typescript -import { MarketDataClient, OutputFormat } from "marketdata-sdk-js"; +import { MarketDataClient, OutputFormat } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.stocks.prices(["AAPL", "MSFT"], { - outputFormat: OutputFormat.JSON, -}); - -result.match( - (json) => console.log(json), - (error) => console.error(error.message), -); +try { + const json = await client.stocks.prices(["AAPL", "MSFT"], { + outputFormat: OutputFormat.JSON, + }); + console.log(json); +} catch (error) { + console.error(error); +} ``` @@ -158,16 +154,16 @@ result.match( ```typescript -import { MarketDataClient, OutputFormat } from "marketdata-sdk-js"; +import { MarketDataClient, OutputFormat } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.stocks.prices(["AAPL", "MSFT"], { +const csv = await client.stocks.prices(["AAPL", "MSFT"], { outputFormat: OutputFormat.CSV, }); // Save the Blob to disk (Node.js) -const filename = await result.save("prices.csv"); +const filename = await csv.save("prices.csv"); console.log(`CSV saved to: ${filename}`); ``` diff --git a/sdk/js/stocks/quotes.mdx b/sdk/js/stocks/quotes.mdx index 04ae5cd..3009618 100644 --- a/sdk/js/stocks/quotes.mdx +++ b/sdk/js/stocks/quotes.mdx @@ -10,7 +10,7 @@ Retrieve stock quotes (bid, ask, mid, last, volume, etc.) for one or more suppor ## Making Requests -Use the `quotes()` method on the `stocks` resource to fetch stock quotes. The method returns a [`MarketDataResult`](/sdk/js/client#MarketDataResult) which resolves to decoded records by default. +Use the `quotes()` method on the `stocks` resource to fetch stock quotes. The method returns a [`MarketDataPromise`](/sdk/js/client#MarketDataPromise) that resolves to decoded records by default, or rejects with a `MarketDataClientError` subclass on failure. | Output Format | Result Payload | Description | |------------------------|---------------------------------------|-------------------------------------------------| @@ -26,12 +26,12 @@ Use the `quotes()` method on the `stocks` resource to fetch stock quotes. The me quotes

( symbols: string | string[], params?: P, -): MarketDataResult +): MarketDataPromise // Object form quotes

( params: P & { symbols: string | string[] }, -): MarketDataResult +): MarketDataPromise ``` Fetches stock quotes for one or more symbols. @@ -59,22 +59,22 @@ Fetches stock quotes for one or more symbols. #### Returns -- [`MarketDataResult`](/sdk/js/client#MarketDataResult) +- [`MarketDataPromise`](/sdk/js/client#MarketDataPromise) ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.stocks.quotes("AAPL"); - -result.match( - (quotes) => console.log(quotes[0]), - (error) => console.error(error.message), -); +try { + const quotes = await client.stocks.quotes("AAPL"); + console.log(quotes[0]); +} catch (error) { + console.error(error); +} ``` @@ -82,20 +82,18 @@ result.match( ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.stocks.quotes(["AAPL", "MSFT", "GOOGL"]); - -result.match( - (quotes) => { - for (const q of quotes) { - console.log(`${q.symbol}: bid=${q.bid}, ask=${q.ask}, last=${q.last}`); - } - }, - (error) => console.error(error.message), -); +try { + const quotes = await client.stocks.quotes(["AAPL", "MSFT", "GOOGL"]); + for (const q of quotes) { + console.log(`${q.symbol}: bid=${q.bid}, ask=${q.ask}, last=${q.last}`); + } +} catch (error) { + console.error(error); +} ``` @@ -103,16 +101,16 @@ result.match( ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.stocks.quotes("AAPL", { "52week": true }); - -result.match( - (quotes) => console.log(quotes[0]), - (error) => console.error(error.message), -); +try { + const quotes = await client.stocks.quotes("AAPL", { "52week": true }); + console.log(quotes[0]); +} catch (error) { + console.error(error); +} ``` @@ -120,19 +118,17 @@ result.match( ```typescript -import { MarketDataClient } from "marketdata-sdk-js"; +import { MarketDataClient } from "marketdata-sdk"; const client = new MarketDataClient(); -const result = await client.stocks.quotes("AAPL", { human: true }); - -result.match( - (quotes) => { - const q = quotes[0]; - console.log(`${q.Symbol}: Bid=${q.Bid} / Ask=${q.Ask}, Volume=${q.Volume}`); - }, - (error) => console.error(error.message), -); +try { + const quotes = await client.stocks.quotes("AAPL", { human: true }); + const q = quotes[0]; + console.log(`${q.Symbol}: Bid=${q.Bid} / Ask=${q.Ask}, Volume=${q.Volume}`); +} catch (error) { + console.error(error); +} ``` From 5604356bf438621814dd4d6f7cceb534b5c054d1 Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Tue, 28 Apr 2026 14:47:27 -0300 Subject: [PATCH 05/29] docs: surface HTTP 203/204 cache responses in API entry-point pages Adds prominent admonitions on the API root page and Authentication page warning that any endpoint may return 203 (cached response) instead of 200, since clients that check only status == 200 silently drop valid cached responses. Adds a Status Codes section to the mode page with the correct 200/203/204 mapping (203 fires on any cache-layer hit, not exclusively on mode=cached) and a Python retry example for the 204 cache-miss path. Tracking the matching OpenAPI spec fix in MarketData-App/api#61. --- api/authentication.mdx | 4 +++ api/index.md | 6 +++++ api/universal-parameters/mode.md | 46 ++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/api/authentication.mdx b/api/authentication.mdx index cf3333b..c030a68 100644 --- a/api/authentication.mdx +++ b/api/authentication.mdx @@ -8,6 +8,10 @@ import TabItem from "@theme/TabItem"; The Market Data API uses a **Bearer Token** for authentication. The token is a programmatic representation of your username and password credentials, so you must keep it secret just as you would your username and password. The token is required for each request you make to the API. +:::caution Accept HTTP 203 as success +Any endpoint may return HTTP `203 Non-Authoritative Information` instead of `200 OK` when the response is served from our caching tier. The body is identical in shape — treat 203 exactly the same as 200. Code samples below that use raw HTTP libraries (rather than our SDKs) must accept both status codes; checking only `status == 200` will silently drop valid responses. See [Troubleshooting](/troubleshooting) for the full list of status codes. +::: + ## Obtaining a Token To obtain it, sign-in to your customer dashboard using your username and password and request a token. It will be delivered by email to the address you used to sign-in. diff --git a/api/index.md b/api/index.md index f36ab9d..a130067 100644 --- a/api/index.md +++ b/api/index.md @@ -10,6 +10,12 @@ The Market Data API is designed around REST and supports standard HTTP response https://api.marketdata.app/ ::: +:::caution Accept HTTP 203 as success +Any endpoint may return HTTP `203 Non-Authoritative Information` instead of `200 OK` when the response is served from our caching tier. This is normal — the body is identical in shape, and you should treat 203 exactly the same as 200. Many HTTP clients (and many OpenAPI-generated SDKs) default to treating only 200 as success; you must update your client to also accept 203 or your integration will silently fail in production. + +A `204 No Content` response can also occur when `mode=cached` is requested and no cached data is available — see [Data Mode](/api/universal-parameters/mode) and [Troubleshooting](/troubleshooting) for the full list of status codes. +::: + ## Try Our API The easiest way to try out our API is using our [Swagger User Interface](https://api.marketdata.app/), which will allow you to try out your API requests directly from your browser. diff --git a/api/universal-parameters/mode.md b/api/universal-parameters/mode.md index 878d9fa..808c6ab 100644 --- a/api/universal-parameters/mode.md +++ b/api/universal-parameters/mode.md @@ -108,6 +108,8 @@ When `mode=cached` is used, successful responses do not return `200 OK`. Instead * `203 NON-AUTHORITATIVE INFORMATION` – Request succeeded and was fulfilled from cache * `204 NO CONTENT` – No cached data available within constraints; no credits charged +`204` is the deterministic cache-miss signal — it can only be returned by `mode=cached` (or `mode=cache` / `mode=stale`). For the full mapping of status codes across all modes, see [Status Codes](#status-codes) below. + ## Delayed Mode The `delayed` mode returns data delayed by **at least 15 minutes**. This mode is the default for all free and trial accounts. Paid accounts may also request delayed data explicitly. @@ -138,3 +140,47 @@ This request returns market data that is delayed by a minimum of 15 minutes. * Choose **`mode=live`** when immediate data freshness is required. * Use **`mode=cached`** to reduce credit usage when working with large symbol sets. * Select **`mode=delayed`** for applications where timing precision is not critical. + +## Status Codes + +Market Data uses HTTP status codes to communicate where a successful response came from. **All three of `200`, `203`, and `204` are success responses — your client must accept all of them.** + +| Status | Meaning | When it occurs | +|-------------------------------------|---------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------| +| `200 OK` | Response was freshly fetched from the upstream provider. | Any mode, when no cache layer can satisfy the request. | +| `203 Non-Authoritative Information` | Response was served from a cache layer (Redis, database quote cache, response log, option-chain cache, etc.). | Any mode. Common during market hours regardless of `mode=live`, `mode=delayed`, or no `mode` specified. The body is identical in shape to a `200`. | +| `204 No Content` | No cached data is available within the requested constraints. No credits charged. | Only when `mode=cached` (also `mode=cache` / `mode=stale`). Never returned by other modes. | + +:::caution Mode does not deterministically map to status code +A common (incorrect) assumption is that `mode=live` always returns `200` and `mode=delayed` always returns `203`. Both can return either `200` or `203` depending on whether a cache layer can satisfy the request at the moment. Only `mode=cached` is deterministic — it returns `203` on cache hit or `204` on cache miss, never `200`. +::: + +### Handling `204` (cache miss on `mode=cached`) + +When `mode=cached` returns `204`, the typical pattern is to fall back to a live request. A short Python example: + +```python +import requests + +def get_option_chain(token): + url = "https://api.marketdata.app/v1/options/chain/AAPL/" + headers = {"Authorization": f"Bearer {token}"} + + r = requests.get(url, params={"mode": "cached"}, headers=headers) + if r.status_code in (200, 203): + return r.json() + if r.status_code == 204: + # Cache miss — fall back to live (incurs normal live-mode credit cost) + r = requests.get(url, params={"mode": "live"}, headers=headers) + if r.status_code in (200, 203): + return r.json() + r.raise_for_status() +``` + +A few notes on the retry pattern: + +- A retry with `mode=live` is billed at the full live-mode credit cost — see [Pricing for Live Mode](#pricing-for-live-mode). The original `204` response is free. +- Cap the retry at one attempt. A subsequent `204` should not occur on `mode=live`, but a circuit breaker is still wise. +- If your plan does not include `mode=cached` access (Free/Trial), `mode=cached` requests return `402 Payment Required` rather than `204`. See [402: Payment Required](/api/troubleshooting/payment-required). + +For the full list of HTTP status codes returned by the API (including `4xx` and `5xx`), see [Troubleshooting](/troubleshooting). From 64ed6892d0701a0daa1e0c955cbf3e296175ebc8 Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Tue, 5 May 2026 20:29:55 -0300 Subject: [PATCH 06/29] docs: add unlisted Commercial Plan page --- account/plans/commercial.mdx | 92 ++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 account/plans/commercial.mdx diff --git a/account/plans/commercial.mdx b/account/plans/commercial.mdx new file mode 100644 index 0000000..e199523 --- /dev/null +++ b/account/plans/commercial.mdx @@ -0,0 +1,92 @@ +--- +title: Commercial Plan +unlisted: true +description: Market Data's commercially-licensed plan for businesses, indie developers, and apps that distribute derived data to end users. +--- + +import Head from '@docusaurus/Head'; + + + + + +The **Commercial Plan** is the most common starting point for businesses launching an app, dashboard, or product on top of Market Data. At **$250/month** it's the most affordable commercially-licensed market data subscription on the market — and it includes a generous package of data that you can redistribute to your end users under our [Commercial Use Addendum](https://www.marketdata.app/terms/commercial-use-addendum/). It's enough to get a product to market, on budget, with the option to add exchange licensing later as your business grows. + +## Pricing + +**$250/month** — month-to-month billing only. The Commercial Plan does not offer an annual billing option. + +## What You Get with the Commercial Plan + +- **Commercial Use License**: The only Market Data plan that permits business and commercial use of our data, governed by our [Commercial Use Addendum](https://www.marketdata.app/terms/commercial-use-addendum/). +- **No Daily API Credit Limit**: Use our API as much as your application needs, with no daily cap. +- **100,000 Credits Per Minute**: A generous per-minute rate limit suited to production workloads. +- **50 Concurrent Requests**: Run requests in parallel to keep latency low under load. +- **Real-Time SmartMid Stock Prices**: Get current-day real-time stock prices on the [`/stocks/prices`](/api/stocks/prices) endpoint via our proprietary **SmartMid** model — a Market Data product that you can redistribute to your end users under our Commercial Use Addendum. **SmartMid is the only real-time data source included on the Commercial Plan.** +- **Historical Data, Full Depth**: Full historical access on every endpoint — stocks, options, and more — for backtesting, analytics, and historical charts. +- **Premium Endpoints**: Full access to all premium endpoints, including fundamentals and earnings data. + +### Exchange Data Is Historical Only + +The Commercial Plan does **not** include any real-time exchange data. All exchange-sourced data — stock bid/ask quotes, daily and intraday candles, options chains and quotes, all of it — is delivered **historical only**, available starting from the **T+1 session** (today's session becomes available the following session). + +The single exception is **SmartMid stock prices**, which are real-time because SmartMid is our own derived product and not exchange data. + +This is what keeps the plan affordable and lets you redistribute data to your end users without additional exchange paperwork. When your business is ready, exchange licensing can be added on top of your Commercial Plan — see [Why SmartMid Instead of Exchange Real-Time?](#why-smartmid-instead-of-exchange-real-time) below. + +## Who the Commercial Plan Is For + +The Commercial Plan is the entry point for businesses bringing a product to market on top of Market Data. Common use cases include: + +- **Stock and options apps** that display data to paying users +- **Financial dashboards** distributed to clients or employees +- **Research products and newsletters** that publish data-driven content +- **Charting and analytics tools** built on historical data +- **Internal business analytics** at firms that need market data for internal use + +Most businesses start here when launching and add exchange licensing later, as their product matures and their budget grows. + +If you're an indie developer **still in development** and not yet charging users or distributing data, you can build on the [Starter Plan](starter) until you're ready to launch. Once you go live with a commercial product, the Commercial Plan is your next step. See our guide on [how stock market data licensing works](https://www.marketdata.app/education/stocks/stock-market-data-licensing) for background. + +### Not Suitable for Trading + +The Commercial Plan is **not suitable for trading applications** or any product where users make execution decisions based on the data. SmartMid is a derived midpoint price — useful for display, charting, and informational purposes — but it is not a live bid/ask quote and should not be used to determine trade prices, route orders, or trigger execution logic. Trading platforms and order-routing tools require real-time exchange data, which means direct exchange licensing. [Contact our sales team](/contact/) to discuss licensing options for trading use cases. + +## Redistribution Rules + +The Commercial Plan permits you to **distribute your own derived data** through your application — including via your own API — but does **not** permit redistributing Market Data's raw API responses. In other words: + +- ✅ You can build a UI, dashboard, or product that displays Market Data's data to your users. +- ✅ You can compute derived metrics, signals, or aggregates from our data and serve those to your users (including over your own API). +- ❌ You cannot resell or proxy our raw API responses to your customers. + +If you're looking to offer a market data API to other businesses, you'll need direct exchange licensing — which the Commercial Plan does not provide. [Contact our sales team](/contact/) if you'd like to discuss your use case. + +## Why SmartMid Instead of Exchange Real-Time? + +Real-time data sourced directly from exchanges has its own [licensing requirements](https://www.marketdata.app/education/stocks/stock-market-data-licensing) that apply on top of any data subscription. By using SmartMid for real-time prices, the Commercial Plan keeps the cost of launching low — most businesses don't need exchange licensing on day one. + +**SmartMid** is our own proprietary derived midpoint price. Because it's a Market Data product rather than exchange data, your application can deliver SmartMid prices to any end user under our Commercial Use Addendum. + +When your business is ready to add real-time exchange data — bid/ask quotes, intraday candles, or real-time options — exchange licensing can be added on top of your Commercial Plan. [Contact our sales team](/contact/) when you're ready. + +## Comparison with Other Plans + +| Feature | Starter | Trader | Prime | **Commercial** | +|-------------------------|---------------|---------------|---------------|--------------------------| +| Price | $30/mo | $75/mo | $250/mo | **$250/mo** | +| Commercial Use License | ❌ | ❌ | ❌ | ✅ | +| Daily API Credits | 10,000/day | 100,000/day | Unlimited | Unlimited | +| Per-Minute Rate Limit | No Limit | No Limit | 100,000 | 100,000 | +| Concurrent Requests | 50 | 50 | 50 | 50 | +| Real-Time Stock Prices | Exchange data | Exchange data | Exchange data | SmartMid (derived) | +| Real-Time Exchange Data | ✅ | ✅ | ✅ | ❌ (T+1 historical only) | +| Real-Time Options | 15-min delay | Real-time | Real-time | ❌ (T+1 historical only) | +| Premium Endpoints | ✅ | ✅ | ✅ | ✅ | +| Historical Data | 5 Years | Full Access | Full Access | Full Access (T+1) | + +## Why Choose the Commercial Plan? + +The Commercial Plan is where most businesses start. At **$250/month**, it's the cheapest commercially-licensed market data subscription on the market — and it's enough to launch a real product, with real-time SmartMid stock prices, full historical depth, no daily credit caps, and generous concurrency. Add exchange licensing later, when your business is ready for it. + +The Commercial Plan is sold through our sales team. [Contact us](/contact/) to get started or to discuss your specific use case. From a6490f89c84a41143689f3039f83293ea9257e04 Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Tue, 5 May 2026 20:36:21 -0300 Subject: [PATCH 07/29] docs: fix contact links on commercial plan page The plain /contact/ links were being rewritten by Docusaurus to /docs/contact/. Use absolute URL to point to the WordPress contact form, matching the existing pattern in api/cors.mdx. --- account/plans/commercial.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/account/plans/commercial.mdx b/account/plans/commercial.mdx index e199523..f62b3c5 100644 --- a/account/plans/commercial.mdx +++ b/account/plans/commercial.mdx @@ -50,7 +50,7 @@ If you're an indie developer **still in development** and not yet charging users ### Not Suitable for Trading -The Commercial Plan is **not suitable for trading applications** or any product where users make execution decisions based on the data. SmartMid is a derived midpoint price — useful for display, charting, and informational purposes — but it is not a live bid/ask quote and should not be used to determine trade prices, route orders, or trigger execution logic. Trading platforms and order-routing tools require real-time exchange data, which means direct exchange licensing. [Contact our sales team](/contact/) to discuss licensing options for trading use cases. +The Commercial Plan is **not suitable for trading applications** or any product where users make execution decisions based on the data. SmartMid is a derived midpoint price — useful for display, charting, and informational purposes — but it is not a live bid/ask quote and should not be used to determine trade prices, route orders, or trigger execution logic. Trading platforms and order-routing tools require real-time exchange data, which means direct exchange licensing. [Contact our sales team](https://www.marketdata.app/contact/) to discuss licensing options for trading use cases. ## Redistribution Rules @@ -60,7 +60,7 @@ The Commercial Plan permits you to **distribute your own derived data** through - ✅ You can compute derived metrics, signals, or aggregates from our data and serve those to your users (including over your own API). - ❌ You cannot resell or proxy our raw API responses to your customers. -If you're looking to offer a market data API to other businesses, you'll need direct exchange licensing — which the Commercial Plan does not provide. [Contact our sales team](/contact/) if you'd like to discuss your use case. +If you're looking to offer a market data API to other businesses, you'll need direct exchange licensing — which the Commercial Plan does not provide. [Contact our sales team](https://www.marketdata.app/contact/) if you'd like to discuss your use case. ## Why SmartMid Instead of Exchange Real-Time? @@ -68,7 +68,7 @@ Real-time data sourced directly from exchanges has its own [licensing requiremen **SmartMid** is our own proprietary derived midpoint price. Because it's a Market Data product rather than exchange data, your application can deliver SmartMid prices to any end user under our Commercial Use Addendum. -When your business is ready to add real-time exchange data — bid/ask quotes, intraday candles, or real-time options — exchange licensing can be added on top of your Commercial Plan. [Contact our sales team](/contact/) when you're ready. +When your business is ready to add real-time exchange data — bid/ask quotes, intraday candles, or real-time options — exchange licensing can be added on top of your Commercial Plan. [Contact our sales team](https://www.marketdata.app/contact/) when you're ready. ## Comparison with Other Plans @@ -89,4 +89,4 @@ When your business is ready to add real-time exchange data — bid/ask quotes, i The Commercial Plan is where most businesses start. At **$250/month**, it's the cheapest commercially-licensed market data subscription on the market — and it's enough to launch a real product, with real-time SmartMid stock prices, full historical depth, no daily credit caps, and generous concurrency. Add exchange licensing later, when your business is ready for it. -The Commercial Plan is sold through our sales team. [Contact us](/contact/) to get started or to discuss your specific use case. +The Commercial Plan is sold through our sales team. [Contact us](https://www.marketdata.app/contact/) to get started or to discuss your specific use case. From c9ed173b92b309ab784129e56bb6e1c4f451014b Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Fri, 8 May 2026 12:16:22 -0300 Subject: [PATCH 08/29] docs(account): add Data Freshness page with per-endpoint rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New page at /docs/account/data-freshness explains the freshness model in three categories — Real-time (<15m), Delayed (15+m, current session), Historical (15+m, prior session closed) — and clarifies the rollover rule that's been silently confusing customers and support: stocks roll to Historical at 4:15 PM ET (close + 15 min), but options roll only at the *next* session's 9:30 AM ET open. So Friday's options data does not become Historical until 9:30:01 Monday — a query at 6:33 AM Wednesday on a Historical-only plan returns Monday's close, not Tuesday's, and that's correct. Each plan tier (Free Forever, Starter Trial, Trader Trial, Starter, Trader, Quant, Prime) gets a per-endpoint freshness table covering every API endpoint family. Cross-reference links added from the plan docs, plan-limits, and the universal-parameters/mode page. A few cells (funds endpoints, UTP-entitled stock candles) are flagged as pending product confirmation in the page footer. --- account/data-freshness.md | 173 +++++++++++++++++++++++++++++++ account/plan-limits.md | 2 + account/plans/free-forever.md | 2 + account/plans/prime.md | 2 + account/plans/quant.md | 2 + account/plans/starter-trial.md | 2 + account/plans/starter.md | 2 + account/plans/trader-trial.md | 2 + account/plans/trader.md | 2 + api/universal-parameters/mode.md | 2 + 10 files changed, 191 insertions(+) create mode 100644 account/data-freshness.md diff --git a/account/data-freshness.md b/account/data-freshness.md new file mode 100644 index 0000000..c410425 --- /dev/null +++ b/account/data-freshness.md @@ -0,0 +1,173 @@ +--- +title: Data Freshness +sidebar_position: 5.5 +--- + +Market Data classifies the data you receive into three freshness categories: + +- **Real-time** — under 15 minutes old. Live trading data. +- **Delayed** — 15+ minutes old, but from the current trading session. +- **Historical** — from a previous, fully-closed trading session. + +Which category applies depends on your plan, the data type, and the time of day. + +## When Delayed data becomes Historical + +Historical requires both a >15-minute delay floor *and* a data-type-specific session-closed condition. The session-closed condition is **different for stocks and options**: + +- **Stocks:** Historical at session close + 15 min — **4:15:01 PM ET** on a regular trading day. Between 4:00 PM and 4:15 PM ET, the data is still Delayed (the 15-minute Delayed window hasn't elapsed yet). +- **Options:** Historical at the *next* session's open — **9:30:01 AM ET** the next trading day, not at the prior session's close. + +Friday's options quotes therefore do **not** become Historical until **9:30:01 AM ET Monday** — they remain Delayed all weekend. + +If you query an options endpoint at 6:33 AM ET Wednesday on a plan that provides Historical-only options data, you will receive **Monday's** close, not Tuesday's. Tuesday's options data does not roll to Historical until 9:30:01 AM ET Wednesday. This is the most common cause of "the data doesn't match my broker" support requests — the behavior is correct, the customer is just querying before the next session has opened. + +## By Plan + +The tables below show the freshness category for every API endpoint, by plan. Real-time stock and options data also requires non-professional status. See [Exchange Entitlements](./entitlements) for the underlying entitlement model. + +### Free Forever + +All pricing data is Historical (24-hour delayed). Metadata and computed-index endpoints remain Real-time. + +| Endpoint | Freshness | Notes | +|----------|-----------|-------| +| `/v1/stocks/quotes/` | Historical | | +| `/v1/stocks/candles/` | Historical | | +| `/v1/stocks/bulkcandles/` | Historical | | +| `/v1/stocks/prices/` | Historical | | +| `/v1/options/quotes/` | Historical | | +| `/v1/options/chain/` | Historical | | +| `/v1/options/strikes/` | Real-time | Contract metadata, no pricing | +| `/v1/options/expirations/` | Real-time | Contract metadata | +| `/v1/options/lookup/` | Real-time | Contract metadata | +| `/v1/indices/quotes/` | Real-time | Indices are computed continuously | +| `/v1/indices/candles/` | Real-time | Indices are computed continuously | +| `/v1/markets/status/` | Real-time | Calendar metadata | +| `/v1/funds/*` | See note | Pending product confirmation | + +### Starter Trial + +Same as Free Forever — all pricing data is Historical (24-hour delayed). + +| Endpoint | Freshness | Notes | +|----------|-----------|-------| +| `/v1/stocks/quotes/` | Historical | | +| `/v1/stocks/candles/` | Historical | | +| `/v1/stocks/bulkcandles/` | Historical | | +| `/v1/stocks/prices/` | Historical | | +| `/v1/options/quotes/` | Historical | | +| `/v1/options/chain/` | Historical | | +| `/v1/options/strikes/` | Real-time | Contract metadata, no pricing | +| `/v1/options/expirations/` | Real-time | Contract metadata | +| `/v1/options/lookup/` | Real-time | Contract metadata | +| `/v1/indices/quotes/` | Real-time | Indices are computed continuously | +| `/v1/indices/candles/` | Real-time | Indices are computed continuously | +| `/v1/markets/status/` | Real-time | Calendar metadata | +| `/v1/funds/*` | See note | Pending product confirmation | + +### Trader Trial + +Same as Starter Trial — all pricing data is Historical (24-hour delayed). + +| Endpoint | Freshness | Notes | +|----------|-----------|-------| +| `/v1/stocks/quotes/` | Historical | | +| `/v1/stocks/candles/` | Historical | | +| `/v1/stocks/bulkcandles/` | Historical | | +| `/v1/stocks/prices/` | Historical | | +| `/v1/options/quotes/` | Historical | | +| `/v1/options/chain/` | Historical | | +| `/v1/options/strikes/` | Real-time | Contract metadata, no pricing | +| `/v1/options/expirations/` | Real-time | Contract metadata | +| `/v1/options/lookup/` | Real-time | Contract metadata | +| `/v1/indices/quotes/` | Real-time | Indices are computed continuously | +| `/v1/indices/candles/` | Real-time | Indices are computed continuously | +| `/v1/markets/status/` | Real-time | Calendar metadata | +| `/v1/funds/*` | See note | Pending product confirmation | + +### Starter + +Real-time stock data, 15-minute Delayed options data. + +| Endpoint | Freshness | Notes | +|----------|-----------|-------| +| `/v1/stocks/quotes/` | Real-time | IEX entitlement | +| `/v1/stocks/candles/` | Real-time | UTP entitlement may impose 15-min delay on intraday candles — see footnote | +| `/v1/stocks/bulkcandles/` | Real-time | Same as `/candles/` | +| `/v1/stocks/prices/` | Real-time | | +| `/v1/options/quotes/` | Delayed | 15 minutes | +| `/v1/options/chain/` | Delayed | 15 minutes | +| `/v1/options/strikes/` | Real-time | Contract metadata | +| `/v1/options/expirations/` | Real-time | Contract metadata | +| `/v1/options/lookup/` | Real-time | Contract metadata | +| `/v1/indices/quotes/` | Real-time | | +| `/v1/indices/candles/` | Real-time | | +| `/v1/markets/status/` | Real-time | Calendar metadata | +| `/v1/funds/*` | See note | Pending product confirmation | + +### Trader + +Real-time data for both stocks and options. + +| Endpoint | Freshness | Notes | +|----------|-----------|-------| +| `/v1/stocks/quotes/` | Real-time | | +| `/v1/stocks/candles/` | Real-time | | +| `/v1/stocks/bulkcandles/` | Real-time | | +| `/v1/stocks/prices/` | Real-time | | +| `/v1/options/quotes/` | Real-time | OPRA entitlement | +| `/v1/options/chain/` | Real-time | OPRA entitlement | +| `/v1/options/strikes/` | Real-time | Contract metadata | +| `/v1/options/expirations/` | Real-time | Contract metadata | +| `/v1/options/lookup/` | Real-time | Contract metadata | +| `/v1/indices/quotes/` | Real-time | | +| `/v1/indices/candles/` | Real-time | | +| `/v1/markets/status/` | Real-time | Calendar metadata | +| `/v1/funds/*` | See note | Pending product confirmation | + +### Quant + +Real-time data for both stocks and options. Same freshness profile as Trader. + +| Endpoint | Freshness | Notes | +|----------|-----------|-------| +| `/v1/stocks/quotes/` | Real-time | | +| `/v1/stocks/candles/` | Real-time | | +| `/v1/stocks/bulkcandles/` | Real-time | | +| `/v1/stocks/prices/` | Real-time | | +| `/v1/options/quotes/` | Real-time | OPRA entitlement | +| `/v1/options/chain/` | Real-time | OPRA entitlement | +| `/v1/options/strikes/` | Real-time | Contract metadata | +| `/v1/options/expirations/` | Real-time | Contract metadata | +| `/v1/options/lookup/` | Real-time | Contract metadata | +| `/v1/indices/quotes/` | Real-time | | +| `/v1/indices/candles/` | Real-time | | +| `/v1/markets/status/` | Real-time | Calendar metadata | +| `/v1/funds/*` | See note | Pending product confirmation | + +### Prime + +Real-time data for both stocks and options. Same freshness profile as Trader and Quant. + +| Endpoint | Freshness | Notes | +|----------|-----------|-------| +| `/v1/stocks/quotes/` | Real-time | | +| `/v1/stocks/candles/` | Real-time | | +| `/v1/stocks/bulkcandles/` | Real-time | | +| `/v1/stocks/prices/` | Real-time | | +| `/v1/options/quotes/` | Real-time | OPRA entitlement | +| `/v1/options/chain/` | Real-time | OPRA entitlement | +| `/v1/options/strikes/` | Real-time | Contract metadata | +| `/v1/options/expirations/` | Real-time | Contract metadata | +| `/v1/options/lookup/` | Real-time | Contract metadata | +| `/v1/indices/quotes/` | Real-time | | +| `/v1/indices/candles/` | Real-time | | +| `/v1/markets/status/` | Real-time | Calendar metadata | +| `/v1/funds/*` | See note | Pending product confirmation | + +## Notes + +- **Real-time data and professional status:** Real-time stock and options data is only available to non-professional users. Professional users on any paid plan revert to Delayed data unless they have signed the OPRA professional subscriber agreement. See [Exchange Entitlements](./entitlements) and [Professional Status](https://www.marketdata.app/education/stocks/professional-status-explained/). +- **`/v1/funds/*` freshness** is documented per fund-data type and is pending publication here. Until then, refer to the individual endpoint pages under [Funds API](../api/funds/). +- **UTP entitlement and intraday stock candles:** the [UTP entitlement](./entitlements#utp-entitlement) grants "15-minute delayed intraday stock candles." On plans with Real-time stock quotes (Starter and above), this means intraday candles may carry a 15-minute delay even though quotes do not. Confirm with the [Plan Limits](./plan-limits) page for your plan. diff --git a/account/plan-limits.md b/account/plan-limits.md index 6525640..93dff90 100644 --- a/account/plan-limits.md +++ b/account/plan-limits.md @@ -51,6 +51,8 @@ Free Forever accounts can access up to 1 year of historical data and Starter acc Free trials of paid plans provide delayed data. Real-time data is only available with paid versions of the Trader plan and above. ::: +For endpoint-level freshness rules and the Delayed → Historical rollover timing — including why options data only rolls at 9:30 AM ET the next trading day rather than at the prior session's close — see [Data Freshness](./data-freshness). + ## API Endpoints The Free Forever plan only provides access to pricing data. Premium endpoints and spreadsheet formulas that contain reference data are not available on free plans. Quant and Prime plans also have access to custom-built endpoints to satisfy the needs of their specific application. diff --git a/account/plans/free-forever.md b/account/plans/free-forever.md index 1d27a62..b9194df 100644 --- a/account/plans/free-forever.md +++ b/account/plans/free-forever.md @@ -37,6 +37,8 @@ To help you better understand the differences between our plans, here's a quick | Standard Endpoints | ✅ | ✅ | | Premium Endpoints | ❌ | ✅ | +For endpoint-level freshness rules and the Delayed → Historical rollover timing (especially for options, where data only rolls at 9:30 AM ET the next trading day), see [Data Freshness](../data-freshness.md). + ## Why Choose Free Forever? The Free Forever plan is an excellent choice for those new to Market Data or for projects that are in the early stages of development. It allows you to test our services and understand how our data can enhance your projects without any financial commitment. Plus, you can upgrade to a paid plan anytime as your needs grow. diff --git a/account/plans/prime.md b/account/plans/prime.md index d808b50..ff308d8 100644 --- a/account/plans/prime.md +++ b/account/plans/prime.md @@ -37,6 +37,8 @@ Here's how the Prime Plan compares to our other offerings: | Premium Endpoints | ❌ | ✅ | ✅ | ✅ + Custom | ✅ + Custom | | Historical Data | 1 Year | 5 Years | Full Access | Full Access | Full Access | +For endpoint-level freshness rules, see [Data Freshness](../data-freshness.md). + ## Why Choose the Prime Plan? The Prime Plan is the ultimate choice for high-powered traders and investors looking for the most comprehensive financial data solution. With its no-limit approach to data access, real-time information, and the ability to customize features, it provides the tools necessary for sophisticated analysis, trading, and decision-making at scale. diff --git a/account/plans/quant.md b/account/plans/quant.md index 5ef4c41..cdeea8e 100644 --- a/account/plans/quant.md +++ b/account/plans/quant.md @@ -37,6 +37,8 @@ Here's how the Quant Plan compares to our other offerings: | Premium Endpoints | ❌ | ✅ | ✅ | ✅ | ✅ | | Historical Data | 1 Year | 5 Years | Full Access | Full Access | Full Access | +For endpoint-level freshness rules, see [Data Freshness](../data-freshness.md). + ## Why Choose the Quant Plan? The Quant Plan is the ultimate choice for high-powered traders and investors looking for the most comprehensive financial data solution. With its no-limit approach to data access, real-time information, and the ability to customize features, it provides the tools necessary for sophisticated analysis, trading, and decision-making at scale. diff --git a/account/plans/starter-trial.md b/account/plans/starter-trial.md index e08e9cf..fe4d18a 100644 --- a/account/plans/starter-trial.md +++ b/account/plans/starter-trial.md @@ -47,6 +47,8 @@ Here's a quick comparison to help you understand the Starter Trial in relation t | Standard Endpoints | ✅ | ✅ | ✅ | | Premium Endpoints | ❌ | AAPL Only | ✅ | +For endpoint-level freshness rules and the Delayed → Historical rollover timing (especially for options, where data only rolls at 9:30 AM ET the next trading day), see [Data Freshness](../data-freshness.md). + ## Why Take A Trail of the Starter Plan? The Starter Trial is an excellent opportunity to test the waters and see how the enhanced features can benefit your project. Whether you're scaling up or need more detailed financial data for analysis, this trial provides a risk-free way to explore our services. diff --git a/account/plans/starter.md b/account/plans/starter.md index f32928d..cd19463 100644 --- a/account/plans/starter.md +++ b/account/plans/starter.md @@ -39,6 +39,8 @@ To give you a clear idea of how the Starter Plan stands against our other offeri | Premium Endpoints | ❌ | ✅ | ✅ | | Historical Data | 1 Year | 5 Years | Full Access | +For endpoint-level freshness rules and the Delayed → Historical rollover timing (especially for options), see [Data Freshness](../data-freshness.md). + ## Why Choose the Starter Plan? The Starter Plan is perfect for users who have outgrown the Free Forever plan and need more robust data access and capabilities. Whether you're scaling up your project or need more detailed financial data for analysis, the Starter Plan provides the tools and data you need to succeed. diff --git a/account/plans/trader-trial.md b/account/plans/trader-trial.md index 4082b9a..82d6846 100644 --- a/account/plans/trader-trial.md +++ b/account/plans/trader-trial.md @@ -49,6 +49,8 @@ This comparison helps you understand the Trader Trial in relation to our other o | Standard Endpoints | ✅ | ✅ | ✅ | | Premium Endpoints | ❌ | AAPL Only | ✅ | +For endpoint-level freshness rules and the Delayed → Historical rollover timing (especially for options, where data only rolls at 9:30 AM ET the next trading day), see [Data Freshness](../data-freshness.md). + ## Why Try the Trader Trial? The Trader Trial offers an unparalleled opportunity to experience the full depth and breadth of our financial data services tailored for high-volume users and professional traders. It's a risk-free way to determine if the Trader Plan's extensive data access and capabilities align with your project's needs. diff --git a/account/plans/trader.md b/account/plans/trader.md index e6aef60..7b3bfab 100644 --- a/account/plans/trader.md +++ b/account/plans/trader.md @@ -38,6 +38,8 @@ Here's how the Trader Plan compares to our other offerings: | Premium Endpoints | ❌ | ✅ | ✅ | | Historical Data | 1 Year | 5 Years | Full Access | +For endpoint-level freshness rules, see [Data Freshness](../data-freshness.md). + ## Why Choose the Trader Plan? The Trader Plan is ideal for professional traders, high-volume users, and projects that require the most detailed and up-to-date financial data available. It's designed to provide the tools and data necessary for sophisticated analysis and decision-making. diff --git a/api/universal-parameters/mode.md b/api/universal-parameters/mode.md index 808c6ab..3c64740 100644 --- a/api/universal-parameters/mode.md +++ b/api/universal-parameters/mode.md @@ -114,6 +114,8 @@ When `mode=cached` is used, successful responses do not return `200 OK`. Instead The `delayed` mode returns data delayed by **at least 15 minutes**. This mode is the default for all free and trial accounts. Paid accounts may also request delayed data explicitly. +For when delayed data crosses into "historical" (a fully-closed prior session) — and why that happens at different times for stocks (4:15 PM ET) vs options (9:30 AM ET the next trading day) — see [Data Freshness](/docs/account/data-freshness). + ### Pricing for Delayed Mode * Pricing is identical to live mode. From d55861e482cc607bf364201dbe47d07836676a8b Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Wed, 13 May 2026 11:52:27 -0300 Subject: [PATCH 09/29] =?UTF-8?q?docs(sdk):=20clarify=20retry=20policy=20?= =?UTF-8?q?=E2=80=94=203=20retries=20with=201s/2s/4s=20backoff?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sdk/sdk-requirements.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/sdk/sdk-requirements.md b/sdk/sdk-requirements.md index 5cdc172..91ade6b 100644 --- a/sdk/sdk-requirements.md +++ b/sdk/sdk-requirements.md @@ -431,10 +431,15 @@ Retry requests only when: ### 9.3 Backoff Strategy -- Use exponential backoff: `min(initial * 2^attempt, max_backoff)` -- Default initial: 1 second -- Default max backoff: 30 seconds -- Default max attempts: 3 +- Use exponential backoff: `initial * 2^retry`, where `retry` is the **0-indexed retry number** (0 = first retry, 1 = second retry, 2 = third retry) +- Initial delay: 1 second (fixed) +- Default max retries: **3** (in addition to the initial attempt, for up to 4 total attempts) +- Backoff schedule at the default of 3 retries: + - Before retry 0 (1st retry): **1s** (`1 * 2^0`) + - Before retry 1 (2nd retry): **2s** (`1 * 2^1`) + - Before retry 2 (3rd retry): **4s** (`1 * 2^2`) + +Max retries is **user-configurable** via a client constructor parameter (e.g., `max_retries`). The default applies when not specified. The initial delay and exponential base are fixed and not configurable. ### 9.4 Retry-After Header From 66561dc615d4222f329e5f2d6be8b2bce8297957 Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Tue, 19 May 2026 14:51:28 -0300 Subject: [PATCH 10/29] =?UTF-8?q?feat(worker,sdk-sync):=20shared=20MDX?= =?UTF-8?q?=E2=86=92MD=20lib=20+=20SDK=20docs=20sync=20workflow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a shared lib/mdx-to-md.js that the Cloudflare Worker, the new SDK export CLI, and (eventually) the LLM-bundle script all use for converting Docusaurus MDX into clean Markdown. Replaces the worker's inline cleanMarkdown. Improvements delivered to the live .md serving via the worker: - Admonitions (:::tip / :::info / :::warning ...) now render as GitHub Alert blockquotes (> [!TIP]) instead of passing through unchanged. - Internal /sdk/, /api/, /img/ links get rewritten to absolute www.marketdata.app/docs URLs. - Code-fence-aware import stripping: imports inside ```typescript fences (e.g. `import { MarketDataClient } from "marketdata-sdk-js"`) are no longer eaten by the regex. The fix is a proper tokenizer, not a patched regex. Adds: - lib/mdx-to-md.js — pure CJS, runs in V8 isolates and Node - lib/__tests__/mdx-to-md.test.js — 33 node --test cases - scripts/export-sdk-docs.js — `yarn export-sdk-docs --sdk js` - .github/workflows/sync-sdk-docs.yml — push-to-staging or manual dispatch opens a PR in MarketDataApp/sdk-js with regenerated /docs. Uses a GitHub App for cross-repo auth; preserves SDK-repo-owned content via banner-based selective deletion. Pilot scope: sdk-js only. Matrix is one-line to extend. --- .github/workflows/sync-sdk-docs.yml | 158 +++++++++++++ lib/__tests__/mdx-to-md.test.js | 344 ++++++++++++++++++++++++++++ lib/mdx-to-md.js | 217 ++++++++++++++++++ scripts/export-sdk-docs.js | 114 +++++++++ worker/handler.js | 38 +-- worker/handler.test.js | 39 +--- 6 files changed, 844 insertions(+), 66 deletions(-) create mode 100644 .github/workflows/sync-sdk-docs.yml create mode 100644 lib/__tests__/mdx-to-md.test.js create mode 100644 lib/mdx-to-md.js create mode 100644 scripts/export-sdk-docs.js diff --git a/.github/workflows/sync-sdk-docs.yml b/.github/workflows/sync-sdk-docs.yml new file mode 100644 index 0000000..68fef28 --- /dev/null +++ b/.github/workflows/sync-sdk-docs.yml @@ -0,0 +1,158 @@ +# Syncs SDK docs from /sdk// in this repo into MarketDataApp/sdk-/docs/ +# by converting MDX → clean Markdown and opening a PR against the target repo. +# +# Pilot scope: sdk-js only. Adding more SDKs is a one-line matrix change. +# +# ──────────────────────────────────────────────────────────────────────────── +# One-time setup (GitHub App auth — survives the eventual repo migration to +# the MarketData-App org without code changes): +# +# 1. Create a GitHub App owned by `MarketData-App` (org settings → Developer +# settings → GitHub Apps → New GitHub App). Suggested name: +# `marketdata-docs-sync`. Set "Where can this be installed?" to "Any +# account" so it can run against repos that still live under the +# MarketDataApp user (current home) and later the MarketData-App org. +# 2. Permissions: Contents = Read & write, Pull requests = Read & write, +# Metadata = Read. No webhook (uncheck "Active"). +# 3. Generate a private key (.pem) and download it. +# 4. Install the App on `MarketDataApp` (user), granting access to +# `documentation` and `sdk-js`. (Add more SDK repos as the matrix grows.) +# 5. In this repo: +# - Repo variable `SDK_DOCS_APP_ID` = the App ID (a number) +# - Repo secret `SDK_DOCS_APP_PRIVATE_KEY` = the .pem contents +# 6. After repo migration to the org, change `TARGET_OWNER` below from +# `MarketDataApp` to `MarketData-App`. +# ──────────────────────────────────────────────────────────────────────────── + +name: "SDK Docs: Sync to SDK Repos" + +on: + push: + branches: [staging] + paths: + - 'sdk/js/**' + - 'lib/mdx-to-md.js' + - 'scripts/export-sdk-docs.js' + - '.github/workflows/sync-sdk-docs.yml' + workflow_dispatch: + inputs: + sdk: + description: 'Which SDK to sync (pilot: js only)' + required: true + default: 'js' + type: choice + options: [js] + +env: + SOURCE_OWNER: MarketDataApp + TARGET_OWNER: MarketDataApp # change to MarketData-App after repo migration + BANNER_TAG: 'Generated from MarketDataApp/documentation/' + +jobs: + sync: + runs-on: ubuntu-latest + permissions: + contents: read + strategy: + fail-fast: false + matrix: + sdk: [js] + steps: + - name: Checkout documentation source + uses: actions/checkout@v4 + with: + path: docs-source + + - uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Run unit tests for the conversion lib + working-directory: docs-source + run: node --test lib/__tests__/*.test.js + + - name: Convert sdk// → clean .md + working-directory: docs-source + env: + SDK: ${{ matrix.sdk }} + OUT_DIR: ${{ runner.temp }}/out + run: node scripts/export-sdk-docs.js --sdk "$SDK" --out "$OUT_DIR" + + - name: Mint cross-repo token (GitHub App) + id: app-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ vars.SDK_DOCS_APP_ID }} + private-key: ${{ secrets.SDK_DOCS_APP_PRIVATE_KEY }} + owner: ${{ env.TARGET_OWNER }} + repositories: sdk-${{ matrix.sdk }} + + - name: Checkout target SDK repo + uses: actions/checkout@v4 + with: + repository: ${{ env.TARGET_OWNER }}/sdk-${{ matrix.sdk }} + token: ${{ steps.app-token.outputs.token }} + path: target + + - name: Replace previously-generated docs with fresh export + # We DO NOT wipe target/docs/ wholesale — it may contain SDK-repo-owned + # content (ADRs, internal READMEs). Instead, delete only files that + # carry our banner, then copy the new export over. + env: + OUT_DIR: ${{ runner.temp }}/out + run: | + set -euo pipefail + mkdir -p target/docs + # Remove only files in target/docs/ that we previously generated. + while IFS= read -r -d '' f; do + if head -n 5 "$f" | grep -q "$BANNER_TAG"; then + rm -f "$f" + fi + done < <(find target/docs -type f -name '*.md' -print0) + # Remove any now-empty directories left behind. + find target/docs -type d -empty -delete || true + mkdir -p target/docs + cp -R "$OUT_DIR/." target/docs/ + + - name: Compute diff summary for PR body + id: diff + working-directory: target + run: | + set -euo pipefail + git add -A docs/ + { + echo "summary<> "$GITHUB_OUTPUT" + + - name: Open / update PR in target SDK repo + uses: peter-evans/create-pull-request@v6 + with: + path: target + token: ${{ steps.app-token.outputs.token }} + branch: sync/docs-from-documentation + delete-branch: true + commit-message: 'docs: sync from documentation@${{ github.sha }}' + title: 'docs: sync from documentation@${{ github.sha }}' + body: | + Automated sync from [`${{ env.SOURCE_OWNER }}/documentation@${{ github.sha }}`](https://github.com/${{ env.SOURCE_OWNER }}/documentation/commit/${{ github.sha }}). + + **SDK:** `${{ matrix.sdk }}` + **Source path:** `sdk/${{ matrix.sdk }}/` + **Workflow run:** [#${{ github.run_id }}](https://github.com/${{ env.SOURCE_OWNER }}/documentation/actions/runs/${{ github.run_id }}) + + ### Changed files + + ``` + ${{ steps.diff.outputs.summary }} + ``` + + > Files carrying the banner + > `` + > are managed by this workflow — edit the source `.mdx` in the + > documentation repo, not the rendered `.md` here. diff --git a/lib/__tests__/mdx-to-md.test.js b/lib/__tests__/mdx-to-md.test.js new file mode 100644 index 0000000..471954f --- /dev/null +++ b/lib/__tests__/mdx-to-md.test.js @@ -0,0 +1,344 @@ +'use strict'; + +const { test } = require('node:test'); +const assert = require('node:assert/strict'); +const { cleanMdx } = require('../mdx-to-md'); + +// --- Frontmatter --- + +test('extracts title from frontmatter and prepends as H1', () => { + const input = '---\ntitle: Test\nsidebar_position: 1\n---\n\nBody.\n'; + assert.equal(cleanMdx(input), '# Test\n\nBody.\n'); +}); + +test('strips frontmatter without a title field', () => { + const input = '---\nsidebar_position: 1\nslug: /foo\n---\n\n# Hello\n'; + assert.equal(cleanMdx(input), '# Hello\n'); +}); + +test('handles quoted frontmatter title', () => { + const input = '---\ntitle: "Quoted Title"\n---\n\nBody.\n'; + assert.equal(cleanMdx(input), '# Quoted Title\n\nBody.\n'); +}); + +test('no frontmatter passes through unchanged structure', () => { + const input = '# Hello\n\nBody.\n'; + assert.equal(cleanMdx(input), '# Hello\n\nBody.\n'); +}); + +// --- Imports --- + +test('strips theme imports outside code fences', () => { + const input = 'import Tabs from "@theme/Tabs";\nimport TabItem from "@theme/TabItem";\n\n# Hello\n'; + assert.equal(cleanMdx(input), '# Hello\n'); +}); + +test('preserves imports inside code fences (the long-standing bug fix)', () => { + const input = [ + '# Example', + '', + '```typescript', + 'import { MarketDataClient } from "marketdata-sdk-js";', + 'const c = new MarketDataClient();', + '```', + '', + ].join('\n'); + const result = cleanMdx(input); + assert.match(result, /import \{ MarketDataClient \} from "marketdata-sdk-js"/); +}); + +test('strips prose-level imports but keeps fenced ones in the same doc', () => { + const input = [ + 'import Tabs from "@theme/Tabs";', + '', + '# Doc', + '', + '```js', + 'import foo from "bar";', + '```', + '', + ].join('\n'); + const result = cleanMdx(input); + assert.ok(!result.includes('@theme/Tabs'), 'theme import should be stripped'); + assert.match(result, /import foo from "bar"/); +}); + +// --- Tabs / TabItem --- + +test('converts TabItem to ### heading and strips Tabs wrapper', () => { + const input = '\n\n\ncode\n\n\n\n'; + const result = cleanMdx(input); + assert.match(result, /### JavaScript/); + assert.ok(!result.includes('')); + assert.ok(!result.includes('')); + assert.ok(!result.includes('')); +}); + +test('handles TabItem with label before value', () => { + const input = '\nbody\n\n'; + const result = cleanMdx(input); + assert.match(result, /### PHP/); +}); + +// --- Admonitions --- + +test('converts :::tip to GitHub Alert', () => { + const input = ':::tip\nUse the SDK.\n:::\n'; + const result = cleanMdx(input); + assert.match(result, /> \[!TIP\]/); + assert.match(result, /> Use the SDK\./); +}); + +test('preserves admonition custom title as bold', () => { + const input = ':::info Premium Parameter\nThis is paid.\n:::\n'; + const result = cleanMdx(input); + assert.match(result, /> \[!NOTE\]/); + assert.match(result, /> \*\*Premium Parameter\*\*/); + assert.match(result, /> This is paid\./); +}); + +test('admonition mapping covers all Docusaurus levels', () => { + const cases = [ + [':::note\nx\n:::', 'NOTE'], + [':::tip\nx\n:::', 'TIP'], + [':::info\nx\n:::', 'NOTE'], + [':::warning\nx\n:::', 'WARNING'], + [':::caution\nx\n:::', 'CAUTION'], + [':::danger\nx\n:::', 'CAUTION'], + ]; + for (const [input, expected] of cases) { + const result = cleanMdx(input); + assert.match(result, new RegExp(`\\[!${expected}\\]`), `${input} should map to ${expected}`); + } +}); + +test('admonition containing a code fence preserves the fence inside the blockquote', () => { + const input = [ + ':::info', + 'Install from GitHub:', + '', + '```bash', + 'pnpm add github:MarketDataApp/sdk-js', + '```', + ':::', + '', + ].join('\n'); + const result = cleanMdx(input); + assert.match(result, /> \[!NOTE\]/); + assert.match(result, /> ```bash/); + assert.match(result, /> pnpm add github:MarketDataApp\/sdk-js/); + assert.match(result, /> ```$/m); +}); + +test('multi-line admonition body each line gets blockquote prefix', () => { + const input = ':::warning\nline one\nline two\n\nline four\n:::\n'; + const result = cleanMdx(input); + // Every body line begins with `> ` (or `>` for blank) + const lines = result.trim().split('\n'); + // Skip the [!WARNING] line; rest should be blockquoted + for (const line of lines.slice(1)) { + assert.match(line, /^>( |$)/); + } +}); + +// --- Link rewriting --- + +test('rewrites /api/... to absolute URL with default baseUrl', () => { + const input = 'See [the API](/api/stocks/quotes) for details.\n'; + const result = cleanMdx(input); + assert.match(result, /\[the API\]\(https:\/\/www\.marketdata\.app\/docs\/api\/stocks\/quotes\)/); +}); + +test('rewrites same-SDK link to relative .md when sdk + sourcePath given', () => { + const result = cleanMdx('See [Client](/sdk/js/client) docs.\n', { + sourcePath: 'sdk/js/stocks/quotes.mdx', + sdk: 'js', + }); + assert.match(result, /\[Client\]\(\.\.\/client\.md\)/); +}); + +test('rewrites same-SDK link from root file to ./neighbor.md', () => { + const result = cleanMdx('See [Auth](/sdk/js/authentication).\n', { + sourcePath: 'sdk/js/client.mdx', + sdk: 'js', + }); + assert.match(result, /\[Auth\]\(\.\/authentication\.md\)/); +}); + +test('rewrites cross-SDK link to absolute URL', () => { + const result = cleanMdx('Also see [Python](/sdk/py/client).\n', { + sourcePath: 'sdk/js/client.mdx', + sdk: 'js', + }); + assert.match(result, /\[Python\]\(https:\/\/www\.marketdata\.app\/docs\/sdk\/py\/client\)/); +}); + +test('preserves #anchor when rewriting same-SDK link', () => { + const result = cleanMdx('See [Result Pattern](/sdk/js/client#ResultPattern).\n', { + sourcePath: 'sdk/js/stocks/quotes.mdx', + sdk: 'js', + }); + assert.match(result, /\[Result Pattern\]\(\.\.\/client\.md#ResultPattern\)/); +}); + +test('preserves external URLs untouched', () => { + const input = 'See [GitHub](https://github.com/MarketDataApp).\n'; + assert.match(cleanMdx(input), /\[GitHub\]\(https:\/\/github\.com\/MarketDataApp\)/); +}); + +test('rewrites links whose label contains a code span with brackets', () => { + // Real-world case from sdk/js/stocks/quotes.mdx + const input = 'Returns [`MarketDataResult`](/sdk/js/client#MarketDataResult).\n'; + const result = cleanMdx(input, { + sourcePath: 'sdk/js/stocks/quotes.mdx', + sdk: 'js', + }); + assert.match(result, /\]\(\.\.\/client\.md#MarketDataResult\)/); +}); + +test('rewrites links whose label contains balanced [...] brackets', () => { + const input = 'See [name [v2] field](/sdk/js/client) details.\n'; + const result = cleanMdx(input, { + sourcePath: 'sdk/js/index.mdx', + sdk: 'js', + }); + assert.match(result, /\]\(\.\/client\.md\)/); +}); + +test('preserves bare #anchor links', () => { + const input = 'Jump to [the section](#section).\n'; + assert.match(cleanMdx(input), /\[the section\]\(#section\)/); +}); + +test('rewrites image paths the same way as links', () => { + const input = '![Logo](/img/logo.svg)\n'; + const result = cleanMdx(input); + assert.match(result, /!\[Logo\]\(https:\/\/www\.marketdata\.app\/docs\/img\/logo\.svg\)/); +}); + +test('rewrites /sdk/{sdk}/ (root with trailing slash) to relative index.md', () => { + const result = cleanMdx('Back to [JS SDK](/sdk/js/).\n', { + sourcePath: 'sdk/js/stocks/quotes.mdx', + sdk: 'js', + }); + assert.match(result, /\[JS SDK\]\(\.\.\/index\.md\)/); +}); + +// --- Empty components --- + +test('strips self-closing', () => { + const input = '# Sections\n\n\n'; + const result = cleanMdx(input); + assert.ok(!result.includes('DocCardList')); +}); + +test('strips ... paired', () => { + const input = '# Sections\n\nfallback\n'; + const result = cleanMdx(input); + assert.ok(!result.includes('DocCardList')); +}); + +test('strips JSX style={{...}} attribute from tags', () => { + const input = 'x\n'; + const result = cleanMdx(input); + assert.ok(!result.includes('style=')); +}); + +// --- MDX comments --- + +test('strips {/* MDX comments */}', () => { + const input = 'Before {/* this is hidden */} after.\n'; + const result = cleanMdx(input); + assert.ok(!result.includes('hidden')); + assert.match(result, /Before\s+after\./); +}); + +// --- Cleanup / shape --- + +test('collapses 3+ blank lines to 2', () => { + const input = '# A\n\n\n\n\n# B\n'; + assert.equal(cleanMdx(input), '# A\n\n# B\n'); +}); + +test('ensures trailing newline', () => { + const input = '# Trailing'; + assert.equal(cleanMdx(input).at(-1), '\n'); +}); + +// --- Idempotency --- + +test('cleanMdx is idempotent', () => { + const input = [ + '---', + 'title: Idempotent', + 'sidebar_position: 1', + '---', + '', + 'import Tabs from "@theme/Tabs";', + '', + '# Idempotent', + '', + ':::tip Pro Tip', + 'Use [the API](/api/stocks) wisely.', + ':::', + '', + '', + '', + '', + '```typescript', + 'import { MarketDataClient } from "marketdata-sdk-js";', + '```', + '', + '', + '', + '', + ].join('\n'); + const once = cleanMdx(input); + const twice = cleanMdx(once); + assert.equal(twice, once, 'second pass should produce identical output'); +}); + +// --- Composite real-world-ish doc --- + +test('processes a representative SDK page end-to-end', () => { + const input = [ + '---', + 'title: Quotes', + 'sidebar_position: 2', + '---', + '', + 'import Tabs from "@theme/Tabs";', + 'import TabItem from "@theme/TabItem";', + '', + 'Fetch [stock quotes](/api/stocks/quotes). See also [Client](/sdk/js/client#MarketDataClient).', + '', + ':::tip', + 'Use bulk endpoints for >1 symbol.', + ':::', + '', + '', + '', + '', + '```typescript', + 'import { MarketDataClient } from "marketdata-sdk-js";', + 'const r = await client.stocks.quotes("AAPL");', + '```', + '', + '', + '', + '', + ].join('\n'); + const result = cleanMdx(input, { + sourcePath: 'sdk/js/stocks/quotes.mdx', + sdk: 'js', + }); + assert.match(result, /^# Quotes\n\n/); + assert.match(result, /\[stock quotes\]\(https:\/\/www\.marketdata\.app\/docs\/api\/stocks\/quotes\)/); + assert.match(result, /\[Client\]\(\.\.\/client\.md#MarketDataClient\)/); + assert.match(result, /> \[!TIP\]\n> Use bulk endpoints/); + assert.match(result, /### Single Symbol/); + assert.match(result, /import \{ MarketDataClient \} from "marketdata-sdk-js"/); + assert.ok(!result.includes('@theme/Tabs')); + assert.ok(!result.includes('sidebar_position')); +}); diff --git a/lib/mdx-to-md.js b/lib/mdx-to-md.js new file mode 100644 index 0000000..edc4f2c --- /dev/null +++ b/lib/mdx-to-md.js @@ -0,0 +1,217 @@ +'use strict'; + +const ADMONITION_MAP = { + note: 'NOTE', + tip: 'TIP', + info: 'NOTE', + important: 'IMPORTANT', + warning: 'WARNING', + caution: 'CAUTION', + danger: 'CAUTION', +}; + +const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n*/; +const MDX_COMMENT_RE = /\{\s*\/\*[\s\S]*?\*\/\s*\}/g; +const IMPORT_FROM_RE = /^import\b[^\n]*?\bfrom\s+["'][^"'\n]+["'];?[ \t]*$/gm; +const IMPORT_BARE_RE = /^import\s+["'][^"'\n]+["'];?[ \t]*$/gm; +const TABS_OPEN_RE = /]*>[ \t]*\n?/g; +const TABS_CLOSE_RE = /<\/Tabs>[ \t]*\n?/g; +const TABITEM_OPEN_RE = /]*?\blabel="([^"]+)"[^>]*>[ \t]*\n?/g; +const TABITEM_CLOSE_RE = /<\/TabItem>[ \t]*\n?/g; +const DOCCARDLIST_SELF_RE = /]*\/>[ \t]*\n?/g; +const DOCCARDLIST_PAIRED_RE = /]*>[\s\S]*?<\/DocCardList>[ \t]*\n?/g; +const USECURRENT_SIDEBAR_RE = /]*\/>[ \t]*\n?/g; +const JSX_STYLE_RE = /\s+style=\{\{[^}]*\}\}/g; +// Link label may contain: non-special chars, inline code spans `...`, or balanced [...] +const LINK_RE = /(!?)\[((?:[^\[\]`]|`[^`]*`|\[[^\]]*\])*)\]\(([^)\s]+)\)/g; +const FENCE_OPEN_RE = /^([ \t]*)(`{3,}|~{3,})(.*)$/; +const ADMONITION_OPEN_RE = /^:::(\w+)(.*)$/; +const ADMONITION_CLOSE_RE = /^:::[ \t]*$/; + +function extractTitle(frontmatter) { + const m = frontmatter.match(/^title:\s*(.+)$/m); + if (!m) return null; + return m[1].trim().replace(/^["']|["']$/g, ''); +} + +function resolveRelative(sourcePath, sdkBase, targetSub) { + const srcRelative = sourcePath.startsWith(sdkBase + '/') + ? sourcePath.slice(sdkBase.length + 1) + : sourcePath; + const lastSlash = srcRelative.lastIndexOf('/'); + const srcDir = lastSlash >= 0 ? srcRelative.slice(0, lastSlash) : ''; + const depth = srcDir ? srcDir.split('/').length : 0; + const up = depth === 0 ? './' : '../'.repeat(depth); + const cleaned = targetSub.replace(/\/$/, ''); + const target = cleaned === '' ? 'index' : cleaned; + return up + target + '.md'; +} + +function rewriteHref(href, opts) { + if (/^(https?:|mailto:|tel:|#)/i.test(href)) return href; + if (!href.startsWith('/')) return href; + + const { sourcePath, sdk, baseUrl } = opts; + + const hashIdx = href.indexOf('#'); + const pathPart = hashIdx >= 0 ? href.slice(0, hashIdx) : href; + const anchor = hashIdx >= 0 ? href.slice(hashIdx) : ''; + + if (sdk && sourcePath) { + const sdkPrefix = `/sdk/${sdk}/`; + const sdkRoot = `/sdk/${sdk}`; + if (pathPart === sdkRoot || pathPart === sdkRoot + '/') { + return resolveRelative(sourcePath, `sdk/${sdk}`, '') + anchor; + } + if (pathPart.startsWith(sdkPrefix)) { + const sub = pathPart.slice(sdkPrefix.length); + return resolveRelative(sourcePath, `sdk/${sdk}`, sub) + anchor; + } + } + + return baseUrl + href; +} + +function transformProse(text, opts) { + text = text.replace(MDX_COMMENT_RE, ''); + text = text.replace(IMPORT_FROM_RE, ''); + text = text.replace(IMPORT_BARE_RE, ''); + text = text.replace(TABS_OPEN_RE, ''); + text = text.replace(TABS_CLOSE_RE, ''); + text = text.replace(TABITEM_OPEN_RE, '### $1\n\n'); + text = text.replace(TABITEM_CLOSE_RE, '\n\n'); + text = text.replace(DOCCARDLIST_PAIRED_RE, ''); + text = text.replace(DOCCARDLIST_SELF_RE, ''); + text = text.replace(USECURRENT_SIDEBAR_RE, ''); + text = text.replace(JSX_STYLE_RE, ''); + text = text.replace(LINK_RE, (_, bang, label, href) => { + return `${bang}[${label}](${rewriteHref(href, opts)})`; + }); + return text; +} + +function isFenceClose(line, marker) { + const trimmed = line.trim(); + if (trimmed.length < marker.length) return false; + const ch = marker[0]; + for (let i = 0; i < trimmed.length; i++) { + if (trimmed[i] !== ch) return false; + } + return true; +} + +function tokenize(text) { + const lines = text.split('\n'); + const tokens = []; + let i = 0; + while (i < lines.length) { + const line = lines[i]; + + const fenceMatch = line.match(FENCE_OPEN_RE); + if (fenceMatch) { + const marker = fenceMatch[2]; + const fenceLines = [line]; + i++; + while (i < lines.length) { + fenceLines.push(lines[i]); + if (isFenceClose(lines[i], marker)) { i++; break; } + i++; + } + tokens.push({ type: 'fence', text: fenceLines.join('\n') }); + continue; + } + + const admMatch = line.match(ADMONITION_OPEN_RE); + if (admMatch && admMatch[1].toLowerCase() in ADMONITION_MAP) { + const kind = admMatch[1]; + const title = admMatch[2].trim(); + i++; + const bodyLines = []; + let closed = false; + while (i < lines.length) { + const l = lines[i]; + if (ADMONITION_CLOSE_RE.test(l)) { i++; closed = true; break; } + bodyLines.push(l); + i++; + } + if (!closed) { + // No closing fence — treat opening as literal prose + tokens.push({ type: 'prose-line', text: line }); + // Re-process the body lines normally + for (const bl of bodyLines) tokens.push({ type: 'prose-line', text: bl }); + continue; + } + tokens.push({ type: 'admonition', kind, title, body: bodyLines.join('\n') }); + continue; + } + + tokens.push({ type: 'prose-line', text: line }); + i++; + } + return tokens; +} + +function renderAdmonition({ kind, title, body }, opts) { + const alert = ADMONITION_MAP[kind.toLowerCase()] || 'NOTE'; + const innerProcessed = renderTokens(tokenize(body), opts).replace(/\s+$/, ''); + const bodyLines = innerProcessed.split('\n').map((l) => (l === '' ? '>' : `> ${l}`)); + const out = [`> [!${alert}]`]; + if (title) { + out.push(`> **${title}**`); + out.push('>'); + } + for (const l of bodyLines) out.push(l); + return out.join('\n'); +} + +function renderTokens(tokens, opts) { + const out = []; + let proseGroup = []; + + function flushProse() { + if (proseGroup.length === 0) return; + const text = proseGroup.join('\n'); + out.push(transformProse(text, opts)); + proseGroup = []; + } + + for (const tok of tokens) { + if (tok.type === 'prose-line') { + proseGroup.push(tok.text); + } else if (tok.type === 'fence') { + flushProse(); + out.push(tok.text); + } else if (tok.type === 'admonition') { + flushProse(); + out.push(renderAdmonition(tok, opts)); + } + } + flushProse(); + return out.join('\n'); +} + +function cleanMdx(text, opts = {}) { + const o = { + sourcePath: opts.sourcePath ?? null, + sdk: opts.sdk ?? null, + baseUrl: opts.baseUrl ?? 'https://www.marketdata.app/docs', + }; + + let title = null; + const fm = text.match(FRONTMATTER_RE); + if (fm) { + title = extractTitle(fm[1]); + text = text.slice(fm[0].length); + } + + let result = renderTokens(tokenize(text), o); + + if (title) { + result = `# ${title}\n\n` + result.replace(/^\s+/, ''); + } + + result = result.replace(/\n{3,}/g, '\n\n').trim() + '\n'; + return result; +} + +module.exports = { cleanMdx }; diff --git a/scripts/export-sdk-docs.js b/scripts/export-sdk-docs.js new file mode 100644 index 0000000..9d879d7 --- /dev/null +++ b/scripts/export-sdk-docs.js @@ -0,0 +1,114 @@ +#!/usr/bin/env node +'use strict'; + +/** + * Export SDK docs from /sdk/{lang}/ as clean .md, ready to land in MarketDataApp/sdk-{lang}/docs. + * + * node scripts/export-sdk-docs.js --sdk js [--out ./build/sdk-docs/js] + * + * The CLI is intentionally thin — all conversion lives in lib/mdx-to-md.js. + * Output dir is wiped before write so deleted source files don't linger in the target. + */ + +const fs = require('fs'); +const path = require('path'); +const { cleanMdx } = require('../lib/mdx-to-md'); + +const REPO_ROOT = path.resolve(__dirname, '..'); +const SUPPORTED_SDKS = ['js', 'py', 'go', 'php']; + +function parseArgs(argv) { + const args = { sdk: null, out: null }; + for (let i = 0; i < argv.length; i++) { + const a = argv[i]; + if (a === '--sdk') args.sdk = argv[++i]; + else if (a === '--out') args.out = argv[++i]; + else if (a === '-h' || a === '--help') args.help = true; + else { + console.error(`Unknown argument: ${a}`); + process.exit(2); + } + } + return args; +} + +function printUsage() { + console.log(`Usage: node scripts/export-sdk-docs.js --sdk <${SUPPORTED_SDKS.join('|')}> [--out

]`); + console.log(''); + console.log('Converts /sdk//*.mdx → clean .md files under (default: build/sdk-docs//).'); +} + +function walkMdx(dir, acc = []) { + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, entry.name); + if (entry.isDirectory()) walkMdx(full, acc); + else if (entry.isFile() && /\.(mdx?|md)$/.test(entry.name)) acc.push(full); + } + return acc; +} + +function emptyDir(dir) { + if (fs.existsSync(dir)) fs.rmSync(dir, { recursive: true }); + fs.mkdirSync(dir, { recursive: true }); +} + +function main() { + const args = parseArgs(process.argv.slice(2)); + if (args.help || !args.sdk) { + printUsage(); + process.exit(args.help ? 0 : 2); + } + if (!SUPPORTED_SDKS.includes(args.sdk)) { + console.error(`--sdk must be one of: ${SUPPORTED_SDKS.join(', ')}`); + process.exit(2); + } + + const sourceDir = path.join(REPO_ROOT, 'sdk', args.sdk); + if (!fs.existsSync(sourceDir)) { + console.error(`Source directory not found: ${sourceDir}`); + process.exit(1); + } + + const outDir = path.resolve(args.out || path.join(REPO_ROOT, 'build', 'sdk-docs', args.sdk)); + emptyDir(outDir); + + const files = walkMdx(sourceDir); + const manifest = []; + + for (const sourceAbs of files) { + const relFromSdk = path.relative(sourceDir, sourceAbs); // e.g. "stocks/quotes.mdx" + const sourcePathRel = path.posix.join('sdk', args.sdk, relFromSdk.split(path.sep).join('/')); + const outRelative = relFromSdk.replace(/\.mdx$/, '.md'); + const outAbs = path.join(outDir, outRelative); + + const raw = fs.readFileSync(sourceAbs, 'utf8'); + const converted = cleanMdx(raw, { sourcePath: sourcePathRel, sdk: args.sdk }); + + const banner = `\n\n`; + const body = banner + converted; + + fs.mkdirSync(path.dirname(outAbs), { recursive: true }); + fs.writeFileSync(outAbs, body, 'utf8'); + manifest.push({ path: outRelative, bytes: Buffer.byteLength(body, 'utf8') }); + } + + // Promote the SDK's index.md to a top-level README.md (and leave index.md in place + // so any code linking to it still resolves) + const indexPath = path.join(outDir, 'index.md'); + if (fs.existsSync(indexPath)) { + fs.copyFileSync(indexPath, path.join(outDir, 'README.md')); + manifest.push({ + path: 'README.md', + bytes: fs.statSync(path.join(outDir, 'README.md')).size, + note: '(copy of index.md)', + }); + } + + manifest.sort((a, b) => a.path.localeCompare(b.path)); + console.log(`Exported ${manifest.length} file(s) to ${outDir}\n`); + for (const { path: p, bytes, note } of manifest) { + console.log(` ${String(bytes).padStart(7)} ${p}${note ? ' ' + note : ''}`); + } +} + +main(); diff --git a/worker/handler.js b/worker/handler.js index 879f3c7..51fa714 100644 --- a/worker/handler.js +++ b/worker/handler.js @@ -14,6 +14,8 @@ * are passed through unchanged. */ +const { cleanMdx } = require('../lib/mdx-to-md'); + /** * Hostname-based routing: maps each hostname to its Cloudflare Pages target. */ @@ -22,36 +24,6 @@ const TARGETS = { 'www-staging.marketdata.app': 'www-staging-marketdata-app.pages.dev', }; -/** - * Converts raw MDX/markdown source into clean markdown by stripping - * frontmatter, imports, and converting Docusaurus components to headings. - * - * @param {string} text - Raw file contents - * @returns {string} - */ -function cleanMarkdown(text) { - // Extract title from frontmatter, then strip it - const fmMatch = text.match(/^---\n([\s\S]*?)\n---/); - let title = ''; - if (fmMatch) { - const titleMatch = fmMatch[1].match(/^title:\s*(.+)$/m); - if (titleMatch) title = titleMatch[1].trim().replace(/^["']|["']$/g, ''); - } - text = text.replace(/^---\n[\s\S]*?\n---\n*/, ''); - // Add title as H1 if present - if (title) text = `# ${title}\n\n${text}`; - // Strip import statements - text = text.replace(/^import\s+.*;\s*\n/gm, ''); - // Convert to ### headings, strip wrappers - text = text.replace(/\n?/g, ''); - text = text.replace(/<\/Tabs>\n?/g, ''); - text = text.replace(/"]*(?:"[^"]*")?)*?label="([^"]*)"(?:[^>"]*(?:"[^"]*")?)*?>\n?/g, '### $1\n\n'); - text = text.replace(/<\/TabItem>\n?/g, ''); - // Clean up excess blank lines - text = text.replace(/\n{3,}/g, '\n\n').trim() + '\n'; - return text; -} - /** * Routes incoming requests to the appropriate Cloudflare Pages deployment * based on the hostname. Non-matching requests pass through to the @@ -121,7 +93,9 @@ async function handleRequest(request) { for (const candidate of candidates) { const res = await fetch(candidate); if (res.ok) { - const text = cleanMarkdown(await res.text()); + const text = cleanMdx(await res.text(), { + baseUrl: 'https://www.marketdata.app/docs', + }); const canonicalUrl = `https://www.marketdata.app/docs/${stem}/`; return new Response(text, { headers: { @@ -166,4 +140,4 @@ async function handleRequest(request) { return response; } -module.exports = { handleRequest, cleanMarkdown, TARGETS }; +module.exports = { handleRequest, TARGETS }; diff --git a/worker/handler.test.js b/worker/handler.test.js index 4818256..c91f1e5 100644 --- a/worker/handler.test.js +++ b/worker/handler.test.js @@ -4,7 +4,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; const mockFetch = vi.fn(); vi.stubGlobal('fetch', mockFetch); -const { handleRequest, cleanMarkdown } = require('./handler'); +const { handleRequest } = require('./handler'); function makeRequest(url, options = {}) { return new Request(url, options); @@ -14,41 +14,12 @@ beforeEach(() => { vi.clearAllMocks(); }); -// --- cleanMarkdown --- - -describe('cleanMarkdown', () => { - it('extracts title from frontmatter as H1', () => { - const input = '---\ntitle: Test\nsidebar_position: 1\n---\n\n# Hello\n'; - expect(cleanMarkdown(input)).toBe('# Test\n\n# Hello\n'); - }); - - it('strips frontmatter without title', () => { - const input = '---\nsidebar_position: 1\n---\n\n# Hello\n'; - expect(cleanMarkdown(input)).toBe('# Hello\n'); - }); - - it('strips import statements', () => { - const input = 'import Tabs from "@theme/Tabs";\nimport TabItem from "@theme/TabItem";\n\n# Hello\n'; - expect(cleanMarkdown(input)).toBe('# Hello\n'); - }); - - it('converts TabItem to headings and strips Tabs wrappers', () => { - const input = '\n\n\ncode here\n\n\n\n'; - const result = cleanMarkdown(input); - expect(result).toContain('### JavaScript'); - expect(result).not.toContain(''); - expect(result).not.toContain(''); - }); - - it('collapses excess blank lines', () => { - const input = '# A\n\n\n\n\n# B\n'; - expect(cleanMarkdown(input)).toBe('# A\n\n# B\n'); - }); -}); - // --- handleRequest --- +// Note: cleanMarkdown/cleanMdx unit tests live in /lib/__tests__/mdx-to-md.test.js +// (run with `node --test`). The tests below cover the worker's request-handling +// behavior — they exercise the conversion in passing via the markdown-serving block. + describe('handleRequest', () => { // --- Non-matching hostname --- From 60414a8f25d789c3f0a982357f7d3d13703aad9e Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Tue, 19 May 2026 15:01:05 -0300 Subject: [PATCH 11/29] refactor(sdk-sync): replace per-file banner with single manifest The "" banner was doing two jobs: telling humans not to edit, and giving the sync workflow a signal to scope deletion to previously-generated files. Splits those responsibilities: - Drop the banner from each .md file; titles now start with `# ...` directly. - CLI writes docs/.sync-manifest.txt listing every generated path. - Workflow reads that manifest to decide what to delete; SDK-repo-owned content (ADRs, READMEs not in the manifest) stays untouched. - PR body now surfaces the "do not edit" notice once, pointing reviewers at the manifest. --- .github/workflows/sync-sdk-docs.yml | 32 +++++++++++++++++------------ scripts/export-sdk-docs.js | 24 ++++++++++++++++++---- 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/.github/workflows/sync-sdk-docs.yml b/.github/workflows/sync-sdk-docs.yml index 68fef28..e56ad05 100644 --- a/.github/workflows/sync-sdk-docs.yml +++ b/.github/workflows/sync-sdk-docs.yml @@ -46,7 +46,7 @@ on: env: SOURCE_OWNER: MarketDataApp TARGET_OWNER: MarketDataApp # change to MarketData-App after repo migration - BANNER_TAG: 'Generated from MarketDataApp/documentation/' + MANIFEST_FILE: docs/.sync-manifest.txt jobs: sync: @@ -96,19 +96,24 @@ jobs: - name: Replace previously-generated docs with fresh export # We DO NOT wipe target/docs/ wholesale — it may contain SDK-repo-owned - # content (ADRs, internal READMEs). Instead, delete only files that - # carry our banner, then copy the new export over. + # content (ADRs, internal READMEs). The previous sync wrote a manifest + # at $MANIFEST_FILE listing every file it generated. Delete exactly + # those paths, then drop the fresh export in (which writes a new + # manifest). env: OUT_DIR: ${{ runner.temp }}/out run: | set -euo pipefail mkdir -p target/docs - # Remove only files in target/docs/ that we previously generated. - while IFS= read -r -d '' f; do - if head -n 5 "$f" | grep -q "$BANNER_TAG"; then - rm -f "$f" - fi - done < <(find target/docs -type f -name '*.md' -print0) + if [ -f "target/$MANIFEST_FILE" ]; then + while IFS= read -r rel; do + # Skip blank lines and comments (lines beginning with '#') + case "$rel" in + ''|'#'*) continue ;; + esac + rm -f "target/docs/$rel" + done < "target/$MANIFEST_FILE" + fi # Remove any now-empty directories left behind. find target/docs -type d -empty -delete || true mkdir -p target/docs @@ -152,7 +157,8 @@ jobs: ${{ steps.diff.outputs.summary }} ``` - > Files carrying the banner - > `` - > are managed by this workflow — edit the source `.mdx` in the - > documentation repo, not the rendered `.md` here. + > **These files are auto-synced from + > [MarketDataApp/documentation](https://github.com/MarketDataApp/documentation/tree/staging/sdk/${{ matrix.sdk }})** — + > edit the source `.mdx` there, not the rendered `.md` here. + > Hand-edits to files listed in `docs/.sync-manifest.txt` will + > be overwritten on the next sync. diff --git a/scripts/export-sdk-docs.js b/scripts/export-sdk-docs.js index 9d879d7..fe445a5 100644 --- a/scripts/export-sdk-docs.js +++ b/scripts/export-sdk-docs.js @@ -82,10 +82,7 @@ function main() { const outAbs = path.join(outDir, outRelative); const raw = fs.readFileSync(sourceAbs, 'utf8'); - const converted = cleanMdx(raw, { sourcePath: sourcePathRel, sdk: args.sdk }); - - const banner = `\n\n`; - const body = banner + converted; + const body = cleanMdx(raw, { sourcePath: sourcePathRel, sdk: args.sdk }); fs.mkdirSync(path.dirname(outAbs), { recursive: true }); fs.writeFileSync(outAbs, body, 'utf8'); @@ -104,6 +101,25 @@ function main() { }); } + // Write a manifest of every file we generated, so the sync workflow can + // delete exactly these paths on the next run without touching repo-owned + // content. The manifest itself is also listed so it gets refreshed each + // sync (and removed cleanly if the workflow is ever retired). + const generatedPaths = [...manifest.map((m) => m.path), '.sync-manifest.txt'].sort(); + const manifestPath = path.join(outDir, '.sync-manifest.txt'); + const manifestBody = + '# Auto-generated by MarketDataApp/documentation/scripts/export-sdk-docs.js.\n' + + '# These files are owned by the docs sync workflow — do not edit them by hand.\n' + + '# Editing this manifest will not break anything; the workflow rebuilds it each run.\n' + + generatedPaths.join('\n') + + '\n'; + fs.writeFileSync(manifestPath, manifestBody, 'utf8'); + manifest.push({ + path: '.sync-manifest.txt', + bytes: Buffer.byteLength(manifestBody, 'utf8'), + note: '(workflow manifest)', + }); + manifest.sort((a, b) => a.path.localeCompare(b.path)); console.log(`Exported ${manifest.length} file(s) to ${outDir}\n`); for (const { path: p, bytes, note } of manifest) { From af6c704871afc1cf49316dafc308cf9688ec3b35 Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Tue, 19 May 2026 15:06:55 -0300 Subject: [PATCH 12/29] fix(sdk-sync): emit README.md per directory and fix internal link targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switches directory indices from index.md to README.md so GitHub auto-renders them at each directory URL, and fixes a real link bug surfaced by the change: links like [Stocks](/sdk/js/stocks) were rewriting to ./stocks.md (a file that never existed) instead of the directory landing page. Mechanism: the export CLI now walks the source tree first, builds a map { Docusaurus-subpath → repo-relative output file }, and passes it to cleanMdx via a new `linkTargets` opt. The lib uses this map to resolve same-SDK links accurately, and collapses common path prefixes so siblings get './neighbor.md' rather than '../dir/neighbor.md'. Worker and LLM-bundle callers don't pass linkTargets — they keep rewriting same-SDK paths to absolute www.marketdata.app/docs URLs as before. Five new unit tests cover the directory-index resolution and prefix collapsing. --- lib/__tests__/mdx-to-md.test.js | 63 +++++++++++++++++++++++++++++++++ lib/mdx-to-md.js | 42 ++++++++++++++++++---- scripts/export-sdk-docs.js | 52 ++++++++++++++++++--------- 3 files changed, 134 insertions(+), 23 deletions(-) diff --git a/lib/__tests__/mdx-to-md.test.js b/lib/__tests__/mdx-to-md.test.js index 471954f..d440629 100644 --- a/lib/__tests__/mdx-to-md.test.js +++ b/lib/__tests__/mdx-to-md.test.js @@ -225,6 +225,69 @@ test('rewrites /sdk/{sdk}/ (root with trailing slash) to relative index.md', () assert.match(result, /\[JS SDK\]\(\.\.\/index\.md\)/); }); +// --- Source-aware link resolution (linkTargets) --- + +test('linkTargets: directory link resolves to README.md', () => { + const linkTargets = { + 'client': 'client.md', + 'stocks': 'stocks/README.md', + 'stocks/quotes': 'stocks/quotes.md', + '': 'README.md', + }; + const result = cleanMdx('Browse [Stocks](/sdk/js/stocks) for details.\n', { + sourcePath: 'sdk/js/client.mdx', + sdk: 'js', + linkTargets, + }); + assert.match(result, /\[Stocks\]\(\.\/stocks\/README\.md\)/); +}); + +test('linkTargets: sibling file gets ./neighbor.md (collapsed prefix)', () => { + const linkTargets = { + 'stocks/quotes': 'stocks/quotes.md', + 'stocks/candles': 'stocks/candles.md', + }; + const result = cleanMdx('See [Candles](/sdk/js/stocks/candles).\n', { + sourcePath: 'sdk/js/stocks/quotes.mdx', + sdk: 'js', + linkTargets, + }); + assert.match(result, /\[Candles\]\(\.\/candles\.md\)/); +}); + +test('linkTargets: file at root from subdir gets ../client.md', () => { + const linkTargets = { 'client': 'client.md' }; + const result = cleanMdx('See [Client](/sdk/js/client).\n', { + sourcePath: 'sdk/js/stocks/quotes.mdx', + sdk: 'js', + linkTargets, + }); + assert.match(result, /\[Client\]\(\.\.\/client\.md\)/); +}); + +test('linkTargets: directory link from inside that directory uses ./README.md', () => { + const linkTargets = { + 'stocks': 'stocks/README.md', + 'stocks/quotes': 'stocks/quotes.md', + }; + const result = cleanMdx('Back to [Stocks home](/sdk/js/stocks).\n', { + sourcePath: 'sdk/js/stocks/quotes.mdx', + sdk: 'js', + linkTargets, + }); + assert.match(result, /\[Stocks home\]\(\.\/README\.md\)/); +}); + +test('linkTargets: root /sdk/{sdk}/ from a subdirectory file resolves to ../README.md', () => { + const linkTargets = { '': 'README.md', 'client': 'client.md' }; + const result = cleanMdx('Home: [JS SDK](/sdk/js/).\n', { + sourcePath: 'sdk/js/stocks/quotes.mdx', + sdk: 'js', + linkTargets, + }); + assert.match(result, /\[JS SDK\]\(\.\.\/README\.md\)/); +}); + // --- Empty components --- test('strips self-closing', () => { diff --git a/lib/mdx-to-md.js b/lib/mdx-to-md.js index edc4f2c..c7e1ff1 100644 --- a/lib/mdx-to-md.js +++ b/lib/mdx-to-md.js @@ -34,7 +34,34 @@ function extractTitle(frontmatter) { return m[1].trim().replace(/^["']|["']$/g, ''); } -function resolveRelative(sourcePath, sdkBase, targetSub) { +// Compute a relative path from one repo-relative POSIX file to another, +// collapsing the common prefix so siblings get './x.md' instead of '../dir/x.md'. +function relativePath(fromFile, toFile) { + const fromParts = fromFile.split('/').slice(0, -1); // directory of source + const toParts = toFile.split('/'); + let i = 0; + while (i < fromParts.length && i < toParts.length - 1 && fromParts[i] === toParts[i]) { + i++; + } + const ups = fromParts.length - i; + const rest = toParts.slice(i).join('/'); + const prefix = ups === 0 ? './' : '../'.repeat(ups); + return prefix + rest; +} + +function resolveSameSdkLink(sourcePath, sdkBase, targetSub, linkTargets) { + const cleanedSub = targetSub.replace(/\/$/, ''); + // If the CLI gave us a source-aware target map, use it. This is the only + // way to disambiguate "/sdk/js/funds" (a directory → funds/README.md) from + // "/sdk/js/client" (a file → client.md) without false guesses. + if (linkTargets && linkTargets[cleanedSub]) { + const srcOutput = sourcePath + .slice(sdkBase.length + 1) + .replace(/\.mdx$/, '.md'); + return relativePath(srcOutput, linkTargets[cleanedSub]); + } + // Fallback for callers that don't supply a target map: assume "foo" → "foo.md". + // Worker/LLM-bundle paths use absolute URLs instead and never hit this. const srcRelative = sourcePath.startsWith(sdkBase + '/') ? sourcePath.slice(sdkBase.length + 1) : sourcePath; @@ -42,8 +69,7 @@ function resolveRelative(sourcePath, sdkBase, targetSub) { const srcDir = lastSlash >= 0 ? srcRelative.slice(0, lastSlash) : ''; const depth = srcDir ? srcDir.split('/').length : 0; const up = depth === 0 ? './' : '../'.repeat(depth); - const cleaned = targetSub.replace(/\/$/, ''); - const target = cleaned === '' ? 'index' : cleaned; + const target = cleanedSub === '' ? 'index' : cleanedSub; return up + target + '.md'; } @@ -51,7 +77,7 @@ function rewriteHref(href, opts) { if (/^(https?:|mailto:|tel:|#)/i.test(href)) return href; if (!href.startsWith('/')) return href; - const { sourcePath, sdk, baseUrl } = opts; + const { sourcePath, sdk, baseUrl, linkTargets } = opts; const hashIdx = href.indexOf('#'); const pathPart = hashIdx >= 0 ? href.slice(0, hashIdx) : href; @@ -61,11 +87,11 @@ function rewriteHref(href, opts) { const sdkPrefix = `/sdk/${sdk}/`; const sdkRoot = `/sdk/${sdk}`; if (pathPart === sdkRoot || pathPart === sdkRoot + '/') { - return resolveRelative(sourcePath, `sdk/${sdk}`, '') + anchor; + return resolveSameSdkLink(sourcePath, `sdk/${sdk}`, '', linkTargets) + anchor; } if (pathPart.startsWith(sdkPrefix)) { const sub = pathPart.slice(sdkPrefix.length); - return resolveRelative(sourcePath, `sdk/${sdk}`, sub) + anchor; + return resolveSameSdkLink(sourcePath, `sdk/${sdk}`, sub, linkTargets) + anchor; } } @@ -195,6 +221,10 @@ function cleanMdx(text, opts = {}) { sourcePath: opts.sourcePath ?? null, sdk: opts.sdk ?? null, baseUrl: opts.baseUrl ?? 'https://www.marketdata.app/docs', + // Map of Docusaurus subpath (no leading slash, no trailing slash) → repo-relative + // output file. Only consulted for same-SDK link rewriting. CLI builds this from + // the source tree; worker / LLM-bundle leave it unset. + linkTargets: opts.linkTargets ?? null, }; let title = null; diff --git a/scripts/export-sdk-docs.js b/scripts/export-sdk-docs.js index fe445a5..9f8dfd0 100644 --- a/scripts/export-sdk-docs.js +++ b/scripts/export-sdk-docs.js @@ -73,34 +73,52 @@ function main() { emptyDir(outDir); const files = walkMdx(sourceDir); + + // Build the source-aware link target map BEFORE writing any file. For each + // source .mdx, compute what /sdk/{sdk}/... URLs in *other* docs would point + // at, and which output file we'll write it as. Directory indices land as + // README.md so GitHub auto-renders them at the directory URL. + // Map keys are Docusaurus subpaths (no leading slash, no extension); values + // are repo-relative POSIX output paths. + const linkTargets = {}; + for (const sourceAbs of files) { + const relFromSdk = path.relative(sourceDir, sourceAbs).split(path.sep).join('/'); + const noExt = relFromSdk.replace(/\.(mdx?|md)$/, ''); + const isIndex = path.posix.basename(noExt) === 'index'; + if (isIndex) { + const dirSub = path.posix.dirname(noExt); + const key = dirSub === '.' ? '' : dirSub; + const out = key === '' ? 'README.md' : `${key}/README.md`; + linkTargets[key] = out; + } else { + linkTargets[noExt] = `${noExt}.md`; + } + } + const manifest = []; for (const sourceAbs of files) { - const relFromSdk = path.relative(sourceDir, sourceAbs); // e.g. "stocks/quotes.mdx" - const sourcePathRel = path.posix.join('sdk', args.sdk, relFromSdk.split(path.sep).join('/')); - const outRelative = relFromSdk.replace(/\.mdx$/, '.md'); - const outAbs = path.join(outDir, outRelative); + const relPosix = path.relative(sourceDir, sourceAbs).split(path.sep).join('/'); + const sourcePathRel = path.posix.join('sdk', args.sdk, relPosix); + const noExt = relPosix.replace(/\.(mdx?|md)$/, ''); + const isIndex = path.posix.basename(noExt) === 'index'; + const outRelative = isIndex + ? (path.posix.dirname(noExt) === '.' ? 'README.md' : `${path.posix.dirname(noExt)}/README.md`) + : `${noExt}.md`; + const outAbs = path.join(outDir, ...outRelative.split('/')); const raw = fs.readFileSync(sourceAbs, 'utf8'); - const body = cleanMdx(raw, { sourcePath: sourcePathRel, sdk: args.sdk }); + const body = cleanMdx(raw, { + sourcePath: sourcePathRel, + sdk: args.sdk, + linkTargets, + }); fs.mkdirSync(path.dirname(outAbs), { recursive: true }); fs.writeFileSync(outAbs, body, 'utf8'); manifest.push({ path: outRelative, bytes: Buffer.byteLength(body, 'utf8') }); } - // Promote the SDK's index.md to a top-level README.md (and leave index.md in place - // so any code linking to it still resolves) - const indexPath = path.join(outDir, 'index.md'); - if (fs.existsSync(indexPath)) { - fs.copyFileSync(indexPath, path.join(outDir, 'README.md')); - manifest.push({ - path: 'README.md', - bytes: fs.statSync(path.join(outDir, 'README.md')).size, - note: '(copy of index.md)', - }); - } - // Write a manifest of every file we generated, so the sync workflow can // delete exactly these paths on the next run without touching repo-owned // content. The manifest itself is also listed so it gets refreshed each From f00392aec90f2fa667f00f4e1295dc7061f42c63 Mon Sep 17 00:00:00 2001 From: Luca Mitas Date: Wed, 20 May 2026 15:31:16 -0300 Subject: [PATCH 13/29] docs(sdk/js): add utilities namespace + options/strikes pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the four utilities pages (status, headers, user) plus an options/strikes reference. Index page updated to list the new namespace alongside stocks/options/ funds/markets. Mirrors the SDK source under sdk-js/src/resources/utilities and sdk-js/src/resources/options/strikes.ts; documents the constructor-issued startup user() call and the /user/ 404→NotFoundError carve-out. Co-Authored-By: Claude Opus 4.7 (1M context) --- sdk/js/index.mdx | 2 +- sdk/js/options/strikes.mdx | 183 +++++++++++++++++++++++++++++++++++ sdk/js/utilities/headers.mdx | 66 +++++++++++++ sdk/js/utilities/index.mdx | 16 +++ sdk/js/utilities/status.mdx | 102 +++++++++++++++++++ sdk/js/utilities/user.mdx | 104 ++++++++++++++++++++ 6 files changed, 472 insertions(+), 1 deletion(-) create mode 100644 sdk/js/options/strikes.mdx create mode 100644 sdk/js/utilities/headers.mdx create mode 100644 sdk/js/utilities/index.mdx create mode 100644 sdk/js/utilities/status.mdx create mode 100644 sdk/js/utilities/user.mdx diff --git a/sdk/js/index.mdx b/sdk/js/index.mdx index e8e32e6..08d1d49 100644 --- a/sdk/js/index.mdx +++ b/sdk/js/index.mdx @@ -32,7 +32,7 @@ This SDK is designed to help you get up and running with Market Data's APIs as q 2. Set up your [authentication token](/sdk/js/authentication) to access the API. 3. Learn about the [client](/sdk/js/client) and how to make your first API requests. 4. Configure [Settings](/sdk/js/settings) to customize output format, date format, and other universal parameters. -5. Explore the available endpoints for [stocks](/sdk/js/stocks), [options](/sdk/js/options), [funds](/sdk/js/funds), and [markets](/sdk/js/markets). +5. Explore the available endpoints for [stocks](/sdk/js/stocks), [options](/sdk/js/options), [funds](/sdk/js/funds), [markets](/sdk/js/markets), and [utilities](/sdk/js/utilities). ### Support diff --git a/sdk/js/options/strikes.mdx b/sdk/js/options/strikes.mdx new file mode 100644 index 0000000..e96dd71 --- /dev/null +++ b/sdk/js/options/strikes.mdx @@ -0,0 +1,183 @@ +--- +title: Strikes +sidebar_position: 5 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Enumerate the strike prices trading for each expiration of a given underlying symbol. + +## Making Requests + +Use the `strikes()` method on the `options` resource to fetch the strike enumeration. + +| Output Format | Result Payload | Description | +|------------------------|-------------------------------------------------------------------------|--------------------------------------------| +| **internal** (default) | `OptionsStrikesResponse` or `OptionsStrikesHumanResponse` | Object with one `number[]` per expiration. | +| **json** | Raw JSON object | The raw response as returned by the API. | + + +## strikes + +```typescript +// Positional form +strikes

( + symbol: string, + params?: P, +): MarketDataPromise + +// Object form +strikes

( + params: P & { symbol: string }, +): MarketDataPromise +``` + +Fetches available strikes for a single underlying symbol. + +#### Parameters + +- `symbol` (string) + + The underlying stock symbol (e.g. `"AAPL"`). + +- `expiration` (string | Date, optional) + + Filter to strikes that trade on a specific expiration date. Without this parameter, every expiration is returned. + +- `date` (string | Date, optional) + + Fetch strikes as-of a specific historical date. + +- [`outputFormat`](/sdk/js/settings#output-format) (optional): The format of the returned data. Alias: `format`. +- [`dateFormat`](/sdk/js/settings#date-format) (optional): Date format. Alias: `dateformat`. +- [`columns`](/sdk/js/settings#columns) (optional): Columns to include. +- [`useHumanReadable`](/sdk/js/settings#human-readable) (optional): Use human-readable field names. Alias: `human`. + +#### Returns + +- [`MarketDataPromise`](/sdk/js/client#MarketDataPromise) + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const res = await client.options.strikes("AAPL"); + // Every key besides `s` and `updated` is an expiration date. + for (const [expiration, strikes] of Object.entries(res)) { + if (expiration === "s" || expiration === "updated") continue; + console.log(`${expiration}: ${(strikes as number[]).length} strikes`); + } +} catch (error) { + console.error(error); +} +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const res = await client.options.strikes("AAPL", { + expiration: "2026-06-19", + }); + console.log(res["2026-06-19"]); // number[] +} catch (error) { + console.error(error); +} +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + // Strikes as-of an end-of-day snapshot + const res = await client.options.strikes("AAPL", { + date: "2024-01-12", + }); + console.log(res); +} catch (error) { + console.error(error); +} +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const res = await client.options.strikes("AAPL", { + expiration: "2026-06-19", + human: true, + }); + console.log(res.Date); // Unix timestamp + console.log(res["2026-06-19"]); // number[] +} catch (error) { + console.error(error); +} +``` + + + + + +## OptionsStrikesResponse + +```typescript +interface OptionsStrikesResponse { + s: string; + updated: number; + [expiration: string]: number[] | string | number; +} +``` + +#### Properties + +- `s` (string): Status indicator (e.g. `"ok"`, `"no_data"`). +- `updated` (number): Unix timestamp when the data was last updated. +- `[expiration]` (number[]): One entry per available expiration date in `YYYY-MM-DD` format, whose value is the array of strike prices trading on that expiration. + + +## OptionsStrikesHumanResponse + +```typescript +interface OptionsStrikesHumanResponse { + Date: number; + [expiration: string]: number[] | number; +} +``` + +`OptionsStrikesHumanResponse` is returned when `human: true` is set. The status field is omitted and `updated` is renamed to `Date`. + +## No-data responses + +Calling `strikes()` against a symbol or expiration with no listings resolves with an empty result rather than rejecting. Inspect `no_data` or `hasData()` on the returned promise: + +```typescript +const pending = client.options.strikes("UNKNOWN_TICKER"); +const res = await pending; +if (pending.no_data) { + console.log("No strikes available for that symbol"); +} +``` diff --git a/sdk/js/utilities/headers.mdx b/sdk/js/utilities/headers.mdx new file mode 100644 index 0000000..8658e68 --- /dev/null +++ b/sdk/js/utilities/headers.mdx @@ -0,0 +1,66 @@ +--- +title: Headers +sidebar_position: 2 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Echo back the request headers your client actually sent to Market Data. Useful for debugging proxies, custom header plumbing, or verifying the User-Agent the SDK is presenting. + +The `Authorization` header is redacted server-side before the response is returned. + +## Making Requests + +Use the `headers()` method on the `utilities` resource. + +| Output Format | Result Payload | Description | +|------------------------|--------------------|----------------------------------------------| +| **internal** (default) | `HeadersResponse` | Map of header name to value as the API saw it.| +| **json** | Raw JSON object | The raw response as returned by the API. | + + +## headers + +```typescript +headers(): MarketDataPromise +``` + +Fetches the request-header echo. Takes no parameters. + +#### Returns + +- [`MarketDataPromise`](/sdk/js/client#MarketDataPromise) + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const h = await client.utilities.headers(); + console.log(h["user-agent"]); // "marketdata-sdk-javascript/0.0.1" + console.log(h["accept-encoding"]); +} catch (error) { + console.error(error); +} +``` + + + + + +## HeadersResponse + +```typescript +type HeadersResponse = Record; +``` + +A bag of header name → value pairs. Header names are lowercased; values may be strings or arrays of strings depending on whether the header was sent multiple times. + +:::tip +Use this to confirm what gets through when you wire the SDK behind a corporate proxy or add custom headers via your runtime's `fetch` interception. +::: diff --git a/sdk/js/utilities/index.mdx b/sdk/js/utilities/index.mdx new file mode 100644 index 0000000..6f971d7 --- /dev/null +++ b/sdk/js/utilities/index.mdx @@ -0,0 +1,16 @@ +--- +title: Utilities +slug: /js/utilities +sidebar_position: 50 +--- + +The JavaScript SDK from Market Data provides methods designed to streamline your use of the following Utilities endpoints. These intuitive methods provide a seamless interface for accessing diagnostic and account-level data, including service status, request-header inspection, and the rate-limit snapshot tied to your API token. + +All three utilities skip the per-token rate-limit gate and bypass the `/v1/` URL prefix, so they remain reachable even when your account is throttled. + +## Utilities Endpoints + +import DocCardList from "@theme/DocCardList"; +import { useCurrentSidebarCategory } from "@docusaurus/theme-common"; + + diff --git a/sdk/js/utilities/status.mdx b/sdk/js/utilities/status.mdx new file mode 100644 index 0000000..04d9da1 --- /dev/null +++ b/sdk/js/utilities/status.mdx @@ -0,0 +1,102 @@ +--- +title: Status +sidebar_position: 1 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Retrieve service-level health information for every Market Data endpoint. No authentication token is required — `status()` is reachable in demo mode and is the SDK's own probe before retrying a 5xx response. + +## Making Requests + +Use the `status()` method on the `utilities` resource to fetch the per-service availability snapshot. + +| Output Format | Result Payload | Description | +|------------------------|----------------------|------------------------------------------------------| +| **internal** (default) | `ApiStatusResponse` | Decoded status snapshot with parallel arrays. | +| **json** | Raw JSON object | The raw response as returned by the API. | + + +## status + +```typescript +status(): MarketDataPromise +``` + +Fetches the current service-status payload. Takes no parameters. + +#### Returns + +- [`MarketDataPromise`](/sdk/js/client#MarketDataPromise) + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const s = await client.utilities.status(); + for (let i = 0; i < s.service.length; i++) { + console.log(`${s.service[i]}: ${s.status[i]} (online=${s.online[i]})`); + } +} catch (error) { + console.error(error); +} +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const s = await client.utilities.status(); + for (let i = 0; i < s.service.length; i++) { + const u30 = s.uptimePct30d?.[i] ?? null; + const u90 = s.uptimePct90d?.[i] ?? null; + console.log(`${s.service[i]} — 30d=${u30}% 90d=${u90}%`); + } +} catch (error) { + console.error(error); +} +``` + + + + + +## ApiStatusResponse + +```typescript +interface ApiStatusResponse { + service: string[]; + status: string[]; + online: boolean[]; + uptimePct30d?: number[]; + uptimePct90d?: number[]; + updated: number[]; +} +``` + +#### Properties + +- `service` (string[]): Service names (e.g. `"stocks/candles"`, `"options/chain"`). +- `status` (string[]): Human-readable status for each service (e.g. `"OPERATIONAL"`, `"OFFLINE"`). +- `online` (boolean[]): Boolean availability flag for each service. +- `uptimePct30d` (number[], optional): Trailing 30-day uptime percentage per service. +- `uptimePct90d` (number[], optional): Trailing 90-day uptime percentage per service. +- `updated` (number[]): Unix timestamps of the last status check per service. + +All arrays are parallel — index `i` across `service`, `status`, `online`, and `updated` refers to the same service. + +:::info +The SDK's internal retry loop calls `utilities.status()` before retrying a 5xx response. If the service that originally failed is marked `online: false`, the retry short-circuits so calls fail fast instead of grinding through the full backoff. +::: diff --git a/sdk/js/utilities/user.mdx b/sdk/js/utilities/user.mdx new file mode 100644 index 0000000..d251026 --- /dev/null +++ b/sdk/js/utilities/user.mdx @@ -0,0 +1,104 @@ +--- +title: User +sidebar_position: 3 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Return the account and rate-limit snapshot tied to the API token in use. The client constructor calls this automatically to populate `client.rateLimits` unless you opt out with `skipStartupValidation: true`. + +## Making Requests + +Use the `user()` method on the `utilities` resource. + +| Output Format | Result Payload | Description | +|------------------------|-----------------|-----------------------------------------------| +| **internal** (default) | `UserResponse` | Account identity + current rate-limit snapshot.| +| **json** | Raw JSON object | The raw response as returned by the API. | + + +## user + +```typescript +user(): MarketDataPromise +``` + +Fetches the bearer-token's account record. Takes no parameters. + +#### Returns + +- [`MarketDataPromise`](/sdk/js/client#MarketDataPromise) + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const u = await client.utilities.user(); + console.log(`${u.plan} plan — ${u.remaining}/${u.limit} credits remaining`); + if (u.reset) { + console.log("Resets at:", new Date(u.reset * 1000).toISOString()); + } +} catch (error) { + console.error(error); +} +``` + + + + + +```typescript +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +// `client.ready` resolves once the constructor-issued `user()` call settles. +// Awaiting it ensures you fail fast on bad credentials before issuing real work. +try { + await client.ready; + console.log("Token validated, rate limits:", client.rateLimits); +} catch (error) { + console.error("Startup validation failed:", error); +} +``` + + + + + +## UserResponse + +```typescript +interface UserResponse { + id?: string; + username?: string; + email?: string; + plan?: string; + limit?: number; + remaining?: number; + consumed?: number; + reset?: number; +} +``` + +#### Properties + +- `id` (string, optional): Internal account identifier. +- `username` (string, optional): Account username. +- `email` (string, optional): Account email address. +- `plan` (string, optional): Subscription plan name (e.g. `"free"`, `"starter"`, `"trader"`). +- `limit` (number, optional): Total request credits available in the current window. +- `remaining` (number, optional): Request credits still available. +- `consumed` (number, optional): Request credits already used in the current window. +- `reset` (number, optional): Unix timestamp at which the rate-limit window resets. + +## Errors + +- **401 Unauthorized** → rejects with [`AuthenticationError`](/sdk/js/client). This is the canonical signal that the configured token is missing, malformed, or expired. +- **404 Not Found** → rejects with [`NotFoundError`](/sdk/js/client). The token authenticated but the account record is gone, which the SDK treats as an integrity error rather than a "no data" condition (`/user/` is the one endpoint where 404 throws instead of resolving to `no_data`). From d3568e7dcb3ef86acd115593c7be136be095d800 Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Wed, 20 May 2026 21:00:36 -0300 Subject: [PATCH 14/29] chore(gitignore): ignore internal sop/ folder Adds the sop/ directory to .gitignore. Internal SOPs are not part of the public docs site. --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 7a6f4a6..6a12acd 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,6 @@ static/robots.txt static/css/ worker/node_modules +# Internal SOPs (not for public docs site) +sop/ + From dfd86d9a41738ddcc22a37123622ebb936bd8c6c Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Wed, 20 May 2026 21:00:36 -0300 Subject: [PATCH 15/29] refactor(scripts): adopt shared mdx-to-md lib in master-docs generator Replaces the inline MDX-cleaning logic in generate-master-docs.js with a call to the shared cleanMdx helper from lib/mdx-to-md, matching the approach used by the SDK docs sync workflow. Also adds two package.json scripts: - export-sdk-docs: produce the SDK docs bundle - test:lib: run lib/__tests__ via node --test --- package.json | 2 + scripts/generate-master-docs.js | 104 +++++++------------------------- 2 files changed, 24 insertions(+), 82 deletions(-) diff --git a/package.json b/package.json index ff44509..2aed246 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,8 @@ "write-heading-ids": "docusaurus write-heading-ids", "typecheck": "tsc", "generate-docs": "node scripts/generate-master-docs.js", + "export-sdk-docs": "node scripts/export-sdk-docs.js", + "test:lib": "node --test lib/__tests__/*.test.js", "lint:tables": "node scripts/fix-table-alignment.js", "fix:tables": "node scripts/fix-table-alignment.js --fix", "test:e2e": "playwright test" diff --git a/scripts/generate-master-docs.js b/scripts/generate-master-docs.js index 4f762f0..aec6a42 100644 --- a/scripts/generate-master-docs.js +++ b/scripts/generate-master-docs.js @@ -48,6 +48,7 @@ const fs = require('fs'); const path = require('path'); +const { cleanMdx } = require('../lib/mdx-to-md'); // Configuration // Output directory for generated master documentation files @@ -98,7 +99,7 @@ function collectFiles(dir, fileList = []) { } /** - * Strip "tg [letter]" tags from title + * Strip "tg [letter]" tags from title (LLM-bundle-specific, not a general MDX concern) */ function stripTitleTags(title) { if (!title) return title; @@ -106,95 +107,34 @@ function stripTitleTags(title) { } /** - * Extract title from frontmatter - */ -function extractTitle(content) { - const frontmatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n/); - if (frontmatterMatch) { - const frontmatter = frontmatterMatch[1]; - const titleMatch = frontmatter.match(/^title:\s*(.+)$/m); - if (titleMatch) { - let title = titleMatch[1].trim().replace(/^["']|["']$/g, ''); - title = stripTitleTags(title); - return title; - } - } - return null; -} - -/** - * Strip frontmatter from content - */ -function stripFrontmatter(content) { - return content.replace(/^---\s*\n[\s\S]*?\n---\s*\n/, ''); -} - -/** - * Remove all import statements - */ -function removeImports(content) { - return content.replace(/^import\s+.*?from\s+["'].*?["'];?\s*$/gm, ''); -} - -/** - * Convert Tabs/TabItem components to markdown headers - */ -function convertTabsToHeaders(content) { - let result = content; - - // Remove opening tag - result = result.replace(/]*>\s*/g, ''); - - // Convert to header and remove closing tag - result = result.replace(/]*>\s*/g, '### $1\n\n'); - result = result.replace(/<\/TabItem>\s*/g, '\n\n'); - - // Remove closing tag - result = result.replace(/<\/Tabs>\s*/g, ''); - - return result; -} - -/** - * Remove empty components like DocCardList - */ -function removeEmptyComponents(content) { - // Remove DocCardList and similar self-closing or empty components - result = content.replace(/]*\/>\s*/g, ''); - result = result.replace(/]*>[\s\S]*?<\/DocCardList>\s*/g, ''); - result = result.replace(/]*\/>\s*/g, ''); - - // Remove import statements that reference these components (already handled, but double-check) - result = result.replace(/import\s+.*?from\s+["']@theme\/DocCardList["'];?\s*$/gm, ''); - result = result.replace(/import\s+.*?from\s+["']@docusaurus\/theme-common["'];?\s*$/gm, ''); - - return result; -} - -/** - * Process a single file + * Process a single file. Conversion is delegated to the shared lib; + * this function only does file IO + extracting the title for the TOC + stripping the + * H1 the lib prepends (combineFiles re-adds it with the right wrapping). */ function processFile(filePath) { try { - let content = fs.readFileSync(filePath, 'utf8'); - let title = extractTitle(content); - if (!title) { + const raw = fs.readFileSync(filePath, 'utf8'); + + // Run the canonical MDX→MD transform with absolute link rewriting + let content = cleanMdx(raw, { baseUrl: 'https://www.marketdata.app/docs' }); + + // Pull the H1 (from frontmatter title) back out so combineFiles can re-emit it + // with its preferred formatting, and strip our own "tg X" suffix from it. + let title; + const h1Match = content.match(/^#\s+(.+?)\n+/); + if (h1Match) { + title = stripTitleTags(h1Match[1].trim()); + content = content.slice(h1Match[0].length); + } else { title = stripTitleTags(path.basename(filePath, path.extname(filePath))); } - - // Process content - content = stripFrontmatter(content); - content = removeImports(content); - content = removeEmptyComponents(content); - content = convertTabsToHeaders(content); - - // Clean up extra blank lines - content = content.replace(/\n{3,}/g, '\n\n').trim(); - + + content = content.trim(); + return { title, content, - path: filePath + path: filePath, }; } catch (error) { console.error(`Error processing ${filePath}:`, error.message); From de2991efcdbc91ebe9218f118644ae35176f4eb4 Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Wed, 20 May 2026 21:26:36 -0300 Subject: [PATCH 16/29] fix(sdk-go): remove stray '>' in TabItem value attribute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The malformed value=">MacLinux" broke the worker's MDX→MD TabItem regex, causing /docs/sdk/go/authentication.md to leak the raw tag and fail the post-deploy integration test. --- sdk/go/authentication.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/go/authentication.mdx b/sdk/go/authentication.mdx index 4cce34b..5d7f6a2 100644 --- a/sdk/go/authentication.mdx +++ b/sdk/go/authentication.mdx @@ -28,7 +28,7 @@ Set the token in the environment variable `MARKETDATA_TOKEN`. Alternatively, you After setting the variable, build and run your code to make a test request. If you have set the variable correctly, you should see the output of the test request. If you see an error, double-check that you have set the variable correctly. - + This command should be run in the terminal. It sets the environment variable for the current session only. If you open a new terminal window or restart your computer, the environment variable will not persist. From 330595e63ed07792fd0b0cb5a06828e1c57620c9 Mon Sep 17 00:00:00 2001 From: Luca Mitas Date: Thu, 21 May 2026 12:45:43 -0300 Subject: [PATCH 17/29] docs(sdk/js): drop options/strikes page and add Quick Start to index Removes sdk/js/options/strikes.mdx after Taylor confirmed the strikes endpoint will no longer be offered. The options DocCardList is sidebar- autogenerated and refreshes automatically. Adds a Quick Start block to sdk/js/index.mdx mirroring the snippet in the sdk-js README, addressing Santiago's review comment on PR #148. Co-Authored-By: Claude Opus 4.7 (1M context) --- sdk/js/index.mdx | 25 +++++ sdk/js/options/strikes.mdx | 183 ------------------------------------- 2 files changed, 25 insertions(+), 183 deletions(-) delete mode 100644 sdk/js/options/strikes.mdx diff --git a/sdk/js/index.mdx b/sdk/js/index.mdx index 08d1d49..8501249 100644 --- a/sdk/js/index.mdx +++ b/sdk/js/index.mdx @@ -12,6 +12,31 @@ Welcome to the Market Data JavaScript SDK documentation. This SDK allows you to The JavaScript SDK is in early development (version `0.0.1`). The API surface and return types may change before 1.0. ::: +## Quick Start + +```typescript +import { MarketDataClient } from "marketdata-sdk"; + +// Initialize client +const client = new MarketDataClient({ + token: "YOUR_API_TOKEN", // Optional - runs in demo mode without token +}); + +// Get stock prices +const prices = await client.stocks.prices("AAPL"); +console.log(prices[0].mid); // 150.25 + +// Get historical candles +const candles = await client.stocks.candles("AAPL", { + resolution: "1H", + from: new Date("2024-01-01"), + to: new Date("2024-01-31"), +}); + +// Get market status +const status = await client.markets.status(); +``` + ## Open Source The SDK is open source and available on GitHub. Feel free to contribute to the project, report bugs, or request new features. diff --git a/sdk/js/options/strikes.mdx b/sdk/js/options/strikes.mdx deleted file mode 100644 index e96dd71..0000000 --- a/sdk/js/options/strikes.mdx +++ /dev/null @@ -1,183 +0,0 @@ ---- -title: Strikes -sidebar_position: 5 ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; - -Enumerate the strike prices trading for each expiration of a given underlying symbol. - -## Making Requests - -Use the `strikes()` method on the `options` resource to fetch the strike enumeration. - -| Output Format | Result Payload | Description | -|------------------------|-------------------------------------------------------------------------|--------------------------------------------| -| **internal** (default) | `OptionsStrikesResponse` or `OptionsStrikesHumanResponse` | Object with one `number[]` per expiration. | -| **json** | Raw JSON object | The raw response as returned by the API. | - - -## strikes - -```typescript -// Positional form -strikes

( - symbol: string, - params?: P, -): MarketDataPromise - -// Object form -strikes

( - params: P & { symbol: string }, -): MarketDataPromise -``` - -Fetches available strikes for a single underlying symbol. - -#### Parameters - -- `symbol` (string) - - The underlying stock symbol (e.g. `"AAPL"`). - -- `expiration` (string | Date, optional) - - Filter to strikes that trade on a specific expiration date. Without this parameter, every expiration is returned. - -- `date` (string | Date, optional) - - Fetch strikes as-of a specific historical date. - -- [`outputFormat`](/sdk/js/settings#output-format) (optional): The format of the returned data. Alias: `format`. -- [`dateFormat`](/sdk/js/settings#date-format) (optional): Date format. Alias: `dateformat`. -- [`columns`](/sdk/js/settings#columns) (optional): Columns to include. -- [`useHumanReadable`](/sdk/js/settings#human-readable) (optional): Use human-readable field names. Alias: `human`. - -#### Returns - -- [`MarketDataPromise`](/sdk/js/client#MarketDataPromise) - - - - -```typescript -import { MarketDataClient } from "marketdata-sdk"; - -const client = new MarketDataClient(); - -try { - const res = await client.options.strikes("AAPL"); - // Every key besides `s` and `updated` is an expiration date. - for (const [expiration, strikes] of Object.entries(res)) { - if (expiration === "s" || expiration === "updated") continue; - console.log(`${expiration}: ${(strikes as number[]).length} strikes`); - } -} catch (error) { - console.error(error); -} -``` - - - - - -```typescript -import { MarketDataClient } from "marketdata-sdk"; - -const client = new MarketDataClient(); - -try { - const res = await client.options.strikes("AAPL", { - expiration: "2026-06-19", - }); - console.log(res["2026-06-19"]); // number[] -} catch (error) { - console.error(error); -} -``` - - - - - -```typescript -import { MarketDataClient } from "marketdata-sdk"; - -const client = new MarketDataClient(); - -try { - // Strikes as-of an end-of-day snapshot - const res = await client.options.strikes("AAPL", { - date: "2024-01-12", - }); - console.log(res); -} catch (error) { - console.error(error); -} -``` - - - - - -```typescript -import { MarketDataClient } from "marketdata-sdk"; - -const client = new MarketDataClient(); - -try { - const res = await client.options.strikes("AAPL", { - expiration: "2026-06-19", - human: true, - }); - console.log(res.Date); // Unix timestamp - console.log(res["2026-06-19"]); // number[] -} catch (error) { - console.error(error); -} -``` - - - - - -## OptionsStrikesResponse - -```typescript -interface OptionsStrikesResponse { - s: string; - updated: number; - [expiration: string]: number[] | string | number; -} -``` - -#### Properties - -- `s` (string): Status indicator (e.g. `"ok"`, `"no_data"`). -- `updated` (number): Unix timestamp when the data was last updated. -- `[expiration]` (number[]): One entry per available expiration date in `YYYY-MM-DD` format, whose value is the array of strike prices trading on that expiration. - - -## OptionsStrikesHumanResponse - -```typescript -interface OptionsStrikesHumanResponse { - Date: number; - [expiration: string]: number[] | number; -} -``` - -`OptionsStrikesHumanResponse` is returned when `human: true` is set. The status field is omitted and `updated` is renamed to `Date`. - -## No-data responses - -Calling `strikes()` against a symbol or expiration with no listings resolves with an empty result rather than rejecting. Inspect `no_data` or `hasData()` on the returned promise: - -```typescript -const pending = client.options.strikes("UNKNOWN_TICKER"); -const res = await pending; -if (pending.no_data) { - console.log("No strikes available for that symbol"); -} -``` From 8199c2bd92b03dec165bedb2c04f1cba5d24d4d5 Mon Sep 17 00:00:00 2001 From: Luca Mitas Date: Wed, 27 May 2026 16:50:01 -0300 Subject: [PATCH 18/29] docs(sdk/js): correct options.chain filter param names to live-API names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The chain.mdx Parameters list and Filtered example used snake_case names (strike_limit, min_open_interest, …) plus days_to_expiration, none of which the live API recognises — mirroring the SDK schema bug fixed in sdk-js #19. Rename to the API names: dte, strikeLimit, minBid/maxBid, minAsk/maxAsk, maxBidAskSpread, maxBidAskSpreadPct, minOpenInterest, minVolume. Co-Authored-By: Claude Opus 4.7 (1M context) --- sdk/js/options/chain.mdx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sdk/js/options/chain.mdx b/sdk/js/options/chain.mdx index 86cdc85..0d025ff 100644 --- a/sdk/js/options/chain.mdx +++ b/sdk/js/options/chain.mdx @@ -50,7 +50,7 @@ Fetches the option chain for a single underlying symbol. Filter by specific expiration date. -- `days_to_expiration` (number, optional) +- `dte` (number, optional) Filter by number of days to expiration. @@ -74,7 +74,7 @@ Fetches the option chain for a single underlying symbol. Filter by target delta value. -- `strike_limit` (number, optional) +- `strikeLimit` (number, optional) Limit the response to the N strikes nearest the underlying price. @@ -82,15 +82,15 @@ Fetches the option chain for a single underlying symbol. Filter by in-the-money / out-of-the-money range (e.g. `"itm"`, `"otm"`). -- `min_bid` / `max_bid` / `min_ask` / `max_ask` (number, optional) +- `minBid` / `maxBid` / `minAsk` / `maxAsk` (number, optional) Filter by quote price thresholds. -- `max_bid_ask_spread` (number, optional), `max_bid_ask_spread_pct` (number, optional) +- `maxBidAskSpread` (number, optional), `maxBidAskSpreadPct` (number, optional) Filter by spread thresholds. -- `min_open_interest` (number, optional), `min_volume` (number, optional) +- `minOpenInterest` (number, optional), `minVolume` (number, optional) Filter by liquidity thresholds. @@ -146,8 +146,8 @@ try { // Calls only, narrow strike window, high open interest const contracts = await client.options.chain("AAPL", { side: "call", - strike_limit: 10, - min_open_interest: 100, + strikeLimit: 10, + minOpenInterest: 100, }); for (const c of contracts) { console.log( From bbb7268373709370d477bdec343eb3af5f9e60a5 Mon Sep 17 00:00:00 2001 From: Luca Mitas Date: Thu, 28 May 2026 12:26:41 -0300 Subject: [PATCH 19/29] docs(sdk/js): address PR #148 client.mdx review - Link UtilitiesResource in the Resources list (parity with the other resources, which all link to their per-resource pages). - Rewrite the NotFoundError row in the Error classes table. The previous copy described an internal sentinel (`{s: "no_data"}`) that never escapes _makeRequest; the SDK exposes 404 as an empty response with `no_data: true`. Also call out the `/user/` opt-in throw path. - Add PaymentRequiredError (402) and ForbiddenError (403) rows so the Error classes table matches the classes actually exported from sdk-js src/error.ts on main. --- sdk/js/client.mdx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sdk/js/client.mdx b/sdk/js/client.mdx index 8c9f005..989a594 100644 --- a/sdk/js/client.mdx +++ b/sdk/js/client.mdx @@ -58,7 +58,7 @@ interface MarketDataConfig { - `options` ([OptionsResource](/sdk/js/options)): Access to options endpoints (chain, expirations, quotes, lookup) - `funds` ([FundsResource](/sdk/js/funds)): Access to funds endpoints (candles) - `markets` ([MarketsResource](/sdk/js/markets)): Access to markets endpoints (status) -- `utilities` (UtilitiesResource): Access to utility endpoints (`status`, `headers`, `user`) +- `utilities` ([UtilitiesResource](/sdk/js/utilities)): Access to utility endpoints (`status`, `headers`, `user`) ### constructor @@ -213,7 +213,9 @@ try { |---|---| | `AuthenticationError` | 401 — token missing, invalid, or expired | | `BadRequestError` | 400 — malformed request or invalid parameters | -| `NotFoundError` | 404 — exported but not thrown by default. 404 resolves to `{s: "no_data"}` instead; see [no-data handling](#NoData). | +| `NotFoundError` | 404 — exported but not thrown by default. The SDK translates 404 into an empty response with `no_data: true`; see [no-data handling](#NoData). The one exception is `/user/`, which opts into the throw path. | +| `PaymentRequiredError` | 402 — request denied by your plan (data older than your plan allows, premium endpoint on Free/Trial, or `mode=cached` on Free/Trial) | +| `ForbiddenError` | 403 — access denied. Typically the multi-IP block: the account is temporarily locked when used from more than one IP. Wait ~5 minutes and retry. | | `RateLimitError` | 429 — per-minute/day rate limit exceeded | | `ServerError` | 5xx — retriable, server-side failure | | `NetworkError` | Transport failure: DNS, connection, TLS, or 99s timeout | From 87ccdb536c693f56047ebbf87bff34142e56f774 Mon Sep 17 00:00:00 2001 From: Luca Mitas Date: Thu, 28 May 2026 16:40:29 -0300 Subject: [PATCH 20/29] docs(sdk/js): drop utilities/user page; rate-limit data lives on client MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The companion sdk-js change removes client.utilities.user() — its declared schema never matched the live API response, so callers always got `{}` at runtime. Rate-limit data is on client.rateLimits (populated automatically at startup from the same /user/ response headers), and sdk-py never exposed this surface in the first place. - Delete sdk/js/utilities/user.mdx - Rework utilities/index.mdx intro: "All three" -> "Both", drop the rate-limit framing on this page, and point readers to client.rateLimits for the data the user page used to advertise. --- sdk/js/utilities/index.mdx | 6 ++- sdk/js/utilities/user.mdx | 104 ------------------------------------- 2 files changed, 4 insertions(+), 106 deletions(-) delete mode 100644 sdk/js/utilities/user.mdx diff --git a/sdk/js/utilities/index.mdx b/sdk/js/utilities/index.mdx index 6f971d7..7b1d15c 100644 --- a/sdk/js/utilities/index.mdx +++ b/sdk/js/utilities/index.mdx @@ -4,9 +4,11 @@ slug: /js/utilities sidebar_position: 50 --- -The JavaScript SDK from Market Data provides methods designed to streamline your use of the following Utilities endpoints. These intuitive methods provide a seamless interface for accessing diagnostic and account-level data, including service status, request-header inspection, and the rate-limit snapshot tied to your API token. +The JavaScript SDK from Market Data provides methods designed to streamline your use of the following Utilities endpoints. These methods provide a seamless interface for accessing diagnostic data: service status and request-header inspection. -All three utilities skip the per-token rate-limit gate and bypass the `/v1/` URL prefix, so they remain reachable even when your account is throttled. +Both utilities skip the per-token rate-limit gate and bypass the `/v1/` URL prefix, so they remain reachable even when your account is throttled. + +Rate-limit data is exposed on the client itself — see [`client.rateLimits`](/sdk/js/client#RateLimits), which the SDK populates automatically at startup from the same `/user/` response headers. ## Utilities Endpoints diff --git a/sdk/js/utilities/user.mdx b/sdk/js/utilities/user.mdx deleted file mode 100644 index d251026..0000000 --- a/sdk/js/utilities/user.mdx +++ /dev/null @@ -1,104 +0,0 @@ ---- -title: User -sidebar_position: 3 ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; - -Return the account and rate-limit snapshot tied to the API token in use. The client constructor calls this automatically to populate `client.rateLimits` unless you opt out with `skipStartupValidation: true`. - -## Making Requests - -Use the `user()` method on the `utilities` resource. - -| Output Format | Result Payload | Description | -|------------------------|-----------------|-----------------------------------------------| -| **internal** (default) | `UserResponse` | Account identity + current rate-limit snapshot.| -| **json** | Raw JSON object | The raw response as returned by the API. | - - -## user - -```typescript -user(): MarketDataPromise -``` - -Fetches the bearer-token's account record. Takes no parameters. - -#### Returns - -- [`MarketDataPromise`](/sdk/js/client#MarketDataPromise) - - - - -```typescript -import { MarketDataClient } from "marketdata-sdk"; - -const client = new MarketDataClient(); - -try { - const u = await client.utilities.user(); - console.log(`${u.plan} plan — ${u.remaining}/${u.limit} credits remaining`); - if (u.reset) { - console.log("Resets at:", new Date(u.reset * 1000).toISOString()); - } -} catch (error) { - console.error(error); -} -``` - - - - - -```typescript -import { MarketDataClient } from "marketdata-sdk"; - -const client = new MarketDataClient(); - -// `client.ready` resolves once the constructor-issued `user()` call settles. -// Awaiting it ensures you fail fast on bad credentials before issuing real work. -try { - await client.ready; - console.log("Token validated, rate limits:", client.rateLimits); -} catch (error) { - console.error("Startup validation failed:", error); -} -``` - - - - - -## UserResponse - -```typescript -interface UserResponse { - id?: string; - username?: string; - email?: string; - plan?: string; - limit?: number; - remaining?: number; - consumed?: number; - reset?: number; -} -``` - -#### Properties - -- `id` (string, optional): Internal account identifier. -- `username` (string, optional): Account username. -- `email` (string, optional): Account email address. -- `plan` (string, optional): Subscription plan name (e.g. `"free"`, `"starter"`, `"trader"`). -- `limit` (number, optional): Total request credits available in the current window. -- `remaining` (number, optional): Request credits still available. -- `consumed` (number, optional): Request credits already used in the current window. -- `reset` (number, optional): Unix timestamp at which the rate-limit window resets. - -## Errors - -- **401 Unauthorized** → rejects with [`AuthenticationError`](/sdk/js/client). This is the canonical signal that the configured token is missing, malformed, or expired. -- **404 Not Found** → rejects with [`NotFoundError`](/sdk/js/client). The token authenticated but the account record is gone, which the SDK treats as an integrity error rather than a "no data" condition (`/user/` is the one endpoint where 404 throws instead of resolving to `no_data`). From c31aa5cf4cdced642f299845d8a9c1845ddf4a84 Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Thu, 28 May 2026 17:20:39 -0300 Subject: [PATCH 21/29] docs(sdk/js): drop stale 'user' from utilities resource list Line still mentioned the user endpoint after PR #148 removed the page. --- sdk/js/client.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/js/client.mdx b/sdk/js/client.mdx index 989a594..2b9cd7c 100644 --- a/sdk/js/client.mdx +++ b/sdk/js/client.mdx @@ -58,7 +58,7 @@ interface MarketDataConfig { - `options` ([OptionsResource](/sdk/js/options)): Access to options endpoints (chain, expirations, quotes, lookup) - `funds` ([FundsResource](/sdk/js/funds)): Access to funds endpoints (candles) - `markets` ([MarketsResource](/sdk/js/markets)): Access to markets endpoints (status) -- `utilities` ([UtilitiesResource](/sdk/js/utilities)): Access to utility endpoints (`status`, `headers`, `user`) +- `utilities` ([UtilitiesResource](/sdk/js/utilities)): Access to utility endpoints (`status`, `headers`) ### constructor From c218e91b7a7d491544c9eb54cbe9d4549abda3bc Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Thu, 28 May 2026 17:55:51 -0300 Subject: [PATCH 22/29] docs(sdk): add JavaScript/TypeScript SDK card to main SDK page Adds JS and TS logos and a card linking to /sdk/js, matching the layout of the other SDK cards. --- sdk/index.mdx | 6 ++++++ static/img/javascript-logo.svg | 4 ++++ static/img/typescript-logo.svg | 4 ++++ 3 files changed, 14 insertions(+) create mode 100644 static/img/javascript-logo.svg create mode 100644 static/img/typescript-logo.svg diff --git a/sdk/index.mdx b/sdk/index.mdx index 6fe0f21..9f78413 100644 --- a/sdk/index.mdx +++ b/sdk/index.mdx @@ -24,6 +24,12 @@ Our SDKs are available for a multitude of programming languages and platforms, s [Perfect for data analysis, backend services, AI, ML, and automation scripts.](/sdk/py) +

+ [![JavaScript Logo](/img/javascript-logo.svg)![TypeScript Logo](/img/typescript-logo.svg)](/sdk/js) + ### [JavaScript / TypeScript SDK](/sdk/js) + [Type-safe Market Data integration for Node.js services and TypeScript applications.](/sdk/js) +
+
[![PHP SDK Logo](/img/php-logo.svg)](/sdk/php) ### [PHP SDK](/sdk/php) diff --git a/static/img/javascript-logo.svg b/static/img/javascript-logo.svg new file mode 100644 index 0000000..700f5e3 --- /dev/null +++ b/static/img/javascript-logo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/static/img/typescript-logo.svg b/static/img/typescript-logo.svg new file mode 100644 index 0000000..dedef54 --- /dev/null +++ b/static/img/typescript-logo.svg @@ -0,0 +1,4 @@ + + + + From 36a99b830e0b509513bd7b5d31e835c87505a5ab Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Thu, 28 May 2026 18:00:56 -0300 Subject: [PATCH 23/29] style(sdk): add 12px gap between adjacent sdk-item logos --- src/css/custom.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/css/custom.css b/src/css/custom.css index 4a02201..c3b5185 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -152,6 +152,10 @@ align-self: center; /* Center the image in the flex container */ } +.sdk-item p img + img { + margin-left: 12px; +} + [data-theme="dark"] .sdk-item { background-color: #1e1e1e; /* Dark background for the items */ color: #ffffff; /* Light text color for readability */ From 7e35577283962d1012d137601fbb1498b12132e1 Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Thu, 28 May 2026 18:05:23 -0300 Subject: [PATCH 24/29] docs(sdk): remove 'New' badge from Python and PHP SDKs --- sdk/php/index.mdx | 2 -- sdk/py/index.mdx | 2 -- 2 files changed, 4 deletions(-) diff --git a/sdk/php/index.mdx b/sdk/php/index.mdx index 6404e56..97bec08 100644 --- a/sdk/php/index.mdx +++ b/sdk/php/index.mdx @@ -2,8 +2,6 @@ title: PHP SDK sidebar_position: 4 slug: /php -sidebar_custom_props: - badge: n --- Welcome to the Market Data PHP SDK documentation. This SDK allows you to easily integrate Market Data services into your PHP applications. diff --git a/sdk/py/index.mdx b/sdk/py/index.mdx index 14158e9..e48cf18 100644 --- a/sdk/py/index.mdx +++ b/sdk/py/index.mdx @@ -2,8 +2,6 @@ title: Python SDK sidebar_position: 3 slug: /py -sidebar_custom_props: - badge: n --- Welcome to the Market Data Python SDK documentation. This SDK allows you to easily integrate Market Data services into your Python applications. From 84626ef557ac056df65a7b91047832f4597dcfd3 Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Fri, 29 May 2026 13:13:34 -0300 Subject: [PATCH 25/29] docs(sdk): publish JS SDK 1.0 as @marketdata/sdk Rename npm package from `marketdata-sdk` to `@marketdata/sdk` across all JavaScript SDK pages, drop the pre-release callouts, and bump the User-Agent examples to 1.0.0. --- sdk/js/authentication.mdx | 4 ++-- sdk/js/client.mdx | 12 ++++++------ sdk/js/funds/candles.mdx | 6 +++--- sdk/js/index.mdx | 6 +----- sdk/js/installation.mdx | 18 +++++------------- sdk/js/markets/status.mdx | 8 ++++---- sdk/js/options/chain.mdx | 8 ++++---- sdk/js/options/expirations.mdx | 6 +++--- sdk/js/options/lookup.mdx | 4 ++-- sdk/js/options/quotes.mdx | 6 +++--- sdk/js/settings.mdx | 24 ++++++++++++------------ sdk/js/stocks/candles.mdx | 8 ++++---- sdk/js/stocks/earnings.mdx | 4 ++-- sdk/js/stocks/news.mdx | 4 ++-- sdk/js/stocks/prices.mdx | 10 +++++----- sdk/js/stocks/quotes.mdx | 8 ++++---- sdk/js/utilities/headers.mdx | 4 ++-- sdk/js/utilities/status.mdx | 4 ++-- 18 files changed, 66 insertions(+), 78 deletions(-) diff --git a/sdk/js/authentication.mdx b/sdk/js/authentication.mdx index b24e10d..12ebf4e 100644 --- a/sdk/js/authentication.mdx +++ b/sdk/js/authentication.mdx @@ -76,7 +76,7 @@ Add `.env` to your `.gitignore` so the token is not committed to source control. Use the following code to verify that your authentication is working by making a test request to `SPY` or any other symbol that requires authentication. Do not use `AAPL` to test your authentication because `AAPL` is a free test symbol and will return data even if you are not authenticated properly. ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; // No need to pass a token here — the SDK reads it from // the MARKETDATA_TOKEN environment variable automatically. @@ -99,7 +99,7 @@ If you decide to pass the token directly when creating the client, you can do so ### Example Code ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const token = "your_token_here"; diff --git a/sdk/js/client.mdx b/sdk/js/client.mdx index 2b9cd7c..4fca59c 100644 --- a/sdk/js/client.mdx +++ b/sdk/js/client.mdx @@ -94,7 +94,7 @@ Creates and configures a new `MarketDataClient` instance. This initializes the c #### Notes -- The client sets a `User-Agent` header of the form `marketdata-sdk-javascript/{version}` (e.g. `marketdata-sdk-javascript/0.0.1`). +- The client sets a `User-Agent` header of the form `marketdata-sdk-javascript/{version}` (e.g. `marketdata-sdk-javascript/1.0.0`). - All authenticated requests include an `Authorization: Bearer {token}` header. - The client reuses a single underlying `fetch` client, which benefits from Node's global connection pooling, and enforces a global 50-request concurrency pool across every endpoint. - Every request has a 99-second timeout; a timed-out fetch rejects with `NetworkError`. @@ -103,7 +103,7 @@ Creates and configures a new `MarketDataClient` instance. This initializes the c #### Example ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; // Token will be read from MARKETDATA_TOKEN environment variable const client = new MarketDataClient(); @@ -124,7 +124,7 @@ const fast = new MarketDataClient({ const debugClient = new MarketDataClient({ debug: true }); // Provide a custom logger -import { DefaultLogger, LogLevel } from "marketdata-sdk"; +import { DefaultLogger, LogLevel } from "@marketdata/sdk"; const logger = new DefaultLogger(LogLevel.WARN); const quietClient = new MarketDataClient({ logger }); ``` @@ -145,7 +145,7 @@ import { AuthenticationError, RateLimitError, NotFoundError, -} from "marketdata-sdk"; +} from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -183,7 +183,7 @@ const prices = await client.stocks ```typescript -import { AuthenticationError } from "marketdata-sdk"; +import { AuthenticationError } from "@marketdata/sdk"; await expect(client.stocks.prices("AAPL")) .rejects.toBeInstanceOf(AuthenticationError); @@ -313,7 +313,7 @@ If rate limits have been fetched and `requestsRemaining` is `0`, the next resour The SDK includes a built-in logger that outputs diagnostic information. You can pass a custom logger or use the default. ```typescript -import { MarketDataClient, DefaultLogger, LogLevel } from "marketdata-sdk"; +import { MarketDataClient, DefaultLogger, LogLevel } from "@marketdata/sdk"; // Default logger (INFO level) const client1 = new MarketDataClient(); diff --git a/sdk/js/funds/candles.mdx b/sdk/js/funds/candles.mdx index 79c81b7..5314564 100644 --- a/sdk/js/funds/candles.mdx +++ b/sdk/js/funds/candles.mdx @@ -77,7 +77,7 @@ Fetches historical daily candles for a single fund symbol. ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -97,7 +97,7 @@ try { ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -118,7 +118,7 @@ try { ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/sdk/js/index.mdx b/sdk/js/index.mdx index 8501249..752b930 100644 --- a/sdk/js/index.mdx +++ b/sdk/js/index.mdx @@ -8,14 +8,10 @@ sidebar_custom_props: Welcome to the Market Data JavaScript SDK documentation. This SDK allows you to integrate Market Data services into your Node.js and TypeScript applications. It ships typed responses, runtime schema validation, and a functional error-handling pattern. -:::info -The JavaScript SDK is in early development (version `0.0.1`). The API surface and return types may change before 1.0. -::: - ## Quick Start ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; // Initialize client const client = new MarketDataClient({ diff --git a/sdk/js/installation.mdx b/sdk/js/installation.mdx index 53c4262..fd2a14f 100644 --- a/sdk/js/installation.mdx +++ b/sdk/js/installation.mdx @@ -19,40 +19,32 @@ This guide will help you install the Market Data JavaScript SDK and configure it ```bash -pnpm add marketdata-sdk +pnpm add @marketdata/sdk ``` ```bash -npm install marketdata-sdk +npm install @marketdata/sdk ``` ```bash -yarn add marketdata-sdk +yarn add @marketdata/sdk ``` -:::info -The SDK is in early development (version `0.0.1`) and has not yet been published to npm. Until it is, install directly from GitHub: - -```bash -pnpm add github:MarketDataApp/sdk-js -``` -::: - ## TypeScript Support The SDK is written in TypeScript and ships first-class type definitions. No `@types/*` package is needed. The SDK builds to both ESM and CommonJS, so it works with either module system. -- **ESM**: `import { MarketDataClient } from "marketdata-sdk";` -- **CommonJS**: `const { MarketDataClient } = require("marketdata-sdk");` +- **ESM**: `import { MarketDataClient } from "@marketdata/sdk";` +- **CommonJS**: `const { MarketDataClient } = require("@marketdata/sdk");` ## Local Development Installation diff --git a/sdk/js/markets/status.mdx b/sdk/js/markets/status.mdx index 0a5e5f9..b6a6a11 100644 --- a/sdk/js/markets/status.mdx +++ b/sdk/js/markets/status.mdx @@ -66,7 +66,7 @@ Fetches market status information. All parameters are optional. ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -85,7 +85,7 @@ try { ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -106,7 +106,7 @@ try { ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -124,7 +124,7 @@ try { ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/sdk/js/options/chain.mdx b/sdk/js/options/chain.mdx index 0d025ff..d69eff4 100644 --- a/sdk/js/options/chain.mdx +++ b/sdk/js/options/chain.mdx @@ -121,7 +121,7 @@ Fetches the option chain for a single underlying symbol. ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -138,7 +138,7 @@ try { ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -164,7 +164,7 @@ try { ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -185,7 +185,7 @@ try { ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/sdk/js/options/expirations.mdx b/sdk/js/options/expirations.mdx index 614956e..55cbfb4 100644 --- a/sdk/js/options/expirations.mdx +++ b/sdk/js/options/expirations.mdx @@ -62,7 +62,7 @@ Fetches the list of expirations for a single underlying symbol. ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -82,7 +82,7 @@ try { ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -100,7 +100,7 @@ try { ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/sdk/js/options/lookup.mdx b/sdk/js/options/lookup.mdx index d0542f1..7b01785 100644 --- a/sdk/js/options/lookup.mdx +++ b/sdk/js/options/lookup.mdx @@ -47,7 +47,7 @@ Resolves a human-readable option description to its OCC symbol. ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -64,7 +64,7 @@ try { ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/sdk/js/options/quotes.mdx b/sdk/js/options/quotes.mdx index c82603d..8d0637e 100644 --- a/sdk/js/options/quotes.mdx +++ b/sdk/js/options/quotes.mdx @@ -59,7 +59,7 @@ Additional endpoint-specific parameters like `from`, `to`, and `date` are passed ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -79,7 +79,7 @@ try { ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -102,7 +102,7 @@ try { ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/sdk/js/settings.mdx b/sdk/js/settings.mdx index 6cecfe4..600f84d 100644 --- a/sdk/js/settings.mdx +++ b/sdk/js/settings.mdx @@ -55,7 +55,7 @@ export MARKETDATA_USE_HUMAN_READABLE=true ``` ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -70,7 +70,7 @@ const result = await client.stocks.prices("AAPL"); Set client-level configuration via the constructor: ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient({ token: "your_token_here", @@ -93,7 +93,7 @@ Constructor arguments override environment variables for the settings they cover Pass parameters directly to resource methods: ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -124,7 +124,7 @@ Controls the format of the API response data. The SDK also exposes an `OutputFormat` enum you can import for type safety: ```typescript -import { OutputFormat } from "marketdata-sdk"; +import { OutputFormat } from "@marketdata/sdk"; // OutputFormat.INTERNAL, OutputFormat.JSON, OutputFormat.CSV ``` @@ -136,7 +136,7 @@ import { OutputFormat } from "marketdata-sdk"; **Example:** ```typescript -import { MarketDataClient, OutputFormat } from "marketdata-sdk"; +import { MarketDataClient, OutputFormat } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -162,7 +162,7 @@ Specifies the format for date and time information in responses. The SDK also exposes a `DateFormat` enum: ```typescript -import { DateFormat } from "marketdata-sdk"; +import { DateFormat } from "@marketdata/sdk"; // DateFormat.TIMESTAMP, DateFormat.UNIX, DateFormat.SPREADSHEET ``` @@ -174,7 +174,7 @@ import { DateFormat } from "marketdata-sdk"; **Example:** ```typescript -import { MarketDataClient, DateFormat } from "marketdata-sdk"; +import { MarketDataClient, DateFormat } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -197,7 +197,7 @@ Limits the response to only the columns you need, reducing data transfer and pro **Example:** ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -222,7 +222,7 @@ Controls whether headers are included in CSV output. **Example:** ```typescript -import { MarketDataClient, OutputFormat } from "marketdata-sdk"; +import { MarketDataClient, OutputFormat } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -250,7 +250,7 @@ Uses human-readable field names in responses instead of short, machine-friendly **Example:** ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -276,7 +276,7 @@ Controls whether the API returns live, cached, or delayed data. The SDK also exposes a `Mode` enum: ```typescript -import { Mode } from "marketdata-sdk"; +import { Mode } from "@marketdata/sdk"; // Mode.LIVE, Mode.CACHED, Mode.DELAYED ``` @@ -288,7 +288,7 @@ import { Mode } from "marketdata-sdk"; **Example:** ```typescript -import { MarketDataClient, Mode } from "marketdata-sdk"; +import { MarketDataClient, Mode } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/sdk/js/stocks/candles.mdx b/sdk/js/stocks/candles.mdx index 56974f3..31c9686 100644 --- a/sdk/js/stocks/candles.mdx +++ b/sdk/js/stocks/candles.mdx @@ -89,7 +89,7 @@ Fetches historical candles for a single symbol. ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -109,7 +109,7 @@ try { ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -133,7 +133,7 @@ try { ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -156,7 +156,7 @@ try { ```typescript -import { MarketDataClient, OutputFormat } from "marketdata-sdk"; +import { MarketDataClient, OutputFormat } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/sdk/js/stocks/earnings.mdx b/sdk/js/stocks/earnings.mdx index 529c85a..a2cc89d 100644 --- a/sdk/js/stocks/earnings.mdx +++ b/sdk/js/stocks/earnings.mdx @@ -63,7 +63,7 @@ Earnings data is a premium endpoint on the Market Data API. Your plan must inclu ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -85,7 +85,7 @@ try { ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/sdk/js/stocks/news.mdx b/sdk/js/stocks/news.mdx index cf76f38..7f2c9f2 100644 --- a/sdk/js/stocks/news.mdx +++ b/sdk/js/stocks/news.mdx @@ -73,7 +73,7 @@ Fetches news articles for a single symbol. ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -92,7 +92,7 @@ try { ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/sdk/js/stocks/prices.mdx b/sdk/js/stocks/prices.mdx index fb8c74d..94a6504 100644 --- a/sdk/js/stocks/prices.mdx +++ b/sdk/js/stocks/prices.mdx @@ -80,7 +80,7 @@ Fetches stock prices for one or more symbols. The return type is narrowed automa ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -97,7 +97,7 @@ try { ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -116,7 +116,7 @@ try { ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -135,7 +135,7 @@ try { ```typescript -import { MarketDataClient, OutputFormat } from "marketdata-sdk"; +import { MarketDataClient, OutputFormat } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -154,7 +154,7 @@ try { ```typescript -import { MarketDataClient, OutputFormat } from "marketdata-sdk"; +import { MarketDataClient, OutputFormat } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/sdk/js/stocks/quotes.mdx b/sdk/js/stocks/quotes.mdx index 3009618..7e10a15 100644 --- a/sdk/js/stocks/quotes.mdx +++ b/sdk/js/stocks/quotes.mdx @@ -65,7 +65,7 @@ Fetches stock quotes for one or more symbols. ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -82,7 +82,7 @@ try { ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -101,7 +101,7 @@ try { ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -118,7 +118,7 @@ try { ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/sdk/js/utilities/headers.mdx b/sdk/js/utilities/headers.mdx index 8658e68..2c4cac1 100644 --- a/sdk/js/utilities/headers.mdx +++ b/sdk/js/utilities/headers.mdx @@ -36,13 +36,13 @@ Fetches the request-header echo. Takes no parameters. ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); try { const h = await client.utilities.headers(); - console.log(h["user-agent"]); // "marketdata-sdk-javascript/0.0.1" + console.log(h["user-agent"]); // "marketdata-sdk-javascript/1.0.0" console.log(h["accept-encoding"]); } catch (error) { console.error(error); diff --git a/sdk/js/utilities/status.mdx b/sdk/js/utilities/status.mdx index 04d9da1..4c8e7c8 100644 --- a/sdk/js/utilities/status.mdx +++ b/sdk/js/utilities/status.mdx @@ -34,7 +34,7 @@ Fetches the current service-status payload. Takes no parameters. ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -53,7 +53,7 @@ try { ```typescript -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); From 4228d8006caed2b1fe7b0b9aad0c2df3b3b28823 Mon Sep 17 00:00:00 2001 From: Market Data Dev Team Date: Fri, 29 May 2026 13:17:30 -0300 Subject: [PATCH 26/29] docs(api): replace NodeJS raw-fetch tabs with JavaScript + TypeScript SDK tabs (#152) Bring the JS examples across the API reference in line with the other languages now that the JavaScript/TypeScript SDK (marketdata-sdk) is shipped. Each affected page's single NodeJS raw-fetch tab is replaced with paired JavaScript and TypeScript tabs using the SDK (await + try/catch), sourced from the matching sdk/js/* page. - 13 endpoint pages: 1:1 SDK swap (stocks/{quotes,prices,candles, earnings,news}, options/{chain,expirations,lookup,quotes}, funds/candles, markets/status, utilities/{status,headers}). - authentication.mdx: env-var + explicit-token forms. - troubleshooting/logging.mdx: SDK DefaultLogger(LogLevel.DEBUG). - troubleshooting/real-time-data.mdx: client.stocks.quotes + Mode.LIVE, keeping the data-age diagnostic logic. - troubleshooting/service-outages.mdx: client.utilities.status(). - stocks/bulkcandles.mdx: SDK has no bulk method yet, so JavaScript keeps raw fetch() and TypeScript uses the typed Response API; a note documents this until bulk support lands. Closes #151 Co-authored-by: Claude Opus 4.8 (1M context) --- api/authentication.mdx | 75 ++++---- api/funds/candles.mdx | 43 ++++- api/markets/status.mdx | 56 ++++-- api/options/chain.mdx | 36 +++- api/options/expirations.mdx | 42 +++- api/options/lookup.mdx | 38 +++- api/options/quotes.mdx | 42 +++- api/stocks/bulkcandles.mdx | 37 +++- api/stocks/candles.mdx | 43 ++++- api/stocks/earnings.mdx | 46 ++++- api/stocks/news.mdx | 40 +++- api/stocks/prices.mdx | 76 ++++++-- api/stocks/quotes.mdx | 76 ++++++-- api/troubleshooting/logging.mdx | 61 ++++-- api/troubleshooting/real-time-data.mdx | 243 ++++++++++++++++-------- api/troubleshooting/service-outages.mdx | 98 ++++++---- api/utilities/headers.mdx | 35 +++- api/utilities/status.mdx | 37 +++- 18 files changed, 819 insertions(+), 305 deletions(-) diff --git a/api/authentication.mdx b/api/authentication.mdx index c030a68..8c9c4f5 100644 --- a/api/authentication.mdx +++ b/api/authentication.mdx @@ -48,44 +48,47 @@ The curly braces around token are a placeholder for this example. Do not actuall ::: - - -```javascript -const https = require('https'); - -// Your token -const token = 'your_token_here'; - -// The API endpoint for retrieving stock quotes for SPY -const url = 'https://api.marketdata.app/v1/stocks/quotes/SPY/'; - -// Making the GET request to the API -https.get(url, { - headers: { - 'Accept': 'application/json', - 'Authorization': `Bearer ${token}` - } -}, (response) => { - let data = ''; - - // A chunk of data has been received. - response.on('data', (chunk) => { - data += chunk; - }); - - // The whole response has been received. Print out the result. - response.on('end', () => { - if (response.statusCode === 200 || response.statusCode === 203) { - console.log(JSON.parse(data)); - } else { - console.log(`Failed to retrieve data: ${response.statusCode}`); - } - }); -}).on("error", (err) => { - console.log("Error: " + err.message); -}); + + +```js title="app.js" +import { MarketDataClient } from "marketdata-sdk"; + +// The SDK reads your token from the MARKETDATA_TOKEN environment variable. +// Alternatively, pass it directly: new MarketDataClient({ token: "your_token_here" }) +const client = new MarketDataClient(); + +try { + const quotes = await client.stocks.quotes("SPY"); + console.log(quotes); +} catch (error) { + console.error(error); +} +``` + +For more information about using the JavaScript SDK, see our [JavaScript SDK documentation](/sdk/js) and [authentication guide](/sdk/js/authentication). + + + + + +```typescript title="app.ts" +import { MarketDataClient } from "marketdata-sdk"; +import type { StockQuote } from "marketdata-sdk"; + +// The SDK reads your token from the MARKETDATA_TOKEN environment variable. +// Alternatively, pass it directly: new MarketDataClient({ token: "your_token_here" }) +const client = new MarketDataClient(); + +try { + const quotes: StockQuote[] = await client.stocks.quotes("SPY"); + console.log(quotes); +} catch (error) { + console.error(error); +} ``` +For more information about using the JavaScript SDK, see our [JavaScript SDK documentation](/sdk/js) and [authentication guide](/sdk/js/authentication). + diff --git a/api/funds/candles.mdx b/api/funds/candles.mdx index 08b9a68..c590f84 100644 --- a/api/funds/candles.mdx +++ b/api/funds/candles.mdx @@ -30,19 +30,42 @@ GET **GET** [https://api.marketdata.app/v1/funds/candles/D/VFINX/?from=2020-01-01&to=2020-01-10](https://api.marketdata.app/v1/funds/candles/D/VFINX/?from=2020-01-01&to=2020-01-10) - + ```js title="fundCandles.js" -fetch( - "https://api.marketdata.app/v1/funds/candles/D/VFINX/?from=2020-01-01&to=2020-01-10" -) - .then((res) => { - console.log(res); - }) - .catch((err) => { - console.log(err); - }); +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + // VFINX is available without authentication (free test symbol). + const candles = await client.funds.candles("VFINX", { countback: 30 }); + for (const c of candles) { + console.log(`t=${c.t} o=${c.o} h=${c.h} l=${c.l} c=${c.c}`); + } +} catch (error) { + console.error(error); +} +``` + + + +```typescript title="fundCandles.ts" +import { MarketDataClient } from "marketdata-sdk"; +import type { FundsCandle } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + // VFINX is available without authentication (free test symbol). + const candles: FundsCandle[] = await client.funds.candles("VFINX", { countback: 30 }); + for (const c of candles) { + console.log(`t=${c.t} o=${c.o} h=${c.h} l=${c.l} c=${c.c}`); + } +} catch (error) { + console.error(error); +} ``` diff --git a/api/markets/status.mdx b/api/markets/status.mdx index 11f2524..9d2ee03 100644 --- a/api/markets/status.mdx +++ b/api/markets/status.mdx @@ -26,26 +26,46 @@ GET **GET** [https://api.marketdata.app/v1/markets/status/?date=yesterday](https://api.marketdata.app/v1/markets/status/?date=yesterday) - + ```js title="app.js" -fetch( - "https://api.marketdata.app/v1/markets/status/?from=2020-01-01&to=2020-12-31" -) - .then((res) => { - console.log(res); - }) - .catch((err) => { - console.log(err); - }); - -fetch("https://api.marketdata.app/v1/markets/status/?date=yesterday") - .then((res) => { - console.log(res); - }) - .catch((err) => { - console.log(err); - }); +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const days = await client.markets.status({ from: "2020-01-01", to: "2020-12-31" }); + for (const d of days) { + console.log(`date=${d.date} status=${d.status}`); + } + + const yesterday = await client.markets.status({ date: "yesterday" }); + console.log(yesterday[0]); +} catch (error) { + console.error(error); +} +``` + + + + +```typescript title="app.ts" +import { MarketDataClient } from "marketdata-sdk"; +import type { MarketStatusResponse } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const days: MarketStatusResponse = await client.markets.status({ from: "2020-01-01", to: "2020-12-31" }); + for (const d of days) { + console.log(`date=${d.date} status=${d.status}`); + } + + const yesterday: MarketStatusResponse = await client.markets.status({ date: "yesterday" }); + console.log(yesterday[0]); +} catch (error) { + console.error(error); +} ``` diff --git a/api/options/chain.mdx b/api/options/chain.mdx index 24de186..e84b8b2 100644 --- a/api/options/chain.mdx +++ b/api/options/chain.mdx @@ -22,16 +22,36 @@ GET https://api.marketdata.app/v1/options/chain/{underlyingSymbol}/ **GET** [https://api.marketdata.app/v1/options/chain/AAPL/?expiration=2025-01-17&side=call](https://api.marketdata.app/v1/options/chain/AAPL/?expiration=2025-01-17&side=call) - + ```js title="app.js" -fetch("https://api.marketdata.app/v1/options/chain/AAPL/") - .then((res) => { - console.log(res); - }) - .catch((err) => { - console.log(err); - }); +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const contracts = await client.options.chain("AAPL"); + console.log(`Got ${contracts.length} contracts`); +} catch (error) { + console.error(error); +} +``` + + + + +```typescript title="app.ts" +import { MarketDataClient } from "marketdata-sdk"; +import type { OptionsChain } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const contracts: OptionsChain[] = await client.options.chain("AAPL"); + console.log(`Got ${contracts.length} contracts`); +} catch (error) { + console.error(error); +} ``` diff --git a/api/options/expirations.mdx b/api/options/expirations.mdx index ad492e0..3d122db 100644 --- a/api/options/expirations.mdx +++ b/api/options/expirations.mdx @@ -24,16 +24,42 @@ GET **GET** [https://api.marketdata.app/v1/options/expirations/AAPL/](https://api.marketdata.app/v1/options/expirations/AAPL/) - + ```js title="app.js" -fetch("https://api.marketdata.app/v1/options/expirations/AAPL/") - .then((res) => { - console.log(res); - }) - .catch((err) => { - console.log(err); - }); +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const data = await client.options.expirations("AAPL"); + console.log(`Updated: ${data.updated}`); + for (const exp of data.expirations) { + console.log(exp); + } +} catch (error) { + console.error(error); +} +``` + + + + +```typescript title="app.ts" +import { MarketDataClient } from "marketdata-sdk"; +import type { OptionsExpirationsResponse } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const data: OptionsExpirationsResponse = await client.options.expirations("AAPL"); + console.log(`Updated: ${data.updated}`); + for (const exp of data.expirations) { + console.log(exp); + } +} catch (error) { + console.error(error); +} ``` diff --git a/api/options/lookup.mdx b/api/options/lookup.mdx index 57d8213..f71c836 100644 --- a/api/options/lookup.mdx +++ b/api/options/lookup.mdx @@ -24,18 +24,36 @@ GET **GET** [https://api.marketdata.app/v1/options/lookup/AAPL%207/28/2023%20200%20Call/](https://api.marketdata.app/v1/options/lookup/AAPL%207/28/2023%20200%20Call/) - + ```js title="app.js" -fetch( - "https://api.marketdata.app/v1/options/lookup/AAPL%207/28/2023%20200%20Call/" -) - .then((res) => { - console.log(res); - }) - .catch((err) => { - console.log(err); - }); +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const data = await client.options.lookup("AAPL 7/28/2023 200 Call"); + console.log(data.optionSymbol); // "AAPL230728C00200000" +} catch (error) { + console.error(error); +} +``` + + + + +```typescript title="app.ts" +import { MarketDataClient } from "marketdata-sdk"; +import type { OptionsLookupResponse } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const data: OptionsLookupResponse = await client.options.lookup("AAPL 7/28/2023 200 Call"); + console.log(data.optionSymbol); // "AAPL230728C00200000" +} catch (error) { + console.error(error); +} ``` diff --git a/api/options/quotes.mdx b/api/options/quotes.mdx index 23f756d..ef195eb 100644 --- a/api/options/quotes.mdx +++ b/api/options/quotes.mdx @@ -28,16 +28,42 @@ GET **GET** [https://api.marketdata.app/v1/options/quotes/AAPL271217C00250000/](https://api.marketdata.app/v1/options/quotes/AAPL271217C00250000/) - + ```js title="app.js" -fetch("https://api.marketdata.app/v1/options/quotes/AAPL271217C00250000/") - .then((res) => { - console.log(res); - }) - .catch((err) => { - console.log(err); - }); +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const quotes = await client.options.quotes("AAPL271217C00250000"); + const q = quotes[0]; + console.log( + `${q.optionSymbol}: bid=${q.bid} ask=${q.ask} delta=${q.delta}` + ); +} catch (error) { + console.error(error); +} +``` + + + + +```typescript title="app.ts" +import { MarketDataClient } from "marketdata-sdk"; +import type { OptionsQuotes } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const quotes: OptionsQuotes[] = await client.options.quotes("AAPL271217C00250000"); + const q = quotes[0]; + console.log( + `${q.optionSymbol}: bid=${q.bid} ask=${q.ask} delta=${q.delta}` + ); +} catch (error) { + console.error(error); +} ``` diff --git a/api/stocks/bulkcandles.mdx b/api/stocks/bulkcandles.mdx index 902ac1b..6021ce4 100644 --- a/api/stocks/bulkcandles.mdx +++ b/api/stocks/bulkcandles.mdx @@ -24,20 +24,51 @@ GET **GET** [https://api.marketdata.app/v1/stocks/bulkcandles/D/?symbols=AAPL,META,MSFT](https://api.marketdata.app/v1/stocks/bulkcandles/D/?symbols=AAPL,META,MSFT) - + + +:::note +The JavaScript/TypeScript SDK does not yet provide a method for the bulk candles endpoint, so these examples use the raw `fetch()` API. They will be replaced with `marketdata-sdk` examples once bulk support is added. +::: ```js title="app.js" fetch( "https://api.marketdata.app/v1/stocks/bulkcandles/D/?symbols=AAPL,META,MSFT" ) - .then((res) => { - console.log(res); + .then((res) => res.json()) + .then((data) => { + console.log(data); }) .catch((err) => { console.log(err); }); ``` + + + +:::note +The JavaScript/TypeScript SDK does not yet provide a method for the bulk candles endpoint, so these examples use the raw `fetch()` API. They will be replaced with `marketdata-sdk` examples once bulk support is added. +::: + +```typescript title="app.ts" +interface BulkCandlesResponse { + s: string; + symbol: string[]; + o: number[]; + h: number[]; + l: number[]; + c: number[]; + v: number[]; + t: number[]; +} + +const response: Response = await fetch( + "https://api.marketdata.app/v1/stocks/bulkcandles/D/?symbols=AAPL,META,MSFT" +); +const data: BulkCandlesResponse = await response.json(); +console.log(data); +``` + diff --git a/api/stocks/candles.mdx b/api/stocks/candles.mdx index 4687da9..f5ca209 100644 --- a/api/stocks/candles.mdx +++ b/api/stocks/candles.mdx @@ -24,19 +24,42 @@ GET **GET** [https://api.marketdata.app/v1/stocks/candles/D/AAPL/?from=2020-01-01&to=2020-12-31](https://api.marketdata.app/v1/stocks/candles/D/AAPL/?from=2020-01-01&to=2020-12-31) - + ```js title="app.js" -fetch( - "https://api.marketdata.app/v1/stocks/candles/D/AAPL/?from=2020-01-01&to=2020-12-31" -) - .then((res) => { - console.log(res); - }) - .catch((err) => { - console.log(err); - }); +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + // Default resolution is "D" (daily) + const candles = await client.stocks.candles("AAPL", { countback: 30 }); + for (const c of candles) { + console.log(`t=${c.t} o=${c.o} h=${c.h} l=${c.l} c=${c.c} v=${c.v}`); + } +} catch (error) { + console.error(error); +} +``` + + + +```typescript title="app.ts" +import { MarketDataClient } from "marketdata-sdk"; +import type { StockCandle } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + // Default resolution is "D" (daily) + const candles: StockCandle[] = await client.stocks.candles("AAPL", { countback: 30 }); + for (const c of candles) { + console.log(`t=${c.t} o=${c.o} h=${c.h} l=${c.l} c=${c.c} v=${c.v}`); + } +} catch (error) { + console.error(error); +} ``` diff --git a/api/stocks/earnings.mdx b/api/stocks/earnings.mdx index 2e01575..c927880 100644 --- a/api/stocks/earnings.mdx +++ b/api/stocks/earnings.mdx @@ -29,16 +29,46 @@ GET **GET** [https://api.marketdata.app/v1/stocks/earnings/AAPL/](https://api.marketdata.app/v1/stocks/earnings/AAPL/) - + ```js title="app.js" -fetch("https://api.marketdata.app/v1/stocks/earnings/AAPL/") - .then((res) => { - console.log(res); - }) - .catch((err) => { - console.log(err); - }); +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const earnings = await client.stocks.earnings("AAPL"); + for (const e of earnings) { + console.log( + `FY${e.fiscalYear} Q${e.fiscalQuarter}: ` + + `reported=${e.reportedEPS} estimated=${e.estimatedEPS}` + ); + } +} catch (error) { + console.error(error); +} +``` + + + + +```typescript title="app.ts" +import { MarketDataClient } from "marketdata-sdk"; +import type { StockEarningsResponse } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const earnings: StockEarningsResponse = await client.stocks.earnings("AAPL"); + for (const e of earnings) { + console.log( + `FY${e.fiscalYear} Q${e.fiscalQuarter}: ` + + `reported=${e.reportedEPS} estimated=${e.estimatedEPS}` + ); + } +} catch (error) { + console.error(error); +} ``` diff --git a/api/stocks/news.mdx b/api/stocks/news.mdx index 37e34f6..2c9f4dd 100644 --- a/api/stocks/news.mdx +++ b/api/stocks/news.mdx @@ -33,16 +33,40 @@ GET **GET** [https://api.marketdata.app/v1/stocks/news/AAPL/](https://api.marketdata.app/v1/stocks/news/AAPL/) - + ```js title="app.js" -fetch("https://api.marketdata.app/v1/stocks/news/AAPL/") - .then((res) => { - console.log(res); - }) - .catch((err) => { - console.log(err); - }); +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const articles = await client.stocks.news("AAPL"); + for (const a of articles) { + console.log(`${a.source}: ${a.headline}`); + } +} catch (error) { + console.error(error); +} +``` + + + + +```typescript title="app.ts" +import { MarketDataClient } from "marketdata-sdk"; +import type { StockNews } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const articles: StockNews[] = await client.stocks.news("AAPL"); + for (const a of articles) { + console.log(`${a.source}: ${a.headline}`); + } +} catch (error) { + console.error(error); +} ``` diff --git a/api/stocks/prices.mdx b/api/stocks/prices.mdx index 0e4edec..7e98471 100644 --- a/api/stocks/prices.mdx +++ b/api/stocks/prices.mdx @@ -36,16 +36,36 @@ GET **GET** [https://api.marketdata.app/v1/stocks/prices/AAPL/](https://api.marketdata.app/v1/stocks/prices/AAPL/) - + ```js title="app.js" -fetch("https://api.marketdata.app/v1/stocks/prices/AAPL/") - .then((res) => { - console.log(res); - }) - .catch((err) => { - console.log(err); - }); +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const prices = await client.stocks.prices("AAPL"); + console.log(prices[0].mid); +} catch (error) { + console.error(error); +} +``` + + + + +```typescript title="app.ts" +import { MarketDataClient } from "marketdata-sdk"; +import type { StockPrice } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const prices: StockPrice[] = await client.stocks.prices("AAPL"); + console.log(prices[0].mid); +} catch (error) { + console.error(error); +} ``` @@ -83,16 +103,40 @@ echo $prices; **GET** [https://api.marketdata.app/v1/stocks/prices/?symbols=AAPL,META,MSFT](https://api.marketdata.app/v1/stocks/prices/?symbols=AAPL,META,MSFT) - + ```js title="app.js" -fetch("https://api.marketdata.app/v1/stocks/prices/?symbols=AAPL,META,MSFT") - .then((res) => { - console.log(res); - }) - .catch((err) => { - console.log(err); - }); +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const prices = await client.stocks.prices(["AAPL", "META", "MSFT"]); + for (const p of prices) { + console.log(`${p.symbol}: mid=${p.mid}`); + } +} catch (error) { + console.error(error); +} +``` + + + + +```typescript title="app.ts" +import { MarketDataClient } from "marketdata-sdk"; +import type { StockPrice } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const prices: StockPrice[] = await client.stocks.prices(["AAPL", "META", "MSFT"]); + for (const p of prices) { + console.log(`${p.symbol}: mid=${p.mid}`); + } +} catch (error) { + console.error(error); +} ``` diff --git a/api/stocks/quotes.mdx b/api/stocks/quotes.mdx index d6b4ecb..e9c4dad 100644 --- a/api/stocks/quotes.mdx +++ b/api/stocks/quotes.mdx @@ -32,16 +32,36 @@ GET **GET** [https://api.marketdata.app/v1/stocks/quotes/AAPL/](https://api.marketdata.app/v1/stocks/quotes/AAPL/) - + ```js title="app.js" -fetch("https://api.marketdata.app/v1/stocks/quotes/AAPL/") - .then((res) => { - console.log(res); - }) - .catch((err) => { - console.log(err); - }); +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const quotes = await client.stocks.quotes("AAPL"); + console.log(quotes); +} catch (error) { + console.error(error); +} +``` + + + + +```typescript title="app.ts" +import { MarketDataClient } from "marketdata-sdk"; +import type { StockQuote } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const quotes: StockQuote[] = await client.stocks.quotes("AAPL"); + console.log(quotes); +} catch (error) { + console.error(error); +} ``` @@ -102,16 +122,40 @@ echo $quote; **GET** [https://api.marketdata.app/v1/stocks/quotes/?symbols=AAPL,META,MSFT](https://api.marketdata.app/v1/stocks/quotes/?symbols=AAPL,META,MSFT) - + ```js title="app.js" -fetch("https://api.marketdata.app/v1/stocks/quotes/?symbols=AAPL,META,MSFT") - .then((res) => { - console.log(res); - }) - .catch((err) => { - console.log(err); - }); +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const quotes = await client.stocks.quotes(["AAPL", "META", "MSFT"]); + for (const q of quotes) { + console.log(`${q.symbol}: bid=${q.bid}, ask=${q.ask}, last=${q.last}`); + } +} catch (error) { + console.error(error); +} +``` + + + + +```typescript title="app.ts" +import { MarketDataClient } from "marketdata-sdk"; +import type { StockQuote } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const quotes: StockQuote[] = await client.stocks.quotes(["AAPL", "META", "MSFT"]); + for (const q of quotes) { + console.log(`${q.symbol}: bid=${q.bid}, ask=${q.ask}, last=${q.last}`); + } +} catch (error) { + console.error(error); +} ``` diff --git a/api/troubleshooting/logging.mdx b/api/troubleshooting/logging.mdx index 5180a26..926c9dc 100644 --- a/api/troubleshooting/logging.mdx +++ b/api/troubleshooting/logging.mdx @@ -19,29 +19,48 @@ When logging errors, the log should include the exact request made, Market Data' ## Logging Examples - + ```js title="logger.js" -const axios = require("axios"); -const fs = require("fs"); - -// Make the API request -axios - .get("https://api.marketdata.app/v1/your_endpoint_here/") - .then((response) => { - // Do nothing for successful responses - }) - .catch((error) => { - if (error.response.status !== 200 && error.response.status !== 203) { - const logData = { - request: error.config.method.toUpperCase() + " " + error.config.url, - response: error.response.data, - cfRayHeader: error.response.headers["cf-ray"] || "Not available", - }; - // Save to a logfile - fs.appendFileSync("api_error_log.json", JSON.stringify(logData) + "\n"); - } - }); +import { MarketDataClient, DefaultLogger, LogLevel } from "marketdata-sdk"; + +// Enable DEBUG-level logging to capture full request/response diagnostics +// (including the CF-Ray header) for every API call. +const client = new MarketDataClient({ + logger: new DefaultLogger(LogLevel.DEBUG), +}); + +try { + const quotes = await client.stocks.quotes("AAPL"); + console.log(quotes); +} catch (error) { + // The SDK logs the failing request and response automatically; + // handle the error here as needed. + console.error(error); +} +``` + + + + +```typescript title="logger.ts" +import { MarketDataClient, DefaultLogger, LogLevel } from "marketdata-sdk"; +import type { StockQuote } from "marketdata-sdk"; + +// Enable DEBUG-level logging to capture full request/response diagnostics +// (including the CF-Ray header) for every API call. +const client = new MarketDataClient({ + logger: new DefaultLogger(LogLevel.DEBUG), +}); + +try { + const quotes: StockQuote[] = await client.stocks.quotes("AAPL"); + console.log(quotes); +} catch (error) { + // The SDK logs the failing request and response automatically; + // handle the error here as needed. + console.error(error); +} ``` diff --git a/api/troubleshooting/real-time-data.mdx b/api/troubleshooting/real-time-data.mdx index d993447..0799ca8 100644 --- a/api/troubleshooting/real-time-data.mdx +++ b/api/troubleshooting/real-time-data.mdx @@ -73,42 +73,81 @@ elif isinstance(quotes, dict) and quotes.get("s") == "ok" and "updated" in quote ``` - - -```javascript -const response = await fetch( - "https://api.marketdata.app/v1/stocks/quotes/AAPL/", - { - headers: { - "Authorization": "Bearer YOUR_TOKEN" + + +```js title="checkDataAge.js" +import { MarketDataClient, Mode } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + // Request live data, then inspect the `updated` timestamp. + const quotes = await client.stocks.quotes("AAPL", { mode: Mode.LIVE }); + const quote = quotes[0]; + + if (quote?.updated) { + // `updated` is a Unix timestamp in seconds. + const updatedTime = new Date(quote.updated * 1000); + const currentTime = new Date(); + + // Calculate the age of the data + const ageMinutes = (currentTime - updatedTime) / 1000 / 60; + + console.log(`Data timestamp: ${updatedTime}`); + console.log(`Current time: ${currentTime}`); + console.log(`Data age: ${ageMinutes.toFixed(1)} minutes`); + + // Determine data type + if (ageMinutes < 1) { + console.log("✅ Real-time data (less than 1 minute old)"); + } else if (ageMinutes < 20) { + console.log("⚠️ 15-minute delayed data"); + } else { + console.log("❌ Historical data (1 day old or more)"); } } -); - -const data = await response.json(); - -if (data.s === "ok" && data.updated) { - // Get the timestamp (Unix epoch seconds) - const updatedTimestamp = data.updated[0] * 1000; // Convert to milliseconds - const updatedTime = new Date(updatedTimestamp); - const currentTime = new Date(); - - // Calculate the age of the data - const ageSeconds = (currentTime - updatedTime) / 1000; - const ageMinutes = ageSeconds / 60; - - console.log(`Data timestamp: ${updatedTime}`); - console.log(`Current time: ${currentTime}`); - console.log(`Data age: ${ageMinutes.toFixed(1)} minutes`); - - // Determine data type - if (ageMinutes < 1) { - console.log("✅ Real-time data (less than 1 minute old)"); - } else if (ageMinutes < 20) { - console.log("⚠️ 15-minute delayed data"); - } else { - console.log("❌ Historical data (1 day old or more)"); +} catch (error) { + console.error(error); +} +``` + + + + +```typescript title="checkDataAge.ts" +import { MarketDataClient, Mode } from "marketdata-sdk"; +import type { StockQuote } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + // Request live data, then inspect the `updated` timestamp. + const quotes: StockQuote[] = await client.stocks.quotes("AAPL", { mode: Mode.LIVE }); + const quote = quotes[0]; + + if (quote?.updated) { + // `updated` is a Unix timestamp in seconds. + const updatedTime = new Date(quote.updated * 1000); + const currentTime = new Date(); + + // Calculate the age of the data + const ageMinutes = (currentTime.getTime() - updatedTime.getTime()) / 1000 / 60; + + console.log(`Data timestamp: ${updatedTime}`); + console.log(`Current time: ${currentTime}`); + console.log(`Data age: ${ageMinutes.toFixed(1)} minutes`); + + // Determine data type + if (ageMinutes < 1) { + console.log("✅ Real-time data (less than 1 minute old)"); + } else if (ageMinutes < 20) { + console.log("⚠️ 15-minute delayed data"); + } else { + console.log("❌ Historical data (1 day old or more)"); + } } +} catch (error) { + console.error(error); } ``` @@ -342,56 +381,104 @@ test_realtime_data("AAPL") ``` - + + +```js title="testRealtimeData.js" +import { MarketDataClient, Mode } from "marketdata-sdk"; + +const client = new MarketDataClient(); -```javascript async function testRealtimeData(symbol = "AAPL") { - const response = await fetch( - `https://api.marketdata.app/v1/stocks/quotes/${symbol}/`, - { - headers: { - "Authorization": "Bearer YOUR_TOKEN" - } + try { + const quotes = await client.stocks.quotes(symbol, { mode: Mode.LIVE }); + const quote = quotes[0]; + + if (!quote?.updated) { + console.log("Warning: No timestamp in response"); + return false; } - ); - - const data = await response.json(); - - if (data.s !== "ok") { - console.log(`Error: ${data.errmsg || "Unknown error"}`); - return false; - } - - if (!data.updated) { - console.log("Warning: No timestamp in response"); + + // `updated` is a Unix timestamp in seconds. + const updatedTime = new Date(quote.updated * 1000); + const currentTime = new Date(); + + // Calculate age + const ageMinutes = (currentTime - updatedTime) / 1000 / 60; + + console.log(`Symbol: ${symbol}`); + console.log(`Data timestamp: ${updatedTime.toISOString()}`); + console.log(`Current time: ${currentTime.toISOString()}`); + console.log(`Data age: ${ageMinutes.toFixed(2)} minutes`); + + // Check if real-time (less than 1 minute old during market hours) + if (ageMinutes < 1) { + console.log("✅ Real-time data is working!"); + return true; + } else if (ageMinutes < 20) { + console.log("⚠️ Receiving 15-minute delayed data"); + console.log(" → Check if IEX agreement is signed and approved"); + return false; + } else { + console.log("❌ Receiving historical data (1 day old)"); + console.log(" → Check if profile is complete and agreements are signed"); + return false; + } + } catch (error) { + console.error(`Error: ${error.message}`); return false; } - - // Get timestamp - const updatedTimestamp = data.updated[0] * 1000; // Convert to milliseconds - const updatedTime = new Date(updatedTimestamp); - const currentTime = new Date(); - - // Calculate age - const ageSeconds = (currentTime - updatedTime) / 1000; - const ageMinutes = ageSeconds / 60; - - console.log(`Symbol: ${symbol}`); - console.log(`Data timestamp: ${updatedTime.toISOString()}`); - console.log(`Current time: ${currentTime.toISOString()}`); - console.log(`Data age: ${ageMinutes.toFixed(2)} minutes`); - - // Check if real-time (less than 1 minute old during market hours) - if (ageMinutes < 1) { - console.log("✅ Real-time data is working!"); - return true; - } else if (ageMinutes < 20) { - console.log("⚠️ Receiving 15-minute delayed data"); - console.log(" → Check if IEX agreement is signed and approved"); - return false; - } else { - console.log("❌ Receiving historical data (1 day old)"); - console.log(" → Check if profile is complete and agreements are signed"); +} + +// Test during market hours for best results +testRealtimeData("AAPL"); +``` + + + + +```typescript title="testRealtimeData.ts" +import { MarketDataClient, Mode } from "marketdata-sdk"; +import type { StockQuote } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +async function testRealtimeData(symbol: string = "AAPL"): Promise { + try { + const quotes: StockQuote[] = await client.stocks.quotes(symbol, { mode: Mode.LIVE }); + const quote = quotes[0]; + + if (!quote?.updated) { + console.log("Warning: No timestamp in response"); + return false; + } + + // `updated` is a Unix timestamp in seconds. + const updatedTime = new Date(quote.updated * 1000); + const currentTime = new Date(); + + // Calculate age + const ageMinutes = (currentTime.getTime() - updatedTime.getTime()) / 1000 / 60; + + console.log(`Symbol: ${symbol}`); + console.log(`Data timestamp: ${updatedTime.toISOString()}`); + console.log(`Current time: ${currentTime.toISOString()}`); + console.log(`Data age: ${ageMinutes.toFixed(2)} minutes`); + + // Check if real-time (less than 1 minute old during market hours) + if (ageMinutes < 1) { + console.log("✅ Real-time data is working!"); + return true; + } else if (ageMinutes < 20) { + console.log("⚠️ Receiving 15-minute delayed data"); + console.log(" → Check if IEX agreement is signed and approved"); + return false; + } else { + console.log("❌ Receiving historical data (1 day old)"); + console.log(" → Check if profile is complete and agreements are signed"); + return false; + } + } catch (error) { + console.error(`Error: ${(error as Error).message}`); return false; } } diff --git a/api/troubleshooting/service-outages.mdx b/api/troubleshooting/service-outages.mdx index 706d05b..29bccb1 100644 --- a/api/troubleshooting/service-outages.mdx +++ b/api/troubleshooting/service-outages.mdx @@ -33,50 +33,74 @@ This endpoint is ideal to allow for automatic switching between Market Data and ::: - + ```js title="status.js" -// Importing the required library -const axios = require('axios'); - -// URL to the status endpoint -const url = "https://api.marketdata.app/status/"; - -// Function to check the status of a specific endpoint -async function checkEndpointStatus(endpointPath) { - try { - const response = await axios.get(url); - const jsonData = response.data; - const index = jsonData.service.indexOf(endpointPath); - - if (index !== -1) { - return { - online: jsonData.online[index], - status: jsonData.status[index], - uptime30d: jsonData.uptimePct30d[index], - uptime90d: jsonData.uptimePct90d[index] - }; - } else { - return null; - } - } catch (error) { - console.error("Error fetching API status:", error); - return null; - } +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +// Look up the status of a specific endpoint in the API status response. +function findEndpointStatus(status, endpointPath) { + const index = status.service.indexOf(endpointPath); + if (index === -1) return null; + return { + online: status.online[index], + status: status.status[index], + uptime30d: status.uptimePct30d?.[index], + uptime90d: status.uptimePct90d?.[index], + }; } -// Checking the status of specific endpoints -async function checkStatuses() { - const stocksQuotes = await checkEndpointStatus("/v1/stocks/quotes/"); - const optionsChain = await checkEndpointStatus("/v1/options/chain/"); - const stocksCandles = await checkEndpointStatus("/v1/stocks/candles/"); +try { + const status = await client.utilities.status(); + + const stocksQuotes = findEndpointStatus(status, "/v1/stocks/quotes/"); + const optionsChain = findEndpointStatus(status, "/v1/options/chain/"); + const stocksCandles = findEndpointStatus(status, "/v1/stocks/candles/"); - console.log(`Stocks Quotes: ${stocksQuotes ? (stocksQuotes.online ? "Online" : "Offline") : "Not found"}`); - console.log(`Options Chain: ${optionsChain ? (optionsChain.online ? "Online" : "Offline") : "Not found"}`); - console.log(`Stocks Candles: ${stocksCandles ? (stocksCandles.online ? "Online" : "Offline") : "Not found"}`); + console.log(`Stocks Quotes: ${stocksQuotes ? (stocksQuotes.online ? "Online" : "Offline") : "Not found"}`); + console.log(`Options Chain: ${optionsChain ? (optionsChain.online ? "Online" : "Offline") : "Not found"}`); + console.log(`Stocks Candles: ${stocksCandles ? (stocksCandles.online ? "Online" : "Offline") : "Not found"}`); +} catch (error) { + console.error("Error fetching API status:", error); } +``` -checkStatuses(); + + + +```typescript title="status.ts" +import { MarketDataClient } from "marketdata-sdk"; +import type { ApiStatusResponse } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +// Look up the status of a specific endpoint in the API status response. +function findEndpointStatus(status: ApiStatusResponse, endpointPath: string) { + const index = status.service.indexOf(endpointPath); + if (index === -1) return null; + return { + online: status.online[index], + status: status.status[index], + uptime30d: status.uptimePct30d?.[index], + uptime90d: status.uptimePct90d?.[index], + }; +} + +try { + const status: ApiStatusResponse = await client.utilities.status(); + + const stocksQuotes = findEndpointStatus(status, "/v1/stocks/quotes/"); + const optionsChain = findEndpointStatus(status, "/v1/options/chain/"); + const stocksCandles = findEndpointStatus(status, "/v1/stocks/candles/"); + + console.log(`Stocks Quotes: ${stocksQuotes ? (stocksQuotes.online ? "Online" : "Offline") : "Not found"}`); + console.log(`Options Chain: ${optionsChain ? (optionsChain.online ? "Online" : "Offline") : "Not found"}`); + console.log(`Stocks Candles: ${stocksCandles ? (stocksCandles.online ? "Online" : "Offline") : "Not found"}`); +} catch (error) { + console.error("Error fetching API status:", error); +} ``` diff --git a/api/utilities/headers.mdx b/api/utilities/headers.mdx index bf429f0..ff63e52 100644 --- a/api/utilities/headers.mdx +++ b/api/utilities/headers.mdx @@ -29,13 +29,38 @@ GET **GET** [https://api.marketdata.app/headers/](https://api.marketdata.app/headers/) - + ```js title="headersCheck.js" -fetch("https://api.marketdata.app/headers/") - .then((res) => res.json()) - .then((json) => console.log(json)) - .catch((err) => console.log(err)); +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const h = await client.utilities.headers(); + console.log(h["user-agent"]); + console.log(h["accept-encoding"]); +} catch (error) { + console.error(error); +} +``` + + + + +```typescript title="headersCheck.ts" +import { MarketDataClient } from "marketdata-sdk"; +import type { HeadersResponse } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const h: HeadersResponse = await client.utilities.headers(); + console.log(h["user-agent"]); + console.log(h["accept-encoding"]); +} catch (error) { + console.error(error); +} ``` diff --git a/api/utilities/status.mdx b/api/utilities/status.mdx index ac2f318..265a3cf 100644 --- a/api/utilities/status.mdx +++ b/api/utilities/status.mdx @@ -31,13 +31,40 @@ GET **GET** [https://api.marketdata.app/status/](https://api.marketdata.app/status/) - + ```js title="statusCheck.js" -fetch("https://api.marketdata.app/status/") - .then((res) => res.json()) - .then((json) => console.log(json)) - .catch((err) => console.log(err)); +import { MarketDataClient } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const s = await client.utilities.status(); + for (let i = 0; i < s.service.length; i++) { + console.log(`${s.service[i]}: ${s.status[i]} (online=${s.online[i]})`); + } +} catch (error) { + console.error(error); +} +``` + + + + +```typescript title="statusCheck.ts" +import { MarketDataClient } from "marketdata-sdk"; +import type { ApiStatusResponse } from "marketdata-sdk"; + +const client = new MarketDataClient(); + +try { + const s: ApiStatusResponse = await client.utilities.status(); + for (let i = 0; i < s.service.length; i++) { + console.log(`${s.service[i]}: ${s.status[i]} (online=${s.online[i]})`); + } +} catch (error) { + console.error(error); +} ``` From 03412771b3c53268dc227cf87772925e845a41a8 Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Fri, 29 May 2026 13:18:02 -0300 Subject: [PATCH 27/29] docs(api): rename JS SDK import to @marketdata/sdk in api examples Follow-up to the JS SDK 1.0 publish: the api/ pages added in #152 used the pre-release `marketdata-sdk` package name. Swap them to the published `@marketdata/sdk` so the API reference matches the SDK docs. --- api/authentication.mdx | 6 +++--- api/funds/candles.mdx | 6 +++--- api/markets/status.mdx | 6 +++--- api/options/chain.mdx | 6 +++--- api/options/expirations.mdx | 6 +++--- api/options/lookup.mdx | 6 +++--- api/options/quotes.mdx | 6 +++--- api/stocks/bulkcandles.mdx | 4 ++-- api/stocks/candles.mdx | 6 +++--- api/stocks/earnings.mdx | 6 +++--- api/stocks/news.mdx | 6 +++--- api/stocks/prices.mdx | 12 ++++++------ api/stocks/quotes.mdx | 12 ++++++------ api/troubleshooting/logging.mdx | 6 +++--- api/troubleshooting/real-time-data.mdx | 12 ++++++------ api/troubleshooting/service-outages.mdx | 6 +++--- api/utilities/headers.mdx | 6 +++--- api/utilities/status.mdx | 6 +++--- 18 files changed, 62 insertions(+), 62 deletions(-) diff --git a/api/authentication.mdx b/api/authentication.mdx index 8c9c4f5..0f4077f 100644 --- a/api/authentication.mdx +++ b/api/authentication.mdx @@ -51,7 +51,7 @@ The curly braces around token are a placeholder for this example. Do not actuall ```js title="app.js" -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; // The SDK reads your token from the MARKETDATA_TOKEN environment variable. // Alternatively, pass it directly: new MarketDataClient({ token: "your_token_here" }) @@ -72,8 +72,8 @@ For more information about using the JavaScript SDK, see our [JavaScript SDK doc ```typescript title="app.ts" -import { MarketDataClient } from "marketdata-sdk"; -import type { StockQuote } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; +import type { StockQuote } from "@marketdata/sdk"; // The SDK reads your token from the MARKETDATA_TOKEN environment variable. // Alternatively, pass it directly: new MarketDataClient({ token: "your_token_here" }) diff --git a/api/funds/candles.mdx b/api/funds/candles.mdx index c590f84..83da93d 100644 --- a/api/funds/candles.mdx +++ b/api/funds/candles.mdx @@ -33,7 +33,7 @@ GET ```js title="fundCandles.js" -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -52,8 +52,8 @@ try { ```typescript title="fundCandles.ts" -import { MarketDataClient } from "marketdata-sdk"; -import type { FundsCandle } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; +import type { FundsCandle } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/api/markets/status.mdx b/api/markets/status.mdx index 9d2ee03..408dc2d 100644 --- a/api/markets/status.mdx +++ b/api/markets/status.mdx @@ -29,7 +29,7 @@ GET ```js title="app.js" -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -50,8 +50,8 @@ try { ```typescript title="app.ts" -import { MarketDataClient } from "marketdata-sdk"; -import type { MarketStatusResponse } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; +import type { MarketStatusResponse } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/api/options/chain.mdx b/api/options/chain.mdx index e84b8b2..f7db1f7 100644 --- a/api/options/chain.mdx +++ b/api/options/chain.mdx @@ -25,7 +25,7 @@ GET https://api.marketdata.app/v1/options/chain/{underlyingSymbol}/ ```js title="app.js" -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -41,8 +41,8 @@ try { ```typescript title="app.ts" -import { MarketDataClient } from "marketdata-sdk"; -import type { OptionsChain } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; +import type { OptionsChain } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/api/options/expirations.mdx b/api/options/expirations.mdx index 3d122db..7e30e51 100644 --- a/api/options/expirations.mdx +++ b/api/options/expirations.mdx @@ -27,7 +27,7 @@ GET ```js title="app.js" -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -46,8 +46,8 @@ try { ```typescript title="app.ts" -import { MarketDataClient } from "marketdata-sdk"; -import type { OptionsExpirationsResponse } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; +import type { OptionsExpirationsResponse } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/api/options/lookup.mdx b/api/options/lookup.mdx index f71c836..8cd8b89 100644 --- a/api/options/lookup.mdx +++ b/api/options/lookup.mdx @@ -27,7 +27,7 @@ GET ```js title="app.js" -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -43,8 +43,8 @@ try { ```typescript title="app.ts" -import { MarketDataClient } from "marketdata-sdk"; -import type { OptionsLookupResponse } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; +import type { OptionsLookupResponse } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/api/options/quotes.mdx b/api/options/quotes.mdx index ef195eb..43dd4f0 100644 --- a/api/options/quotes.mdx +++ b/api/options/quotes.mdx @@ -31,7 +31,7 @@ GET ```js title="app.js" -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -50,8 +50,8 @@ try { ```typescript title="app.ts" -import { MarketDataClient } from "marketdata-sdk"; -import type { OptionsQuotes } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; +import type { OptionsQuotes } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/api/stocks/bulkcandles.mdx b/api/stocks/bulkcandles.mdx index 6021ce4..1565344 100644 --- a/api/stocks/bulkcandles.mdx +++ b/api/stocks/bulkcandles.mdx @@ -27,7 +27,7 @@ GET :::note -The JavaScript/TypeScript SDK does not yet provide a method for the bulk candles endpoint, so these examples use the raw `fetch()` API. They will be replaced with `marketdata-sdk` examples once bulk support is added. +The JavaScript/TypeScript SDK does not yet provide a method for the bulk candles endpoint, so these examples use the raw `fetch()` API. They will be replaced with `@marketdata/sdk` examples once bulk support is added. ::: ```js title="app.js" @@ -47,7 +47,7 @@ fetch( :::note -The JavaScript/TypeScript SDK does not yet provide a method for the bulk candles endpoint, so these examples use the raw `fetch()` API. They will be replaced with `marketdata-sdk` examples once bulk support is added. +The JavaScript/TypeScript SDK does not yet provide a method for the bulk candles endpoint, so these examples use the raw `fetch()` API. They will be replaced with `@marketdata/sdk` examples once bulk support is added. ::: ```typescript title="app.ts" diff --git a/api/stocks/candles.mdx b/api/stocks/candles.mdx index f5ca209..6f1d209 100644 --- a/api/stocks/candles.mdx +++ b/api/stocks/candles.mdx @@ -27,7 +27,7 @@ GET ```js title="app.js" -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -46,8 +46,8 @@ try { ```typescript title="app.ts" -import { MarketDataClient } from "marketdata-sdk"; -import type { StockCandle } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; +import type { StockCandle } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/api/stocks/earnings.mdx b/api/stocks/earnings.mdx index c927880..357d923 100644 --- a/api/stocks/earnings.mdx +++ b/api/stocks/earnings.mdx @@ -32,7 +32,7 @@ GET ```js title="app.js" -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -53,8 +53,8 @@ try { ```typescript title="app.ts" -import { MarketDataClient } from "marketdata-sdk"; -import type { StockEarningsResponse } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; +import type { StockEarningsResponse } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/api/stocks/news.mdx b/api/stocks/news.mdx index 2c9f4dd..cc2381e 100644 --- a/api/stocks/news.mdx +++ b/api/stocks/news.mdx @@ -36,7 +36,7 @@ GET ```js title="app.js" -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -54,8 +54,8 @@ try { ```typescript title="app.ts" -import { MarketDataClient } from "marketdata-sdk"; -import type { StockNews } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; +import type { StockNews } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/api/stocks/prices.mdx b/api/stocks/prices.mdx index 7e98471..b399af4 100644 --- a/api/stocks/prices.mdx +++ b/api/stocks/prices.mdx @@ -39,7 +39,7 @@ GET ```js title="app.js" -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -55,8 +55,8 @@ try { ```typescript title="app.ts" -import { MarketDataClient } from "marketdata-sdk"; -import type { StockPrice } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; +import type { StockPrice } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -106,7 +106,7 @@ echo $prices; ```js title="app.js" -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -124,8 +124,8 @@ try { ```typescript title="app.ts" -import { MarketDataClient } from "marketdata-sdk"; -import type { StockPrice } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; +import type { StockPrice } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/api/stocks/quotes.mdx b/api/stocks/quotes.mdx index e9c4dad..4185986 100644 --- a/api/stocks/quotes.mdx +++ b/api/stocks/quotes.mdx @@ -35,7 +35,7 @@ GET ```js title="app.js" -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -51,8 +51,8 @@ try { ```typescript title="app.ts" -import { MarketDataClient } from "marketdata-sdk"; -import type { StockQuote } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; +import type { StockQuote } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -125,7 +125,7 @@ echo $quote; ```js title="app.js" -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -143,8 +143,8 @@ try { ```typescript title="app.ts" -import { MarketDataClient } from "marketdata-sdk"; -import type { StockQuote } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; +import type { StockQuote } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/api/troubleshooting/logging.mdx b/api/troubleshooting/logging.mdx index 926c9dc..19265a0 100644 --- a/api/troubleshooting/logging.mdx +++ b/api/troubleshooting/logging.mdx @@ -22,7 +22,7 @@ When logging errors, the log should include the exact request made, Market Data' ```js title="logger.js" -import { MarketDataClient, DefaultLogger, LogLevel } from "marketdata-sdk"; +import { MarketDataClient, DefaultLogger, LogLevel } from "@marketdata/sdk"; // Enable DEBUG-level logging to capture full request/response diagnostics // (including the CF-Ray header) for every API call. @@ -44,8 +44,8 @@ try { ```typescript title="logger.ts" -import { MarketDataClient, DefaultLogger, LogLevel } from "marketdata-sdk"; -import type { StockQuote } from "marketdata-sdk"; +import { MarketDataClient, DefaultLogger, LogLevel } from "@marketdata/sdk"; +import type { StockQuote } from "@marketdata/sdk"; // Enable DEBUG-level logging to capture full request/response diagnostics // (including the CF-Ray header) for every API call. diff --git a/api/troubleshooting/real-time-data.mdx b/api/troubleshooting/real-time-data.mdx index 0799ca8..d17e1f3 100644 --- a/api/troubleshooting/real-time-data.mdx +++ b/api/troubleshooting/real-time-data.mdx @@ -76,7 +76,7 @@ elif isinstance(quotes, dict) and quotes.get("s") == "ok" and "updated" in quote ```js title="checkDataAge.js" -import { MarketDataClient, Mode } from "marketdata-sdk"; +import { MarketDataClient, Mode } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -115,8 +115,8 @@ try { ```typescript title="checkDataAge.ts" -import { MarketDataClient, Mode } from "marketdata-sdk"; -import type { StockQuote } from "marketdata-sdk"; +import { MarketDataClient, Mode } from "@marketdata/sdk"; +import type { StockQuote } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -384,7 +384,7 @@ test_realtime_data("AAPL") ```js title="testRealtimeData.js" -import { MarketDataClient, Mode } from "marketdata-sdk"; +import { MarketDataClient, Mode } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -437,8 +437,8 @@ testRealtimeData("AAPL"); ```typescript title="testRealtimeData.ts" -import { MarketDataClient, Mode } from "marketdata-sdk"; -import type { StockQuote } from "marketdata-sdk"; +import { MarketDataClient, Mode } from "@marketdata/sdk"; +import type { StockQuote } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/api/troubleshooting/service-outages.mdx b/api/troubleshooting/service-outages.mdx index 29bccb1..e95d736 100644 --- a/api/troubleshooting/service-outages.mdx +++ b/api/troubleshooting/service-outages.mdx @@ -36,7 +36,7 @@ This endpoint is ideal to allow for automatic switching between Market Data and ```js title="status.js" -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -71,8 +71,8 @@ try { ```typescript title="status.ts" -import { MarketDataClient } from "marketdata-sdk"; -import type { ApiStatusResponse } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; +import type { ApiStatusResponse } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/api/utilities/headers.mdx b/api/utilities/headers.mdx index ff63e52..3b2b382 100644 --- a/api/utilities/headers.mdx +++ b/api/utilities/headers.mdx @@ -32,7 +32,7 @@ GET ```js title="headersCheck.js" -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -49,8 +49,8 @@ try { ```typescript title="headersCheck.ts" -import { MarketDataClient } from "marketdata-sdk"; -import type { HeadersResponse } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; +import type { HeadersResponse } from "@marketdata/sdk"; const client = new MarketDataClient(); diff --git a/api/utilities/status.mdx b/api/utilities/status.mdx index 265a3cf..4543f12 100644 --- a/api/utilities/status.mdx +++ b/api/utilities/status.mdx @@ -34,7 +34,7 @@ GET ```js title="statusCheck.js" -import { MarketDataClient } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); @@ -52,8 +52,8 @@ try { ```typescript title="statusCheck.ts" -import { MarketDataClient } from "marketdata-sdk"; -import type { ApiStatusResponse } from "marketdata-sdk"; +import { MarketDataClient } from "@marketdata/sdk"; +import type { ApiStatusResponse } from "@marketdata/sdk"; const client = new MarketDataClient(); From e676623e4d05b65ac381f6e08a9719c4de9536c6 Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Fri, 29 May 2026 13:32:28 -0300 Subject: [PATCH 28/29] docs(sdk): update JS SDK User-Agent format to marketdata-sdk-js The SDK now emits `marketdata-sdk-js/{version}` instead of `marketdata-sdk-javascript/{version}`. Update the client notes and the utilities.headers() example to match. --- sdk/js/client.mdx | 2 +- sdk/js/utilities/headers.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/js/client.mdx b/sdk/js/client.mdx index 4fca59c..7dce85b 100644 --- a/sdk/js/client.mdx +++ b/sdk/js/client.mdx @@ -94,7 +94,7 @@ Creates and configures a new `MarketDataClient` instance. This initializes the c #### Notes -- The client sets a `User-Agent` header of the form `marketdata-sdk-javascript/{version}` (e.g. `marketdata-sdk-javascript/1.0.0`). +- The client sets a `User-Agent` header of the form `marketdata-sdk-js/{version}` (e.g. `marketdata-sdk-js/1.0.0`). - All authenticated requests include an `Authorization: Bearer {token}` header. - The client reuses a single underlying `fetch` client, which benefits from Node's global connection pooling, and enforces a global 50-request concurrency pool across every endpoint. - Every request has a 99-second timeout; a timed-out fetch rejects with `NetworkError`. diff --git a/sdk/js/utilities/headers.mdx b/sdk/js/utilities/headers.mdx index 2c4cac1..6bee76d 100644 --- a/sdk/js/utilities/headers.mdx +++ b/sdk/js/utilities/headers.mdx @@ -42,7 +42,7 @@ const client = new MarketDataClient(); try { const h = await client.utilities.headers(); - console.log(h["user-agent"]); // "marketdata-sdk-javascript/1.0.0" + console.log(h["user-agent"]); // "marketdata-sdk-js/1.0.0" console.log(h["accept-encoding"]); } catch (error) { console.error(error); From 06e42a9b66147e8101bd7cc1c5d6e240d6b84f80 Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Fri, 29 May 2026 14:55:13 -0300 Subject: [PATCH 29/29] chore(docs): fix markdown table alignment Auto-fix from scripts/fix-table-alignment.js. Resolves the table-alignment CI check on PR #153. --- account/data-freshness.md | 198 +++++++++++++++++------------------ sdk/js/client.mdx | 24 ++--- sdk/js/utilities/headers.mdx | 8 +- sdk/js/utilities/status.mdx | 8 +- 4 files changed, 119 insertions(+), 119 deletions(-) diff --git a/account/data-freshness.md b/account/data-freshness.md index c410425..1d4cf05 100644 --- a/account/data-freshness.md +++ b/account/data-freshness.md @@ -30,141 +30,141 @@ The tables below show the freshness category for every API endpoint, by plan. Re All pricing data is Historical (24-hour delayed). Metadata and computed-index endpoints remain Real-time. -| Endpoint | Freshness | Notes | -|----------|-----------|-------| -| `/v1/stocks/quotes/` | Historical | | -| `/v1/stocks/candles/` | Historical | | -| `/v1/stocks/bulkcandles/` | Historical | | -| `/v1/stocks/prices/` | Historical | | -| `/v1/options/quotes/` | Historical | | -| `/v1/options/chain/` | Historical | | -| `/v1/options/strikes/` | Real-time | Contract metadata, no pricing | -| `/v1/options/expirations/` | Real-time | Contract metadata | -| `/v1/options/lookup/` | Real-time | Contract metadata | +| Endpoint | Freshness | Notes | +|----------------------------|------------|-----------------------------------| +| `/v1/stocks/quotes/` | Historical | | +| `/v1/stocks/candles/` | Historical | | +| `/v1/stocks/bulkcandles/` | Historical | | +| `/v1/stocks/prices/` | Historical | | +| `/v1/options/quotes/` | Historical | | +| `/v1/options/chain/` | Historical | | +| `/v1/options/strikes/` | Real-time | Contract metadata, no pricing | +| `/v1/options/expirations/` | Real-time | Contract metadata | +| `/v1/options/lookup/` | Real-time | Contract metadata | | `/v1/indices/quotes/` | Real-time | Indices are computed continuously | | `/v1/indices/candles/` | Real-time | Indices are computed continuously | -| `/v1/markets/status/` | Real-time | Calendar metadata | -| `/v1/funds/*` | See note | Pending product confirmation | +| `/v1/markets/status/` | Real-time | Calendar metadata | +| `/v1/funds/*` | See note | Pending product confirmation | ### Starter Trial Same as Free Forever — all pricing data is Historical (24-hour delayed). -| Endpoint | Freshness | Notes | -|----------|-----------|-------| -| `/v1/stocks/quotes/` | Historical | | -| `/v1/stocks/candles/` | Historical | | -| `/v1/stocks/bulkcandles/` | Historical | | -| `/v1/stocks/prices/` | Historical | | -| `/v1/options/quotes/` | Historical | | -| `/v1/options/chain/` | Historical | | -| `/v1/options/strikes/` | Real-time | Contract metadata, no pricing | -| `/v1/options/expirations/` | Real-time | Contract metadata | -| `/v1/options/lookup/` | Real-time | Contract metadata | +| Endpoint | Freshness | Notes | +|----------------------------|------------|-----------------------------------| +| `/v1/stocks/quotes/` | Historical | | +| `/v1/stocks/candles/` | Historical | | +| `/v1/stocks/bulkcandles/` | Historical | | +| `/v1/stocks/prices/` | Historical | | +| `/v1/options/quotes/` | Historical | | +| `/v1/options/chain/` | Historical | | +| `/v1/options/strikes/` | Real-time | Contract metadata, no pricing | +| `/v1/options/expirations/` | Real-time | Contract metadata | +| `/v1/options/lookup/` | Real-time | Contract metadata | | `/v1/indices/quotes/` | Real-time | Indices are computed continuously | | `/v1/indices/candles/` | Real-time | Indices are computed continuously | -| `/v1/markets/status/` | Real-time | Calendar metadata | -| `/v1/funds/*` | See note | Pending product confirmation | +| `/v1/markets/status/` | Real-time | Calendar metadata | +| `/v1/funds/*` | See note | Pending product confirmation | ### Trader Trial Same as Starter Trial — all pricing data is Historical (24-hour delayed). -| Endpoint | Freshness | Notes | -|----------|-----------|-------| -| `/v1/stocks/quotes/` | Historical | | -| `/v1/stocks/candles/` | Historical | | -| `/v1/stocks/bulkcandles/` | Historical | | -| `/v1/stocks/prices/` | Historical | | -| `/v1/options/quotes/` | Historical | | -| `/v1/options/chain/` | Historical | | -| `/v1/options/strikes/` | Real-time | Contract metadata, no pricing | -| `/v1/options/expirations/` | Real-time | Contract metadata | -| `/v1/options/lookup/` | Real-time | Contract metadata | +| Endpoint | Freshness | Notes | +|----------------------------|------------|-----------------------------------| +| `/v1/stocks/quotes/` | Historical | | +| `/v1/stocks/candles/` | Historical | | +| `/v1/stocks/bulkcandles/` | Historical | | +| `/v1/stocks/prices/` | Historical | | +| `/v1/options/quotes/` | Historical | | +| `/v1/options/chain/` | Historical | | +| `/v1/options/strikes/` | Real-time | Contract metadata, no pricing | +| `/v1/options/expirations/` | Real-time | Contract metadata | +| `/v1/options/lookup/` | Real-time | Contract metadata | | `/v1/indices/quotes/` | Real-time | Indices are computed continuously | | `/v1/indices/candles/` | Real-time | Indices are computed continuously | -| `/v1/markets/status/` | Real-time | Calendar metadata | -| `/v1/funds/*` | See note | Pending product confirmation | +| `/v1/markets/status/` | Real-time | Calendar metadata | +| `/v1/funds/*` | See note | Pending product confirmation | ### Starter Real-time stock data, 15-minute Delayed options data. -| Endpoint | Freshness | Notes | -|----------|-----------|-------| -| `/v1/stocks/quotes/` | Real-time | IEX entitlement | -| `/v1/stocks/candles/` | Real-time | UTP entitlement may impose 15-min delay on intraday candles — see footnote | -| `/v1/stocks/bulkcandles/` | Real-time | Same as `/candles/` | -| `/v1/stocks/prices/` | Real-time | | -| `/v1/options/quotes/` | Delayed | 15 minutes | -| `/v1/options/chain/` | Delayed | 15 minutes | -| `/v1/options/strikes/` | Real-time | Contract metadata | -| `/v1/options/expirations/` | Real-time | Contract metadata | -| `/v1/options/lookup/` | Real-time | Contract metadata | -| `/v1/indices/quotes/` | Real-time | | -| `/v1/indices/candles/` | Real-time | | -| `/v1/markets/status/` | Real-time | Calendar metadata | -| `/v1/funds/*` | See note | Pending product confirmation | +| Endpoint | Freshness | Notes | +|----------------------------|-----------|----------------------------------------------------------------------------| +| `/v1/stocks/quotes/` | Real-time | IEX entitlement | +| `/v1/stocks/candles/` | Real-time | UTP entitlement may impose 15-min delay on intraday candles — see footnote | +| `/v1/stocks/bulkcandles/` | Real-time | Same as `/candles/` | +| `/v1/stocks/prices/` | Real-time | | +| `/v1/options/quotes/` | Delayed | 15 minutes | +| `/v1/options/chain/` | Delayed | 15 minutes | +| `/v1/options/strikes/` | Real-time | Contract metadata | +| `/v1/options/expirations/` | Real-time | Contract metadata | +| `/v1/options/lookup/` | Real-time | Contract metadata | +| `/v1/indices/quotes/` | Real-time | | +| `/v1/indices/candles/` | Real-time | | +| `/v1/markets/status/` | Real-time | Calendar metadata | +| `/v1/funds/*` | See note | Pending product confirmation | ### Trader Real-time data for both stocks and options. -| Endpoint | Freshness | Notes | -|----------|-----------|-------| -| `/v1/stocks/quotes/` | Real-time | | -| `/v1/stocks/candles/` | Real-time | | -| `/v1/stocks/bulkcandles/` | Real-time | | -| `/v1/stocks/prices/` | Real-time | | -| `/v1/options/quotes/` | Real-time | OPRA entitlement | -| `/v1/options/chain/` | Real-time | OPRA entitlement | -| `/v1/options/strikes/` | Real-time | Contract metadata | -| `/v1/options/expirations/` | Real-time | Contract metadata | -| `/v1/options/lookup/` | Real-time | Contract metadata | -| `/v1/indices/quotes/` | Real-time | | -| `/v1/indices/candles/` | Real-time | | -| `/v1/markets/status/` | Real-time | Calendar metadata | -| `/v1/funds/*` | See note | Pending product confirmation | +| Endpoint | Freshness | Notes | +|----------------------------|-----------|------------------------------| +| `/v1/stocks/quotes/` | Real-time | | +| `/v1/stocks/candles/` | Real-time | | +| `/v1/stocks/bulkcandles/` | Real-time | | +| `/v1/stocks/prices/` | Real-time | | +| `/v1/options/quotes/` | Real-time | OPRA entitlement | +| `/v1/options/chain/` | Real-time | OPRA entitlement | +| `/v1/options/strikes/` | Real-time | Contract metadata | +| `/v1/options/expirations/` | Real-time | Contract metadata | +| `/v1/options/lookup/` | Real-time | Contract metadata | +| `/v1/indices/quotes/` | Real-time | | +| `/v1/indices/candles/` | Real-time | | +| `/v1/markets/status/` | Real-time | Calendar metadata | +| `/v1/funds/*` | See note | Pending product confirmation | ### Quant Real-time data for both stocks and options. Same freshness profile as Trader. -| Endpoint | Freshness | Notes | -|----------|-----------|-------| -| `/v1/stocks/quotes/` | Real-time | | -| `/v1/stocks/candles/` | Real-time | | -| `/v1/stocks/bulkcandles/` | Real-time | | -| `/v1/stocks/prices/` | Real-time | | -| `/v1/options/quotes/` | Real-time | OPRA entitlement | -| `/v1/options/chain/` | Real-time | OPRA entitlement | -| `/v1/options/strikes/` | Real-time | Contract metadata | -| `/v1/options/expirations/` | Real-time | Contract metadata | -| `/v1/options/lookup/` | Real-time | Contract metadata | -| `/v1/indices/quotes/` | Real-time | | -| `/v1/indices/candles/` | Real-time | | -| `/v1/markets/status/` | Real-time | Calendar metadata | -| `/v1/funds/*` | See note | Pending product confirmation | +| Endpoint | Freshness | Notes | +|----------------------------|-----------|------------------------------| +| `/v1/stocks/quotes/` | Real-time | | +| `/v1/stocks/candles/` | Real-time | | +| `/v1/stocks/bulkcandles/` | Real-time | | +| `/v1/stocks/prices/` | Real-time | | +| `/v1/options/quotes/` | Real-time | OPRA entitlement | +| `/v1/options/chain/` | Real-time | OPRA entitlement | +| `/v1/options/strikes/` | Real-time | Contract metadata | +| `/v1/options/expirations/` | Real-time | Contract metadata | +| `/v1/options/lookup/` | Real-time | Contract metadata | +| `/v1/indices/quotes/` | Real-time | | +| `/v1/indices/candles/` | Real-time | | +| `/v1/markets/status/` | Real-time | Calendar metadata | +| `/v1/funds/*` | See note | Pending product confirmation | ### Prime Real-time data for both stocks and options. Same freshness profile as Trader and Quant. -| Endpoint | Freshness | Notes | -|----------|-----------|-------| -| `/v1/stocks/quotes/` | Real-time | | -| `/v1/stocks/candles/` | Real-time | | -| `/v1/stocks/bulkcandles/` | Real-time | | -| `/v1/stocks/prices/` | Real-time | | -| `/v1/options/quotes/` | Real-time | OPRA entitlement | -| `/v1/options/chain/` | Real-time | OPRA entitlement | -| `/v1/options/strikes/` | Real-time | Contract metadata | -| `/v1/options/expirations/` | Real-time | Contract metadata | -| `/v1/options/lookup/` | Real-time | Contract metadata | -| `/v1/indices/quotes/` | Real-time | | -| `/v1/indices/candles/` | Real-time | | -| `/v1/markets/status/` | Real-time | Calendar metadata | -| `/v1/funds/*` | See note | Pending product confirmation | +| Endpoint | Freshness | Notes | +|----------------------------|-----------|------------------------------| +| `/v1/stocks/quotes/` | Real-time | | +| `/v1/stocks/candles/` | Real-time | | +| `/v1/stocks/bulkcandles/` | Real-time | | +| `/v1/stocks/prices/` | Real-time | | +| `/v1/options/quotes/` | Real-time | OPRA entitlement | +| `/v1/options/chain/` | Real-time | OPRA entitlement | +| `/v1/options/strikes/` | Real-time | Contract metadata | +| `/v1/options/expirations/` | Real-time | Contract metadata | +| `/v1/options/lookup/` | Real-time | Contract metadata | +| `/v1/indices/quotes/` | Real-time | | +| `/v1/indices/candles/` | Real-time | | +| `/v1/markets/status/` | Real-time | Calendar metadata | +| `/v1/funds/*` | See note | Pending product confirmation | ## Notes diff --git a/sdk/js/client.mdx b/sdk/js/client.mdx index 7dce85b..2fe87b7 100644 --- a/sdk/js/client.mdx +++ b/sdk/js/client.mdx @@ -209,18 +209,18 @@ try { ### Error classes -| Class | When | -|---|---| -| `AuthenticationError` | 401 — token missing, invalid, or expired | -| `BadRequestError` | 400 — malformed request or invalid parameters | -| `NotFoundError` | 404 — exported but not thrown by default. The SDK translates 404 into an empty response with `no_data: true`; see [no-data handling](#NoData). The one exception is `/user/`, which opts into the throw path. | -| `PaymentRequiredError` | 402 — request denied by your plan (data older than your plan allows, premium endpoint on Free/Trial, or `mode=cached` on Free/Trial) | -| `ForbiddenError` | 403 — access denied. Typically the multi-IP block: the account is temporarily locked when used from more than one IP. Wait ~5 minutes and retry. | -| `RateLimitError` | 429 — per-minute/day rate limit exceeded | -| `ServerError` | 5xx — retriable, server-side failure | -| `NetworkError` | Transport failure: DNS, connection, TLS, or 99s timeout | -| `ParseError` | Response body failed schema or JSON parsing | -| `ValidationError` | Client-side input validation failure (before the network call) | +| Class | When | +|------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `AuthenticationError` | 401 — token missing, invalid, or expired | +| `BadRequestError` | 400 — malformed request or invalid parameters | +| `NotFoundError` | 404 — exported but not thrown by default. The SDK translates 404 into an empty response with `no_data: true`; see [no-data handling](#NoData). The one exception is `/user/`, which opts into the throw path. | +| `PaymentRequiredError` | 402 — request denied by your plan (data older than your plan allows, premium endpoint on Free/Trial, or `mode=cached` on Free/Trial) | +| `ForbiddenError` | 403 — access denied. Typically the multi-IP block: the account is temporarily locked when used from more than one IP. Wait ~5 minutes and retry. | +| `RateLimitError` | 429 — per-minute/day rate limit exceeded | +| `ServerError` | 5xx — retriable, server-side failure | +| `NetworkError` | Transport failure: DNS, connection, TLS, or 99s timeout | +| `ParseError` | Response body failed schema or JSON parsing | +| `ValidationError` | Client-side input validation failure (before the network call) | ### No-data responses diff --git a/sdk/js/utilities/headers.mdx b/sdk/js/utilities/headers.mdx index 6bee76d..4ba23aa 100644 --- a/sdk/js/utilities/headers.mdx +++ b/sdk/js/utilities/headers.mdx @@ -14,10 +14,10 @@ The `Authorization` header is redacted server-side before the response is return Use the `headers()` method on the `utilities` resource. -| Output Format | Result Payload | Description | -|------------------------|--------------------|----------------------------------------------| -| **internal** (default) | `HeadersResponse` | Map of header name to value as the API saw it.| -| **json** | Raw JSON object | The raw response as returned by the API. | +| Output Format | Result Payload | Description | +|------------------------|-------------------|------------------------------------------------| +| **internal** (default) | `HeadersResponse` | Map of header name to value as the API saw it. | +| **json** | Raw JSON object | The raw response as returned by the API. | ## headers diff --git a/sdk/js/utilities/status.mdx b/sdk/js/utilities/status.mdx index 4c8e7c8..6ea0c00 100644 --- a/sdk/js/utilities/status.mdx +++ b/sdk/js/utilities/status.mdx @@ -12,10 +12,10 @@ Retrieve service-level health information for every Market Data endpoint. No aut Use the `status()` method on the `utilities` resource to fetch the per-service availability snapshot. -| Output Format | Result Payload | Description | -|------------------------|----------------------|------------------------------------------------------| -| **internal** (default) | `ApiStatusResponse` | Decoded status snapshot with parallel arrays. | -| **json** | Raw JSON object | The raw response as returned by the API. | +| Output Format | Result Payload | Description | +|------------------------|---------------------|-----------------------------------------------| +| **internal** (default) | `ApiStatusResponse` | Decoded status snapshot with parallel arrays. | +| **json** | Raw JSON object | The raw response as returned by the API. | ## status