# Market Data Account Source: https://www.marketdata.app/docs/account/index.md Manage your Market Data Account through the [Customer Dashboard](https://www.marketdata.app/dashboard/). --- # Billing Portal Source: https://www.marketdata.app/docs/account/billing-portal/index.md For heightened security, Market Data uses an independent third-party billing system to process your transactions. We do not store your credit card information and we do not have access to your billing details. Customers can manage their own subscriptions and billing details via the [Billing Portal](https://cc.payproglobal.com/Customer/Account/Login). ## How to Access the Billing Portal for the First Time > [!NOTE] > The Billing Portal is only available to **paid customers**. A billing portal account is created automatically when you make your first purchase. If you have a free or trial account and want to subscribe, you can do so directly from the Market Data [Customer Dashboard](https://www.marketdata.app/dashboard/). To access your billing information for the first time, follow these steps: 1. Visit the [Billing Portal](https://cc.payproglobal.com/Customer/Account/Login). 2. Since it's your first time, you'll need to set up a password. Click on the "Forgot Password" link to initiate this process. Please note that the [Billing Portal](https://cc.payproglobal.com/Customer/Account/Login) operates independently from your Market Data account, meaning your Market Data credentials will not work here. 3. After setting your password, log in with your new credentials. 4. You will now be able to view and manage your billing details and subscriptions. ## Actions You Can Take in the Billing Portal The [Billing Portal](https://cc.payproglobal.com/Customer/Account/Login) allows you to take any of the following actions: - View your order details - Generate a PDF invoice for your subscription charges - Update your credit card information - Add a back-up credit card - Request billing support - Cancel your subscription ## Billing Support You can also request assistance with billing-related issues after logging-in to the [Billing Portal](https://cc.payproglobal.com/Customer/Account/Login). Customers request assistance via: - Support ticket - Online chat - Schedule a call-back ### Limitations of Billing Support Please be aware that billing support staff can only assist you with billing-related questions. For technical issues, API troubleshooting, assistance with Sheets formulas, etc., please open a support ticket in the Market Data [Customer Dashboard](https://www.marketdata.app/dashboard/). Billing staff cannot assist with custom quotes, student discounts, or other special billing requests. For these types of inquiries, please contact our sales team via the Market Data [Customer Dashboard](https://www.marketdata.app/dashboard/). --- # Cancellations Source: https://www.marketdata.app/docs/account/cancellations/index.md Your Market Data account can be cancelled at any time, directly from your member page. 1. Login and go to your [member page](https://dashboard.marketdata.app/marketdata/member). 2. Find your subscription. A **cancel** button appears on the subscription itself, next to **upgrade**. 3. Click **cancel** and confirm. 4. You will receive a confirmation email once your subscription has been cancelled. > [!TIP] > If your decision to cancel is due to technical issues, missing features, or any other reason, we invite you to first open a support ticket in the [Customer Dashboard](https://www.marketdata.app/dashboard/) before cancelling. Our team is here to help and we will do everything we can to resolve your issue and make sure you're happy with Market Data. ## Cancelling from the Billing Portal You can also cancel from the Billing Portal, though this requires a separate login. Use it if you cannot reach your member page. 1. Login to the [Billing Portal](https://cc.payproglobal.com/Customer/Account/Login). 2. If you have never logged-in to the billing portal before, [see our support page](https://www.marketdata.app/docs/account/billing-portal) on how to set-up access for the first time. 3. Navigate to the "Subscriptions" tab on the left sidebar. 4. Select your Market Data subscription and cancel it. 5. You will receive a confirmation email once your subscription has been cancelled. ## Unused Time When you cancel a paid subscription, you will continue to have access to Market Data until your account's renewal date. Feel free to continue utilizing Market Data's services until that time. After the renewal date passes, you will not lose access. Your account will immediately revert to a `Free Forever` subscription, allowing you to continue making 100 API calls or 100 Sheets formulas per day. ## Restarting a Cancelled Subscription If your subscription has not yet expired, you may be able to restart it. Open a Billing ticket in the [Customer Dashboard](https://www.marketdata.app/dashboard/) and we'll reverse your cancellation. This will allow billing to continue and ensure uninterrupted access to Market Data. If Market Data pricing has changed, restarting an unexpired subscription will allow you to maintain your previous plan's pricing. If your subscription has already expired, it cannot be restarted. Please make a new purchase and a new subscription will be created for your account. It is important to note that once a subscription has expired, any new subscription will always be made using the current pricing. --- # Data Freshness Source: https://www.marketdata.app/docs/account/data-freshness/index.md 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, and assume non-professional status. Real-time exchange data is never available to professional subscribers — see the [Notes](#notes) below. For the underlying entitlement model, see [Exchange Entitlements](https://www.marketdata.app/docs/account/entitlements). > [!NOTE] > **[Real-time stock pricing comes from `/v1/stocks/prices/`]** > > [`/v1/stocks/prices/`](https://www.marketdata.app/docs/api/stocks/prices) delivers Real-time stock prices with no delay on every paid plan. `/v1/stocks/quotes/`, `/v1/stocks/candles/`, and `/v1/stocks/bulkcandles/` carry the standard 15-minute exchange delay. Point your application at `/v1/stocks/prices/` when you need the current price. ### Free Forever All pricing data is Historical (24-hour delayed). Metadata 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/expirations/` | Real-time | Contract metadata | | `/v1/options/lookup/` | Real-time | Contract metadata | | `/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/expirations/` | Real-time | Contract metadata | | `/v1/options/lookup/` | Real-time | Contract metadata | | `/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/expirations/` | Real-time | Contract metadata | | `/v1/options/lookup/` | Real-time | Contract metadata | | `/v1/markets/status/` | Real-time | Calendar metadata | | `/v1/funds/*` | See note | Pending product confirmation | ### Starter Real-time stock prices. Stock quotes, stock candles, and options data carry the 15-minute exchange delay. | Endpoint | Freshness | Notes | |----------------------------|-------------|------------------------------| | `/v1/stocks/quotes/` | 15m delayed | | | `/v1/stocks/candles/` | 15m delayed | | | `/v1/stocks/bulkcandles/` | 15m delayed | | | `/v1/stocks/prices/` | Real-time | | | `/v1/options/quotes/` | 15m delayed | | | `/v1/options/chain/` | 15m delayed | | | `/v1/options/expirations/` | Real-time | Contract metadata | | `/v1/options/lookup/` | Real-time | Contract metadata | | `/v1/markets/status/` | Real-time | Calendar metadata | | `/v1/funds/*` | See note | Pending product confirmation | ### Trader Real-time stock prices and Real-time options data. Stock quotes and candles carry the 15-minute exchange delay. | Endpoint | Freshness | Notes | |----------------------------|-------------|------------------------------| | `/v1/stocks/quotes/` | 15m delayed | | | `/v1/stocks/candles/` | 15m delayed | | | `/v1/stocks/bulkcandles/` | 15m delayed | | | `/v1/stocks/prices/` | Real-time | | | `/v1/options/quotes/` | Real-time | OPRA entitlement | | `/v1/options/chain/` | Real-time | OPRA entitlement | | `/v1/options/expirations/` | Real-time | Contract metadata | | `/v1/options/lookup/` | Real-time | Contract metadata | | `/v1/markets/status/` | Real-time | Calendar metadata | | `/v1/funds/*` | See note | Pending product confirmation | ### Quant Same freshness profile as Trader. | Endpoint | Freshness | Notes | |----------------------------|-------------|------------------------------| | `/v1/stocks/quotes/` | 15m delayed | | | `/v1/stocks/candles/` | 15m delayed | | | `/v1/stocks/bulkcandles/` | 15m delayed | | | `/v1/stocks/prices/` | Real-time | | | `/v1/options/quotes/` | Real-time | OPRA entitlement | | `/v1/options/chain/` | Real-time | OPRA entitlement | | `/v1/options/expirations/` | Real-time | Contract metadata | | `/v1/options/lookup/` | Real-time | Contract metadata | | `/v1/markets/status/` | Real-time | Calendar metadata | | `/v1/funds/*` | See note | Pending product confirmation | ### Prime Same freshness profile as Trader and Quant. | Endpoint | Freshness | Notes | |----------------------------|-------------|------------------------------| | `/v1/stocks/quotes/` | 15m delayed | | | `/v1/stocks/candles/` | 15m delayed | | | `/v1/stocks/bulkcandles/` | 15m delayed | | | `/v1/stocks/prices/` | Real-time | | | `/v1/options/quotes/` | Real-time | OPRA entitlement | | `/v1/options/chain/` | Real-time | OPRA entitlement | | `/v1/options/expirations/` | Real-time | Contract metadata | | `/v1/options/lookup/` | Real-time | Contract metadata | | `/v1/markets/status/` | Real-time | Calendar metadata | | `/v1/funds/*` | See note | Pending product confirmation | ## Notes - **Real-time data and professional status:** Real-time exchange data is only available to non-professional subscribers. Professional subscribers **never** receive Real-time exchange data, no matter which exchange agreements they sign. Signing an agreement does not change this, and no self-service plan changes it. Delayed / historical data access remains available, depending on the data policies of each exchange. Real-time exchange data for professional use requires direct exchange licensing — [contact our sales team](https://www.marketdata.app/contact/) to discuss it. See [Professional Status Policy](https://www.marketdata.app/docs/docs/account/data-policies/professional-status/), [Exchange Entitlements](https://www.marketdata.app/docs/account/entitlements), and [Professional Status Explained](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](https://www.marketdata.app/docs/api/funds/). - **Stock quotes and candles carry the 15-minute exchange delay.** `/v1/stocks/quotes/`, `/v1/stocks/candles/`, and `/v1/stocks/bulkcandles/` are delayed 15 minutes on every plan under the [UTP entitlement](https://www.marketdata.app/docs/account/entitlements#utp-entitlement). The delay comes from the exchange, so `mode=live` does not shorten it — see [Data Mode](https://www.marketdata.app/docs/api/universal-parameters/mode). --- # Data Policies Source: https://www.marketdata.app/docs/account/data-policies/index.md Market Data distributes financial data under agreements with stock and options exchanges. These policies explain the obligations those agreements impose on subscribers and how they affect your access to data. ## Policies - [Exchange Agreements](https://www.marketdata.app/docs/docs/account/data-policies/exchange-agreements/) — The subscriber agreements for UTP, OPRA, and IEX that govern your use of exchange data - [Free Accounts](https://www.marketdata.app/docs/docs/account/data-policies/free-accounts/) — Why free and trial accounts are limited to 24-hour delayed data - [Single IP Address](https://www.marketdata.app/docs/docs/account/data-policies/single-ip/) — Why each account may only connect from one IP address at a time - [Professional Status](https://www.marketdata.app/docs/docs/account/data-policies/professional-status/) — Self-service plans are for non-professional subscribers only; certification and misclassification obligations - [Data Redistribution](https://www.marketdata.app/docs/docs/account/data-policies/data-redistribution/) — What you may and may not do with the data you receive, and how to obtain redistribution rights - [Account Verification](https://www.marketdata.app/docs/docs/account/data-policies/account-verification/) — Market Data's policy of verifying all paid subscriber identities and employment for exchange classification --- # Account Verification Policy Source: https://www.marketdata.app/docs/account/data-policies/account-verification/index.md Market Data verifies the identity and employment information of all paid subscribers. This is a requirement of the exchanges whose data we distribute: before we can activate real-time data on an account, we must classify the subscriber as either a professional or non-professional user and certify that classification to the exchange. See [Professional Status Policy](https://www.marketdata.app/docs/docs/account/data-policies/professional-status/) for details on what the classifications mean and the consequences of misclassification. Verification activates real-time data only for subscribers classified as non-professional. Professional subscribers never receive real-time exchange data on a self-service plan, no matter which exchange agreements they sign. Real-time access for professional use requires direct exchange licensing — [contact our sales team](https://www.marketdata.app/contact/) to discuss it. > [!TIP] > **[Having trouble with verification?]** > > If your account verification is delayed or you've been asked to provide additional documentation, see [Account Troubleshooting](https://www.marketdata.app/docs/docs/account/troubleshooting/) for help with common issues. ## Information We Collect | Contact Information | Employer Information | |---------------------|----------------------| | Name | Company Name | | Address | Address | | Email | Job Title | | Phone Number | LinkedIn Profile URL | Subscribers employed by banks, insurance companies, or other financial institutions are subject to a more thorough review. ## Verification Process After sign-up, we verify that the information you provided matches third-party databases, including LinkedIn. We also review FINRA BrokerCheck to determine whether you hold any active securities licenses. The outcome of this review determines your professional or non-professional classification. Profiles with missing or incomplete information cannot be classified. Your account will not be activated for real-time data until verification is complete. ### Missing or Mismatching Information All fields are required and must match third-party databases. If your profile contains missing, incomplete, or mismatched information, we will request additional documentation — such as a CV, employment letter, or other materials that clarify your job function. ### LinkedIn Profiles A LinkedIn profile is not required, but it is the easiest way for us to verify your employment information because it is a widely used, publicly accessible professional database. If you do not have a LinkedIn profile or prefer not to share it, just write `https://www.linkedin.com` in the form field and you can provide any of the following as an alternative: - A current resume or CV - A photo of your business card showing your current job title and employer - A recent paystub (please redact any sensitive financial data) - Any other document that confirms your current employer and role ### Subscribers Who Are Retired or Not Currently Working If you are retired or not currently working, you must provide work history that clearly documents your most recent position. Your LinkedIn profile, resume, or CV must show all of the following: - **Employer** — the name of the company or organization - **Job title** — the role you held - **End date** — the month and year your most recent position ended We cannot accept a profile or resume that only lists a job title or employer without a clear end date, as this does not allow us to confirm that you are no longer employed. If your LinkedIn profile does not show this information, please provide a resume or CV that does. This helps us confirm that you are not currently employed in a role that would qualify as professional use under exchange rules. See [Professional Status Policy](https://www.marketdata.app/docs/docs/account/data-policies/professional-status/) for details on how professional status is determined. ### Self-Employed and Business Owners If you are self-employed or own a business, your professional status will depend on the nature of the business itself — specifically, whether the business has a need for market data as part of its products or services. This triggers an additional verification step beyond what is required for employed subscribers. You must provide at least one document that confirms what your business does. Acceptable documentation includes: - A company LinkedIn page - A business website - A brochure or marketing materials describing your products or services - A business registration or filing that describes the nature of the business A self-reported business name and description alone is not sufficient. We need independent documentation that confirms the business is not involved in financial services, trading, or other activities that would classify you as a professional user under exchange rules. See [Professional Status Policy](https://www.marketdata.app/docs/docs/account/data-policies/professional-status/) for details. ### Verification Timeframe We will complete verification within 1 business day of you providing the required information. ## Annual Review Once per year, we will ask you to confirm that your information is still accurate. You can do this by logging into your account and responding to the prompt that appears after one year has passed since your last verification. If your employment or professional registration has changed in a way that affects your classification, you are required to update your information and notify us. See [Professional Status Policy](https://www.marketdata.app/docs/docs/account/data-policies/professional-status/) for more details. ## Privacy The information we collect is used solely to verify your account and comply with exchange regulations. We do not sell your information to third parties. We are required to share it with the stock and options exchanges, who review it periodically for accuracy. --- # Data Redistribution Policy Source: https://www.marketdata.app/docs/account/data-policies/data-redistribution/index.md Market Data's self-service plans are personal licenses that allow you to access and use market data for your own purposes only. Redistribution of that data — in any form — is not permitted under these plans. ## What Counts as Redistribution Redistribution includes any method of making Market Data's data available to others, including: - Selling, reselling, or retransmitting data to third parties - Exposing data through your own API or data feed - Distributing data in bulk export files shared with others - Sharing API access or account credentials with other users - Embedding live or recent data in a product or service accessible to others - Sharing a spreadsheet or workspace that pulls live data from Market Data with collaborators The single-IP restriction is one enforcement mechanism for this policy. See [Single IP Address Policy](https://www.marketdata.app/docs/docs/account/data-policies/single-ip/) for details. ## Commercial Redistribution To display market data to the users of your own app, dashboard, or product, use the [Commercial Plan](https://www.marketdata.app/docs/docs/account/plans/commercial/). It includes a business-wide commercial license with display rights, and there are no exchange agreements to sign, no exchange fees, and no per-user fees. For dedicated exchange feeds, API distribution to other businesses, or resale rights, [contact us](https://www.marketdata.app/contact/) about enterprise licensing. ## Violations Redistribution of market data without a commercial license is a breach of your exchange agreements and Market Data's [Terms of Service](https://www.marketdata.app/terms/), and will result in immediate account termination. --- # Exchange Agreements Source: https://www.marketdata.app/docs/account/data-policies/exchange-agreements/index.md Market Data is an authorized vendor for the exchanges listed below. When you access market data through Market Data's API or other services, you agree to the subscriber agreement of each exchange whose data you receive. Your use of market data through Market Data's services is subject to Market Data's [Terms of Service](https://www.marketdata.app/terms/) and the applicable exchange agreements below. ## UTP Plan The **Unlisted Trading Privileges (UTP) Plan** provides consolidated stock market data for NASDAQ-listed securities, including quotes, last sale prices, and trade data. [UTP Plan Subscriber Agreement](pathname:///docs/exchange-agreements/utp-subscriber-agreement.pdf) ## OPRA The **Options Price Reporting Authority (OPRA)** is the exclusive provider of consolidated options market data in the U.S., including last sale prices and current quotations for all listed options. [OPRA Subscriber Agreement](pathname:///docs/exchange-agreements/opra-subscriber-agreement.pdf) ## IEX The **Investors Exchange (IEX)** is a registered national securities exchange providing real-time stock quotes and trade data. [IEX Controlled Data Recipient Agreement](pathname:///docs/exchange-agreements/iex-controlled-data-recipient-agreement.pdf) --- # Free and Trial Account Data Policy Source: https://www.marketdata.app/docs/account/data-policies/free-accounts/index.md Free Forever and trial accounts are limited to data that is at least 24 hours old. This restriction arises from two independent requirements: exchange rules on user identification and exchange rules on free trials. ## User Identification Requirement Before an exchange permits a vendor to distribute real-time or intraday delayed data to a subscriber, the subscriber must be identified and their professional status determined. Free and trial account holders have not completed this process. Market Data is required by the exchanges to collect, verify, and report the identity of all subscribers who receive real-time or intraday data. Until your account is verified and you have signed the required [exchange agreements](https://www.marketdata.app/docs/docs/account/data-policies/exchange-agreements/), Market Data can only provide data that is at least 24 hours old, which is not subject to the same identification requirements. See [Account Compliance](https://www.marketdata.app/docs/docs/account/data-policies/account-verification/) for details on the verification process. ## Exchange Restrictions on Free Trials Separately from identification requirements, the exchanges place limits on which subscribers may receive real-time or intraday data at no charge: - **Non-professional subscribers** are not permitted to receive real-time or intraday data through a free trial under any circumstances. - **Professional subscribers** may be eligible for limited free trial access from some exchanges, but this is not universally permitted across all exchanges. Because Market Data serves a broad base of non-professional subscribers, and because free trial access to real-time or intraday data is prohibited for non-professionals regardless of plan, all free and trial accounts are limited to 24-hour delayed data. ## Accessing Real-Time and Intraday Data To access real-time or 15-minute delayed data, all of the following are required: 1. **A paid subscription** — real-time and intraday data are not available on free or trial plans. 2. **Account verification** — your identity and professional status must be confirmed by Market Data before exchange data can be enabled. See [Account Compliance](https://www.marketdata.app/docs/docs/account/data-policies/account-verification/). 3. **Non-professional classification** — professional subscribers never receive real-time exchange data on a self-service plan, no matter which exchange agreements they sign. Real-time access for professional use requires direct exchange licensing — [contact our sales team](https://www.marketdata.app/contact/) to discuss it. See [Professional Status Policy](https://www.marketdata.app/docs/docs/account/data-policies/professional-status/). 4. **Signed exchange agreements** — you must sign the subscriber agreement for each exchange whose data you wish to receive. See [Exchange Entitlements](https://www.marketdata.app/docs/docs/account/entitlements/). --- # Professional Status Policy Source: https://www.marketdata.app/docs/account/data-policies/professional-status/index.md All Market Data self-service plans are available to **non-professional subscribers only**. Exchange rules require vendors to classify and certify the status of every subscriber who receives real-time market data. It is your responsibility to subscribe under the correct classification. Business use of market data is only available under the [Commercial Plan](https://www.marketdata.app/docs/docs/account/plans/commercial/), which includes real-time stock and options prices with no exchange licensing and no exchange fees. For a full explanation of how the exchanges define professional status and how to determine your own classification, see [Professional Status Explained](https://www.marketdata.app/education/stocks/professional-status-explained/). ## Professional Subscribers and Real-Time Data Professional subscribers never receive real-time exchange data on a self-service plan, no matter which exchange agreements they sign. Signing an agreement does not change this, and neither does upgrading your plan. Delayed / historical data access remains available, depending on the data policies of each exchange. Real-time exchange data for professional use requires **direct exchange licensing**, which Market Data arranges individually. [Contact our sales team](https://www.marketdata.app/contact/) to discuss exchange licensing for your use case. All real-time data requests from professional subscribers go through the sales team. ## Self-Certification Requirement After purchasing a plan, all subscribers must complete a **Data Use Interview** — a self-certification process in which you confirm that your use of market data meets the non-professional criteria set by the exchanges. Your account cannot be fully activated for real-time data until this process is complete. Non-professional certifications must be renewed once per year. You will be prompted to re-certify annually to confirm your status has not changed. ## Obligation to Update Your Status If your employment, registration, or data use changes in a way that affects your classification, you are required to notify Market Data. Continued use of a non-professional plan after becoming a professional user is a breach of your exchange agreements and this policy. ## Consequences of Misclassification If Market Data determines that a subscriber has incorrectly self-certified as non-professional: - The account will be reclassified as professional - Exchange fees will be assessed retroactively from the date of the incorrect certification - The account may be suspended or terminated without refund - Market Data reserves the right to pursue all remedies available under the [Terms of Service](https://www.marketdata.app/terms/) Refunds are not available on the basis of unexpected classification outcomes. All classifications are made according to exchange rules based on the information you provide. ## Unverified Accounts If Market Data is unable to confirm your non-professional status — for example, because your profile is incomplete or your information cannot be verified — your account will be restricted to delayed and historical data until verification is complete. See [Account Compliance](https://www.marketdata.app/docs/docs/account/data-policies/account-verification/) for details on the verification process. --- # Single IP Address Policy Source: https://www.marketdata.app/docs/account/data-policies/single-ip/index.md Each Market Data account may only access the API from one IP address at a time. This policy is a direct requirement of the exchange agreements under which Market Data distributes market data. ## Why This Policy Exists The exchanges that license their data to Market Data prohibit unauthorized redistribution. When you sign an [exchange agreement](https://www.marketdata.app/docs/docs/account/data-policies/exchange-agreements/), you agree to use the data solely for your own personal or business purposes and not to share or redistribute it to others. Allowing a single account to be used from multiple IP addresses simultaneously would make it impossible to enforce this restriction, because it would allow one subscriber to feed data to multiple people or systems at once. The single IP policy is how Market Data enforces the exchange's per-subscriber distribution rules. ## What Is and Is Not Permitted **Permitted:** - Switching from one device or IP address to another (e.g., moving from your laptop to a cloud server, or reconnecting after your IP changes) - Using a VPN, provided requests consistently originate from a single IP at any given time **Not permitted:** - Connecting simultaneously from more than one IP address - Sharing your API token with other users - Redistributing market data received through your account to any third party ## How the Restriction Is Enforced Market Data monitors for patterns that indicate simultaneous access from multiple IP addresses. A single IP change is permitted. However, if requests alternate back and forth between two IP addresses within a five-minute window — a pattern consistent with two active devices — your account is temporarily blocked for 5 minutes. If your account has been blocked, see [403: Multiple IP Addresses](https://www.marketdata.app/docs/docs/api/troubleshooting/multiple-ip-addresses/) for resolution steps. ## Commercial Use and Multi-Device Access The single IP policy applies to all self-service plans. The [Commercial Plan](https://www.marketdata.app/docs/docs/account/plans/commercial/) has no IP limit: if your use case requires API access from multiple IP addresses simultaneously — for example, distributed systems or serving data to multiple users — the Commercial Plan lets you call the API from as many servers and IP addresses as your product needs. For needs beyond the Commercial Plan, [contact us](https://www.marketdata.app/contact/) about enterprise licensing. ## Violations Attempting to circumvent this policy by sharing accounts or redistributing data will result in permanent account suspension. --- # Exchange Entitlements Source: https://www.marketdata.app/docs/account/entitlements/index.md Exchange entitlements are permissions granted by exchanges that allow Market Data to distribute their data to you. Each entitlement requires you to sign the relevant exchange agreement and adhere to the exchange's data usage policies. For your obligations under these agreements, see [Exchange Agreements](https://www.marketdata.app/docs/docs/account/data-policies/exchange-agreements/). For restrictions on how the data may be used, see [Data Redistribution](https://www.marketdata.app/docs/docs/account/data-policies/data-redistribution/). ## Available Entitlements ### IEX Entitlement - **Data Type**: Stock quotes and trades - **Exchange**: Investors Exchange (IEX) - **Benefit**: Access to stock quote and trade data - **Plan Requirement**: Any paid Market Data plan ### UTP Entitlement - **Data Type**: Consolidated UTP data (candlesticks, quotes and trades) - **Exchange**: Unlisted Trading Privileges (UTP) — includes NASDAQ, NYSE, and other major exchanges - **Benefit**: Access to 15-minute delayed intraday stock candles from major exchanges - **Plan Requirement**: Any paid Market Data plan ### OPRA Entitlement - **Data Type**: Options quotes and trades - **Exchange**: Options Price Reporting Authority (OPRA) - **Benefit**: Access to options data - **Plan Requirement**: Paid Trader plan or above ## How to Get Your Entitlement 1. **Log into your account** at [Market Data Dashboard](https://dashboard.marketdata.app/marketdata/) 2. **Navigate to the Profile section** 3. **Review and sign** the required agreements for the data you need 4. **Wait for approval** (typically processed within 1 business day) --- # Feature Requests Source: https://www.marketdata.app/docs/account/feature-requests/index.md Market Data is _extremely_ customer-focused and we want our products and services to satisfy our users. Please make sure to visit our [Product Roadmap](https://roadmap.marketdata.app/) to see what we have planned along with our [Feature Request Board](https://roadmap.marketdata.app/features) to submit or upvote your own ideas. ## Feature Request Procedure Any user can make a feature request with their Market Data account. Just visit the [Feature Request Board](https://roadmap.marketdata.app/features) and add your requested feature. If enough people upvote it, we'll consider adding your feature to a future version of Market Data. ## Urgent Feature Requests If you urgently need a specific feature and are willing to pay to have it implemented immediately, please let us know. For features that would demand less than 2 days of developer time, we can normally accommodate these requests if you subscribe to a yearly Trader plan (at a cost of $360.00) or extend an existing membership by another year. If you are hesitating to sign-up to Market Data because you need a specific feature that we do not currently offer, we encourage you to contact us to see if we can implement your feature request as an urgent feature. ### Urgent Feature Guarantee If we agree to your urgent feature request in return for a new subscription or an upgrade of an existing subscription, we'll implement your feature request within 14 days. If we fail to meet the deadline, your Market Data account will be free until we deploy the feature you've paid for. --- # Free Accounts Source: https://www.marketdata.app/docs/account/free-accounts/index.md Market Data offers two ways for users to use our service for free. We offer the "Free Forever" plan, which allows for 100 API requests per day. We also offer a free 30 day trial (no credit card required) so that users can test our services and determine which plan best meets their needs. Trial accounts work almost exactly the same as paid accounts and have the same [plan limits](https://www.marketdata.app/docs/account/plan-limits). There are a few differences, however. ## Comparison Table | Feature | Free Forever | Trial Plans | Paid Plans | |------------------------|--------------|-------------|-------------| | 24 Hour Delayed Data | ✅ | ✅ | ❌ | | 15 Minute Delayed Data | ❌ | ❌ | ✅ | | Real-Time Data\* | ❌ | ❌ | ✅ | | Standard Endpoints | ✅ | ✅ | ✅ | | Premium Endpoints | ❌ | AAPL Only | ✅ | | Historical Data | 1 Year | 1 Year | Full Access | \* For endpoint-level freshness rules, see [Data Freshness](https://www.marketdata.app/docs/docs/account/data-freshness/). ## 24 Hour Delayed Data Free Forever and trial accounts only have access to data that is delayed by at least 24 hours. Check the `date` column in the Add-on or the `updated` key in the API's JSON response to get the exact date and time of the quote data you've received. Real-time data is only available with paid subscription plans. For the reasons behind this restriction, see the [Free Accounts Data Policy](https://www.marketdata.app/docs/docs/account/data-policies/free-accounts/). ## Standard vs Premium Endpoints Free and trial plans have access to most standard endpoints that give pricing information. However, our premium endpoints that provide fundamental data (such as earnings data) are only accessible with a paid subscription. ## Historical Data Free and trial plans cannot access data older than 1 year. We welcome you to use our free trial to test our historical data going back one year. However, if you need older data, a subscription is required. --- # Invoices Source: https://www.marketdata.app/docs/account/invoices/index.md There are two ways to access your invoices: through the order confirmation email you received at the time of purchase, or through the Billing Portal. ## Download From Your Email When you made your purchase, an order confirmation email was sent to the email address associated with your order. This email includes your order details, subscription information, and a **Download invoice** link that lets you download a PDF invoice immediately — no account or login required. To find the email, search your inbox for **"Market Data" "order invoice"**. The email will include details such as: - The plan you purchased and the amount charged - Your subscription renewal date and billing cycle - A **Download invoice** link > [!TIP] > If you can't find the email, check your spam or junk folder. The confirmation email is sent by our billing partner, not directly from Market Data, so it may be filtered. ## Download From the Billing Portal You can also generate and download invoices for any of your orders by logging in to the [Billing Portal](https://cc.payproglobal.com/Customer/Account/Login). 1. Log in to the [Billing Portal](https://cc.payproglobal.com/Customer/Account/Login). 2. Navigate to your order history. 3. Select the order you need an invoice for. 4. Download the PDF invoice. If this is your first time accessing the Billing Portal, see [Billing Portal](https://www.marketdata.app/docs/docs/account/billing-portal#how-to-access-the-billing-portal-for-the-first-time) for instructions on setting up your password. --- # Plan Limits Source: https://www.marketdata.app/docs/account/plan-limits/index.md 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. ## Standard Plans | | Free Forever | Starter | Trader | Quant | Prime Plans | |--------------------|--------------|-------------------|-------------|------------------|------------------| | Daily Credits | 100 | 10,000 | 100,000 | No Limit | No Limit | | Per-Minute Credits | No Limit | No Limit | No Limit | 10,000 | 100,000 | | Historical Data | 1 Year | 5 Years | No Limit | No Limit | No Limit | | Stocks Data Type | Delayed | Real-time\* | Real-time\* | Real-time\* | Real-time\* | | Options Data Type | Delayed | 15-minute delayed | Real-time | Real-time | Real-time | | API Endpoints | Standard | Premium | Premium | Premium + Custom | Premium + Custom | \* See [Data Freshness](https://www.marketdata.app/docs/account/data-freshness) for endpoint-level freshness. ## Credits Each time you call the API, the system increases your credits counter. Normally each successful response consumes 1 credit. However, **if you request multiple symbols in a single API call using `stocks/quotes`, `stocks/prices`, `stocks/bulkcandles`, or `options/chain`, each symbol included in the response consumes credits**. > [!CAUTION] > For users working with options, take care before repeatedly requesting quotes for an entire option chain. **Each option symbol included in the response consumes credits**. If you download the entire SPX option chain (which has 20,000+ option symbols), you can exhaust your credit limit very quickly. Use our option chain filters to request only the strikes and expirations you need. > [!NOTE] > **[Trial plans]** > > Starter Trial, Trader Trial, and Prime Trial do not support `mode=cached`. ## Device Restrictions All plans are subject to a single IP address limit. Each account may only connect from one IP address at a time. Multiple simultaneous connections, account sharing, and data redistribution are not permitted. See the [Single IP Address Policy](https://www.marketdata.app/docs/docs/account/data-policies/single-ip/) for details. ### Throttling All plans are subject to a maximum of 50 simultaneous requests at any given time. ## Historical Data Age Limit Free Forever accounts can access up to 1 year of historical data and Starter accounts can access up to 5 years. Trader, Quant, and Prime plans have no historical data age limit. If you request data older than your plan allows, the request will be denied. ## Data Type - Free Forever provides delayed stocks/options data. - Starter provides real-time stocks\* and 15-minute delayed options data. - Trader, Quant, and Prime provide real-time stocks\*/options data. > [!NOTE] > 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](https://www.marketdata.app/docs/account/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. ## About Usage Windows - Free Forever, Starter, and Trader use daily windows that reset at 9:30 AM Eastern Time (NYSE opening bell). - Quant and Prime use per-minute credit windows. - Any single API request is permitted as long as you have at least 1 credit remaining in the current window, even if the request consumes more credits than you have available. After the request, your balance may go negative and further requests will be blocked until the window resets. --- # Data Plans Source: https://www.marketdata.app/docs/account/plans/index.md Market Data provides a variety of data subscriptions for users who need on-going access to Market Data. Explore the details of all our plans and decide which one is right for you. ## Explore our Plans --- # Commercial Plan Source: https://www.marketdata.app/docs/account/plans/commercial/index.md The **Commercial Plan** gives your business real-time, delayed, historical, and tick-level market data for stocks and options, together with a commercial license to display it to your users — all for one price, starting at **$333/month**. There are no exchange agreements to sign, no exchange fees, and no per-user fees. ## Pricing **$333/month to start, billed quarterly.** The price steps up twice during the first year, then stays fixed: | Months | Monthly Price | Billed Each Quarter | |-----------|---------------|---------------------| | 1–6 | $333/month | $999 | | 7–12 | $666/month | $1,998 | | 13 and on | $999/month | $2,997 | The price includes the commercial license. There are no exchange fees and no per-user fees on top of it, however many users your product serves. Every Commercial Plan starts with a **14-day free trial**, with no charge until the trial ends, and there is no long-term contract. [Contact us](https://www.marketdata.app/contact/) to get started. ## No Exchange Licensing, No Extra Fees A business that shows market data to its users usually has to sign agreements with each exchange, pay monthly exchange fees, and pay again for every user who sees the data. Those costs grow with every user you add, and the paperwork comes before you can launch. The Commercial Plan removes all of it. Real-time prices, delayed exchange data, historical data, and tick-level data all come under one license, at one price. You sign nothing with the exchanges, you pay no exchange fees, and you pay nothing more as your user base grows — so your data costs stay the same whether your product has ten users or ten thousand. ## What You Get with the Commercial Plan ### Data - **Real-Time Stock Prices**: Real-time derived prices that closely track exchange prices, with last price, open, close, volume, and pre-market and post-market volume. - **Real-Time Options Prices**: Real-time derived options prices, with implied volatility and the greeks — delta, gamma, theta, and vega. - **WebSocket Streaming**: Stream real-time stock and options prices over WebSocket, or request them over the REST API. - **Tick-Level Market Data**: Market data down to the individual tick. - **15-Minute Delayed CBOE Data**: Exchange data from CBOE on a 15-minute delay. - **End-of-Day and Historical Data**: End-of-day and historical data for stocks and options, for backtesting, analytics, and historical charts. - **US Fundamentals**: Fundamentals data for US companies. ### Rights - **Commercial Use and Display Rights**: A business-wide commercial license. Use the data across your business and display it to the users of your app, dashboard, or product. - **No Exchange Licensing**: No exchange agreements to sign and no exchange paperwork. - **No Exchange Fees or Per-User Fees**: One price, however many users see the data. - **No IP Limit**: Call the API from as many servers and IP addresses as your product needs. ### Tools and Limits - **AI Assistant Access**: Connect Claude, ChatGPT, and other AI assistants to the data through an MCP server. - **Rate Limits**: 2,000 API calls per minute, 1 request per minute for bulk downloads and paging, and 3 WebSocket connections. ## What You Can Build The Commercial Plan is made for businesses bringing a product to market on top of market data: - **Stock and options apps** that display real-time and historical data to paying users - **SaaS and fintech products** built on top of the data - **Financial dashboards** distributed to clients or employees - **Research products and newsletters** that publish data-driven content - **Charting and analytics tools** built on real-time, historical, and tick-level data - **Internal business analytics** across your whole company ## Comparison with Other Plans | Feature | Starter | Trader | Prime | **Commercial** | |--------------------------------|---------|--------|---------|----------------------| | Price | $30/mo | $75/mo | $250/mo | **$333/mo to start** | | Commercial Use | ❌ | ❌ | ❌ | ✅ | | Public Display to Your Users | ❌ | ❌ | ❌ | ✅ | | Redistribution in Your Product | ❌ | ❌ | ❌ | ✅ | | WebSocket Streaming | ❌ | ❌ | ❌ | ✅ | | Tick-Level Market Data | ❌ | ❌ | ❌ | ✅ | | Multiple IP Addresses | ❌ | ❌ | ❌ | ✅ | ## Why Choose the Commercial Plan? The Commercial Plan is the simplest way to launch a product on market data. One price covers real-time stock and options prices over REST and WebSocket, tick-level, delayed, and historical data, and the commercial license to display all of it — with no exchange agreements, no exchange fees, and no per-user fees as you grow. It starts at **$333/month, billed quarterly**, with a 14-day free trial. [Contact us](https://www.marketdata.app/contact/) to get started or to discuss your specific use case. ## Need More? For dedicated exchange feeds, API distribution to other businesses, or resale rights, [contact us](https://www.marketdata.app/contact/) about enterprise licensing. --- # Free Forever Plan Source: https://www.marketdata.app/docs/account/plans/free-forever/index.md The **Free Forever Plan** is designed for users who want to explore Market Data's capabilities without any cost. It's perfect for individuals or small projects needing basic access to financial data. Here's everything you need to know about what the Free Forever plan offers and its limitations. ## Pricing **Free** — $0/month, no credit card required. ## What You Get with Free Forever - **100 API Credits per Day**: Ideal for small-scale applications or personal projects. - **24-Hour Delayed Data**: Access to data that is delayed by at least 24 hours. - **Standard Endpoints**: Use most of our standard endpoints that offer pricing information, allowing you to integrate financial data into your applications. - **1 Year of Historical Data**: Explore and utilize historical data up to one year back for your analysis and projects. ## Limitations of the Free Forever Plan While the Free Forever plan offers a great starting point, there are some limitations to be aware of: - **No 15-Minute Delayed Data**: Access to 15-minute delayed financial data is not available under this plan. - **No Real-Time Data**: Access to real-time financial data is not available under this plan. - **No Premium Endpoints**: Fundamental data, such as earnings data provided through our premium endpoints, are not accessible. - **Historical Data Limited to 1 Year**: If your project requires data older than one year, you would need to consider upgrading to a paid subscription. ## Comparison To help you better understand the differences between our plans, here's a quick comparison: | Feature | Free Forever | Paid Plans | |------------------------|--------------|-------------| | Historical Data | 1 Year | Full Access | | 15-Minute Delayed Data | ❌ | ✅ | | Real-Time Data\* | ❌ | ✅ | | 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. Start exploring the financial world with Market Data's Free Forever plan today and unlock the potential of financial data for your projects! --- # Prime Plan Source: https://www.marketdata.app/docs/account/plans/prime/index.md The **Prime Plan** is Market Data's most robust offering, designed for high-powered traders and investors who require extensive data access and customization options. This plan is tailored to meet the needs of sophisticated trading operations, offering unparalleled data access with the flexibility to handle massive volumes of API requests. Here's a comprehensive overview of the Prime Plan. ## Pricing **$250/month** — month-to-month billing only. The Prime Plan does not offer an annual billing option. ## What You Get with the Prime Plan - **No Daily Limit on API Credits**: Enjoy unrestricted access to our API, designed to support the most demanding data needs without the worry of hitting daily limits. - **100,000 Credits Per Minute**: While there's no daily limit, the plan is subject to a generous per-minute rate limit to ensure optimal performance for all users. - **Real-Time Data\***: Access up-to-the-second financial data, ensuring your trading decisions are based on the most current market information. Note that real-time data is only available to users who qualify as non-professionals. [Learn more about professional status requirements](https://www.marketdata.app/education/stocks/professional-status-explained/). - **Premium + Custom API Endpoints**: Not only do you get access to all premium endpoints, but we also offer the possibility of custom-built endpoints to satisfy your specific trading requirements. - **Unlimited Historical Data**: There are no restrictions on the age of historical data you can access, making it ideal for deep historical analysis and backtesting. ## Features of the Prime Plan The Prime Plan is designed with the highest level of access and customization in mind: - **Customization and Flexibility**: Tailor the plan to fit your specific trading needs, including custom API endpoints developed in collaboration with our team. - **Optimized for High Volume**: Handle massive data volumes with a high per-minute rate limit, ensuring your trading operations run smoothly without interruption. - **Comprehensive Data Access**: From real-time data to unlimited historical data, get everything you need for analysis, trading, and decision-making. ## Comparison with Other Plans Here's how the Prime Plan compares to our other offerings: | Feature | Free Forever | Starter | Trader | Quant | Prime | |-----------------------|--------------|------------|-------------|-------------|-------------| | Daily API Credits | 100/day | 10,000/day | 100,000/day | Unlimited | Unlimited | | Per Minute Rate Limit | No Limit | No Limit | No Limit | 10,000 | 100,000 | | Real-Time Data\* | ❌ | ✅ | ✅ | ✅ | ✅ | | 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. Embrace the full potential of Market Data with the Prime Plan and elevate your trading capabilities to new heights. --- # Quant Plan Source: https://www.marketdata.app/docs/account/plans/quant/index.md The **Quant Plan** is Market Data's offering designed for high-powered traders and investors who require extensive data access and customization options. This plan is tailored to meet the needs of sophisticated trading operations, offering unparalleled data access with the flexibility to handle massive volumes of API requests. Here's a comprehensive overview of the Quant Plan. ## Pricing **$125/month** — month-to-month billing only. The Quant Plan does not offer an annual billing option. ## What You Get with the Quant Plan - **No Daily Limit on API Credits**: Enjoy unrestricted access to our API, designed to support the most demanding data needs without the worry of hitting daily limits. - **10,000 Credits Per Minute**: While there's no daily limit, the plan is subject to a generous per-minute rate limit to ensure optimal performance for all users. - **Real-Time Data\***: Access up-to-the-second financial data, ensuring your trading decisions are based on the most current market information. Note that real-time data is only available to users who qualify as non-professionals. [Learn more about professional status requirements](https://www.marketdata.app/education/stocks/professional-status-explained/). - **Premium + Custom API Endpoints**: Not only do you get access to all premium endpoints, but we also offer the possibility of custom-built endpoints to satisfy your specific trading requirements. - **Unlimited Historical Data**: There are no restrictions on the age of historical data you can access, making it ideal for deep historical analysis and backtesting. ## Features of the Quant Plan The Quant Plan is designed with the highest level of access and customization in mind: - **Customization and Flexibility**: Tailor the plan to fit your specific trading needs, including custom API endpoints developed in collaboration with our team. - **Optimized for High Volume**: Handle massive data volumes with a high per-minute rate limit, ensuring your trading operations run smoothly without interruption. - **Comprehensive Data Access**: From real-time data to unlimited historical data, get everything you need for analysis, trading, and decision-making. ## Comparison with Other Plans Here's how the Quant Plan compares to our other offerings: | Feature | Free Forever | Starter | Trader | Quant | Prime | |-----------------------|--------------|------------|-------------|-------------|-------------| | Daily API Credits | 100/day | 10,000/day | 100,000/day | Unlimited | Unlimited | | Per Minute Rate Limit | No Limit | No Limit | No Limit | 10,000 | 100,000 | | Real-Time Data\* | ❌ | ✅ | ✅ | ✅ | ✅ | | 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. Embrace the full potential of Market Data with the Quant Plan and elevate your trading capabilities to new heights. --- # Starter Trial Source: https://www.marketdata.app/docs/account/plans/starter-trial/index.md Market Data is excited to offer a **30-day free trial** of our Starter Plan, no credit card required. This trial is designed to let users explore the capabilities of the Starter Plan, ensuring it's the right fit for their needs before committing to a subscription. Here's what you need to know about the Starter Trial Plan. ## Pricing **$0 for 30 days** — no credit card required. The trial is strictly limited to 30 days and **cannot be renewed or extended**. Because no credit card is required to start the trial, **you will never be auto-billed when it ends**. If you take no action, your account automatically converts to the [Free Forever Plan](https://www.marketdata.app/docs/account/plans/free-forever) at the end of the 30 days. To keep premium features, you can subscribe to the [Starter Plan](https://www.marketdata.app/docs/account/plans/starter) for $12/month (billed annually) or $30/month at any time. ## What You Get with the Starter Trial Plan - **10,000 API Credits per Day**: Experience the increased capacity for API requests and see how it supports larger projects. - **24-Hour Delayed Data for Stocks and Options**: Access to data that is delayed by at least 24 hours for both stocks and options. - **1 Year Of Historical Data Access**: Explore our extensive historical data archives and fetch data from the last year. - **Full Historical Data Access (AAPL Ticker)**: Fetch complete historical data from our extensive archives for the AAPL ticker with no date restrictions. - **Premium Endpoints (AAPL Ticker)**: Explore our premium endpoints using the AAPL ticker, including fundamental data like earnings. ## Features of the Starter Trial Plan The Starter Trial Plan mirrors the full Starter Plan in terms of features, with a few key differences: - **Duration**: The trial lasts for 30 days, giving you ample time to test out the features and decide if the plan meets your needs. - **No Credit Card Required**: Start your trial without any financial commitment, allowing you to explore our services risk-free. - **Full Access with Limitations**: While you have access to most of the same features as the paid Starter Plan, remember that the trial is designed for evaluation purposes. It is not a full substitute of the actual Starter Plan. ## What You Don't Get With the Starter Trial - **No Real-Time Stock Data**: Access to real-time stock data is not included in the trial. - **No 15-Minute Delayed Options Data**: Access to 15-minute delayed options data is not available under this trial. - **Historical Data Access (All Tickers)**: Historical data access on tickers other than AAPL is limited to 1 year. - **Premium Endpoints (All Tickers)**: Get full access to our premium endpoints with all tickers. ## Comparison with Other Plans Here's a quick comparison to help you understand the Starter Trial in relation to our other offerings: | Feature | Free Forever | Starter Trial | Starter Plan | |--------------------|--------------|----------------|----------------| | API Credits | 100 per day | 10,000 per day | 10,000 per day | | Stock Data Delay | 24 hours | 24 hours | Real-time\* | | Options Data Delay | 24 hours | 24 hours | 15 minutes | | Historical Data | 1 Year | 1 Year | 5 Years | | 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. Start your journey with Market Data's Starter Trial today and unlock the potential of comprehensive financial data analysis without any upfront commitment. --- # Starter Plan Source: https://www.marketdata.app/docs/account/plans/starter/index.md The **Starter Plan** is tailored for users who require more from their financial data access, with extensive historical records and premium features. It's an ideal choice for growing projects that need deeper insights and more data points. Here's a detailed overview of what the Starter Plan offers. ## Pricing - **Annual**: $12/month (billed annually at $144/year) - **Monthly**: $30/month ## What You Get with the Starter Plan - **10,000 API Credits per Day**: A significant increase in daily API requests to support larger projects. - **Real-Time Stock Data\***: Access to real-time market data for stocks, ensuring you're working with current information. Note that real-time data is only available to users who qualify as non-professionals. [Learn more about professional status requirements](https://www.marketdata.app/education/stocks/professional-status-explained/). - **15-Minute Delayed Options Data**: Access to options data with a 15-minute delay. - **5 Years of Historical Data**: Dive deeper into financial trends with access to data going back five years, ideal for thorough analysis and backtesting. - **Premium Endpoints**: Unlock our all our premium endpoints, including fundamental data like earnings, for a comprehensive understanding of market dynamics. ## Advantages of the Starter Plan The Starter Plan comes with several key advantages designed to support your project's growth: - **Comprehensive Data Access**: With premium endpoints, real-time stock data, and delayed options data, you can make informed decisions based on thorough analysis. - **Extended Historical Data**: The ability to access 5 years of historical data opens up new possibilities for analysis and insight. - **Increased API Credits**: The jump to 10,000 API credits per day allows for more extensive data collection and integration. ## Comparison To give you a clear idea of how the Starter Plan stands against our other offerings, here's a comparison: | Feature | Free Forever | Starter Plan | Trader Plan | |--------------------|--------------|----------------|-----------------| | API Credits | 100 per day | 10,000 per day | 100,000 per day | | Stock Data Delay | 24 hours | Real-time\* | Real-time\* | | Options Data Delay | 24 hours | 15 minutes | Real-time | | Standard Endpoints | ✅ | ✅ | ✅ | | 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. Upgrade to the Starter Plan today and take your financial data analysis to the next level with Market Data! --- # Trader Trial Source: https://www.marketdata.app/docs/account/plans/trader-trial/index.md Market Data is thrilled to introduce a **30-day free trial** of our Trader Plan, no credit card required. This trial is crafted to allow users to experience the capabilities of the Trader Plan, ensuring it aligns with their high-volume data needs before making a subscription commitment. Here's an overview of what the Trader Trial Plan entails. ## Pricing **$0 for 30 days** — no credit card required. The trial is strictly limited to 30 days and **cannot be renewed or extended**. Because no credit card is required to start the trial, **you will never be auto-billed when it ends**. If you take no action, your account automatically converts to the [Free Forever Plan](https://www.marketdata.app/docs/account/plans/free-forever) at the end of the 30 days. To keep premium features, you can subscribe to the [Trader Plan](https://www.marketdata.app/docs/account/plans/trader) for $30/month (billed annually) or $75/month at any time. ## What You Get with the Trader Trial - **100,000 API Credits per Day**: Test the limits with a substantial increase in daily API requests, catering to the most demanding projects. - **24-Hour Delayed Data for Stocks and Options**: Access to data that is delayed by at least 24 hours for both stocks and options. - **1 Year Of Historical Data Access**: Explore our extensive historical data archives and fetch data from the last year. - **Full Historical Data Access (AAPL Ticker)**: Fetch complete historical data from our extensive archives for the AAPL ticker with no date restrictions. - **Premium Endpoints (AAPL Ticker)**: Dive into our premium endpoints using the AAPL ticker for a taste of our detailed fundamental data, including earnings insights. ## Features of the Trader Trial The Trader Trial Plan offers a glimpse into the full capabilities of the Trader Plan with some tailored differences: - **Duration**: Enjoy a full 30 days to explore and evaluate the features of the Trader Plan, ensuring it meets your high-volume data needs. - **No Credit Card Required**: Begin your trial with no financial commitment, allowing for a risk-free exploration of our highest-tier services. - **Full Access with Limitations**: While the trial provides access to many features akin to the paid Trader Plan, it's designed for evaluation purposes only and not as a complete substitute for the paid plan. ## What You Don't Get With the Trader Trial - **No Real-Time Stock Data**: Access to real-time stock data is not included in the trial. - **No Real-Time Options Data**: Access to real-time options data is not included in the trial. - **No 15-Minute Delayed Options Data**: Access to 15-minute delayed options data is not available under this trial. - **Historical Data Access (All Tickers)**: Historical data access on tickers other than AAPL is limited to 1 year. - **Premium Endpoints (All Tickers)**: Full access to our premium endpoints across all tickers is reserved for paying customers only. ## Comparison with Other Plans This comparison helps you understand the Trader Trial in relation to our other offerings: | Feature | Free Forever | Trader Trial | Trader Plan | |--------------------|--------------|-----------------|-----------------| | API Credits | 100 per day | 100,000 per day | 100,000 per day | | Stock Data Delay | 24 hours | 24 hours | Real-time\* | | Options Data Delay | 24 hours | 24 hours | Real-time | | Historical Data | 1 Year | 1 Year | Full Access | | 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. Start your journey with Market Data's Trader Trial today and explore the ultimate in financial data analysis and access without any upfront commitment. --- # Trader Plan Source: https://www.marketdata.app/docs/account/plans/trader/index.md The **Trader Plan** is specifically designed for high-volume users and traders who demand the utmost from their financial data services. With an enormous allocation of API credits and access to the most comprehensive data sets available, the Trader Plan is the ultimate choice for those who need maximum performance and depth in their financial analysis. Here's what you can expect from the Trader Plan. ## Pricing - **Annual**: $30/month (billed annually at $360/year) - **Monthly**: $75/month ## What You Get with the Trader Plan - **100,000 API Credits per Day**: Cater to high-demand projects with an extensive number of daily API requests. - **Real-Time Data for Stocks\* and Options**: Immediate access to financial data for both stocks and options, ensuring you're always working with the most current information. Note that real-time data is only available to users who qualify as non-professionals. [Learn more about professional status requirements](https://www.marketdata.app/education/stocks/professional-status-explained/). - **Full Historical Data Access**: Unrestricted access to our entire historical data archive, perfect for deep historical analysis and backtesting without limitations. - **Premium Endpoints**: Full access to all our premium endpoints, including detailed fundamental data, to provide a comprehensive view of market dynamics. ## Advantages of the Trader Plan The Trader Plan is packed with features to support the most demanding projects: - **Unparalleled Data Access**: With the highest number of daily API credits, real-time data for both stocks and options, and full historical access, you're equipped to handle any data challenge. - **Comprehensive Market Insights**: Premium endpoints offer insights into earnings, company fundamentals, and more, allowing for detailed market analysis. - **Scalability**: Designed to support projects as they grow in scope and complexity, ensuring your data needs are always met. ## Visual Comparison Here's how the Trader Plan compares to our other offerings: | Feature | Free Forever | Starter Plan | Trader Plan | |--------------------|--------------|----------------|-----------------| | API Credits | 100 per day | 10,000 per day | 100,000 per day | | Stock Data Delay | 24 hours | Real-time\* | Real-time\* | | Options Data Delay | 24 hours | 15 minutes | Real-time | | Standard Endpoints | ✅ | ✅ | ✅ | | 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. Elevate your financial data capabilities with the Trader Plan and gain the edge you need to succeed in today's fast-paced markets. --- # Review Us Source: https://www.marketdata.app/docs/account/review-us/index.md We welcome your reviews and feedback on social media and specialized software review sites. If you would like to share your experience with Market Data with the internet, please consider tagging us on social media or leaving us a review at any of the sites listed below. Thank you for spreading the word. ## Social Media - Twitter - [@MarketDataApp](https://twitter.com/MarketDataApp) - YouTube - [@MarketData-App](https://www.youtube.com/channel/UCtMeqaVWugbYyCYIAKlyC6Q) - Facebook - [MarketDataApp](https://www.facebook.com/MarketDataApp/) ## Consumer Review Sites - [Google Workspace Marketplace](https://workspace.google.com/marketplace/app/market_data/453586334945) - [TrustPilot](https://www.trustpilot.com/review/www.marketdata.app) ## B2B Review Sites - [ProductHunt](https://www.producthunt.com/products/market-data) - [GetApp](https://www.getapp.com/it-management-software/a/market-data/) - [Capterra](https://www.capterra.com/p/10004187/Market-Data/) - [G2](https://www.g2.com/products/market-data/reviews) - [SourceForge](https://sourceforge.net/software/product/Market-Data/) - [Slashdot](https://slashdot.org/software/p/Market-Data/) --- # Troubleshooting (Account) Source: https://www.marketdata.app/docs/account/troubleshooting/index.md This section covers common account issues and how to resolve them. Most problems are related to account verification — specifically, providing incomplete or insufficient information during sign-up. ## Account Verification After you subscribe to a paid plan, Market Data must verify your identity and employment information before activating real-time data on your account. This is a requirement of the stock and options exchanges. If your profile is incomplete or doesn't match third-party records, verification will be delayed. - [Incomplete Profile](https://www.marketdata.app/docs/docs/account/troubleshooting/incomplete-profile) — Missing or vague employer, job title, or contact information. - [LinkedIn Profile Issues](https://www.marketdata.app/docs/docs/account/troubleshooting/linkedin-issues) — Fixing missing, private, outdated, or mismatched LinkedIn profiles (or verifying without one). - [Retired or Not Working](https://www.marketdata.app/docs/docs/account/troubleshooting/retired-or-unemployed) — Work history requirements for subscribers who are not currently employed. - [Self-Employed or Business Owner](https://www.marketdata.app/docs/docs/account/troubleshooting/self-employed) — Additional documentation needed when you own or operate a business. - [Financial Industry Employer](https://www.marketdata.app/docs/docs/account/troubleshooting/financial-employer) — Why subscribers at banks, brokerages, and financial institutions receive additional review. - [Disagree With Your Classification](https://www.marketdata.app/docs/docs/account/troubleshooting/classification-dispute) — What to do if you believe your professional/non-professional classification is incorrect. ## Billing - [Billing Portal Access](https://www.marketdata.app/docs/docs/account/troubleshooting/billing-portal-access) — Problems accessing the billing portal or managing your subscription. ## Account Management - [Account Deletion](https://www.marketdata.app/docs/docs/account/troubleshooting/account-deletion) — How to request deletion of your account and personal data. --- # Account Deletion Source: https://www.marketdata.app/docs/account/troubleshooting/account-deletion/index.md ## Overview You can request the deletion of your Market Data account and all associated personal data at any time. Account deletion is permanent and cannot be undone. ## How to Request Deletion Contact our [support helpdesk](https://www.marketdata.app/dashboard/) and request that your account and personal data be deleted. Please include the email address associated with your account. ## What Happens Next 1. Our support team will acknowledge your request and confirm the details. 2. You will receive a confirmation email notifying you that your account and data have been deleted. 3. Your account, profile information, and personal data will be permanently removed from our systems. If you have an active subscription, access ends immediately. Once deletion is complete, you will no longer be able to log in to the Customer Dashboard or access any account features. This is permanent — we cannot recover a deleted account. ## What Is Deleted - Your account login and dashboard access - Your API tokens and usage history - Any personal data not subject to a regulatory retention requirement (see below) ## What Is Retained The stock and options exchanges require Market Data to maintain records of all subscriber classifications (professional vs. non-professional) for a minimum of **3 years**. To comply with this obligation, we are required to retain the following even after account deletion: - Your name and employment information - Your professional/non-professional classification and the basis for that determination - Your signed exchange agreements This data is retained solely for regulatory compliance purposes. It is not used for marketing or any other purpose. Additionally, billing records held by our payment processor are subject to their own data retention policies and are outside of our control. ## Before You Request Deletion - **Active subscriptions lose access immediately.** If you request deletion while you have an active paid subscription, your account will be deleted right away — you will not retain access for the remainder of your billing period. No prorated refunds will be issued. If you want to use your subscription through the end of the current billing period, request a [cancellation](https://www.marketdata.app/docs/docs/account/cancellations/) instead. Cancellation stops future charges but keeps your account active until the period ends. - **Download your invoices.** Once your account is deleted, you will not be able to access the [Billing Portal](https://cc.payproglobal.com/Customer/Account/Login) to download past invoices. Download any invoices you need before requesting deletion. ## Still Need Help? If you have questions about what data we hold or how deletion works, contact our [support helpdesk](https://www.marketdata.app/dashboard/) before submitting your request. We typically respond within a few hours during market hours or within 1 business day outside of market hours. --- # Billing Portal Access Source: https://www.marketdata.app/docs/account/troubleshooting/billing-portal-access/index.md ## Problem Overview You are having trouble logging in to the [Billing Portal](https://cc.payproglobal.com/Customer/Account/Login) to manage your subscription, update your payment method, or view your invoices. ## Common Issues ### "I Don't Have a Billing Portal Account" The Billing Portal is only available to **paid customers**. A billing portal account is created automatically when you make your first purchase. If you have never had a paid subscription with Market Data, you do not have a billing portal account. - **Free Forever accounts** do not have billing portal access. - **Trial accounts** do not have billing portal access. If you are on a free or trial plan and want to subscribe, you can do so directly from the Market Data [Customer Dashboard](https://www.marketdata.app/dashboard/). A billing portal account will be created for you when you complete your first purchase. ### "I Don't Know My Password" The Billing Portal is operated by a third-party payment processor (PayProGlobal) and is **completely separate** from your Market Data account. Your Market Data login credentials will not work in the Billing Portal. There is no initial password — you must create one the first time you log in: 1. Visit the [Billing Portal](https://cc.payproglobal.com/Customer/Account/Login). 2. Click **"Forgot Password"**. 3. Enter the email address you used on the credit card payment form when you first subscribed. 4. Follow the instructions in the password reset email to set your password. 5. Log in with your new credentials. ### "I'm Not Receiving the Password Reset Email" The password reset email is sent to the email address that was used on the **original credit card payment form** — not necessarily the email address currently on your Market Data account. If you have changed your email address with Market Data since your first purchase, the billing portal still uses the **original email from the payment form**. Try the password reset using that earlier email address. If you are unsure which email address was used, contact our [support helpdesk](https://www.marketdata.app/dashboard/) and we can help you identify the correct email. ## Still Need Help? If you are still unable to access the Billing Portal, contact our [support helpdesk](https://www.marketdata.app/dashboard/). You can also request billing support directly through the [Billing Portal](https://cc.payproglobal.com/Customer/Account/Login) once logged in — see [Billing Portal](https://www.marketdata.app/docs/docs/account/billing-portal/) for the full list of actions available. We typically respond within a few hours during market hours or within 1 business day outside of market hours. --- # Disagree With Your Classification Source: https://www.marketdata.app/docs/account/troubleshooting/classification-dispute/index.md ## Problem Overview You believe you should be classified as a non-professional data user, but Market Data has classified your account as professional. This page explains how classifications are determined, why your account may have been flagged, and what you can do if you believe the classification is incorrect. ## How Professional Status Is Determined Professional status is not based on how you think of yourself — it is defined by the stock and options exchanges based on three objective criteria: 1. **How you use the data** — Any use of market data for business or commercial purposes is professional use, even if the business is not in financial services. 2. **Your job function** — Roles that involve trading, portfolio management, financial analysis, or investment advisory are classified as professional. 3. **Registration** — Being registered or qualified with a securities regulatory body (SEC, FINRA, CFTC, FCA, etc.) typically qualifies as professional, even if you are not currently active. Market Data does not set these rules. They come directly from the exchanges and apply to all vendors who distribute real-time market data. See [Professional Status Policy](https://www.marketdata.app/docs/docs/account/data-policies/professional-status/) for the full criteria, or read [Professional Status Explained](https://www.marketdata.app/education/stocks/professional-status-explained/) for a detailed guide with examples. ## Common Reasons for Professional Classification ### Using Data for a Business or Startup If you plan to use market data in connection with a business — including a startup, an LLC, or a side project — that is commercial use under exchange rules, regardless of whether: - The business is profitable yet - You are the sole owner or employee - The business is not in financial services - You personally paid for the subscription Any use of data to support a business function — such as building a product, providing a service, generating reports for clients, or integrating data into an application — is classified as professional. This includes personal LLCs, even single-member pass-through entities. Non-professional plans are for **personal, non-commercial use only**. If you need data for a business, the [Commercial Plan](https://www.marketdata.app/docs/docs/account/plans/commercial/) is made for business use. [Contact our sales team](https://www.marketdata.app/contact/) to get started. ### Job Function Suggests Professional Use If your employer and job title suggest that your role involves securities-related activities, we may classify your account as professional even if you signed up under a personal account. This applies when your role involves: - Trading or executing securities, commodities, or derivatives - Managing investment portfolios or making investment decisions - Providing investment advice or financial planning to clients - Producing investment research - Developing software or systems that support investment activities For example, a financial analyst at an investment firm who signs up for a personal account would still be classified as professional, because their job function involves market data. The exchanges require this classification regardless of whether the individual intends to use the data at work or at home. ## What You Can Do ### If You Believe the Classification Is Wrong If you believe your role or intended use does not meet the criteria for professional status, contact our [support helpdesk](https://www.marketdata.app/dashboard/) and provide: - A clear description of your **day-to-day job responsibilities** (if the classification is based on your employer) - A description of **how you intend to use the data** (if the classification is based on commercial use) - Any supporting documentation — such as a LinkedIn profile, resume, or job description — that clarifies your role We will review the information and update your classification if the evidence supports it. Our goal is to classify every subscriber correctly — not to default to professional. ### If the Classification Is Correct If your use case or job function does qualify as professional under exchange rules, your options are: - **License real-time exchange data directly.** Professional subscribers never receive real-time exchange data on a self-service plan, no matter which agreements they sign. Real-time access for professional use requires direct exchange licensing, which Market Data arranges individually. [Contact our sales team](https://www.marketdata.app/contact/) to discuss it. - **Continue with delayed or historical data.** If you are not eligible for non-professional real-time data, you can still use Market Data to access delayed or historical data based on each exchange's policies. - **Cancel your subscription** if the pricing does not work for you. See [Cancellations](https://www.marketdata.app/docs/docs/account/cancellations/). ## Still Need Help? If you are unsure why your account was classified as professional, or if you want to discuss your specific situation, contact our [support helpdesk](https://www.marketdata.app/dashboard/). We are happy to walk you through the classification criteria and help you determine the correct status. We typically respond within a few hours during market hours or within 1 business day outside of market hours. --- # Financial Industry Employer Source: https://www.marketdata.app/docs/account/troubleshooting/financial-employer/index.md ## Problem Overview Your account verification requires additional review because your employer is a bank, brokerage, insurance company, investment firm, or other financial institution. Working at a financial institution does not automatically make you a professional data user — but it does require us to look more closely at your actual job responsibilities before we can classify your account. ## Why Financial Industry Employees Get Additional Review Exchange regulations require Market Data to classify every subscriber as either a professional or non-professional data user. For most subscribers, a job title and employer are enough to make this determination. But at financial institutions, the same company can employ both professional and non-professional users depending on the role: | Role | Classification | Reason | |-------------------------------|------------------|-------------------------------------------------------------------------------| | Portfolio Manager | Professional | Makes investment decisions | | Investment Adviser | Professional | Provides tailored investment advice to clients | | Trader | Professional | Executes securities or derivatives trades | | Research Analyst | Professional | Produces investment research that influences trading decisions | | Software Developer (fin-tech) | Professional | Develops software that supports investment activities | | Administrative Assistant | Non-Professional | Provides administrative support without engaging in investment activities | | Customer Service Rep | Non-Professional | Handles inquiries without providing investment advice | | Compliance Officer | Non-Professional | Ensures regulatory compliance but does not engage in investment decisions | | HR Manager | Non-Professional | Manages human resources, not involved in investment activities | | Accountant | Non-Professional | Handles financial records and reporting, not directly involved in investments | Because we cannot determine your classification from your employer name alone, we need to understand what you actually do day-to-day. ## How to Fix Contact our [support helpdesk](https://www.marketdata.app/dashboard/) and provide a brief description of your **actual daily job responsibilities**. Specifically, we need to know whether your role involves any of the following: - Trading, executing, or managing securities, commodities, or derivatives - Providing investment advice or recommendations to clients - Managing investment portfolios or making investment decisions - Producing research that influences trading or advisory decisions - Developing software or systems that directly support investment activities If your role does **not** involve any of the above — for example, if you work in IT, operations, HR, compliance, accounting, marketing, or administrative support — you will typically be classified as non-professional, even though your employer is a financial institution. ### Supporting Documentation To speed up the review, you can also provide any of the following: - A link to your **LinkedIn profile** showing your current role and responsibilities - A **resume or CV** that describes your job function - A **company directory listing** or job description that outlines your responsibilities ## What Determines Your Classification Your classification is based on your **job function**, not your employer. The key question is whether your day-to-day work involves securities-related activities — not whether the company you work for is in financial services. For example, a software developer at a social media company is non-professional, but a software developer at a fin-tech startup building trading tools is professional. The same logic applies within a bank: an HR manager is non-professional, but a relationship manager advising clients on investments is professional. See [Professional Status Policy](https://www.marketdata.app/docs/docs/account/data-policies/professional-status/) for the full classification criteria, or read [Professional Status Explained](https://www.marketdata.app/education/stocks/professional-status-explained/) for a detailed guide with additional examples. ## If You Are Classified as Professional Professional subscribers never receive real-time exchange data on a self-service plan, no matter which exchange agreements they sign. Real-time access for professional use requires direct exchange licensing, which Market Data arranges individually. [Contact our sales team](https://www.marketdata.app/contact/) to discuss it. Delayed / historical data access remains available on your current plan, depending on the data policies of each exchange. ## Still Need Help? If you are unsure how your role would be classified, contact our [support helpdesk](https://www.marketdata.app/dashboard/) and describe your day-to-day responsibilities. Our support team will let you know what classification applies. We typically respond within a few hours during market hours or within 1 business day outside of market hours. --- # Incomplete Profile Source: https://www.marketdata.app/docs/account/troubleshooting/incomplete-profile/index.md ## Problem Overview Your account verification is delayed because your profile contains missing, vague, or placeholder information. Market Data must verify your identity and employment details before activating real-time data, and we cannot complete verification when required fields are incomplete or do not match third-party records. ## Common Examples The following are examples of profile entries that will prevent verification from completing: ### Employer Field - **"N/A"** or left blank — An employer is required for all subscribers. If you are retired or not working, see [Retired or Not Working](https://www.marketdata.app/docs/docs/account/troubleshooting/retired-or-unemployed). If you are self-employed, see [Self-Employed or Business Owner](https://www.marketdata.app/docs/docs/account/troubleshooting/self-employed). - **"Government"** — This is too vague. We need the specific agency or department (e.g., "U.S. Department of the Treasury" or "Ministry of Finance, UAE"). - **A job title instead of an employer** — For example, entering "Software Engineer" or "Investor" in the employer field. The employer field must contain the name of the company or organization you work for. ### Job Title Field - **"N/A"**, **"Investor"**, or left blank — A specific job title is required. "Investor" is not a job title — it describes how you intend to use the data, not your employment. - **An employer name instead of a job title** — The job title field should describe your role (e.g., "Financial Analyst", "IT Manager", "Retired"). - **Same value in both fields** — Entering your job title in both the employer and job title fields, or your employer name in both fields. ### Contact Information - **Incomplete address** — A full mailing address is required, including city, state/province, and country. - **Invalid phone number** — The phone number must be a working number where you can be reached. ## Why This Is Required Market Data is required by the stock and options exchanges to verify the identity and employment of every paid subscriber before granting access to real-time data. We cross-reference the information you provide against third-party databases, including LinkedIn and FINRA BrokerCheck, to classify you as a professional or non-professional user. When fields are missing or contain placeholder values, we cannot complete this verification and your account remains in a pending state. See [Account Verification Policy](https://www.marketdata.app/docs/docs/account/data-policies/account-verification/) for full details. Completing your profile activates real-time data only if the review classifies you as non-professional. Professional subscribers never receive real-time exchange data on a self-service plan, no matter which exchange agreements they sign. Real-time access for professional use requires direct exchange licensing — [contact our sales team](https://www.marketdata.app/contact/) to discuss it. ## How to Fix 1. Log in to the [Customer Dashboard](https://www.marketdata.app/dashboard/). 2. Navigate to your profile settings. 3. Update all fields with accurate, specific information: - **Employer**: The full legal name of the company or organization you work for. - **Job Title**: Your actual role or position at that company. - **Address**: Your complete mailing address. - **Phone Number**: A valid phone number. 4. Save your changes. Once your profile is updated, verification will be completed within 1 business day. ## Still Need Help? If you have updated your profile and your account has not been activated, please contact our [support helpdesk](https://www.marketdata.app/dashboard/) and let our support team know that you have updated your profile with complete information. We typically respond within a few hours during market hours or within 1 business day outside of market hours. --- # LinkedIn Profile Issues Source: https://www.marketdata.app/docs/account/troubleshooting/linkedin-issues/index.md ## Problem Overview Your account verification is delayed because we cannot verify your employment information from the LinkedIn profile you provided. This is one of the most common reasons for verification delays — more often than not, the profile is unusable rather than missing entirely. ## Common Scenarios - **No LinkedIn profile provided** — You entered the placeholder URL (`https://www.linkedin.com`) or left the field blank. - **Broken URL** — The LinkedIn link you provided does not work or leads to a page that does not exist. - **Company page instead of personal profile** — You provided a link to your company's LinkedIn page instead of your own. A company page tells us where you work, but not your specific role. - **Private profile** — Your LinkedIn profile is set to private, so we cannot view your employment history. - **Stub profile** — You created a LinkedIn profile to complete the sign-up form, but it does not list your employer, job title, or work history. A profile with only a name and photo is not sufficient for verification. - **Name mismatch** — The name on your LinkedIn profile does not match the name on your data use profile. We cannot confirm that the LinkedIn profile belongs to you if the names do not match. - **Employment mismatch** — The employment on your data use profile does not match what your LinkedIn profile shows. For example, you declared one employer or job title but your LinkedIn shows a different active role. We cannot confirm your declared employment when the two sources disagree. ## Why We Need This Market Data is required by the stock and options exchanges to verify the identity and employment of every paid subscriber before granting access to data. A LinkedIn profile is the fastest way for us to do this because it is a widely used, publicly accessible professional database. When we cannot verify your employment through LinkedIn, we need alternative documentation to complete the process. See [Account Verification Policy](https://www.marketdata.app/docs/docs/account/data-policies/account-verification/) for full details. ## How to Fix You have two options: ### Option 1: Fix Your LinkedIn Profile Make sure your LinkedIn profile is: - **Public** (at least your employment section) - **Accessible** via the URL you provided - **A personal profile**, not a company page - **Consistent** — the employment shown matches what you declared on your data use profile (if a role has ended, make sure it has an end date) - **Matches the name** on your data use profile - **Complete enough** to confirm your role (more than just a name and photo) Once updated, contact our [support helpdesk](https://www.marketdata.app/dashboard/) with a link to your profile. ### Option 2: Provide Alternative Documentation If you do not have a LinkedIn profile or prefer not to use one, you can provide any of the following: - A current resume or CV - A photo of your business card showing your current job title and employer - A recent paystub (please redact any sensitive financial data) - A company directory or website listing that shows your name and role - Any other document that confirms your current employer and role This documentation helps us confirm your employment so we can classify your account correctly and ensure you receive the appropriate pricing for market data access. ## Still Need Help? If you are unsure what documentation to provide, please contact our [support helpdesk](https://www.marketdata.app/dashboard/) and our support team will let you know exactly what is needed to complete your verification. We typically respond within a few hours during market hours or within 1 business day outside of market hours. --- # Retired or Not Working Source: https://www.marketdata.app/docs/account/troubleshooting/retired-or-unemployed/index.md ## Problem Overview Your account verification is delayed because you indicated that you are retired or not currently employed, but we do not have sufficient documentation to confirm this. The stock and options exchanges require us to independently verify the employment status of every paid subscriber before activating real-time data — a self-reported status of "retired" alone is not sufficient. ## Why This Is Required Exchange regulations require Market Data to classify every subscriber as either a professional or non-professional data user. We cannot classify you as non-professional until we receive external confirmation that you are no longer employed in a role that would qualify as professional use. These rules come directly from the exchanges and apply before we can activate access to real-time market data. See [Account Verification Policy](https://www.marketdata.app/docs/docs/account/data-policies/account-verification/) and [Professional Status Policy](https://www.marketdata.app/docs/docs/account/data-policies/professional-status/) for full details. ## How to Fix Provide **one** of the following documents to confirm your retirement or unemployment status: 1. A link to your **LinkedIn profile** that clearly shows your most recent role with an **end date**, or otherwise indicates that you are retired. 2. A **resume or CV** that includes the **end date of your last position** and indicates you are no longer working. 3. An official **retirement letter** or documentation from your previous employer. 4. A **pension award letter**, **Social Security benefit statement**, or other government retirement document. 5. **Tax documentation** showing retirement income (e.g., Form 1099-R in the U.S.). You may redact your Social Security Number and any other sensitive financial details — we only need to confirm that the document reflects retirement income. 6. Any other reasonable documentation that confirms you are no longer actively employed in any professional capacity. Contact our [support helpdesk](https://www.marketdata.app/dashboard/) with any of the documents listed above. All documentation will remain confidential and used solely to verify your status. ### What We Look For The key detail in any document is a **clear end date** for your most recent position. A LinkedIn profile or resume that only lists a job title and employer without an end date does not confirm that you have left the role — it looks the same as a current position. ## Special Circumstances ### Consulting or Part-Time Work If you are doing consulting work, even on a part-time basis, this may affect your classification. Include details about the nature of the consulting you perform when contacting support so we can determine the correct classification. ### Retired From One Field, Working in Another If you have retired from one career but are now working in a different field, please provide details of your current role. As long as the work is not securities-related, we can typically classify your account as non-professional. ### Selected "Retired" for Privacy Reasons If you are still employed and selected "retired" because you preferred not to share your employment details, you can simply provide your current job title and employer instead. As long as your work is not securities-related, we can typically process your account as non-professional. See [Account Verification Policy](https://www.marketdata.app/docs/docs/account/data-policies/account-verification/) for details on what information we collect and how we use it. ## If You Are Classified as Professional Professional subscribers never receive real-time exchange data on a self-service plan, no matter which exchange agreements they sign. Real-time access for professional use requires direct exchange licensing, which Market Data arranges individually. [Contact our sales team](https://www.marketdata.app/contact/) to discuss it. Delayed / historical data access remains available on your current plan, depending on the data policies of each exchange. ## Still Need Help? If you are unsure what documentation to provide, please contact our [support helpdesk](https://www.marketdata.app/dashboard/) and our support team will let you know exactly what is needed to complete your verification. We typically respond within a few hours during market hours or within 1 business day outside of market hours. --- # Self-Employed or Business Owner Source: https://www.marketdata.app/docs/account/troubleshooting/self-employed/index.md ## Problem Overview Your account verification is delayed because you indicated that you are self-employed or own a business, but we cannot determine what your business does. When there is no public information available about your business — no website, no LinkedIn page, no public filings describing the business activity — we need additional documentation before we can classify your account. ## Why This Is Required Exchange regulations require Market Data to classify every subscriber as either a professional or non-professional data user. For self-employed subscribers, the classification depends on the nature of the business itself — specifically, whether the business is involved in financial services, investment management, or other activities that would qualify as professional use under exchange rules. A self-reported business name alone is not sufficient. We need to independently confirm what the business does. See [Account Verification Policy](https://www.marketdata.app/docs/docs/account/data-policies/account-verification/) and [Professional Status Policy](https://www.marketdata.app/docs/docs/account/data-policies/professional-status/) for full details. ## How to Fix ### Option 1: Provide a Public Link The fastest way to resolve this is to provide a link to any public source that describes your business: - A business website - A company LinkedIn page - A business directory listing - A public filing that describes the nature of the business Contact our [support helpdesk](https://www.marketdata.app/dashboard/) with the link and we can typically verify your account the same day. ### Option 2: Answer a Few Questions If your business does not have a public presence, contact our [support helpdesk](https://www.marketdata.app/dashboard/) with the following information: 1. The **registered name** of your business (if applicable). 2. Your **primary business activities and services** — what does the business do? 3. Whether your business: - Provides any investment advice or recommendations - Manages investments for others - Uses market data as part of its products or services - Shares or redistributes market data to others If your business is not involved in any of the above, we can typically classify your account as non-professional. ### Option 3: Provide Supporting Documentation You can also send any document that confirms your line of business: - A business registration or filing that describes the nature of the business - A brochure or marketing materials describing your products or services - Any other document that makes clear what the business does ## What Triggers Professional Classification Your business activity — not your job title — determines your classification. Businesses that do any of the following are generally classified as professional: - Provide investment advice or financial planning - Manage investments or trade on behalf of others - Use market data as part of a commercial product or service - Redistribute or display market data to third parties If your business is in a non-financial field (e.g., construction, healthcare, retail, technology), you will typically be classified as non-professional regardless of the fact that you are self-employed. ## If You Are Classified as Professional Professional subscribers never receive real-time exchange data on a self-service plan, no matter which exchange agreements they sign. Real-time access for professional use requires direct exchange licensing, which Market Data arranges individually. [Contact our sales team](https://www.marketdata.app/contact/) to discuss it. Delayed / historical data access remains available on your current plan, depending on the data policies of each exchange. If your business uses market data in a product or service, the [Commercial Plan](https://www.marketdata.app/docs/docs/account/plans/commercial/) is made for it: real-time stock and options prices with a commercial license, and no exchange licensing or exchange fees. ## Still Need Help? If you are unsure how your business would be classified, please contact our [support helpdesk](https://www.marketdata.app/dashboard/) and describe what your business does. Our support team will let you know what classification applies and what documentation, if any, is needed. We typically respond within a few hours during market hours or within 1 business day outside of market hours. --- # Plan Upgrades Source: https://www.marketdata.app/docs/account/upgrades/index.md At Market Data, we strive to make your experience with our services as seamless and positive as possible. When you decide to upgrade your plan, we ensure that your unused time is not wasted. The method we use to calculate your savings is the same, but the way you receive the credit is slightly different depending on whether you're moving from a monthly plan to an annual plan or from an annual plan to a monthly plan. Here's how we calculate and apply your unused time towards your new plan: ## Switching from a Monthly Plan to an Annual Plan If you decide to upgrade before your current monthly subscription period ends, we calculate the credit from the unused portion of your monthly plan. This credit is then applied as a discount on the upfront cost of your new annual plan. ### Step-by-Step Process: 1. **Calculate unused days**: We start by figuring out how many days remain in your current monthly subscription. 2. **Determine the daily rate**: We calculate the daily rate of your monthly plan by dividing the total cost by 30 (average number of days per month). 3. **Calculate your credit**: We multiply the number of unused days by the daily rate to find out how much credit you have. 4. **Apply this credit**: This amount is then discounted from the price of your new annual plan. ### Examples Let's look at two specific examples to understand how the discounts are calculated and applied when upgrading to an annual plan: #### Example 1: From Monthly Starter Plan to Annual Starter Plan - **Original Plan**: Monthly Starter Plan costing $30/month. - **New Plan**: Annual Starter Plan costing $144/year ($12/month billed annually). **Scenario**: Suppose you are halfway through your monthly cycle when you decide to upgrade. - **Unused Time Calculation**: 15 days remaining in the month. - **Daily Rate**: $30 / 30 = $1 per day. - **Credit Amount**: 15 days x $1/day = $15. - **Discount Applied**: Your new annual plan will be discounted by $15, so you pay $144 - $15 = $129 initially. #### Example 2: From Monthly Trader Plan to Annual Trader Plan - **Original Plan**: Monthly Trader Plan costing $75/month. - **New Plan**: Annual Trader Plan costing $360/year ($30/month billed annually). **Scenario**: Suppose you decide to upgrade with 10 days left in your monthly plan. - **Unused Time Calculation**: 10 days remaining. - **Daily Rate**: $75 / 30 = $2.50 per day. - **Credit Amount**: 10 days x $2.50/day = $25.00. - **Discount Applied**: Your new annual plan will be discounted by $25.00, so you pay $360 - $25.00 = $335.00 initially. ## Switching from an Annual Plan to a Monthly Plan When you upgrade to a different plan before your current annual subscription expires, we calculate the credit you will receive based on the percentage of unused days remaining in your subscription. This credit is then applied to extend the renewal date of your new, upgraded plan. ### Step-by-Step Calculation: 1. **Determine the unused days**: First, we count how many days are left until your current plan was set to expire. 2. **Calculate the percentage of the year left**: We calculate what percentage of the year remains from the moment you upgrade. 3. **Determine the credit**: We apply this percentage to the cost of your annual plan to figure out your credit amount. 4. **Apply the credit to your new plan**: This credit is then used to extend the renewal date of your newly purchased monthly plan. ### Examples To give you a clearer picture, here are two examples of how the credit is calculated and applied: #### Example 1: Upgrading from Annual Trader Plan to Prime Plan - **Original Plan**: Annual Trader Plan costing $360/year ($30/month billed annually). - **New Plan**: Prime Plan costing $250/month ($8.33 per day) **Scenario**: Suppose you decide to upgrade with 90 days left in your subscription year. - **Unused Time Calculation**: 90 days / 365 days = ~24.7% of the year left. - **Credit Amount**: 24.7% of $360 = $88.92. - **Application of Credit**: This $88.92 will be credited to your new Prime plan, extending the renewal date by 11 days. #### Example 2: Upgrading from Annual Starter Plan to Prime Plan - **Original Plan**: Annual Starter Plan costing $144/year ($12/month billed annually). - **New Plan**: Prime Plan costing $250/month. ($8.33 per day) **Scenario**: Suppose you decide to upgrade with 200 days left in your subscription year. - **Unused Time Calculation**: 200 days / 365 days = ~54.8% of the year left. - **Credit Amount**: 54.8% of $144 = $78.91. - **Application of Credit**: This $78.91 will be credited to your new Prime plan, extending the renewal date by 9 days. ## How To Get More Information We strive to make upgrading your plan a seamless and value-retaining process. At Market Data, we guarantee that every day of your subscription is accounted for, ensuring that you can upgrade your plan whenever you wish without forfeiting the financial worth of your initial investment. Should you have any questions or uncertainties about upgrading, please don't hesitate to reach out to us. --- # Market Data API Documentation Source: https://www.marketdata.app/docs/api/index.md The Market Data API is designed around REST and supports standard HTTP response codes and methods. All responses are delivered via JSON for programmatic use of the information or via CSV files to load into your preferred spreadsheet application. > [!NOTE] > **[Root Endpoint]** > > 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](https://www.marketdata.app/docs/api/universal-parameters/mode) and [Troubleshooting](https://www.marketdata.app/docs/api/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. > [!TIP] > Our endpoints have **lots of optional parameters** to allow users to sort and filter responses. It can be overwhelming to new users at first. When you're first getting started testing our API in Swagger, scroll to the required parameters and ignore all the optional ones. Most endpoints require only a ticker symbol as a required parameter. ### Get Started Quick — No Registration Required! You can try stock, option, and mutual fund endpoints with several different symbols that are unlocked and require no authorization token. That means these symbols can be used throughout our API with no registration required! - Stock endpoints: Use **AAPL**. - Options endpoints: Use any AAPL contract, for example: **AAPL271217C00250000**. - Mutual fund endpoints: Use **VFINX**. Once you would like to experiment with other symbols, [register a free account](https://www.marketdata.app/signup/) (no credit card required) and you be able to choose a free trial. After your trial ends, if you decide not to subscribe, you will still get 100 free requests per day. Make the decision to pay only after making a complete trial of our API. --- # Authentication Source: https://www.marketdata.app/docs/api/authentication/index.md 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](https://www.marketdata.app/docs/api/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. ## Using the Token There are two ways to pass this token to the API with your requests: 1. Header Authentication 2. URL Parameter Authentication > [!TIP] > We recommend using header-based authentication to ensure your token is not stored or cached. While Market Data makes a conscientious effort to delete tokens from our own server logs, we cannot guarantee that your token will not be stored by any of our third party cloud infrastructure partners. ## Header Authentication Add the token to the ```Authorization``` header using the word ```Bearer```. ### Code Examples ### HTTP ```http GET /v1/stocks/quotes/SPY/ HTTP/1.1 Host: api.marketdata.app Accept: application/json Authorization: Bearer {token} ``` > [!TIP] > The curly braces around token are a placeholder for this example. Do not actually wrap your token with curly braces. ### JavaScript ```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](https://www.marketdata.app/docs/sdk/js) and [authentication guide](https://www.marketdata.app/docs/sdk/js/authentication). ### TypeScript ```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](https://www.marketdata.app/docs/sdk/js) and [authentication guide](https://www.marketdata.app/docs/sdk/js/authentication). ### Python ```python from marketdata import MarketDataClient # Initialize the client (token is read from MARKETDATA_TOKEN environment variable) # Or pass it directly: client = MarketDataClient(token="your_token_here") client = MarketDataClient() # Get stock quotes for SPY quotes = client.stocks.quotes("SPY") print(quotes) ``` For more information about using the Python SDK, see our [Python SDK documentation](https://www.marketdata.app/docs/sdk/py) and [authentication guide](https://www.marketdata.app/docs/sdk/py/authentication). ### Go ```go // Import the Market Data SDK import api "github.com/MarketDataApp/sdk-go" func main() { // Create a new Market Data client instance marketDataClient := api.New() // Set the token for authentication // Replace "your_token_here" with your actual token marketDataClient.Token("your_token_here") // Now the client is ready to make authenticated requests to the Market Data API // Use the client to create a StockQuoteRequest sqr, err := api.StockQuote(marketDataClient).Symbol("SPY").Get() if err != nil { fmt.Println("Error fetching stock quotes:", err) return } // Process the retrieved quote for _, quote := range quotes { fmt.Printf(quote) } } ``` ### Java ```java title="App.java" import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.stocks.StockQuoteRequest; public class App { public static void main(String[] args) { // The no-arg constructor reads your token from the MARKETDATA_TOKEN // environment variable (or a .env file in the working directory). // Alternatively, pass it directly: // new MarketDataClient("your_token_here", null, null, true) try (MarketDataClient client = new MarketDataClient()) { client.stocks().quote(StockQuoteRequest.of("SPY")).values().forEach(System.out::println); } } } ``` ### Kotlin ```kotlin title="App.kt" import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.stocks.StockQuoteRequest fun main() { // The no-arg constructor reads your token from the MARKETDATA_TOKEN // environment variable (or a .env file in the working directory). // Alternatively, pass it directly: // MarketDataClient("your_token_here", null, null, true) MarketDataClient().use { client -> client.stocks().quote(StockQuoteRequest.of("SPY")).values().forEach(::println) } } ``` ### C# ```csharp title="Program.cs" using MarketDataApp; // With no options supplied, the SDK reads your token from the MARKETDATA_TOKEN // environment variable (or a .env file / user secrets) and validates it on startup. // Alternatively, pass it directly: // await MarketDataClient.CreateAsync(new MarketDataClientOptions { ApiToken = "your_token_here" }) using var client = await MarketDataClient.CreateAsync(); foreach (var quote in (await client.Stocks.GetQuoteAsync("SPY")).Values) { Console.WriteLine(quote); } ``` ## URL Parameter Authentication Add the token as a variable directly in the URL using the format ```token=YOUR_TOKEN_HERE```. For example: ``` https://api.marketdata.app/v1/stocks/quotes/SPY/?token={token} ``` > [!TIP] > The curly braces around token are a placeholder for this example. Do not actually wrap your token with curly braces. ## Demo The API With No Authentication You can try stock and option endpoints with several different symbols that are unlocked and do not require a token. Please be aware that only historical data for these tickers is available without a token. - Try any stock endpoint with **AAPL**, no token required. - Try any option endpoint with any AAPL contract, for example: **AAPL271217C00250000**. No token required. ## IP Address Restrictions Each account may only connect from one IP address at a time. You can switch devices, but you cannot use two devices simultaneously. Back-and-forth switching between IP addresses within a 5-minute window triggers a temporary block. See the [Single IP Address Policy](https://www.marketdata.app/docs/docs/account/data-policies/single-ip/) for full details, or [403: Multiple IP Addresses](https://www.marketdata.app/docs/docs/api/troubleshooting/multiple-ip-addresses/) if your account is blocked. --- # CORS Source: https://www.marketdata.app/docs/api/cors/index.md The Market Data API includes **Cross-Origin Resource Sharing (CORS)** headers as a convenience for subscribers who want to make API requests directly from a browser during development or for personal use. CORS support is not an invitation to build public-facing applications — self-service plans are personal licenses and [data redistribution](https://www.marketdata.app/docs/docs/account/data-policies/data-redistribution) is not permitted. ## Permitted Use Cases CORS is intended for **personal, non-public use** of the API in a browser environment. Examples of permitted use: - **Local development and prototyping** — building and testing apps on `localhost`. - **Personal dashboards and tools** — browser-based apps that only you access. These use cases are consistent with your self-service plan's personal license. The data you retrieve is for your own consumption — you are not making it available to others. ## What Is Not Permitted Using CORS to serve market data to other people is **redistribution**, regardless of whether your app is free or paid. This includes: - Public websites or apps that display market data to visitors - Browser-based products or services that expose Market Data's data to your users or customers - Shared dashboards or tools accessible to people outside your organization These are the uses the [Commercial Plan](https://www.marketdata.app/docs/docs/account/plans/commercial/) is made for: it includes the right to display data to your users, with no exchange licensing and no per-user fees. See the [Data Redistribution Policy](https://www.marketdata.app/docs/docs/account/data-policies/data-redistribution) for details, or [contact our sales team](https://www.marketdata.app/contact/) to get started. ## CORS Headers The API returns the following CORS headers on all responses, including preflight (`OPTIONS`) requests: | Header | Value | |--------------------------------|----------------------------------------------------------------------------------------------------------------| | `Access-Control-Allow-Origin` | `*` | | `Access-Control-Allow-Methods` | `DELETE, GET, OPTIONS, PATCH, POST, PUT` | | `Access-Control-Allow-Headers` | `accept, accept-encoding, authorization, content-type, dnt, origin, user-agent, x-csrftoken, x-requested-with` | | `Access-Control-Max-Age` | `86400` (24 hours) | ## Token Security When you use the API from a browser, your token is included in client-side code that **anyone with access to the page can inspect**. Only use your token in browser environments where you control who has access. > [!CAUTION] > **[Never Expose Your Token on a Public Website]** > > Do not embed your API token in any website, page, or app that is accessible to the public internet without authentication. Anyone who visits the page can extract your token from the network requests or source code and use it to make requests on your account, consuming your credits and accessing your data. > > If your token is compromised, contact our helpdesk via the [customer dashboard](https://www.marketdata.app/dashboard/) immediately to have it revoked and reissued. ## Example The following example is suitable for local development or a personal/internal app: ```javascript const response = await fetch("https://api.marketdata.app/v1/stocks/quotes/AAPL/", { headers: { Authorization: "Bearer YOUR_TOKEN", }, }); const data = await response.json(); console.log(data); ``` --- # Dates and Times Source: https://www.marketdata.app/docs/api/dates-and-times/index.md ## Response Timestamps By default, all timestamps returned by the API are **Unix timestamps** (seconds since January 1, 1970 UTC). You can change the output format using the [`dateformat`](https://www.marketdata.app/docs/docs/api/universal-parameters/date-format) universal parameter, which supports `unix` (default), `timestamp` (ISO 8601 with timezone), and `spreadsheet` formats. Refer to individual endpoint documentation for details on how timestamps are set for each response type. ## Response Timezone > [!NOTE] > **[All Timestamps Use Eastern Time]** > > All US market timestamps returned by the Market Data API are anchored to the **America/New_York** timezone (US Eastern Time). This applies to **all asset classes**, including equities and options. Even though many US-listed options trade on Chicago exchanges, the API normalizes all US timestamps to Eastern Time. The API automatically adjusts for **daylight saving time** (EST/EDT). During Eastern Standard Time (EST, UTC-5), midnight Eastern is 05:00 UTC. During Eastern Daylight Time (EDT, UTC-4), midnight Eastern is 04:00 UTC. You do not need to handle this offset yourself — simply parse all timestamps using the `America/New_York` timezone identifier and your date library will resolve the correct date. ### Date-Based Timestamps For responses that represent a **calendar date** rather than a specific moment in time — such as daily stock candles — the Unix timestamp is set to **midnight Eastern Time (00:00 America/New_York)** on the relevant date. For example, a daily candle for **March 6, 2026** has Unix timestamp `1772773200`, which equals **05:00 UTC** (= 00:00 Eastern during EST). The 05:00 UTC value does not represent a market event — it is simply midnight Eastern expressed as a UTC-based Unix timestamp. The date portion (March 6) identifies the trading session. > [!CAUTION] > **[Common Mistake]** > > If you parse a daily timestamp as UTC instead of Eastern Time, the date may appear shifted. For example, Unix timestamp `1772773200` is `2026-03-06 05:00:00 UTC` but `2026-03-06 00:00:00 America/New_York`. If your code extracts the date using UTC, it will produce the correct result in this case, but during EDT the offset changes to 04:00 UTC and edge cases can cause the date to shift by a day. Always use `America/New_York` when extracting dates from these timestamps. > [!TIP] > **[SDK Users]** > > If you use an official [Market Data SDK](https://www.marketdata.app/docs/docs/sdk), timezone handling is done for you automatically. The SDKs convert all API timestamps to proper date and time objects in America/New_York — for example, Carbon in PHP, datetime in Python, and time.Time in Go — so you don't need to manage timezone conversion yourself. ### Real-Time and Intraday Timestamps For real-time quotes, intraday candles, and `updated` fields, the timestamp represents the actual moment the data was captured or last refreshed. These are standard Unix timestamps and can be converted to any timezone, but they correspond to Eastern Time market hours (regular session 9:30 AM – 4:00 PM ET). ### Options Timestamps Although US-listed options primarily trade on exchanges in Chicago (CBOE), the API returns all option timestamps in **Eastern Time**, consistent with the rest of the API. The `updated` field on option quotes and chain responses reflects the Eastern Time snapshot of the data. Option expiration timestamps reflect the **actual expiration time**, not midnight. Equity options expire at **4:00 PM Eastern** and index options expire at **4:15 PM Eastern**. ## Request Date Formats All Market Data endpoints support advanced date-handling features to allow you to work with dates in a way that works best for your application. Our API will accept date inputs in any of the following formats: - **American Numeric Notation** Dates and times in MM/DD/YYYY format. For example, closing bell on Dec 30, 2020 for the NYSE would be: 12/30/2020 4:00 PM. - **Timestamp** An ISO 8601 timestamp in the format YYYY-MM-DD. For example, closing bell on Dec 30, 2020 for the NYSE would be: 2020-12-30 16:00:00. - **Unix** Dates and times in unix format (seconds after the unix epoch). For example, closing bell on Dec 30, 2020 for the NYSE would be: 1609362000. - **Spreadsheet** Dates and times in spreadsheet format (days after the Excel epoch). For example, closing bell on Dec 30, 2020 for the NYSE would be: 44195.66667 - **Relative Dates and Times** Keywords or key phrases that indicate specific days, relative to the current date. For example, "today" or "yesterday". - **Option Expiration Dates** Keyphrase that select specific dates that correspond with dates in the US option expiration calendar. ## Relative Dates and Times This feature allows you to use natural language to specify dates and times in a way that is easy for humans to read and understand, but can be tricky for machines to parse. Relative dates allow Market Data endpoints to continually modify the date sent to the endpoint based on the current date. We have a lot of relative date keywords supported already and quite a few more planned for the future, so keep an eye out on this section for continual improvements to this feature. - **Time-based Parameters** Time keyphrases let you select a specific time of day, relative to the current time. Time-based parameters are typically used to model intraday stock movements. - `now` Equivalent to the current time. Use this keyword to select the current open candle, for example. - `-[number] minutes` Use negative minutes to specify a time in the past _n_ minutes before. When this is used alone, it is relative to the current time. When used in conjunction in `from` field (i.e. the starting date/time), it is relative to the `to` field (i.e. the ending date/time). For example, if the current time is 10:30 AM, but 10:00 AM is used in the `to` field and `-10 minutes` in the `from` field, then the starting time will be 9:50 AM. The query would return values from 9:50 AM to 10:00 AM. However, if the `to` field were to be omitted, then the same query would return data from 10:20 AM to 10:30 AM since `-10 minutes` would be relative to the current time of 10:30 AM. - `[number] minutes ago` The `minutes ago` keyword lets you select a relative time, _n_ minutes before the current time. For example, if the time is 10:00 AM then `30 minutes ago` would refer to 9:30 AM of the current day. - `-[number] hours` Use negative hours to specify a time in the past _n_ hours before. When this is used alone, it is relative to the current time. When used in conjunction in `from` field (i.e. the starting date/time), it is relative to the `to` field (i.e. the ending date/time). For example, if the current time is 10:30 AM, but 10:00 AM is used in the `to` field and `-1 hour` in the `from` field, then the starting time will be 9:00 AM. The query would return values from 9:00 AM to 10:00 AM. However, if the `to` field were to be omitted, then the same query would return data from 9:30 AM to 10:30 AM since `-1 hour` would be relative to the current time of 10:30 AM. - `[number] hours ago` The `hours ago` keyword lets you select a relative time, _n_ hours before the current time. For example, if the time is 4:00 PM then `4 hours ago` would refer to 12:00 PM of the current day. - **Daily Parameters** Daily keyphrases let you select a specific day, relative to the current day. - `today` Equivalent to today's date. - `yesterday` Yesterday's date. - `last session` Returns the date of the last closed market session. This keyphrase always gets the previous closed session, accounting for weekends, holidays, and half-days (which are counted as valid sessions). For example, on Monday during market hours, it would return Friday's date. After market close on Monday, it would return Monday's date. This allows you to always track the previous session without having to account for weekends, holidays, or other market closures. - `-[number] days` Use negative days to specify a time in the past _n_ days before. When this is used alone, it is relative to the current day. When used in conjunction in `from` field (i.e. the starting date), it is relative to the `to` field (i.e. the ending date). For example, if the current date is January 20, but January 10 is used in the `to` field and `-5 days` in the `from` field, then the starting day will be January 5. The query would return values from January 5 to January 10. However, if the `to` field were to be omitted, then the same query would return data from January 15 to January 20 since `-5 days` would be relative to the current date of January 20. - `[number] days ago` The `days ago` keyword lets you select a relative day, _n_ days before the current date. For example, if today is January 5, 2024, then using `2 days ago` would select the date January 3, 2024. - **Weekly Parameters** Weekly keyphrases let you select a day of the week in the current, previous, or following week. - `-[number] weeks` Use negative weeks to specify a date in the past _n_ weeks before. When this is used alone, it is relative to the current day. When used in conjunction in `from` field (i.e. the starting date), it is relative to the date in the `to` field (i.e. the ending date). For example, if the current date is October 15, 2023 but October 8 is used in the `to` field and `-1 week` in the `from` field, then the starting day will be October 2, 2023. The query would return values from October 2 to October 8. However, if the `to` field were to be omitted, then the same query would return data from October 9 to October 15 since `-5 days` would be relative to the current date of January 20. - `[number] weeks ago` The `weeks ago` keyword lets you select a relative week, _n_ weeks before the current date. For example, if today is January 1, 2024, then using `2 weeks ago` would select the date January 3, 2024. - **Monthly Dates** Monthly keyphrases let you select a specific day of a specific month. - `-[number] months` Use negative months to specify a date in the past _n_ months before. When this is used alone, it is relative to the current day. When used in conjunction in `from` field (i.e. the starting date), it is relative to the date in the `to` field (i.e. the ending date). For example, if the current date is October 15 but October 8 is used in the `to` field and `-1 month` in the `from` field, then the starting day will be September 8. The query would return values from September 8 to October 8. However, if the `to` field were to be omitted, then the same query would return data from September 15 to October since `-1 month` would be relative to the current date of October 15. - `[number] months ago` The months ago keyword lets you select a relative date, _n_ months before the current date. For example, if today is January 5, 2024, then using `3 months ago` would select the date October 5, 2023. - **Yearly Dates** Yearly keyphrases let you select a specific day of in the current, previous, or following year. - `-[number] years` Use negative years to specify a date in the past _n_ years before. When this is used alone, it is relative to the current day. When used in conjunction in `from` field (i.e. the starting date), it is relative to the date in the `to` field (i.e. the ending date). For example, if the current date is October 15, 2023 but October 8, 2023 is used in the `to` field and `-1 year` in the `from` field, then the starting day will be September 8, 2022. The query would return values from September 8, 2022 to October 8, 2023. However, if the `to` field were to be omitted, then the same query would return data from September 15, 2022 to October 15, 2023 since `-1 year` would be relative to the current date of October 15, 2023. - `[number] years ago` The years ago keyword lets you select a relative date, 365 days before the current date. For example, if today is January 5, 2024, then using `2 years ago` would select the date January 5, 2022. > [!CAUTION] > **[Coming Soon]** > > > The following relative date parameters are planned for the future and have not yet been implemented. - **Time-based Parameters** Time keyphrases let you select a specific time of day, relative to the current time. Time-based parameters are typically used to model intraday stock movements. - `at open`, `opening bell`, `market open` These keyphrases let you select the opening time for the market day. The phase is relative to each exchange's opening time. For example, if you were trading AAPL in the United States, using `at open` would set a time of 9:30 AM ET. - `at close`, `closing bell`, `market close` These keyphrases let you select the closing time for the market day. The phase is relative to each exchange's closing time. For example, if you were trading AAPL in the United States, using `at close` would set a time of 4:00 PM ET. - `[number] [minutes|hours] before [open|close]` These before keyword lets you select a relative time before market open or close. For example `30 minutes before close` would select the time 3:30 PM ET if you are trading a stock on a U.S. exchange. - `[number] [minutes|hours] after [open|close]` These after keyword lets you select a relative time after market open or close. For example `1 hour after open` would select the time 10:30 AM ET if you are trading a stock on a U.S. exchange. - **Weekly Parameters** Weekly keyphrases let you select a day of the week in the current, previous, or following week. - `this [day of the week]` Works the same way as specifying the day without adding _this_. The day in the _current_ week. For example, if today is Tuesday and the expression is `this Monday`, the date returned would be yesterday. If the expression were `this Wednesday` the date returned would be tomorrow. The word _this_ is optional. If it is omitted, the keyword will still return the date in the current week that corresponds with the day mentioned. - `last [day of the week]` The day in the _previous_ week. For example, if today is Tuesday and the expression used is `last Monday`, it would not refer to the Monday that occurred yesterday, but the Monday 8 days prior that occurred in the previous week. - `next [day of the week]` The day in the _following_ week. For example, if today is Monday and the expression is `next Tuesday` it would not refer to tomorrow, but the Tuesday that occurs 8 days from now. - **Monthly Dates** Monthly keyphrases let you select a specific day of a specific month. - `[ordinal number] of [the|this] month` - The nth day of the current month. For example, if today is September 10th and the phrase used is, `8th of this month` the date returned would be September 8. The keyphrase `of [the/this] month` is optional. Using a single ordinal number `8th` will also return the 8th of the current month. - `[ordinal number] of last month` - The nth day of the current month. For example, if today is December 15th and the phrase used is, `8th of last month` the date returned would be November 8. - `ordinal number] of next month` - The nth day of the following month. For example, if today is December 15th and the phrase used is, `8th of next month` the date returned would be January 8 of the following year. - `last day of [the|this|last|next] month` - Using the `last day of` keyword will always select the final day of the month. Since months can end on the 28th, 29th, 30th, or 31st, this keyword allows you to always select the final day of a month. For example: `last day of this month`, `last day of next month`. It can also be used to select the last day in February without needing to determine whether the current year is a leap year, `last day of february`. - `ordinal number] [day of the week] of [the|this|last|next] month` - Combine ordinal numbers and weekdays to specify a specific day of the week in the current, previous, or following month. For example, ``3nd Friday of last month``. - `last [day of the week] of [the|this|last|next] month` - Selects the last day of the week in a month relative to the current month. If the last Monday of the month is needed, instead of using the keyphrase `4th Monday of this month`, it is safer to use `last Monday of this month`, since months can have 4 or 5 Mondays, depending on length. - `last [day of the week] in [month` - Selects the last day of the week in a specific month. For example, Memorial Day could be selected by using the keyphrase `last Monday in May`. - **Yearly Dates** Yearly keyphrases let you select a specific day of in the current, previous, or following year. - `[month] [number]` A specific date in the current year. For example `February 18` would return February 18 of the current year. - `[month] [number] [this|last|next] year` A specific date in the current, previous, or following year. For example, if today was Dec 31, 2022, `February 18 next year` would return February 18, 2023. ## Option Expiration Dates Option expiration dates let you target the expiration dates for option contracts. Dates are based on the US option expirations calendar and are only meant for use with US markets. > [!CAUTION] > > Option date parameters are planned for the future and have not yet been implemented. > > --- > > Option-related keyphrases cannot be used to return expiration dates far in the future for options which have not yet been traded or for options in the past which have already expired. For example, if today is January 15, 2023, you couldn't use `November 2023's 1st weekly expiration` since weekly options for November would not exist yet. The API will return a `No data` response if you try to request an expiration that does not exist, whether in the future or the past. - **Monthly Expirations** - Target a relative month or specific month's option expiration date. - `[month] [year] expiration` - The standard monthly option expiration date for [month] during [year]. This is useful for targeting the expiration date for a specific month. Although options normally expire the 3rd Friday, sometimes market holidays can modify this schedule. Using an option expiration keyphrase will ensure that you always obtain the exact date that options expire in a specific month. For example, if today was January 1, 2022, using `December expiration` or `December 2022 expiration` would both return _December 16, 2022_. - [year] is optional. If [month] is used without [year] the lookup is relative to the current date and expired options will not be returned. For example, if today is April 8, 2022, `January expiration` will return January 20, 2023 and not the options which expired in January of 2022. - `this|last|next] month's expiration` - Returns the monthly option expiration date for the current, previous, or following month relative to the current month. For example if today is October 5, 2022, and `next month's expiration` is used, the date returned would be _November 18, 2022_. > [!TIP] > > Not all underlying tickers offer weekly or quarterly options. Before building an API request that uses them, ensure that your underlying offers weekly or quarterly option contracts. - **Weekly Expirations** - Target a relative week or specific week's option expiration date. - `[this|last|next] week's expiration` - Returns the weekly option expiration date for the current, previous, or following week relative to the current week. For example if today is October 5, 2022, and `next week's expiration` is used, the date returned would be _October 14, 2022_. - `expiration in [number] weeks` - Returns closest expiration that will occur [number] weeks from today without taking into account the current week. For example, if today is August 1, 2022 the phrase `expiration in 6 weeks` would return September 16, 2022. - `[month] [year] [ordinal number] weekly expiration` - Returns the nth option expiration date for [month] during [year]. When both a month and year are combined, this can be used to lookup a weekly option date for an expired or unexpired option. For example, `March 2020's 2nd expiration` would return _March 14, 2020_. - **Quarterly Expirations** - Returns a quarterly expiration date for a relative date or specifically targeted date. - `[ordinal number] quarter's expiration` - Returns the quarterly option expiration date for the 1st, 2nd, 3rd, or 4th quarter in the current financial year. For example if today is March 1, 2022, and `4th quarter's expiration` is used, the date returned would be _December 30, 2022_. This will lookup both expired and unexpired options. - `[this|last|next] quarter's expiration` - Returns the quarterly option expiration date for the current, previous, or following quarter relative to the current date. For example if today is March 1, 2026, and `this quarter's expiration` is used, the date returned would be _March 31, 2026_. - `[expiration in [number] quarters` - Returns closest quarterly expiration that will occur [number] quarters from today without taking into account the current quarter. For example, if today is March 1, 2022 the phrase `expiration in 2 quarters` would return September 30, 2022. - `[year] [ordinal number] quarter expiration` - Returns the option expiration date for [nth] quarter during [year]. For example, `2020's 2nd quarter expiration` would return _June 30, 2020_. - **Specific Contract Expirations** - Target a specific date based on when a contract is first traded or when it expires. - `at expiration` - Returns the expiration date for the option contract. This must be used in the context of a specific option contract. For example, if you used at expiration with AAPL271217C00250000, the date returned would be December 17, 2027. - `first traded` - Returns the date when the contract was traded for the first time. This must be used in the context of a specific option contract. For example, if you used first traded with AAPL271217C00250000, the date returned would be March 13, 2025. --- # Mutual Funds Source: https://www.marketdata.app/docs/api/funds/index.md The mutual funds endpoints offer access to historical pricing data for mutual funds. ## Root Endpoint For Mutual Funds ``` https://api.marketdata.app/v1/funds/ ``` ## Funds Endpoints --- # Mutual Fund Candles Source: https://www.marketdata.app/docs/api/funds/candles/index.md Get historical price candles for a mutual fund. > [!WARNING] > This endpoint will be live on May 1, 2024. Before May 1, use the stocks/candles endpoint to query mutual fund candles. ## Endpoint ``` https://api.marketdata.app/v1/funds/candles/{resolution}/{symbol}/ ``` ### Method ``` GET ``` ## Request Example ### HTTP **GET** [https://api.marketdata.app/v1/funds/candles/D/VFINX/?from=2024-01-01&to=2024-01-31](https://api.marketdata.app/v1/funds/candles/D/VFINX/?from=2024-01-01&to=2024-01-31) ### JavaScript ```js title="fundCandles.js" 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", { resolution: "D", from: "2024-01-01", to: "2024-01-31", }); 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 ```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", { resolution: "D", from: "2024-01-01", to: "2024-01-31", }); 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); } ``` ### Python ```python title="fundCandles.py" from marketdata import MarketDataClient client = MarketDataClient() candles = client.funds.candles("VFINX", resolution="D", from_date="2024-01-01", to_date="2024-01-31") print(candles) ``` ### Go ```go title="fundCandles.go" import ( "fmt" api "github.com/MarketDataApp/sdk-go" ) func ExampleFundCandlesRequest() { fcr, err := FundCandles().Resolution("D").Symbol("VFINX").From("2024-01-01").To("2024-01-31").Get() if err != nil { fmt.Print(err) return } for _, candle := range fcr { fmt.Println(candle) } } ``` ### PHP ```php title="fundCandles.php" use MarketDataApp\Client; $client = new Client(); $candles = $client->mutual_funds->candles( symbol: "VFINX", from: "2024-01-01", to: "2024-01-31", resolution: "D" ); // Display formatted candles summary echo $candles; ``` ### Java ```java title="FundCandles.java" import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.funds.FundCandlesRequest; import com.marketdata.sdk.funds.FundResolution; import java.time.LocalDate; public class FundCandles { public static void main(String[] args) { try (MarketDataClient client = new MarketDataClient()) { // VFINX is available without authentication (free test symbol). client.funds() .candles(FundCandlesRequest.builder(FundResolution.DAILY, "VFINX") .from(LocalDate.parse("2024-01-01")) .to(LocalDate.parse("2024-01-31")) .build()) .values() .forEach(System.out::println); } } } ``` ### Kotlin ```kotlin title="FundCandles.kt" import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.funds.FundCandlesRequest import com.marketdata.sdk.funds.FundResolution import java.time.LocalDate fun main() { MarketDataClient().use { client -> // VFINX is available without authentication (free test symbol). client.funds() .candles( FundCandlesRequest.builder(FundResolution.DAILY, "VFINX") .from(LocalDate.parse("2024-01-01")) .to(LocalDate.parse("2024-01-31")) .build() ) .values() .forEach(::println) } } ``` ### C# ```csharp title="FundCandles.cs" using MarketDataApp; using MarketDataApp.Funds; using var client = await MarketDataClient.CreateAsync(); // VFINX is available without authentication (free test symbol). var candles = await client.Funds.GetCandlesAsync( FundResolution.Daily, "VFINX", from: DateOnly.Parse("2024-01-01"), to: DateOnly.Parse("2024-01-31")); foreach (var candle in candles.Values) { Console.WriteLine(candle); } ``` ## Response Example ```json { "s":"ok", "t":[1577941200,1578027600,1578286800,1578373200,1578459600,1578546000,1578632400], "o":[300.69,298.6,299.65,298.84,300.32,302.39,301.53], "h":[300.69,298.6,299.65,298.84,300.32,302.39,301.53], "l":[300.69,298.6,299.65,298.84,300.32,302.39,301.53], "c":[300.69,298.6,299.65,298.84,300.32,302.39,301.53] } ``` ## Request Parameters ### Required - **resolution** `string` The duration of each candle. Intraday resolutions are not supported for funds. *Case-insensitive*. - Daily Resolutions: (`daily`, `D`, `1D`, `2D`, ...) - Weekly Resolutions: (`weekly`, `W`, `1W`, `2W`, ...) - Monthly Resolutions: (`monthly`, `M`, `1M`, `2M`, ...) - Yearly Resolutions: (`yearly`, `Y`, `1Y`, `2Y`, ...) - **symbol** `string` The mutual fund's ticker symbol. - **from** `date` The leftmost candle on a chart (inclusive). If you use `countback`, `to` is not required. Accepted timestamp inputs: ISO 8601, unix, spreadsheet. - **to** `date` The rightmost candle on a chart (inclusive). Accepted timestamp inputs: ISO 8601, unix, spreadsheet. - **countback** `number` Will fetch a number of candles before (to the left of) `to`. If you use `from`, `countback` is not required. ## Response Attributes ### Success - **s** `string` ll always be `ok` when there is data for the candles requested. - **o** `array[number]` Open price. - **h** `array[number]` High price. - **l** `array[number]` Low price. - **c** `array[number]` Close price. - **t** `array[number]` Candle time. This is midnight US Eastern Time on the session date. All timestamps use US Eastern Time (America/New_York). See [Response Timezone](https://www.marketdata.app/docs/docs/api/dates-and-times#response-timezone) for details. ### No Data - **s** `string` Status will be `no_data` if no candles are found for the request. - **nextTime** `number` optional Unix time of the next quote if there is no data in the requested period, but there is data in a subsequent period. ### Error - **s** `string` Status will be `error` if the request produces an error response. - **errmsg** `string` An error message. --- # Markets Source: https://www.marketdata.app/docs/api/markets/index.md The Markets endpoints provide reference and status data about the markets covered by Market Data. ## Root Endpoint For Markets ``` https://api.marketdata.app/v1/markets/ ``` ## Markets Endpoints --- # Market Status Source: https://www.marketdata.app/docs/api/markets/status/index.md Get the past, present, or future status for a stock market. The endpoint will respond with "open" for trading days or "closed" for weekends or market holidays. ## Endpoint ``` https://api.marketdata.app/v1/markets/status/ ``` ### Method ``` GET ``` ## Request Example ### HTTP **GET** [https://api.marketdata.app/v1/markets/status/?from=2024-01-01&to=2024-01-31](https://api.marketdata.app/v1/markets/status/?from=2024-01-01&to=2024-01-31) **GET** [https://api.marketdata.app/v1/markets/status/?date=yesterday](https://api.marketdata.app/v1/markets/status/?date=yesterday) ### JavaScript ```js title="app.js" import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); try { const days = await client.markets.status({ from: "2024-01-01", to: "2024-01-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 ```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: "2024-01-01", to: "2024-01-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); } ``` ### Python ```python title="app.py" from marketdata import MarketDataClient client = MarketDataClient() status1 = client.markets.status(from_date="2024-01-01", to_date="2024-01-31") status2 = client.markets.status(date="yesterday") print(status1) print(status2) ``` ### Go ```go title="marketstatus.go" import ( "fmt" api "github.com/MarketDataApp/sdk-go" ) func ExampleMarketStatus() { msr, err := api.MarketStatus().From("2024-01-01").To("2024-01-31").Get() if err != nil { fmt.Print(err) return } for _, report := range msr { fmt.Println(report) } } func ExampleMarketStatus_relativeDates() { msr, err := api.MarketStatus().Date("yesterday").Get() if err != nil { fmt.Print(err) return } for _, report := range msr { fmt.Println(report) } } ``` ### PHP ```php title="marketStatus.php" use MarketDataApp\Client; $client = new Client(); // Get market status for a date range $statuses = $client->markets->status( from: "2024-01-01", to: "2024-01-31" ); // Display formatted market statuses echo $statuses; // Get market status for yesterday $yesterday = $client->markets->status( date: "yesterday" ); echo $yesterday; ``` ### Java ```java title="MarketStatusApp.java" import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.markets.MarketStatusRequest; import java.time.LocalDate; public class MarketStatusApp { public static void main(String[] args) { try (MarketDataClient client = new MarketDataClient()) { client.markets() .status(MarketStatusRequest.builder() .from(LocalDate.of(2024, 1, 1)) .to(LocalDate.of(2024, 1, 31)) .build()) .values() .forEach(System.out::println); client.markets() .status(MarketStatusRequest.builder() .date(LocalDate.now().minusDays(1)) .build()) .values() .forEach(System.out::println); } } } ``` ### Kotlin ```kotlin title="MarketStatusApp.kt" import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.markets.MarketStatusRequest import java.time.LocalDate fun main() { MarketDataClient().use { client -> client.markets() .status( MarketStatusRequest.builder() .from(LocalDate.of(2024, 1, 1)) .to(LocalDate.of(2024, 1, 31)) .build() ) .values() .forEach(::println) client.markets() .status( MarketStatusRequest.builder() .date(LocalDate.now().minusDays(1)) .build() ) .values() .forEach(::println) } } ``` ### C# ```csharp title="MarketStatusApp.cs" using MarketDataApp; using var client = await MarketDataClient.CreateAsync(); var january = await client.Markets.GetStatusAsync( from: new DateOnly(2024, 1, 1), to: new DateOnly(2024, 1, 31)); foreach (var day in january.Values) { Console.WriteLine(day); } var yesterday = await client.Markets.GetStatusAsync( date: DateOnly.FromDateTime(DateTime.Today.AddDays(-1))); foreach (var day in yesterday.Values) { Console.WriteLine(day); } ``` ## Response Example ```json { "s": "ok", "date": [1680580800], "status": ["open"] } ``` ## Request Parameters ### Required - There are no required parameters for `status`. If no parameter is given, the request will return the market status in the United States for the current day. ### Optional - **country** `string` Use to specify the country. Use the two digit ISO 3166 country code. If no country is specified, `US` will be assumed. Only countries that Market Data supports for stock price data are available (currently only the United States). - **date** `date` Consult whether the market was open or closed on the specified date. Accepted timestamp inputs: ISO 8601, unix, spreadsheet, relative date strings. - **from** `date` The earliest date (inclusive). If you use countback, from is not required. Accepted timestamp inputs: ISO 8601, unix, spreadsheet, relative date strings. - **to** `date` The last date (inclusive). Accepted timestamp inputs: ISO 8601, unix, spreadsheet, relative date strings. - **countback** `number` Countback will fetch a number of dates before `to` If you use from, countback is not required. ## Response Attributes ### Success - **s** `string` ll always be `ok` when there is data for the dates requested. - **date** `array[dates]` The date. - **status** `array[string]` The market status. This will always be `open` or `closed` or `null`. Half days or partial trading days are reported as `open`. Requests for days further in the past or further in the future than our data will be returned as `null`. ### No Data - **s** `string` Status will be `no_data` if no data is found for the request. ### Error - **s** `string` Status will be `error` if the request produces an error response. - **errmsg** `string` An error message. --- # Options Source: https://www.marketdata.app/docs/api/options/index.md The Market Data API provides a comprehensive suite of options endpoints, designed to cater to various needs around options data. These endpoints are designed to be flexible and robust, supporting both real-time and historical data queries. They accommodate a wide range of optional parameters for detailed data retrieval, making the Market Data API a versatile tool for options traders and financial analysts. ## Root Endpoint For Options ``` https://api.marketdata.app/v1/options/ ``` ## Options Endpoints --- # Option Chain Source: https://www.marketdata.app/docs/api/options/chain/index.md Get a current or historical end of day option chain for an underlying ticker symbol. Optional parameters allow for extensive filtering of the chain. ## Endpoint ```http GET https://api.marketdata.app/v1/options/chain/{underlyingSymbol}/ ``` ## Request Example ### HTTP **GET** [https://api.marketdata.app/v1/options/chain/AAPL/?expiration=2027-12-17&side=call](https://api.marketdata.app/v1/options/chain/AAPL/?expiration=2027-12-17&side=call) ### JavaScript ```js title="app.js" import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); try { const contracts = await client.options.chain("AAPL", { expiration: "2027-12-17", side: "call", }); console.log(`Got ${contracts.length} contracts`); } catch (error) { console.error(error); } ``` ### TypeScript ```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", { expiration: "2027-12-17", side: "call", }); console.log(`Got ${contracts.length} contracts`); } catch (error) { console.error(error); } ``` ### Python ```python title="app.py" from marketdata import MarketDataClient client = MarketDataClient() chain = client.options.chain("AAPL", expiration="2027-12-17", side="call") print(chain) ``` ### Go ```go title="optionChain.go" import ( "fmt" api "github.com/MarketDataApp/sdk-go" ) func ExampleOptionChainRequest() { AAPL, err := api.OptionChain().UnderlyingSymbol("AAPL").Expiration("2027-12-17").Side("call").Get() if err != nil { fmt.Println("Error fetching option chain:", err) return } for _, contract := range AAPL { fmt.Println(contract) } } ``` ### PHP ```php title="optionChain.php" use MarketDataApp\Client; use MarketDataApp\Enums\Side; $client = new Client(); $chain = $client->options->option_chain( symbol: "AAPL", expiration: "2027-12-17", side: Side::CALL ); // Display chain summary (expirations, contract counts) echo $chain; // Convenience methods available $calls = $chain->getCalls(); $puts = $chain->getPuts(); $strikes = $chain->getStrikes(); ``` ### Java ```java title="OptionChain.java" import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.options.ExpirationFilter; import com.marketdata.sdk.options.OptionSide; import com.marketdata.sdk.options.OptionsChainRequest; import java.time.LocalDate; try (MarketDataClient client = new MarketDataClient()) { client.options().chain( OptionsChainRequest.builder("AAPL") .expirationFilter(ExpirationFilter.onDate(LocalDate.parse("2027-12-17"))) .side(OptionSide.CALL) .build()) .values().forEach(System.out::println); } ``` ### Kotlin ```kotlin title="OptionChain.kt" import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.options.ExpirationFilter import com.marketdata.sdk.options.OptionSide import com.marketdata.sdk.options.OptionsChainRequest import java.time.LocalDate MarketDataClient().use { client -> client.options().chain( OptionsChainRequest.builder("AAPL") .expirationFilter(ExpirationFilter.onDate(LocalDate.parse("2027-12-17"))) .side(OptionSide.CALL) .build()) .values().forEach(::println) } ``` ### C# ```csharp title="OptionChain.cs" using MarketDataApp; using MarketDataApp.Options; using var client = await MarketDataClient.CreateAsync(); var chain = await client.Options.GetChainAsync( "AAPL", expiration: ExpirationFilter.ForDate(DateOnly.Parse("2027-12-17")), side: OptionSide.Call); foreach (var contract in chain.Values) { Console.WriteLine(contract); } ``` ## Response Example This is a current chain, so the Greek columns are populated. A historical request (`date=`) returns `null` for all five — see [the note on the `date` parameter](#request-parameters). ```json { "s": "ok", "optionSymbol": ["AAPL271217C00005000", "AAPL271217C00010000", "AAPL271217C00015000", "AAPL271217C00020000", "AAPL271217C00025000", "AAPL271217C00030000", "AAPL271217C00035000", "AAPL271217C00040000", "AAPL271217C00045000", "AAPL271217C00050000", "AAPL271217C00055000", "AAPL271217C00060000", "AAPL271217C00065000", "AAPL271217C00070000", "AAPL271217C00075000", "AAPL271217C00080000", "AAPL271217C00085000", "AAPL271217C00090000", "AAPL271217C00095000", "AAPL271217C00100000", "AAPL271217C00105000", "AAPL271217C00110000", "AAPL271217C00115000", "AAPL271217C00120000", "AAPL271217C00125000", "AAPL271217C00130000", "AAPL271217C00135000", "AAPL271217C00140000", "AAPL271217C00145000", "AAPL271217C00150000", "AAPL271217C00155000", "AAPL271217C00160000", "AAPL271217C00165000", "AAPL271217C00170000", "AAPL271217C00175000", "AAPL271217C00180000", "AAPL271217C00185000", "AAPL271217C00190000", "AAPL271217C00195000", "AAPL271217C00200000", "AAPL271217C00205000", "AAPL271217C00210000", "AAPL271217C00215000", "AAPL271217C00220000", "AAPL271217C00225000", "AAPL271217C00230000", "AAPL271217C00235000", "AAPL271217C00240000", "AAPL271217C00245000", "AAPL271217C00250000", "AAPL271217C00255000", "AAPL271217C00260000", "AAPL271217C00265000", "AAPL271217C00270000", "AAPL271217C00275000", "AAPL271217C00280000", "AAPL271217C00285000", "AAPL271217C00290000", "AAPL271217C00295000", "AAPL271217C00300000", "AAPL271217C00305000", "AAPL271217C00310000", "AAPL271217C00315000", "AAPL271217C00320000", "AAPL271217C00325000", "AAPL271217C00330000", "AAPL271217C00335000", "AAPL271217C00340000", "AAPL271217C00345000", "AAPL271217C00350000", "AAPL271217C00355000", "AAPL271217C00360000", "AAPL271217C00365000", "AAPL271217C00370000", "AAPL271217C00375000", "AAPL271217C00380000", "AAPL271217C00385000", "AAPL271217C00390000", "AAPL271217C00395000", "AAPL271217C00400000", "AAPL271217C00405000", "AAPL271217C00410000", "AAPL271217C00415000", "AAPL271217C00420000", "AAPL271217C00425000", "AAPL271217C00430000", "AAPL271217C00435000", "AAPL271217C00440000", "AAPL271217C00450000", "AAPL271217C00460000", "AAPL271217C00470000", "AAPL271217C00480000", "AAPL271217C00490000", "AAPL271217C00500000", "AAPL271217C00510000", "AAPL271217C00520000", "AAPL271217C00530000", "AAPL271217C00540000", "AAPL271217C00550000", "AAPL271217C00560000", "AAPL271217C00570000", "AAPL271217C00580000", "AAPL271217C00590000", "AAPL271217C00600000"], "underlying": ["AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL"], "expiration": [1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200, 1829077200], "side": ["call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call", "call"], "strike": [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 105, 110, 115, 120, 125, 130, 135, 140, 145, 150, 155, 160, 165, 170, 175, 180, 185, 190, 195, 200, 205, 210, 215, 220, 225, 230, 235, 240, 245, 250, 255, 260, 265, 270, 275, 280, 285, 290, 295, 300, 305, 310, 315, 320, 325, 330, 335, 340, 345, 350, 355, 360, 365, 370, 375, 380, 385, 390, 395, 400, 405, 410, 415, 420, 425, 430, 435, 440, 450, 460, 470, 480, 490, 500, 510, 520, 530, 540, 550, 560, 570, 580, 590, 600], "firstTraded": [1761744600, 1761744600, 1761744600, 1761744600, 1761744600, 1761744600, 1761744600, 1761744600, 1761744600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1741872600, 1761312600, 1761312600, 1761312600, 1761312600, 1761312600, 1761312600, 1761744600, 1761744600, 1761744600, 1764858600, 1764858600, 1779111000, 1779111000, 1779111000, 1779111000, 1779111000], "dte": [485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485], "updated": [1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600, 1787169600], "bid": [309.95, 305.1, 300.35, 295.6, 290.85, 286.15, 281.4, 276.7, 272, 267.25, 262.55, 257.85, 253.2, 248.5, 243.8, 239.15, 234.45, 229.8, 225.1, 220.45, 215.8, 211.15, 206.5, 201.85, 197.25, 192.65, 188.05, 183.45, 178.85, 174.3, 169.8, 165.2, 160.75, 156.25, 151.8, 147.4, 143, 138.65, 134.3, 130.2, 125.8, 121.6, 117.75, 113.4, 109.45, 105.55, 101.4, 97.55, 94.05, 90.4, 86.8, 82.95, 79.35, 76.05, 72.6, 69.95, 66.45, 63.8, 60.85, 58.2, 55.4, 52.6, 49.7, 47.3, 45.6, 42.9, 40.9, 38.9, 36.8, 35.25, 33.35, 31.4, 29.85, 28.1, 26.55, 25.05, 23.65, 22.3, 21, 20, 18.7, 17.6, 16.75, 15.65, 14.75, 13.9, 13.1, 12.4, 11, 9.7, 8.6, 7.65, 6.8, 6.1, 5.4, 4.8, 4.25, 3.8, 3.35, 3, 2.7, 2.41, 2.2, 1.99], "bidSize": [11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 10, 10, 10, 10, 10, 10, 27, 88, 139, 101, 110, 177, 261, 207, 75, 130, 336, 304, 137, 70, 263, 189, 262, 287, 204, 352, 20, 200, 392, 121, 213, 180, 230, 152, 184, 222, 217, 146, 115, 31, 146, 42, 44, 99, 11, 56, 37, 44, 29, 44, 30, 36, 28, 41, 50, 53, 28, 34, 98, 32, 29, 86, 48, 54, 78, 23, 25, 21, 62, 28, 77, 188, 107, 35, 37, 27, 28, 71, 54], "mid": [311.85, 307, 302.25, 297.5, 292.75, 288.05, 283.3, 278.6, 273.875, 269.15, 264.45, 259.75, 255.075, 250.4, 245.7, 241.025, 236.35, 231.675, 227, 222.35, 217.7, 213.05, 208.4, 203.75, 199.15, 194.525, 189.925, 185.35, 180.75, 176.15, 171.525, 167.075, 162.5, 158, 153.55, 149.125, 144.75, 139.95, 136.025, 131.425, 127.55, 122.9, 119.3, 114.9, 111.025, 107.2, 102.775, 99.275, 95.225, 91.7, 87.9, 84.2, 81.125, 77.325, 74.05, 70.925, 67.7, 65.075, 62.05, 59.1, 56.65, 53.6, 51.075, 48.275, 46.225, 43.75, 41.85, 39.6, 37.45, 35.65, 33.775, 31.9, 30.25, 28.575, 26.975, 25.525, 24.125, 22.725, 21.475, 20.325, 19.1, 18.05, 17.125, 16.025, 15.175, 14.3, 13.475, 12.7, 11.35, 10.05, 8.875, 7.9, 7.025, 6.35, 5.6, 5.075, 4.425, 4.025, 3.625, 3.25, 2.925, 2.63, 2.385, 2.15], "ask": [313.75, 308.9, 304.15, 299.4, 294.65, 289.95, 285.2, 280.5, 275.75, 271.05, 266.35, 261.65, 256.95, 252.3, 247.6, 242.9, 238.25, 233.55, 228.9, 224.25, 219.6, 214.95, 210.3, 205.65, 201.05, 196.4, 191.8, 187.25, 182.65, 178, 173.25, 168.95, 164.25, 159.75, 155.3, 150.85, 146.5, 141.25, 137.75, 132.65, 129.3, 124.2, 120.85, 116.4, 112.6, 108.85, 104.15, 101, 96.4, 93, 89, 85.45, 82.9, 78.6, 75.5, 71.9, 68.95, 66.35, 63.25, 60, 57.9, 54.6, 52.45, 49.25, 46.85, 44.6, 42.8, 40.3, 38.1, 36.05, 34.2, 32.4, 30.65, 29.05, 27.4, 26, 24.6, 23.15, 21.95, 20.65, 19.5, 18.5, 17.5, 16.4, 15.6, 14.7, 13.85, 13, 11.7, 10.4, 9.15, 8.15, 7.25, 6.6, 5.8, 5.35, 4.6, 4.25, 3.9, 3.5, 3.15, 2.85, 2.57, 2.31], "askSize": [11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 17, 17, 12, 17, 1, 15, 15, 18, 17, 17, 25, 29, 10, 42, 10, 26, 10, 86, 25, 71, 31, 12, 69, 7, 33, 10, 16, 76, 24, 20, 10, 40, 72, 60, 53, 23, 56, 59, 21, 18, 55, 43, 44, 34, 21, 21, 23, 35, 13, 10, 45, 38, 10, 38, 25, 10, 42, 55, 14, 57, 26, 113, 15, 39, 35, 15, 7, 12, 48, 41, 113, 5, 105, 29, 39, 38, 30, 99, 16], "last": [310.06, 295, 231.29, 227.46, 223.24, 270.6, null, 214.49, 204.68, 255.03, 249.56, 242.5, 238.3, 235.95, 268.6, 229.45, 232.42, 227.75, 217.3, 210.86, 207.2, 203.4, 198.6, 193.6, 196.83, 185.05, 180.45, 176, 179, 175, 175.05, 152.1, 151.95, 150.8, 143, 138.65, 133.15, 136.4, 134.6, 132.15, 116.2, 124, 119.2, 109.17, 110.15, 106.55, 104.9, 99, 90.98, 92.1, 89.95, 83.29, 80.35, 78, 63.66, 71.55, 68.5, 66.04, 61.9, 58.85, 56.1, 53.04, 51.52, 47.75, 45.3, 42.72, 41.46, 40.67, 37.13, 34.65, 32.95, 31.5, 31, 28.3, 26.43, 25.25, 22.65, 18.2, 21.6, 20.25, 19, 16.8, 15.85, 16.5, 12.45, 13.8, 11.9, 12.33, 11.3, 9.9, 6.87, 6.5, 5.2, 6.23, 4.4, 4.66, 4.3, 3.37, 2.65, 2.83, 2.32, 2.1, 2.04, 2.16], "openInterest": [1, 3, 25, 1, 1, 9, 0, 8, 17, 820, 411, 297, 157, 168, 132, 427, 435, 172, 151, 365, 102, 231, 384, 271, 447, 443, 108, 443, 581, 636, 94, 410, 266, 497, 342, 841, 1260, 572, 371, 3959, 875, 770, 916, 983, 2026, 1552, 584, 810, 562, 3469, 552, 2447, 618, 2537, 394, 2184, 469, 1600, 443, 8516, 778, 6420, 822, 798, 575, 2129, 2276, 2342, 715, 1644, 2073, 1061, 1453, 1122, 1108, 698, 469, 932, 494, 5461, 310, 173, 182, 479, 190, 581, 364, 7435, 2920, 536, 145, 109, 314, 790, 423, 65, 348, 157, 792, 31, 42, 31, 75, 467], "volume": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 3, 4, 0, 2, 3, 0, 1, 3, 1, 1, 1, 4, 6, 89, 78, 89, 0, 9, 2, 6, 5, 55, 4, 30, 8, 36, 12, 8, 5, 1, 1, 150, 44, 14, 11, 3, 7, 16, 34, 0, 6, 177, 10, 4, 2, 3, 0, 1, 1, 10, 6, 7, 0, 0, 0, 2, 0, 0, 6, 0, 0, 0, 0, 0, 0, 11], "inTheMoney": [true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "intrinsicValue": [311.9, 306.9, 301.9, 296.9, 291.9, 286.9, 281.9, 276.9, 271.9, 266.9, 261.9, 256.9, 251.9, 246.9, 241.9, 236.9, 231.9, 226.9, 221.9, 216.9, 211.9, 206.9, 201.9, 196.9, 191.9, 186.9, 181.9, 176.9, 171.9, 166.9, 161.9, 156.9, 151.9, 146.9, 141.9, 136.9, 131.9, 126.9, 121.9, 116.9, 111.9, 106.9, 101.9, 96.9, 91.9, 86.9, 81.9, 76.9, 71.9, 66.9, 61.9, 56.9, 51.9, 46.9, 41.9, 36.9, 31.9, 26.9, 21.9, 16.9, 11.9, 6.9, 1.9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "extrinsicValue": [0.05, 0.1, 0.35, 0.6, 0.85, 1.15, 1.4, 1.7, 1.975, 2.25, 2.55, 2.85, 3.175, 3.5, 3.8, 4.125, 4.45, 4.775, 5.1, 5.45, 5.8, 6.15, 6.5, 6.85, 7.25, 7.625, 8.025, 8.45, 8.85, 9.25, 9.625, 10.175, 10.6, 11.1, 11.65, 12.225, 12.85, 13.05, 14.125, 14.525, 15.65, 16.0, 17.4, 18.0, 19.125, 20.3, 20.875, 22.375, 23.325, 24.8, 26.0, 27.3, 29.225, 30.425, 32.15, 34.025, 35.8, 38.175, 40.15, 42.2, 44.75, 46.7, 49.175, 48.275, 46.225, 43.75, 41.85, 39.6, 37.45, 35.65, 33.775, 31.9, 30.25, 28.575, 26.975, 25.525, 24.125, 22.725, 21.475, 20.325, 19.1, 18.05, 17.125, 16.025, 15.175, 14.3, 13.475, 12.7, 11.35, 10.05, 8.875, 7.9, 7.025, 6.35, 5.6, 5.075, 4.425, 4.025, 3.625, 3.25, 2.925, 2.63, 2.385, 2.15], "underlyingPrice": [316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9, 316.9], "iv": [2.2106, 1.6815, 1.445, 1.2905, 1.1768, 1.0959, 1.0217, 0.9659, 0.9142, 0.8686, 0.8307, 0.7962, 0.767, 0.7398, 0.7124, 0.6887, 0.6664, 0.6451, 0.6249, 0.6071, 0.5899, 0.5733, 0.5572, 0.5416, 0.5286, 0.5148, 0.5022, 0.4906, 0.4783, 0.4662, 0.4534, 0.4459, 0.4347, 0.4255, 0.4173, 0.4095, 0.4025, 0.3867, 0.3881, 0.3762, 0.3768, 0.3639, 0.3672, 0.3578, 0.3554, 0.3529, 0.3424, 0.3428, 0.3361, 0.3347, 0.3297, 0.3253, 0.3263, 0.3198, 0.3176, 0.3161, 0.3131, 0.3144, 0.3117, 0.3091, 0.3097, 0.3052, 0.304, 0.3003, 0.3014, 0.2989, 0.2998, 0.2978, 0.2959, 0.2959, 0.2949, 0.2934, 0.2929, 0.2918, 0.2906, 0.2901, 0.2895, 0.2884, 0.288, 0.2878, 0.2867, 0.2865, 0.2868, 0.2854, 0.2856, 0.2852, 0.2849, 0.2846, 0.2848, 0.2842, 0.2834, 0.2835, 0.2835, 0.2848, 0.2842, 0.2857, 0.2843, 0.2859, 0.2868, 0.2873, 0.288, 0.2887, 0.2897, 0.2904], "delta": [0.9938, 0.9928, 0.9919, 0.9913, 0.9906, 0.9899, 0.9893, 0.9885, 0.9878, 0.9871, 0.9863, 0.9854, 0.9844, 0.9833, 0.9823, 0.9812, 0.98, 0.9788, 0.9775, 0.976, 0.9744, 0.9728, 0.9711, 0.9694, 0.9672, 0.9652, 0.9628, 0.9602, 0.9577, 0.9551, 0.9526, 0.9486, 0.9455, 0.9417, 0.9373, 0.9327, 0.9275, 0.9252, 0.9165, 0.9123, 0.9031, 0.8987, 0.8874, 0.8809, 0.8711, 0.8608, 0.8535, 0.8411, 0.8312, 0.8185, 0.8069, 0.7945, 0.7797, 0.7671, 0.7524, 0.7373, 0.7221, 0.7056, 0.6897, 0.6735, 0.6567, 0.64, 0.6229, 0.6055, 0.5884, 0.5708, 0.554, 0.5364, 0.5189, 0.5021, 0.4851, 0.4679, 0.4515, 0.4349, 0.4185, 0.4028, 0.3873, 0.3717, 0.3569, 0.3428, 0.3281, 0.3146, 0.3021, 0.2881, 0.2762, 0.2641, 0.2524, 0.2411, 0.2205, 0.2005, 0.1818, 0.1653, 0.1502, 0.1377, 0.1242, 0.114, 0.1019, 0.0937, 0.0855, 0.0778, 0.0709, 0.0646, 0.0592, 0.054], "gamma": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0002, 0.0002, 0.0002, 0.0002, 0.0002, 0.0003, 0.0003, 0.0003, 0.0003, 0.0004, 0.0004, 0.0004, 0.0005, 0.0005, 0.0006, 0.0006, 0.0006, 0.0007, 0.0008, 0.0008, 0.0009, 0.0009, 0.001, 0.0011, 0.0012, 0.0013, 0.0014, 0.0015, 0.0016, 0.0017, 0.0018, 0.0019, 0.002, 0.0021, 0.0022, 0.0024, 0.0024, 0.0026, 0.0027, 0.0028, 0.0029, 0.003, 0.0031, 0.0032, 0.0032, 0.0033, 0.0034, 0.0035, 0.0035, 0.0036, 0.0036, 0.0036, 0.0037, 0.0037, 0.0037, 0.0037, 0.0037, 0.0037, 0.0037, 0.0036, 0.0036, 0.0036, 0.0035, 0.0035, 0.0034, 0.0034, 0.0033, 0.0033, 0.0032, 0.0031, 0.0031, 0.003, 0.0028, 0.0027, 0.0026, 0.0024, 0.0022, 0.0021, 0.002, 0.0019, 0.0017, 0.0016, 0.0015, 0.0014, 0.0013, 0.0012, 0.0011, 0.001], "theta": [-0.002, -0.0032, -0.0041, -0.0048, -0.0055, -0.0063, -0.0069, -0.0077, -0.0083, -0.009, -0.0097, -0.0105, -0.0113, -0.0121, -0.0128, -0.0136, -0.0144, -0.0152, -0.016, -0.0168, -0.0176, -0.0184, -0.0192, -0.02, -0.0209, -0.0218, -0.0227, -0.0236, -0.0245, -0.0253, -0.026, -0.0272, -0.028, -0.029, -0.0301, -0.0312, -0.0323, -0.0325, -0.0345, -0.035, -0.0369, -0.0373, -0.0395, -0.0402, -0.0416, -0.0431, -0.0435, -0.0452, -0.0459, -0.0473, -0.0482, -0.049, -0.0505, -0.051, -0.0519, -0.0528, -0.0534, -0.0545, -0.0549, -0.0553, -0.056, -0.056, -0.0562, -0.0561, -0.0564, -0.0562, -0.0563, -0.056, -0.0556, -0.0553, -0.0548, -0.0542, -0.0536, -0.0529, -0.0521, -0.0513, -0.0505, -0.0495, -0.0486, -0.0477, -0.0466, -0.0456, -0.0447, -0.0434, -0.0424, -0.0413, -0.0402, -0.0391, -0.037, -0.0348, -0.0325, -0.0305, -0.0285, -0.027, -0.025, -0.0235, -0.0216, -0.0203, -0.019, -0.0177, -0.0165, -0.0153, -0.0143, -0.0133], "vega": [0.0204, 0.0308, 0.0388, 0.0455, 0.0515, 0.0586, 0.0641, 0.0711, 0.0773, 0.0835, 0.0907, 0.0981, 0.1067, 0.1155, 0.1234, 0.1326, 0.1421, 0.1518, 0.1617, 0.173, 0.1847, 0.1965, 0.2087, 0.2212, 0.2364, 0.2506, 0.2664, 0.2837, 0.3002, 0.317, 0.3331, 0.3574, 0.3764, 0.3991, 0.4242, 0.4505, 0.479, 0.4916, 0.5373, 0.5586, 0.6043, 0.6252, 0.6773, 0.7062, 0.7485, 0.7908, 0.8197, 0.867, 0.903, 0.9467, 0.985, 1.0238, 1.0678, 1.1028, 1.1411, 1.1782, 1.2127, 1.2472, 1.2778, 1.3064, 1.333, 1.3567, 1.3782, 1.3971, 1.4128, 1.4262, 1.4363, 1.4439, 1.4488, 1.4507, 1.4501, 1.4467, 1.441, 1.4326, 1.4219, 1.4091, 1.3943, 1.3771, 1.3586, 1.339, 1.3163, 1.2937, 1.271, 1.2437, 1.2188, 1.1918, 1.1642, 1.1361, 1.0811, 1.0224, 0.9628, 0.9066, 0.8512, 0.8029, 0.7478, 0.7038, 0.6493, 0.6104, 0.5704, 0.5311, 0.4949, 0.4605, 0.4301, 0.4001] } ``` ## Request Parameters ### Required - **underlyingSymbol** `string` The underlying ticker symbol for the options chain you wish to lookup. ### Optional - **date** `date` Use to lookup a historical end of day options chain from a specific trading day. If no date parameter is specified the chain will be the most current chain available during market hours. When the market is closed the chain will be from the previous session. Accepted date inputs: `ISO 8601`, `unix`, `spreadsheet`. :::info[Historical chains carry no Greeks] Greeks and implied volatility are not stored historically. On any request that includes `date`, the `iv`, `delta`, `gamma`, `theta` and `vega` columns are returned as `null`, on every plan. For the same reason the `delta` filter cannot be combined with `date` (the request is rejected with `400`); filter historical chains by `strike` instead. ::: ### Expiration Filters - **expiration** `date` - Limits the option chain to a specific expiration date. Accepted date inputs: `ISO 8601`, `unix`, `spreadsheet`. - If omitted the next monthly expiration for real-time quotes or the next monthly expiration relative to the `date` parameter for historical quotes will be returned. - Use the keyword `all` to return the complete option chain. :::caution Combining the `all` parameter with large options chains such as SPX, SPY, QQQ, etc. can cause you to consume your requests very quickly. The full SPX option chain has more than 20,000 contracts. A request is consumed for each contact you request with a price in the option chain. ::: - **dte** `number` Days to expiry. Limit the option chain to a single expiration date closest to the `dte` provided. Should not be used together with `from` and `to`. Take care before combining with `weekly`, `monthly`, `quarterly`, since that will limit the expirations `dte` can return. If you are using the `date` parameter, dte is relative to the `date` provided. - **from** `date` Limit the option chain to expiration dates after `from` (inclusive). Should be combined with `to` to create a range. Accepted date inputs: `ISO 8601`, `unix`, `spreadsheet`. If omitted all expirations will be returned. - **to** `date` Limit the option chain to expiration dates before `to` (inclusive). Should be combined with `from` to create a range. Accepted date inputs: `ISO 8601`, `unix`, `spreadsheet`. If omitted all expirations will be returned. - **month** `number` Limit the option chain to options that expire in a specific month (**1-12**). - **year** `number` Limit the option chain to options that expire in a specific **year**. - **weekly** `boolean` Limit the option chain to weekly expirations by setting weekly to `true`. If set to `false`, no weekly expirations will be returned. - **monthly** `boolean` Limit the option chain to standard monthly expirations by setting monthly to `true`. If set to `false`, no monthly expirations will be returned. - **quarterly** `boolean` Limit the option chain to quarterly expirations by setting quarterly to `true`. If set to `false`, no quarterly expirations will be returned. > [!CAUTION] > When combining the `weekly`, `monthly`, and `quarterly` parameters, only identical boolean values will be honored. For example, `weekly=true&monthly=false` will return an error. You must use these parameters to either include or exclude values, but you may not include and exclude at the same time. A valid use would be `monthly=true&quarterly=true` to return both monthly and quarterly expirations. ### Strike Filters - **strike** `string` - Limit the option chain to options with the specific strike specified. (e.g. `400`) - Limit the option chain to a specific set of strikes (e.g. `400,405`) - Limit the option chain to an open interval of strikes using a logical expression (e.g. `>400`) - Limit the option chain to a closed interval of strikes by specifying both endpoints. (e.g. `400-410`) - **delta** `number` - Limit the option chain to a single strike closest to the `delta` provided. (e.g. `.50`) - Limit the option chain to a specific set of deltas (e.g. `.60,.30`) - Limit the option chain to an open interval of strikes using a logical expression (e.g. `>.50`) - Limit the option chain to a closed interval of strikes by specifying both endpoints. (e.g. `.30-.60`) :::tip Filter strikes using the absolute value of the delta. The values used will always return both sides of the chain (e.g. puts & calls). This means you must filter using `side` to exclude puts or calls. Delta cannot be used to filter the side of the chain, only the strikes. ::: :::caution[Not available on historical requests] `delta` cannot be combined with the `date` parameter. Greeks are not stored historically, so there is no delta to filter on, and the request is rejected with `400 Bad Request`: ```json {"s":"error","errmsg":"The 'delta' parameter is not supported on historical requests for now."} ``` Use the `strike` filter for historical chains. ::: - **strikeLimit** `number` Limit the number of total strikes returned by the option chain. For example, if a complete chain included 30 strikes and the limit was set to 10, the 20 strikes furthest from the money will be excluded from the response. :::tip If `strikeLimit` is combined with the `range` or `side` parameter, those parameters will be applied first. In the above example, if the range were set to `itm` (in the money) and side set to `call`, all puts and out of the money calls would be first excluded by the range parameter and then strikeLimit will return a maximum of 10 in the money calls that are closest to the money. If the `side` parameter has not been used but `range` has been specified, then `strikeLimit` will return the requested number of calls and puts for each side of the chain, but duplicating the number of strikes that are received. ::: - **range** `string` Limit the option chain to strikes that are in the money, out of the money, at the money, or include all. If omitted all options will be returned. Valid inputs: `itm`, `otm`, `all`. ### Price/Liquidity Filters - **minBid** `number` Limit the option chain to options with a bid price greater than or equal to the `number` provided. - **maxBid** `number` Limit the option chain to options with a bid price less than or equal to the `number` provided. - **minAsk** `number` Limit the option chain to options with an ask price greater than or equal to the `number` provided. - **maxAsk** `number` Limit the option chain to options with an ask price less than or equal to the `number` provided. - **maxBidAskSpread** `number` Limit the option chain to options with a bid-ask spread less than or equal to the `number` provided. - **maxBidAskSpreadPct** `number` Limit the option chain to options with a bid-ask spread less than or equal to the `percent` provided (relative to the underlying). For example, a value of `0.5%` would exclude all options trading with a bid-ask spread greater than $1.00 in an underlying that trades at $200. - **minOpenInterest** `number` Limit the option chain to options with an open interest greater than or equal to the `number` provided. - **minVolume** `number` Limit the option chain to options with a volume transacted greater than or equal to the `number` provided. ### Other Filters - **nonstandard** `boolean` Include non-standard contracts by setting `nonstandard` to `true`. If set to `false`, no non-standard options expirations will be returned. If no parameter is provided, the output will default to false. - **side** `string` Limit the option chain to either `call` or `put`. If omitted, both sides will be returned. - **am** `boolean` Limit the option chain to A.M. expirations by setting `am` to `true`. If set to `false`, no A.M. expirations will be returned. This parameter is only applicable for index options such as SPX, NDX, etc. If no parameter is provided, both A.M. and P.M. expirations will be returned. - **pm** `boolean` Limit the option chain to P.M. expirations by setting `pm` to `true`. If set to `false`, no P.M. expirations will be returned. This parameter is only applicable for index options such as SPX, NDX, etc. If no parameter is provided, both A.M. and P.M. expirations will be returned. > [!CAUTION] > The `am` and `pm` parameters are only applicable for index options such as SPX, NDX, etc. If they are used for stocks or ETFs, a bad parameters error will be returned. ## Response Attributes ### Success - **s** `string` Status will always be `ok` when there is the quote requested. - **optionSymbol** `array[string]` The option symbol according to OCC symbology. - **underlying** `array[string]` The ticker symbol of the underlying security. - **expiration** `array[number]` The option's expiration date and time. Equity options expire at 4:00 PM Eastern and index options expire at 4:15 PM Eastern. All timestamps use US Eastern Time (America/New_York). See [Response Timezone](https://www.marketdata.app/docs/docs/api/dates-and-times#response-timezone) for details. - **side** `array[string]` The response will be `call` or `put`. - **strike** `array[number]` The exercise price of the option. - **firstTraded** `array[date]` The date the option was first traded. - **dte** `array[number]` The number of days until the option expires. - **ask** `array[number]` The ask price. - **askSize** `array[number]` The number of contracts offered at the ask price. - **bid** `array[number]` The bid price. - **bidSize** `array[number]` The number of contracts offered at the bid price. - **mid** `array[number]` The midpoint price between the ask and the bid, also known as the mark price. - **last** `array[number]` The last price negotiated for this option contract at the time of this quote. - **volume** `array[number]` The number of contracts negotiated during the trading day at the time of this quote. - **openInterest** `array[number]` The total number of contracts outstanding. Open interest is published once per trading day: it is calculated after the close and released at the start of the next session, and it does not change intraday. On a historical request (`date=D`), this is therefore the figure settled out of the D−1 session and available before the open on D — unlike every other field in the row, which is as of the 16:00 ET `updated` timestamp. To get the open interest produced by trading on day D, request the next trading day. - **underlyingPrice** `array[number]` The last price of the underlying security at the time of this quote. - **inTheMoney** `array[booleans]` Specifies whether the option contract was in the money true or false at the time of this quote. - **intrinsicValue** `array[number]` The intrinsic value of the option. - **extrinsicValue** `array[number]` The extrinsic value of the option. - **updated** `array[number]` The date and time of this quote snapshot. All timestamps use US Eastern Time (America/New_York). See [Response Timezone](https://www.marketdata.app/docs/docs/api/dates-and-times#response-timezone) for details. > [!NOTE] > **[Greeks on historical requests]** > > `iv`, `delta`, `gamma`, `theta` and `vega` are only populated on current (real-time, delayed or end-of-day) chains. On any request that includes the `date` parameter these columns are returned as `null`, on every plan, because Greeks and implied volatility are not stored historically. - **iv** `array[number]` The [implied volatility](https://www.investopedia.com/terms/i/iv.asp) of the option. - **delta** `array[number]` The [delta](https://www.investopedia.com/terms/d/delta.asp) of the option. - **gamma** `array[number]` The [gamma](https://www.investopedia.com/terms/g/gamma.asp) of the option. - **theta** `array[number]` The [theta](https://www.investopedia.com/terms/t/theta.asp) of the option. - **vega** `array[number]` The [vega](https://www.investopedia.com/terms/v/vega.asp) of the option. ### No Data - **s** `string` Status will be `no_data` if no candles are found for the request. - **nextTime** `number` optional Unix time of the next quote if there is no data in the requested period, but there is data in a subsequent period. - **prevTime** `number` optional Unix time of the previous quote if there is no data in the requested period, but there is data in a previous period. ### Error - **s** `string` Status will be `error` if the request produces an error response. - **errmsg** `string` An error message. ## Usage Information ### Data Availability The type of option chain data you receive depends on your user type and OPRA entitlement. This may include real-time data, 15-minute delayed data, or historical data, depending on the plan or access level. To get real-time options data, users need to sign the OPRA agreement. [Learn more about entitlements](https://www.marketdata.app/docs/account/entitlements). | User Type | OPRA Entitlement | Data Type | |------------------|------------------|------------------------| | Non-Professional | ✅ | Real-time | | Non-Professional | ❌ | 15-min delayed | | Professional | Any | Historical (1 day old) | | Unknown | Any | Historical (1 day old) | ### Point-in-Time Semantics One historical row does not carry one as-of time. On a `date=D` request: | Field | As of | |----------------------------------------------------------|----------------------------------------------| | `openInterest` | before the open on D, settled out of D−1 | | `bid`, `ask`, `mid`, `last`, `underlyingPrice`, `volume` | the row's `updated` timestamp, 16:00 ET on D | | `iv`, `delta`, `gamma`, `theta`, `vega` | not returned — see the note on `date` above | Two consequences matter when you build a point-in-time dataset: - `openInterest` carries no look-ahead. It was published before trading on D began, so it is safe to use at any time on D. To read the open interest that D's trading produced, request the next trading day. - `volume` does carry look-ahead relative to an intraday cutoff. It is the full-session total for D, not the volume up to a given moment. ### Pricing The cost of using the option chain API endpoint depends on the type of data you choose and your usage pattern. Here's a breakdown of the pricing: | Data Type | Cost Basis | Credits Required per Unit | |------------------|-------------------------|---------------------------| | Real-Time Data | Per option symbol | 1 credit | | 15m Delayed Data | Per option symbol | 1 credit | | Cached Data | Per API call | 1 credit | | Historical Data | Per 1000 option symbols | 1 credit | #### Examples 1. **Real-Time Usage** - If you query all strikes and all expirations for SPX (which has 22,718 total option contracts) in real-time, it will cost you 22,718 credits. 2. **15-minute Delayed Usage** - If you query all strikes and all expirations for SPX (which has 22,718 total option contracts) and opt for 15m delayed data, it will cost you 22,718 credits. 3. **Cached Mode Usage** - A single API call to SPX using the Cached Mode, regardless of the number of option symbols queried, will cost you 1 credit. 4. **Historical Data Usage** - When including the `date` parameter for historical queries, the cost is 1 credit per 1000 option symbols returned in the response. - For example, if a historical query returns 5,000 option symbols, it will cost 5 credits (5,000 ÷ 1000 = 5). --- # Option Expirations Source: https://www.marketdata.app/docs/api/options/expirations/index.md Get a list of current or historical option expiration dates for an underlying symbol. If no optional parameters are used, the endpoint returns all expiration dates in the option chain. ## Endpoint ``` https://api.marketdata.app/v1/options/expirations/{underlyingSymbol}/ ``` ### Method ``` GET ``` ## Request Example ### HTTP **GET** [https://api.marketdata.app/v1/options/expirations/AAPL/](https://api.marketdata.app/v1/options/expirations/AAPL/) ### JavaScript ```js title="app.js" 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 ```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); } ``` ### Python ```python title="app.py" from marketdata import MarketDataClient client = MarketDataClient() expirations = client.options.expirations("AAPL") print(expirations) ``` ### Go ```go title="optionExpirations.go" import ( "fmt" api "github.com/MarketDataApp/sdk-go" ) func ExampleOptionsExpirationsRequest() { expirations, err := OptionsExpirations().UnderlyingSymbol("AAPL").Get() if err != nil { fmt.Print(err) return } for _, expiration := range expirations { fmt.Println(expiration) } } ``` ### PHP ```php title="optionExpirations.php" use MarketDataApp\Client; $client = new Client(); $expirations = $client->options->expirations("AAPL"); // Display all expiration dates echo $expirations; ``` ### Java ```java title="OptionExpirations.java" import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.options.OptionsExpirationsRequest; try (MarketDataClient client = new MarketDataClient()) { client.options().expirations(OptionsExpirationsRequest.of("AAPL")).values().forEach(System.out::println); } ``` ### Kotlin ```kotlin title="OptionExpirations.kt" import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.options.OptionsExpirationsRequest MarketDataClient().use { client -> client.options().expirations(OptionsExpirationsRequest.of("AAPL")).values().forEach(::println) } ``` ### C# ```csharp title="OptionExpirations.cs" using MarketDataApp; using var client = await MarketDataClient.CreateAsync(); foreach (var expiration in (await client.Options.GetExpirationsAsync("AAPL")).Values) { Console.WriteLine(expiration); } ``` ## Response Example ```json { "s": "ok", "expirations": [ "2022-09-23", "2022-09-30", "2022-10-07", "2022-10-14", "2022-10-21", "2022-10-28", "2022-11-18", "2022-12-16", "2023-01-20", "2023-02-17", "2023-03-17", "2023-04-21", "2023-06-16", "2023-07-21", "2023-09-15", "2024-01-19", "2024-06-21", "2025-01-17" ], "updated": 1663704000 } ``` ## Request Parameters ### Required - **underlyingSymbol** `string` The underlying ticker symbol for the options chain you wish to lookup. ### Optional - **strike** `number` Limit the lookup of expiration dates to the strike provided. This will cause the endpoint to only return expiration dates that include this strike. - **date** `date` Use to lookup a historical list of expiration dates from a specific previous trading day. If date is omitted the expiration dates will be from the current trading day during market hours or from the last trading day when the market is closed. Accepted date inputs: `ISO 8601`, `unix`, `spreadsheet`. ## Response Attributes ### Success - **s** `string` Status will always be `ok` when there is strike data for the underlying/expirations requested. - **expirations** `array[date]` The expiration dates requested for the underlying with the option strikes for each expiration. - **updated** `date` The date and time of this list of options expirations was last updated. All timestamps use US Eastern Time (America/New_York). For historical expirations, this should match the `date` parameter. See [Response Timezone](https://www.marketdata.app/docs/docs/api/dates-and-times#response-timezone) for details. ### No Data - **s** `string` Status will be `no_data` if no data is found for the request. - **nextTime** `number` optional Unix time of the next quote if there is no data in the requested period, but there is data in a subsequent period. - **prevTime** `number` optional Unix time of the previous quote if there is no data in the requested period, but there is data in a previous period. ### Error - **s** `string` Status will be `error` if the request produces an error response. - **errmsg** `string` An error message. ## Usage Information ### Data Availability This endpoint provides options expiration dates data. This endpoint does not require any exchange entitlements and is available to all users. | User Type | Exchange Entitlement | Data Type | |-----------|----------------------|--------------| | All Users | Not Required | Options Data | ### Pricing The cost of using the expirations API endpoint is 1 credit per API call. | Data Type | Cost Basis | Credits Required per Unit | |------------------|--------------|---------------------------| | Expirations Data | Per API call | 1 credit | --- # Option Lookup Source: https://www.marketdata.app/docs/api/options/lookup/index.md Generate a properly formatted OCC option symbol based on the user's human-readable description of an option. This endpoint converts text such as "AAPL 12/17/27 $250 Call" to OCC option symbol format: AAPL271217C00250000. The user input must be URL-encoded. ## Endpoint ``` https://api.marketdata.app/v1/options/lookup/{userInput}/ ``` ### Method ``` GET ``` ## Request Example ### HTTP **GET** [https://api.marketdata.app/v1/options/lookup/AAPL%2012/17/2027%20250%20Call/](https://api.marketdata.app/v1/options/lookup/AAPL%2012/17/2027%20250%20Call/) ### JavaScript ```js title="app.js" import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); try { const data = await client.options.lookup("AAPL 12/17/2027 250 Call"); console.log(data.optionSymbol); // "AAPL271217C00250000" } catch (error) { console.error(error); } ``` ### TypeScript ```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 12/17/2027 250 Call"); console.log(data.optionSymbol); // "AAPL271217C00250000" } catch (error) { console.error(error); } ``` ### Python ```python title="app.py" from marketdata import MarketDataClient client = MarketDataClient() lookup = client.options.lookup("AAPL 12/17/2027 250 Call") print(lookup) ``` ### Go ```go title="optionLookup.go" import ( "fmt" api "github.com/MarketDataApp/sdk-go" ) func ExampleOptionLookupRequest() { optionSymbol, err := OptionLookup().UserInput("AAPL 12/17/2027 250 Call").Get() if err != nil { fmt.Print(err) return } fmt.Println(optionSymbol) } ``` ### PHP ```php title="optionLookup.php" use MarketDataApp\Client; $client = new Client(); $lookup = $client->options->lookup("AAPL 12/17/2027 250 Call"); // Display the OCC symbol echo $lookup; ``` ### Java ```java title="OptionLookup.java" import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.options.OptionsLookupRequest; try (MarketDataClient client = new MarketDataClient()) { System.out.println(client.options().lookup(OptionsLookupRequest.of("AAPL 12/17/2027 250 Call")).values()); // "AAPL271217C00250000" } ``` ### Kotlin ```kotlin title="OptionLookup.kt" import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.options.OptionsLookupRequest MarketDataClient().use { client -> val optionSymbol = client.options().lookup(OptionsLookupRequest.of("AAPL 12/17/2027 250 Call")).values() println(optionSymbol) // "AAPL271217C00250000" } ``` ### C# ```csharp title="OptionLookup.cs" using MarketDataApp; using var client = await MarketDataClient.CreateAsync(); var lookup = await client.Options.GetLookupAsync("AAPL 12/17/2027 250 Call"); Console.WriteLine(lookup.Values); // "AAPL271217C00250000" ``` ## Response Example ```json { "s": "ok", "optionSymbol": "AAPL271217C00250000" } ``` ## Request Parameters ### Required - **userInput** `string` The human-readable string input that contains (1) stock symbol (2) strike (3) expiration date (4) option side (i.e. put or call). This endpoint will translate the user's input into a valid OCC option symbol. ## Response Attributes ### Success - **s** `string` Status will always be `ok` when the OCC option symbol is successfully generated. - **optionSymbol** `string` The generated OCC option symbol based on the user's input. ### Error - **s** `string` Status will be `error` if the request produces an error response. - **errmsg** `string` An error message. ## Notes - This endpoint will return an error if the option symbol that would be formed by the user's input does not exist. ## Usage Information ### Data Availability This endpoint provides option symbol lookup functionality. This endpoint does not require any exchange entitlements and is available to all users. | User Type | Exchange Entitlement | Data Type | |-----------|----------------------|--------------| | All Users | Not Required | Utility Data | ### Pricing The cost of using the lookup API endpoint is 1 credit per API call. | Data Type | Cost Basis | Credits Required per Unit | |-------------|--------------|---------------------------| | Lookup Data | Per API call | 1 credit | --- # Option Quotes Source: https://www.marketdata.app/docs/api/options/quotes/index.md Get a current or historical end of day quote for a single options contract. ## Endpoint ``` https://api.marketdata.app/v1/options/quotes/{optionSymbol}/ ``` ### Method ``` GET ``` ## Request Example ### HTTP **GET** [https://api.marketdata.app/v1/options/quotes/AAPL271217C00250000/](https://api.marketdata.app/v1/options/quotes/AAPL271217C00250000/) ### JavaScript ```js title="app.js" 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 ```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); } ``` ### Python ```python title="app.py" from marketdata import MarketDataClient client = MarketDataClient() quotes = client.options.quotes("AAPL271217C00250000") print(quotes) ``` ### Go ```go title="optionQuotes.go" import ( "fmt" api "github.com/MarketDataApp/sdk-go" ) func ExampleOptionQuoteRequest() { quotes, err := OptionQuote().OptionSymbol("AAPL271217C00250000").Get() if err != nil { fmt.Print(err) return } for _, quote := range quotes { fmt.Println(quote) } } ``` ### PHP ```php title="optionQuotes.php" use MarketDataApp\Client; $client = new Client(); $quotes = $client->options->quotes("AAPL271217C00250000"); // Display formatted quotes with Greeks echo $quotes; ``` ### Java ```java title="OptionQuotes.java" import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.options.OptionsQuoteRequest; try (MarketDataClient client = new MarketDataClient()) { client.options().quote(OptionsQuoteRequest.of("AAPL271217C00250000")).values().forEach(System.out::println); } ``` ### Kotlin ```kotlin title="OptionQuotes.kt" import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.options.OptionsQuoteRequest MarketDataClient().use { client -> client.options().quote(OptionsQuoteRequest.of("AAPL271217C00250000")).values().forEach(::println) } ``` ### C# ```csharp title="OptionQuotes.cs" using MarketDataApp; using var client = await MarketDataClient.CreateAsync(); foreach (var quote in (await client.Options.GetQuoteAsync("AAPL271217C00250000")).Values) { Console.WriteLine(quote); } ``` ## Response Example ```json { "s": "ok", "optionSymbol": ["AAPL271217C00250000"], "ask": [5.25], "askSize": [57], "bid": [5.15], "bidSize": [994], "mid": [5.2], "last": [5.25], "volume": [977], "openInterest": [61289], "underlyingPrice": [136.12], "inTheMoney": [false], "updated": [1665673292], "iv": [0.3468], "delta": [0.347], "gamma": [0.015], "theta": [-0.05], "vega": [0.264], "intrinsicValue": [13.88], "extrinsicValue": [8.68] } ``` ## Request Parameters ### Required - **optionSymbol** `string` The option symbol (as defined by the OCC) for the option you wish to lookup. Use the current OCC option symbol format, even for historic options that quoted before the format change in 2010. ### Optional - **date** `date` Use to lookup a historical end of day quote from a specific trading day. If no date is specified the quote will be the most current price available during market hours. When the market is closed the quote will be from the last trading day. Accepted date inputs: `ISO 8601`, `unix`, `spreadsheet`. :::info[Historical quotes carry no Greeks] Greeks and implied volatility are not stored historically. On any request that includes `date`, `from` or `to`, the `iv`, `delta`, `gamma`, `theta` and `vega` columns are returned as `null`, on every plan. ::: - **from** `date` Use to lookup a series of end of day quotes. From is the oldest (leftmost) date to return (inclusive). If from/to is not specified the quote will be the most current price available during market hours. When the market is closed the quote will be from the last trading day. Accepted date inputs: `ISO 8601`, `unix`, `spreadsheet`. - **to** `date` Use to lookup a series of end of day quotes. From is the newest (rightmost) date to return (exclusive). If from/to is not specified the quote will be the most current price available during market hours. When the market is closed the quote will be from the last trading day. Accepted date inputs: `ISO 8601`, `unix`, `spreadsheet`. ## Response Attributes ### Success - **s** `string` Status will always be `ok` when there is data for the quote requested. - **optionSymbol** `array[string]` The option symbol according to OCC symbology. - **underlying** `array[string]` The ticker symbol of the underlying security. - **expiration** `array[number]` The option's expiration date and time. Equity options expire at 4:00 PM Eastern and index options expire at 4:15 PM Eastern. All timestamps use US Eastern Time (America/New_York). See [Response Timezone](https://www.marketdata.app/docs/docs/api/dates-and-times#response-timezone) for details. - **side** `array[string]` The response will be `call` or `put`. - **strike** `array[number]` The exercise price of the option. - **firstTraded** `array[date]` The date the option was first traded. - **dte** `array[number]` The number of days until the option expires. - **ask** `array[number]` The ask price. - **askSize** `array[number]` The number of contracts offered at the ask price. - **bid** `array[number]` The bid price. - **bidSize** `array[number]` The number of contracts offered at the bid price. - **mid** `array[number]` The midpoint price between the ask and the bid, also known as the mark price. - **last** `array[number]` The last price negotiated for this option contract at the time of this quote. - **volume** `array[number]` The number of contracts negotiated during the trading day at the time of this quote. - **openInterest** `array[number]` The total number of contracts outstanding. Open interest is published once per trading day: it is calculated after the close and released at the start of the next session, and it does not change intraday. On a historical request (`date=D`, or a `from`/`to` series), this is therefore the figure settled out of the D−1 session and available before the open on D — unlike every other field in the row, which is as of the 16:00 ET `updated` timestamp. To get the open interest produced by trading on day D, request the next trading day. - **underlyingPrice** `array[number]` The last price of the underlying security at the time of this quote. - **inTheMoney** `array[booleans]` Specifies whether the option contract was in the money true or false at the time of this quote. - **intrinsicValue** `array[number]` The instrinisc value of the option. - **extrnisicValue** `array[number]` The extrinsic value of the option. - **updated** `array[number]` The date and time of this quote snapshot. All timestamps use US Eastern Time (America/New_York). See [Response Timezone](https://www.marketdata.app/docs/docs/api/dates-and-times#response-timezone) for details. > [!NOTE] > **[Greeks on historical requests]** > > `iv`, `delta`, `gamma`, `theta` and `vega` are only populated on current (real-time, delayed or end-of-day) quotes. On any request that includes `date`, `from` or `to` these columns are returned as `null`, on every plan, because Greeks and implied volatility are not stored historically. - **iv** `array[number]` The [implied volatility](https://www.investopedia.com/terms/i/iv.asp) of the option. - **delta** `array[number]` The [delta](https://www.investopedia.com/terms/d/delta.asp) of the option. - **gamma** `array[number]` The [gamma](https://www.investopedia.com/terms/g/gamma.asp) of the option. - **theta** `array[number]` The [theta](https://www.investopedia.com/terms/t/theta.asp) of the option. - **vega** `array[number]` The [vega](https://www.investopedia.com/terms/v/vega.asp) of the option. ### No Data Returned when the request is valid, but **no quote data exists** for the requested symbol or time period. This response corresponds to an **HTTP 404 Not Found** status. - **s** `string` Will be `no_data`. - **errmsg** `string` A descriptive message explaining why no data is available. For example: - `"No option found. No option was found for this strike and expiration."` This occurs when even though the option symbol format is valid, no real, tradable contract exists for the specified strike and expiration. A `no_data` response may occur when: - The market is closed (weekends or holidays) - The symbol or option contract had no trading quotes on the requested date - The contract had not begun trading yet - No option exists for the requested strike and expiration Even when the option symbol format is valid, the absence of a real, tradable contract or any quote data results in a `404` / `no_data` response. ### Error Returned when the request itself is invalid or cannot be processed. - **s** `string` Will be `error`. - **errmsg** `string` A human-readable description of the error. These cases correspond to **HTTP 400 Bad Request**, such as: - Invalid or malformed option symbol format - Parameters that cannot be parsed - Unsupported or invalid values Use the HTTP status code—not just the `s` field—to distinguish between invalid input (`400`) and valid requests that simply have no data (`404` via `no_data`). ## Usage Information ### Data Availability The type of option quote data you receive depends on your user type and OPRA entitlement. This may include real-time data, 15-minute delayed data, or historical data, depending on the plan or access level. To get real-time options data, users need to sign the OPRA agreement. [Learn more about entitlements](https://www.marketdata.app/docs/account/entitlements). | User Type | OPRA Entitlement | Data Type | |------------------|------------------|------------------------| | Non-Professional | ✅ | Real-time | | Non-Professional | ❌ | 15-min delayed | | Professional | Any | Historical (1 day old) | | Unknown | Any | Historical (1 day old) | ### Pricing The cost of using the option quote API endpoint depends on the type of data you choose and your usage pattern. Here's a breakdown of the pricing: | Data Type | Cost Basis | Credits Required per Unit | |------------------|-------------------|---------------------------| | Real-Time Data | Per option symbol | 1 credit | | 15m Delayed Data | Per option symbol | 1 credit | | Historical Data | Per 1000 quotes | 1 credit | --- # Rate Limits Source: https://www.marketdata.app/docs/api/rate-limiting/index.md We enforce rate limits to ensure our API remains accessible and efficient for all users. We have two types of limits: API credits (total credits per unit of time) and a concurrent request limit (simultaneous requests). For troubleshooting, use: - [429: Credit Limit Reached](https://www.marketdata.app/docs/api/troubleshooting/running-out-of-credits) - [429: Too Many Concurrent Requests](https://www.marketdata.app/docs/api/troubleshooting/too-many-concurrent-requests) ## API Credits Normally each API call consumes a single credit. However, **if the response includes more than a single symbol, it can consume multiple credits**. Often, users can navigate around a rate limit by making the most of the diverse filters we provide (e.g. instead of retrieving an entire option chain, apply specific filters to narrow down the results). **Any single API request is permitted as long as you have at least 1 credit remaining**, even if the request will consume more credits than your current balance. After such a request, your balance may go negative and further requests will return `429` responses until the credit window resets. ### Usage Counter Reset Time The usage counter for all plans with a daily limit resets at **9:30 AM Eastern Time** (NYSE opening bell). This reset timing is crucial for users to understand so they can plan their API usage efficiently without hitting the rate limit unexpectedly. This reset time is also a safety control: if a script runs out of control overnight, a 9:30 AM reset limits impact to one trading day budget instead of potentially consuming two trading-day budgets across midnight. > [!TIP] > **[Managing Timezone Changes]** > > To handle the reset time accurately regardless of your local timezone, it's recommended to use the `America/New_York` timezone identifier. This ensures that your application adjusts for any changes in Eastern Time, including daylight saving shifts, automatically. > > By aligning your application's timing functions with the `America/New_York` timezone, you can ensure that your usage of the API remains within the allocated rate limits, taking into account the precise reset timing at 9:30 AM Eastern Time. ## Concurrent Request Limit To maintain the stability and performance of our API, we enforce a limit of no more than 50 concurrent requests across all subscription plans. This means that at any given time, you should not have more than 50 active API calls in progress. Requests in excess of the concurrency limit will generate 429 responses. To adhere to this limit, it is advisable to implement a worker or thread pool mechanism in your application that does not exceed 50 workers. Each worker should handle no more than one API request at a time. This setup helps in efficiently managing API calls without breaching the concurrent request limit and ensures fair usage among all users. ## Rate Limits By Plan Different plans have specific rate limits. Free/Starter/Trader use daily limits, while Quant/Prime use per-minute limits. | | Free Forever | Starter | Trader | Quant | Prime | |--------------------------|--------------|----------|----------|----------|----------| | Daily API Credits | 100 | 10,000 | 100,000 | No Limit | No Limit | | Per Minute API Credits | No Limit | No Limit | No Limit | 10,000 | 100,000 | | Concurrent Request Limit | 50 | 50 | 50 | 50 | 50 | ### Summary - **Free Forever Plan:** 100 credits per day. - **Starter Plan:** 10,000 credits per day. - **Trader Plan:** 100,000 credits per day. - **Quant Plan:** 10,000 credits per minute. - **Prime Plan:** 100,000 credits per minute. ## Credits Each time you call the API, the system increases your credits counter. Normally each successful response consumes 1 credit. However, **if you request multiple symbols in a single API call using `stocks/quotes`, `stocks/prices`, `stocks/bulkcandles`, or `options/chain`, credits are consumed per symbol included in the response**. > [!CAUTION] > For users working with options, take care before repeatedly requesting quotes for an entire option chain. **Each option symbol included in the response consumes credits**. If you download the entire SPX option chain (which has 20,000+ option symbols), you can exhaust your credit limit very quickly. Use our option chain filters to request only the strikes and expirations you need. ## Headers to Manage the Rate Limit We provide the following headers in our responses to help you manage the rate limit and throttle your applications when necessary: - `X-Api-Ratelimit-Limit`: The maximum number of API credits permitted (per day for Free/Starter/Trader plans or per minute for Quant/Prime users). - `X-Api-Ratelimit-Remaining`: The number of API credits remaining in the current rate day/period. - `X-Api-Ratelimit-Reset`: The time at which the current rate limit window resets in UTC epoch seconds. - `X-Api-Ratelimit-Consumed`: The quantity of API credits consumed by the current request. ## Detailed Rate Limit Rules - Each successful response increases the counter by a minimum of 1 credit. - Only status 200/203 responses consume credits. - NULL responses are not counted. - Error responses are not counted. - Requests consume more than 1 credit if the response includes prices for more than one symbol (for example, `options/chain` or `stocks/quotes` bulk requests). - Responses that include more than one symbol, but do not include the **bid**, **ask**, **mid**, or **last** columns _**do not**_ consume multiple credits and are counted as a single credit. - Certain free trial symbols like AAPL stock, AAPL options, and the VFINX mutual fund do not consume credits. ## Strategies To Avoid Rate Limiting - Exclude the bid, ask, mid, and last columns from your option chain requests if the current price is not needed. - Use the extensive option chain filters such as `strikeLimit` to exclude unnecessary strikes from your requests. - Paying customers can make use of the reduced-price cached mode. Use the `mode=cached` parameter on supported bulk endpoints to retrieve previously cached quotes instead of making a live request. This can save thousands of credits. For more details, refer to the [mode parameter documentation](https://www.marketdata.app/docs/api/universal-parameters/mode). > [!NOTE] > **[Trial plans]** > > `mode=cached` is not available on Starter Trial, Trader Trial, and Prime Trial plans. --- # Stocks Source: https://www.marketdata.app/docs/api/stocks/index.md Stock endpoints include numerous fundamental, technical, and pricing data. ## Root Endpoint For Stocks ``` https://api.marketdata.app/v1/stocks/ ``` ## Stocks Endpoints --- # Bulk Stock Candles Source: https://www.marketdata.app/docs/api/stocks/bulkcandles/index.md Get bulk historical candle data for stocks. This endpoint returns bulk daily candle data for multiple stocks. Unlike the standard candles endpoint, this endpoint returns a single daily for each symbol provided. ## Endpoint ``` https://api.marketdata.app/v1/stocks/bulkcandles/{resolution}/ ``` ### Method ``` GET ``` ## Request Example ### HTTP **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) ### JavaScript > [!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) => res.json()) .then((data) => { console.log(data); }) .catch((err) => { console.log(err); }); ``` ### TypeScript > [!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); ``` ### Python ```python title="app.py" import requests url = "https://api.marketdata.app/v1/stocks/bulkcandles/D/?symbols=AAPL,META,MSFT" response = requests.request("GET", url) print(response.text) ``` ### Go ```go title="bulkStockCandles.go" import ( "fmt" api "github.com/MarketDataApp/sdk-go" ) func ExampleBulkStockCandlesRequest_get() { symbols := []string{"AAPL", "META", "MSFT"} candles, err := BulkStockCandles().Resolution("D").Symbols(symbols).Get() if err != nil { fmt.Print(err) return } for _, candle := range candles { fmt.Println(candle) } } ``` ### PHP ```php title="bulkStockCandles.php" use MarketDataApp\Client; $client = new Client(); $candles = $client->stocks->bulkCandles( symbols: ["AAPL", "META", "MSFT"], resolution: "D" ); // Display formatted candles summary echo $candles; ``` ### Java > [!NOTE] > The Java SDK does not yet provide a method for the bulk candles endpoint, so this example uses the JDK's `java.net.http.HttpClient` directly. It will be replaced with a `marketdata-sdk-java` example once bulk support is added. ```java title="BulkStockCandles.java" import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; HttpClient http = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.marketdata.app/v1/stocks/bulkcandles/D/?symbols=AAPL,META,MSFT")) .header("Authorization", "Bearer " + System.getenv("MARKETDATA_TOKEN")) .GET() .build(); HttpResponse response = http.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); ``` ### Kotlin > [!NOTE] > The Java SDK does not yet provide a method for the bulk candles endpoint, so this example uses the JDK's `java.net.http.HttpClient` directly. It will be replaced with a `marketdata-sdk-java` example once bulk support is added. ```kotlin title="BulkStockCandles.kt" import java.net.URI import java.net.http.HttpClient import java.net.http.HttpRequest import java.net.http.HttpResponse val http = HttpClient.newHttpClient() val request = HttpRequest.newBuilder() .uri(URI.create("https://api.marketdata.app/v1/stocks/bulkcandles/D/?symbols=AAPL,META,MSFT")) .header("Authorization", "Bearer ${System.getenv("MARKETDATA_TOKEN")}") .GET() .build() val response = http.send(request, HttpResponse.BodyHandlers.ofString()) println(response.body()) ``` ### C# > [!NOTE] > The C# SDK does not yet provide a method for the bulk candles endpoint, so this example uses `System.Net.Http.HttpClient` directly. It will be replaced with a `MarketDataApp` SDK example once bulk support is added. ```csharp title="BulkStockCandles.cs" using System.Net.Http.Headers; using var http = new HttpClient(); http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( "Bearer", Environment.GetEnvironmentVariable("MARKETDATA_TOKEN")); var response = await http.GetAsync("https://api.marketdata.app/v1/stocks/bulkcandles/D/?symbols=AAPL,META,MSFT"); Console.WriteLine(await response.Content.ReadAsStringAsync()); ``` ## Response Example ```json { "s": "ok", "symbol": ["AAPL", "META", "MSFT"], "o": [196.16, 345.58, 371.49], "h": [196.95, 353.6, 373.26], "l": [195.89, 345.12, 369.84], "c": [196.94, 350.36, 373.26], "v": [40714051, 17729362, 20593658], "t": [1703048400,1703048400,1703048400] } ``` ## Request Parameters ### Required - **resolution** `string` The duration of each candle. Only daily candles are supported at this time. - Daily Resolutions: (`daily`, `D`, `1D`) - **symbols** `string` The ticker symbols to return in the response, separated by commas. The symbols parameter may be omitted if the `snapshot` parameter is set to `true`. > [!TIP] > **[Batching multi-symbol requests]** > > There is no application-level cap on the number of symbols, but the HTTP request line is limited to 4094 bytes. For typical 4-character tickers that's roughly 700–800 symbols per request. To stay clear of the limit, chunk into batches of **500 symbols or fewer**. See [400: Bad Request — URL Too Long](https://www.marketdata.app/docs/api/troubleshooting/bad-request#case-5-url-too-long-multi-symbol-requests) for details. ### Optional - **snapshot** `boolean` Returns candles for all available symbols for the date indicated. The `symbols` parameter can be omitted if `snapshot` is set to true. - **date** `date` The date of the candles to be returned. If no date is specified, during market hours the candles returned will be from the current session. If the market is closed the candles will be from the most recent session. Accepted date inputs: `ISO 8601`, `unix`, `spreadsheet`. - **adjustsplits** `boolean` Adjust historical data for for historical splits and reverse splits. Market Data uses the CRSP methodology for adjustment. Daily candles default: `true`. ## Response Attributes ### Success - **s** `string` Will always be `ok` when there is data for the candles requested. - **symbol** `string` The ticker symbol of the stock. - **o** `array[number]` Open price. - **h** `array[number]` High price. - **l** `array[number]` Low price. - **c** `array[number]` Close price. - **v** `array[number]` Volume. - **t** `array[number]` Candle time. This is midnight US Eastern Time on the session date. All timestamps use US Eastern Time (America/New_York). See [Response Timezone](https://www.marketdata.app/docs/docs/api/dates-and-times#response-timezone) for details. ### No Data - **s** `string` Status will be `no_data` if no candles are found for the request. ### Error - **s** `string` Status will be `error` if the request produces an error response. - **errmsg** `string` An error message. ## Usage Information ### Data Availability **Real-time data is not available for this endpoint under any plan or entitlement.** The type of candle data you receive depends on your user type and UTP entitlement. This may include 15-minute delayed candles or historical candles (1 day old), depending on the plan or access level. | User Type | UTP Entitlement | Candle Type | |------------------|-----------------|------------------------| | Non-Professional | ✅ | 15-min delayed | | Non-Professional | ❌ | Historical (1 day old) | | Professional | Any | Historical (1 day old) | | Unknown | Any | Historical (1 day old) | > [!NOTE] > **[What are entitlements?]** > > Entitlements are permissions granted by exchanges that allow access to their data. To get 15-minute delayed candles, non-professional users need to sign the [UTP agreement](https://www.marketdata.app/docs/account/entitlements). [Learn more about entitlements](https://www.marketdata.app/docs/account/entitlements). ### Pricing The cost of using the bulk candles API endpoint is 1 credit per symbol returned in the response. | Data Type | Cost Basis | Credits Required per Unit | |------------------------|---------------|---------------------------| | 15-minute Delayed Data | Per symbol | 1 credit | | Historical Data | Per symbol | 1 credit | | Real-Time Data | Not available | N/A | --- # Stock Candles Source: https://www.marketdata.app/docs/api/stocks/candles/index.md Get historical price candles for stocks or ETFs. ## Endpoint ``` https://api.marketdata.app/v1/stocks/candles/{resolution}/{symbol}/ ``` ### Method ``` GET ``` ## Request Example ### HTTP **GET** [https://api.marketdata.app/v1/stocks/candles/D/AAPL/?from=2024-01-01&to=2024-01-31](https://api.marketdata.app/v1/stocks/candles/D/AAPL/?from=2024-01-01&to=2024-01-31) ### JavaScript ```js title="app.js" import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); try { const candles = await client.stocks.candles("AAPL", { resolution: "D", from: "2024-01-01", to: "2024-01-31", }); 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 ```typescript title="app.ts" import { MarketDataClient } from "@marketdata/sdk"; import type { StockCandle } from "@marketdata/sdk"; const client = new MarketDataClient(); try { const candles: StockCandle[] = await client.stocks.candles("AAPL", { resolution: "D", from: "2024-01-01", to: "2024-01-31", }); 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); } ``` ### Python ```python title="app.py" from marketdata import MarketDataClient client = MarketDataClient() candles = client.stocks.candles("AAPL", resolution="D", from_date="2024-01-01", to_date="2024-01-31") print(candles) ``` ### Go ```go title="stockCandles.go" import ( "fmt" api "github.com/MarketDataApp/sdk-go" ) func ExampleStockCandlesRequest() { candles, err := StockCandles().Resolution("D").Symbol("AAPL").From("2024-01-01").To("2024-01-31").Get() if err != nil { fmt.Print(err) return } for _, candle := range candles { fmt.Println(candle) } } ``` ### PHP ```php title="stockCandles.php" use MarketDataApp\Client; $client = new Client(); $candles = $client->stocks->candles( symbol: "AAPL", from: "2024-01-01", to: "2024-01-31", resolution: "D" ); // Display formatted candles summary echo $candles; ``` ### Java ```java title="StockCandles.java" import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.stocks.StockCandlesRequest; import com.marketdata.sdk.stocks.StockResolution; import java.time.LocalDate; try (MarketDataClient client = new MarketDataClient()) { var candles = client.stocks().candles( StockCandlesRequest.builder(StockResolution.DAILY, "AAPL") .from(LocalDate.parse("2024-01-01")) .to(LocalDate.parse("2024-01-31")) .build()); candles.values().forEach(System.out::println); } ``` ### Kotlin ```kotlin title="StockCandles.kt" import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.stocks.StockCandlesRequest import com.marketdata.sdk.stocks.StockResolution import java.time.LocalDate MarketDataClient().use { client -> val candles = client.stocks().candles( StockCandlesRequest.builder(StockResolution.DAILY, "AAPL") .from(LocalDate.parse("2024-01-01")) .to(LocalDate.parse("2024-01-31")) .build()) candles.values().forEach(::println) } ``` ### C# ```csharp title="StockCandles.cs" using MarketDataApp; using MarketDataApp.Stocks; using var client = await MarketDataClient.CreateAsync(); var candles = await client.Stocks.GetCandlesAsync( StockResolution.Daily, "AAPL", from: DateOnly.Parse("2024-01-01"), to: DateOnly.Parse("2024-01-31")); foreach (var candle in candles.Values) { Console.WriteLine(candle); } ``` ## Response Example ```json { "s": "ok", "c": [217.68, 221.03, 219.89], "h": [222.49, 221.5, 220.94], "l": [217.19, 217.1402, 218.83], "o": [221.03, 218.55, 220], "t": [1569297600, 1569384000, 1569470400], "v": [33463820, 24018876, 20730608] } ``` ## Request Parameters ### Required - **resolution** `string` The duration of each candle. *Case-insensitive*. **Intraday Resolutions:** - Minutely Resolutions: (`minutely`, `1`, `3`, `5`, `15`, `30`, `45`, ...) - Hourly Resolutions: (`hourly`, `H`, `1H`, `2H`, ...) **Daily Resolutions:** - Daily Resolutions: (`daily`, `D`, `1D`, `2D`, ...) - Weekly Resolutions: (`weekly`, `W`, `1W`, `2W`, ...) - Monthly Resolutions: (`monthly`, `M`, `1M`, `2M`, ...) - Yearly Resolutions: (`yearly`, `Y`, `1Y`, `2Y`, ...) - **symbol** `string` The company's ticker symbol. ### Dates All `date` parameters are optional. By default the most recent candle is returned if no date parameters are provided. - **from** `date` The leftmost candle on a chart (inclusive). From and countback are mutually exclusive. If you use `countback`, `from` must be omitted. Accepted timestamp inputs: ISO 8601, unix, spreadsheet. - **to** `date` The rightmost candle on a chart (inclusive). Accepted timestamp inputs: ISO 8601, unix, spreadsheet. - **countback** `number` Will fetch a specific number of candles before (to the left of) `to`. From and countback are mutually exclusive. If you use `from`, `countback` must be omitted. > [!NOTE] > There is no maximum date range limit on daily candles. When requesting intraday candles of any resolution, no more than 1 year of data can be requested in a single request. ### Optional - **extended** `boolean` Include extended hours trading sessions when returning *intraday* candles. Daily resolutions _never_ return extended hours candles. - Daily candles default: `false`. - Intraday candles default: `false`. - **adjustsplits** `boolean` Adjust historical data for stock splits. Market Data uses the CRSP methodology for adjustment. Only certain combinations with `adjustdividends` are supported — see [Split and Dividend Adjustments](#split-and-dividend-adjustments). - Daily candles default: `true`. - Intraday candles default: `false`. - **adjustdividends** `boolean` Adjust historical data for dividends. Market Data uses the CRSP methodology for adjustment. Dividend adjustment is available for daily resolutions only. Unsupported combinations are rejected with an HTTP 400 — see [Split and Dividend Adjustments](#split-and-dividend-adjustments). - Daily candles default: `true`. - Intraday candles default: `false`. - **adjustdividends** `boolean` Adjust historical data for cash dividends. Market Data uses the CRSP methodology for adjustment. - Daily candles default: `true`. - Intraday candles default: `false`. By default, daily candles are fully CRSP-adjusted for both splits and dividends. To retrieve split-adjusted-only data (the most common request), set `adjustdividends=false` and leave `adjustsplits` at its default of `true`. > [!NOTE] > There is no dividend-only adjustment. Requesting `adjustsplits=false` while `adjustdividends` remains `true` (its default) is unsupported and returns an HTTP `400` error. Because `adjustdividends` defaults to `true` on daily candles, passing only `adjustsplits=false` resolves to this rejected combination — the response's `errmsg` states exactly this and tells you to also set `adjustdividends=false` explicitly. To retrieve fully raw, unadjusted daily data, you must set both `adjustsplits=false` and `adjustdividends=false` explicitly. ### Split and Dividend Adjustments Only three price series are supported: **raw**, **split-only**, and **fully adjusted** (CRSP-style). There is no dividend-only series — no data source publishes one — so that combination is rejected with an HTTP 400. **Daily** — the `adjustsplits=false, adjustdividends=true` combination is rejected. Because `adjustdividends` defaults to `true` on daily candles, this is most commonly triggered by passing `adjustsplits=false` alone. To receive raw prices on daily candles, set **both** `adjustsplits=false` and `adjustdividends=false`. | `adjustsplits` | `adjustdividends` | Series | Result | |:--------------:|:-----------------:|----------------|--------------| | `false` | `false` | raw | ✅ | | `true` | `false` | split-only | ✅ | | `true` | `true` | fully adjusted | ✅ (default) | | `false` | `true` | dividend-only | ❌ HTTP 400 | The rejected daily combination returns: ```json {"s":"error","errmsg":"Unsupported parameter combination: adjustsplits=false with adjustdividends=true. Note: adjustdividends defaults to true on daily candles, so if your request only sets adjustsplits=false (without explicitly setting adjustdividends), dividend adjustment is still being applied via the default. Dividend-only adjustment is not supported. To receive unadjusted prices, set both adjustsplits=false and adjustdividends=false explicitly. To keep CRSP-style full adjustment, omit adjustsplits (or set it to true)."} ``` **Intraday** — any `adjustdividends=true` is rejected regardless of `adjustsplits`, because no intraday data source implements dividend adjustment. The intraday default is `false`, so the error only occurs on an explicit `adjustdividends=true`. | `adjustsplits` | `adjustdividends` | Series | Result | |:--------------:|:-----------------:|----------------|--------------| | `false` | `false` | raw | ✅ (default) | | `true` | `false` | split-only | ✅ | | `false` | `true` | dividend-only | ❌ HTTP 400 | | `true` | `true` | fully adjusted | ❌ HTTP 400 | Both rejected intraday combinations return: ```json {"s":"error","errmsg":"adjustdividends=true is not supported for intraday candles. Dividend adjustment is only available for daily resolutions."} ``` ## Response Attributes ### Success - **s** `string` ll always be `ok` when there is data for the candles requested. - **o** `array[number]` Open price. - **h** `array[number]` High price. - **l** `array[number]` Low price. - **c** `array[number]` Close price. - **v** `array[number]` Volume. - **t** `array[number]` Candle time. For intraday candles, this is the candle's start time. For daily and longer resolutions, this is midnight US Eastern Time on the session date. All timestamps use US Eastern Time (America/New_York). See [Response Timezone](https://www.marketdata.app/docs/docs/api/dates-and-times#response-timezone) for details. ### No Data - **s** `string` Status will be `no_data` if no candles are found for the request. - **nextTime** `number` optional Unix time of the next quote if there is no data in the requested period, but there is data in a subsequent period. ### Error - **s** `string` Status will be `error` if the request produces an error response. - **errmsg** `string` An error message. ## Usage Information ### Data Availability **Real-time data is not available for this endpoint under any plan or entitlement.** The type of candle data you receive depends on your user type and UTP entitlement. This may include 15-minute delayed candles or historical candles (1 day old), depending on the plan or access level. | User Type | UTP Entitlement | Candle Type | |------------------|-----------------|------------------------| | Non-Professional | ✅ | 15-min delayed | | Non-Professional | ❌ | Historical (1 day old) | | Professional | Any | Historical (1 day old) | | Unknown | Any | Historical (1 day old) | > [!NOTE] > **[What are entitlements?]** > > Entitlements are permissions granted by exchanges that allow access to their data. To get 15-minute delayed candles, non-professional users need to sign the [UTP agreement](https://www.marketdata.app/docs/account/entitlements). [Learn more about entitlements](https://www.marketdata.app/docs/account/entitlements). ### Pricing The cost of using the candle API endpoint is 1 credit per 1000 candles. | Data Type | Cost Basis | Credits Required per Unit | |------------------------|------------------|---------------------------| | 15-minute Delayed Data | Per 1000 candles | 1 credit | | Historical Data | Per 1000 candles | 1 credit | | Real-Time Data | Not available | N/A | --- # Stock Earnings Source: https://www.marketdata.app/docs/api/stocks/earnings/index.md Get historical earnings per share data or a future earnings calendar for a stock. ## Endpoint ``` https://api.marketdata.app/v1/stocks/earnings/{symbol}/ ``` ### Method ``` GET ``` ## Request Example ### HTTP **GET** [https://api.marketdata.app/v1/stocks/earnings/AAPL/](https://api.marketdata.app/v1/stocks/earnings/AAPL/) ### JavaScript ```js title="app.js" 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 ```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); } ``` ### Python ```python title="app.py" from marketdata import MarketDataClient client = MarketDataClient() earnings = client.stocks.earnings("AAPL") print(earnings) ``` ### Go ```go title="stockEarnings.go" import ( "fmt" api "github.com/MarketDataApp/sdk-go" ) func ExampleStockEarningsRequest() { earningsReports, err := StockEarnings().Symbol("AAPL").Get() if err != nil { fmt.Print(err) return } for _, report := range earningsReports { fmt.Println(report) } } ``` ### PHP ```php title="stockEarnings.php" use MarketDataApp\Client; $client = new Client(); $earnings = $client->stocks->earnings(symbol: "AAPL"); // Display formatted earnings summary echo $earnings; ``` ### Java ```java title="StockEarnings.java" import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.stocks.StockEarningsRequest; try (MarketDataClient client = new MarketDataClient()) { client.stocks().earnings(StockEarningsRequest.of("AAPL")).values().forEach(System.out::println); } ``` ### Kotlin ```kotlin title="StockEarnings.kt" import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.stocks.StockEarningsRequest MarketDataClient().use { client -> client.stocks().earnings(StockEarningsRequest.of("AAPL")).values().forEach(::println) } ``` ### C# ```csharp title="StockEarnings.cs" using MarketDataApp; using var client = await MarketDataClient.CreateAsync(); foreach (var report in (await client.Stocks.GetEarningsAsync("AAPL")).Values) { Console.WriteLine(report); } ``` ## Response Example ```json { "s": "ok", "symbol": ["AAPL"], "fiscalYear": [2023], "fiscalQuarter": [1], "date": [1672462800], "reportDate": [1675314000], "reportTime": ["before market open"], "currency": ["USD"], "reportedEPS": [1.88], "estimatedEPS": [1.94], "surpriseEPS": [-0.06], "surpriseEPSpct": [-3.0928], "updated": [1701690000] } ``` ## Request Parameters ### Required - **symbol** `string` The company's ticker symbol. ### Optional - **from** `date` The earliest earnings report to include in the output. If you use countback, `from` is not required. Accepted timestamp inputs: ISO 8601, unix, spreadsheet. - **to** `date` The latest earnings report to include in the output. Accepted timestamp inputs: ISO 8601, unix, spreadsheet. - **countback** `number` Countback will fetch a specific number of earnings reports before `to`. If you use `from`, countback is not required. - **date** `date` Retrieve a specific earnings report by date. Accepted timestamp inputs: ISO 8601, unix, spreadsheet. ## Response Attributes ### Success - **s** `string` Will always be `ok` when there is data for the symbol requested. - **symbol** `array[string]` The symbol of the stock. - **fiscalYear** `array[number]` The fiscal year of the earnings report. This may not always align with the calendar year. - **fiscalQuarter** `array[number]` The fiscal quarter of the earnings report. This may not always align with the calendar quarter. - **date** `array[date]` The last calendar day that corresponds to this earnings report. - **reportDate** `array[date]` The date the earnings report was released or is projected to be released. :::warning[Estimated Dates for Future Earnings] For upcoming earnings events, the `reportDate` is an **estimate** based on historical reporting patterns and is not a confirmed date. These dates frequently change as companies update their reporting schedules. Only after a company has actually reported its earnings does the `reportDate` become a confirmed, historical data point. Do not make trading decisions based on estimated future earnings dates. ::: - **reportTime** `array[string]` The value will be either `before market open`, `after market close`, or `during market hours`. - **currency** `array[string]` The currency of the earnings report. - **reportedEPS** `array[number]` The earnings per share reported by the company. Earnings reported are typically non-GAAP unless the company does not report non-GAAP earnings. :::tip GAAP (Generally Accepted Accounting Principles) earnings per share (EPS) count all financial activities except for discontinued operations and major changes in accounting methods. Non-GAAP EPS, on the other hand, typically doesn't include losses or devaluation of assets, and often leaves out irregular expenses like significant restructuring costs, large tax or legal charges, especially for companies not in the financial sector. ::: - **estimatedEPS** `array[number]` The average consensus estimate by Wall Street analysts. - **surpriseEPS** `array[number]` The difference (in earnings per share) between the estimated earnings per share and the reported earnings per share. - **surpriseEPSpct** `array[number]` The difference in percentage terms between the estimated EPS and the reported EPS, expressed as a decimal. For example, if the estimated EPS is 1.00 and the reported EPS is 1.20, the surpriseEPSpct would be 0.20 (or 20%). - **updated** `array[date]` The date/time the earnings data for this ticker was last updated. ### No Data - **s** `string` Status will be `no_data` if no earnings data can be found for the symbol. ### Error - **s** `string` Status will be `error` if the request produces an error response. - **errmsg** `string` An error message. ## Usage Information ### Data Availability This endpoint provides historical earnings data and future earnings calendar information. Earnings data is fundamental data that is updated after companies report their quarterly and annual results. | User Type | Exchange Entitlement | Data Type | |-----------|----------------------|------------------------| | All Users | Not Required | Historical/Fundamental | ### Pricing The cost of using the earnings API endpoint is 1 credit per API call. | Data Type | Cost Basis | Credits Required per Unit | |---------------|--------------|---------------------------| | Earnings Data | Per API call | 1 credit | --- # Stock News Source: https://www.marketdata.app/docs/api/stocks/news/index.md > [!WARNING] > **[Beta Endpoint]** > > The News endpoint is still in beta and has not yet been optimized for performance. Use caution before adding this endpoint in a prodution environment. Get news for a stock. ## Endpoint ``` https://api.marketdata.app/v1/stocks/news/{symbol}/ ``` ### Method ``` GET ``` ## Request Example ### HTTP **GET** [https://api.marketdata.app/v1/stocks/news/AAPL/](https://api.marketdata.app/v1/stocks/news/AAPL/) ### JavaScript ```js title="app.js" 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 ```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); } ``` ### Python ```python title="app.py" from marketdata import MarketDataClient client = MarketDataClient() news = client.stocks.news("AAPL") print(news) ``` ### Go ```go title="stockNews.go" import ( "fmt" api "github.com/MarketDataApp/sdk-go" ) func ExampleStockNewsRequest_get() { news, err := StockNews().Symbol("AAPL").Get() if err != nil { fmt.Print(err) return } for _, article := range news { fmt.Println(article) } } ``` ### PHP ```php title="stockNews.php" use MarketDataApp\Client; $client = new Client(); $news = $client->stocks->news(symbol: "AAPL"); // Display formatted news summary echo $news; ``` ### Java ```java title="StockNews.java" import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.stocks.StockNewsRequest; try (MarketDataClient client = new MarketDataClient()) { client.stocks().news(StockNewsRequest.of("AAPL")).values().forEach(System.out::println); } ``` ### Kotlin ```kotlin title="StockNews.kt" import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.stocks.StockNewsRequest MarketDataClient().use { client -> client.stocks().news(StockNewsRequest.of("AAPL")).values().forEach(::println) } ``` ### C# ```csharp title="StockNews.cs" using MarketDataApp; using var client = await MarketDataClient.CreateAsync(); foreach (var article in (await client.Stocks.GetNewsAsync("AAPL")).Values) { Console.WriteLine(article); } ``` ## Response Example ```json { "s":"ok", "symbol": ["AAPL"], "headline": ["Whoa, There! Let Apple Stock Take a Breather Before Jumping in Headfirst."], "content": ["Apple is a rock-solid company, but this doesn't mean prudent investors need to buy AAPL stock at any price."], "source": ["https://investorplace.com/2023/12/whoa-there-let-apple-stock-take-a-breather-before-jumping-in-headfirst/"], "publicationDate": ["1703041200"], "updated": 1703041200 } ``` ## Request Parameters ### Required - **symbol** `string` The company's ticker symbol. ### Optional - **from** `date` The earliest news to include in the output. If you use countback, `from` is not required. Accepted timestamp inputs: ISO 8601, unix, spreadsheet. - **to** `date` The latest news to include in the output. Accepted timestamp inputs: ISO 8601, unix, spreadsheet. - **countback** `number` Countback will fetch a specific number of news before `to`. If you use `from`, countback is not required. - **date** `date` Retrieve news for a specific day. Accepted timestamp inputs: ISO 8601, unix, spreadsheet. ## Response Attributes ### Success - **s** `string` Will always be `ok` when there is data for the symbol requested. - **symbol** `array[string]` The symbol of the stock. - **headline** `array[string]` The headline of the news article. - **content** `array[string]` The content of the article, if available. :::tip Please be aware that this may or may not include the full content of the news article. Additionally, it may include captions of images, copyright notices, syndication information, and other elements that may not be suitable for reproduction without additional filtering. ::: - **source** `array[url]` The source URL where the news appeared. - **publicationDate** `array[date]` The date the news was published on the source website. - **updated** `date` The timestamp of the last update to the news data. ### No Data - **s** `string` Status will be `no_data` if no news can be found for the symbol. ### Error - **s** `string` Status will be `error` if the request produces an error response. - **errmsg** `string` An error message. ## Usage Information ### Data Availability This endpoint provides news articles related to stocks. News data is auxiliary data that is updated as articles are published and aggregated. | User Type | Exchange Entitlement | Data Type | |-----------|----------------------|----------------| | All Users | Not Required | News/Auxiliary | ### Pricing The cost of using the news API endpoint is 1 credit per API call. | Data Type | Cost Basis | Credits Required per Unit | |-----------|--------------|---------------------------| | News Data | Per API call | 1 credit | --- # Real-Time Prices Source: https://www.marketdata.app/docs/api/stocks/prices/index.md Get real-time midpoint prices for one or more stocks. This endpoint returns real-time prices for stocks, using the [SmartMid](https://www.marketdata.app/smart-mid/) model. ## Endpoint ``` https://api.marketdata.app/v1/stocks/prices/{symbol}/ ``` or ``` https://api.marketdata.app/v1/stocks/prices/?symbols={symbol1},{symbol2},... ``` ### Method ``` GET ``` ## Request Examples ### Single Symbol ### HTTP **GET** [https://api.marketdata.app/v1/stocks/prices/AAPL/](https://api.marketdata.app/v1/stocks/prices/AAPL/) ### JavaScript ```js title="app.js" 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 ```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); } ``` ### Python ```python title="app.py" from marketdata import MarketDataClient client = MarketDataClient() prices = client.stocks.prices("AAPL") print(prices) ``` ### PHP ```php title="stockPrices.php" use MarketDataApp\Client; $client = new Client(); $prices = $client->stocks->prices("AAPL"); // Display formatted price echo $prices; ``` ### Java ```java title="StockPrices.java" import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.stocks.StockPricesRequest; try (MarketDataClient client = new MarketDataClient()) { client.stocks().prices(StockPricesRequest.of("AAPL")).values().forEach(System.out::println); } ``` ### Kotlin ```kotlin title="StockPrices.kt" import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.stocks.StockPricesRequest MarketDataClient().use { client -> client.stocks().prices(StockPricesRequest.of("AAPL")).values().forEach(::println) } ``` ### C# ```csharp title="StockPrices.cs" using MarketDataApp; using var client = await MarketDataClient.CreateAsync(); foreach (var price in (await client.Stocks.GetPriceAsync("AAPL")).Values) { Console.WriteLine(price); } ``` ### Multiple Symbols ### HTTP **GET** [https://api.marketdata.app/v1/stocks/prices/?symbols=AAPL,META,MSFT](https://api.marketdata.app/v1/stocks/prices/?symbols=AAPL,META,MSFT) ### JavaScript ```js title="app.js" 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 ```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); } ``` ### Python ```python title="app.py" from marketdata import MarketDataClient client = MarketDataClient() prices = client.stocks.prices(["AAPL", "META", "MSFT"]) print(prices) ``` ### PHP ```php title="stockPrices.php" use MarketDataApp\Client; $client = new Client(); $prices = $client->stocks->prices(["AAPL", "META", "MSFT"]); // Display formatted prices summary echo $prices; ``` ### Java ```java title="StockPrices.java" import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.stocks.StockPricesRequest; try (MarketDataClient client = new MarketDataClient()) { client.stocks().prices(StockPricesRequest.of("AAPL", "META", "MSFT")).values().forEach(System.out::println); } ``` ### Kotlin ```kotlin title="StockPrices.kt" import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.stocks.StockPricesRequest MarketDataClient().use { client -> client.stocks().prices(StockPricesRequest.of("AAPL", "META", "MSFT")).values().forEach(::println) } ``` ### C# ```csharp title="StockPrices.cs" using MarketDataApp; using var client = await MarketDataClient.CreateAsync(); foreach (var price in (await client.Stocks.GetPricesAsync(["AAPL", "META", "MSFT"])).Values) { Console.WriteLine(price); } ``` ## Response Example ```json { "s": "ok", "symbol": ["AAPL", "META", "MSFT"], "mid": [149.07, 320.45, 380.12], "change": [-2.052, 1.23, -0.85], "changepct": [-0.0088, 0.0039, -0.0022], "updated": [1663958092, 1663958092, 1663958092] } ``` ## Request Parameters ### Required You can provide the symbol(s) in one of two ways: 1. As part of the URL path: - **symbol** `string` The company's ticker symbol. 2. As a query parameter: - **symbols** `string` Comma-separated list of ticker symbols. > [!TIP] > **[Batching multi-symbol requests]** > > There is no application-level cap on the number of symbols, but the HTTP request line is limited to 4094 bytes. For typical 4-character tickers that's roughly 700–800 symbols per request. To stay clear of the limit, chunk into batches of **500 symbols or fewer**. See [400: Bad Request — URL Too Long](https://www.marketdata.app/docs/api/troubleshooting/bad-request#case-5-url-too-long-multi-symbol-requests) for details. ### Optional - **extended** `boolean` Control the inclusion of extended hours data in the price output. Defaults to `true` if omitted. - When set to `true`, the most recent price is always returned, without regard to whether the market is open for primary trading or extended hours trading. - When set to `false`, only prices from the primary trading session are returned. When the market is closed or in extended hours, a historical price from the last closing bell of the primary trading session is returned instead of an extended hours price. ## Response Attributes ### Success - **s** `string` Will always be `ok` when there is data for the symbols requested. - **symbol** `array[string]` Array of ticker symbols that were requested. - **mid** `array[number]` Array of midpoint prices, as calculated by the [SmartMid](https://www.marketdata.app/smart-mid/) model. - **change** `array[number]` Array of price changes in currency units compared to the closing price of the previous primary trading session. - **changepct** `array[number]` Array of price changes in percent, expressed as a decimal, compared to the closing price of the previous day. For example, a 3% change will be represented as 0.03. > [!NOTE] > - When the market is open for primary trading, **change** and **changepct** are always calculated using the current midpoint price and the last primary session close. When the market is closed or in extended hours, this criteria is also used as long as `extended` is omitted or set to `true`. > - When `extended` is set to `false`, and the market is closed or in extended hours, prices from extended hours are not considered. The values for **change** and **changepct** will be calculated using the last two closing prices instead. - **updated** `array[date]` Array of date/times for each stock price. All timestamps use US Eastern Time (America/New_York). See [Response Timezone](https://www.marketdata.app/docs/docs/api/dates-and-times#response-timezone) for details. ### No Data - **s** `string` Status will only be `no_data` if no prices can be found for all of the symbols. If a price for any symbol can be returned, the request will be successful. ### Error - **s** `string` Status will be `error` if the request produces an error response. - **errmsg** `string` An error message. ## Usage Information ### Data Availability This endpoint is available to all users and does not require any exchange entitlements. All users receive real-time prices regardless of their plan or access level. **Cached, delayed, and historical data are not available on this endpoint - only real-time prices are provided.** | User Type | Exchange Entitlement | Price Type | |-----------|----------------------|------------| | All Users | Not Required | Real-time | ### Pricing The cost of using the stock prices API endpoint is 1 credit per symbol. | Data Type | Cost Basis | Credits Required per Unit | |-----------------|---------------|---------------------------| | Real-Time Data | Per symbol | 1 credit | | Cached Data | Not available | N/A | | Delayed Data | Not available | N/A | | Historical Data | Not available | N/A | --- # Delayed Stock Quotes Source: https://www.marketdata.app/docs/api/stocks/quotes/index.md Retrieve the most recent available delayed quote for one or more stocks. ## Endpoint ``` https://api.marketdata.app/v1/stocks/quotes/{symbol}/ ``` or ``` https://api.marketdata.app/v1/stocks/quotes/?symbols={symbol1},{symbol2},... ``` ### Method ``` GET ``` ## Request Examples ### Single Symbol ### HTTP **GET** [https://api.marketdata.app/v1/stocks/quotes/AAPL/](https://api.marketdata.app/v1/stocks/quotes/AAPL/) ### JavaScript ```js title="app.js" 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 ```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); } ``` ### Python ```python title="app.py" from marketdata import MarketDataClient client = MarketDataClient() quotes = client.stocks.quotes("AAPL") print(quotes) ``` ### Go ```go title="stockQuote.go" import ( "fmt" api "github.com/MarketDataApp/sdk-go" ) func ExampleStockQuoteRequest() { quotes, err := StockQuote().Symbol("AAPL").Get() if err != nil { fmt.Print(err) return } for _, quote := range quotes { fmt.Println(quote) } } ``` ### PHP ```php title="stockQuote.php" use MarketDataApp\Client; $client = new Client(); $quote = $client->stocks->quote("AAPL"); // Display formatted quote with price, change, bid/ask, volume echo $quote; ``` ### Java ```java title="StockQuote.java" import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.stocks.StockQuoteRequest; try (MarketDataClient client = new MarketDataClient()) { client.stocks().quote(StockQuoteRequest.of("AAPL")).values().forEach(System.out::println); } ``` ### Kotlin ```kotlin title="StockQuote.kt" import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.stocks.StockQuoteRequest MarketDataClient().use { client -> client.stocks().quote(StockQuoteRequest.of("AAPL")).values().forEach(::println) } ``` ### C# ```csharp title="StockQuote.cs" using MarketDataApp; using var client = await MarketDataClient.CreateAsync(); foreach (var quote in (await client.Stocks.GetQuoteAsync("AAPL")).Values) { Console.WriteLine(quote); } ``` ### Multiple Symbols ### HTTP **GET** [https://api.marketdata.app/v1/stocks/quotes/?symbols=AAPL,META,MSFT](https://api.marketdata.app/v1/stocks/quotes/?symbols=AAPL,META,MSFT) ### JavaScript ```js title="app.js" 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 ```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); } ``` ### Python ```python title="app.py" from marketdata import MarketDataClient client = MarketDataClient() quotes = client.stocks.quotes(["AAPL", "META", "MSFT"]) print(quotes) ``` ### Go ```go title="stockQuotes.go" import ( "fmt" api "github.com/MarketDataApp/sdk-go" ) func ExampleBulkStockQuotesRequest() { quotes, err := api.BulkStockQuotes().Symbols([]string{"AAPL", "META", "MSFT"}).Get() if err != nil { fmt.Print(err) return } for _, quote := range quotes { fmt.Println(quote) } } ``` ### PHP ```php title="stockQuotes.php" use MarketDataApp\Client; $client = new Client(); $quotes = $client->stocks->quotes(["AAPL", "META", "MSFT"]); // Display formatted quotes summary echo $quotes; ``` ### Java ```java title="StockQuotes.java" import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.stocks.StockQuotesRequest; try (MarketDataClient client = new MarketDataClient()) { client.stocks().quotes(StockQuotesRequest.of("AAPL", "META", "MSFT")).values().forEach(System.out::println); } ``` ### Kotlin ```kotlin title="StockQuotes.kt" import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.stocks.StockQuotesRequest MarketDataClient().use { client -> client.stocks().quotes(StockQuotesRequest.of("AAPL", "META", "MSFT")).values().forEach(::println) } ``` ### C# ```csharp title="StockQuotes.cs" using MarketDataApp; using var client = await MarketDataClient.CreateAsync(); foreach (var quote in (await client.Stocks.GetQuotesAsync(["AAPL", "META", "MSFT"])).Values) { Console.WriteLine(quote); } ``` ## Response Example ```json { "s": "ok", "symbol": ["AAPL", "META", "MSFT"], "ask": [247.94, 641.42, 465.95], "askSize": [200, 100, 200], "bid": [247.87, 641.25, 465.94], "bidSize": [400, 100, 40], "mid": [247.905, 641.335, 465.945], "last": [247.94, 641.42, 465.94], "change": [-0.41, 7.84, 14.80], "changepct": [-0.0017, 0.0124, 0.0328], "volume": [27255134, 8921456, 26896268], "updated": [1769210435, 1769210435, 1769210429] } ``` ## Request Parameters ### Required You can provide the symbol(s) in one of two ways: 1. As part of the URL path: - **symbol** `string` The company's ticker symbol. 2. As a query parameter: - **symbols** `string` Comma-separated list of ticker symbols. ### Optional - **52week** `boolean` Enable the output of 52-week high and 52-week low data in the quote output. By default this parameter is `false` if omitted. - **extended** `boolean` Control the inclusion of extended hours data in the quote output. Defaults to `true` if omitted. - When set to `true`, the most recent quote is always returned, without regard to whether the market is open for primary trading or extended hours trading. - When set to `false`, only quotes from the primary trading session are returned. When the market is closed or in extended hours, a historical quote from the last closing bell of the primary trading session is returned instead of an extended hours quote. ## Response Attributes ### Success - **s** `string` Will always be `ok` when there is data for the symbol requested. - **symbol** `array[string]` Array of ticker symbols that were requested. - **ask** `array[number]` The ask price of the stock. - **askSize** `array[number]` The number of shares offered at the ask price. - **bid** `array[number]` The bid price. - **bidSize** `array[number]` The number of shares that may be sold at the bid price. - **mid** `array[number]` The midpoint price between the ask and the bid. - **last** `array[number]` The last price the stock traded at. - **change** `array[number]` The difference in price in currency units compared to the closing price of the previous primary trading session. - **changepct** `array[number]` The difference in price in percent, expressed as a decimal, compared to the closing price of the previous day. For example, a 3% change will be represented as 0.03. > [!NOTE] > - When the market is open for primary trading, **change** and **changepct** are always calculated using the last traded price and the last primary session close. When the market is closed or in extended hours, this criteria is also used as long as `extended` is omitted or set to `true`. > - When `extended` is set to `false`, and the market is closed or in extended hours, quotes from extended hours are not considered. The values for **change** and **changepct** will be calculated using the last two closing prices instead. - **52weekHigh** `array[number]` The 52-week high for the stock. This parameter is omitted unless the optional 52week request parameter is set to true. - **52weekLow** `array[number]` The 52-week low for the stock. This parameter is omitted unless the optional 52week request parameter is set to true. - **volume** `array[number]` The number of shares traded during the current session. - **updated** `array[date]` The date/time of the current stock quote. All timestamps use US Eastern Time (America/New_York). See [Response Timezone](https://www.marketdata.app/docs/docs/api/dates-and-times#response-timezone) for details. ### No Data - **s** `string` Status will only be `no_data` if no quotes can be found for all of the symbols. If a quote for any symbol can be returned, the request will be successful. ### Error - **s** `string` Status will be `error` if the request produces an error response. - **errmsg** `string` An error message. ## Usage Information ### Data Availability The type of quote you receive depends on your user type and UTP entitlement. This may include a 15-minute delayed quote or historical (1 day old) quote, depending on the plan or access level. Real-time quotes are not currently available. | User Type | UTP Entitlement | Quote Type | |------------------|-----------------|------------------------| | Non-Professional | ✅ | 15-min delayed | | Non-Professional | ❌ | Historical (1 day old) | | Professional | Any | Historical (1 day old) | | Unknown | Any | Historical (1 day old) | > [!NOTE] > **[What are entitlements?]** > > Entitlements are permissions granted by exchanges that allow access to their data. To get delayed quotes, users need to sign the [UTP agreement](https://www.marketdata.app/docs/account/entitlements). [Learn more about entitlements](https://www.marketdata.app/docs/account/entitlements). ### Pricing The cost of using the stock quote API endpoint depends on the type of data you choose and your usage pattern. Here's a breakdown of the pricing: | Data Type | Cost Basis | Credits Required per Unit | |------------------|------------|---------------------------| | 15m Delayed Data | Per symbol | 1 credit | | Historical Data | Per symbol | 1 credit | --- # Troubleshooting Source: https://www.marketdata.app/docs/api/troubleshooting/index.md The Market Data API uses standard HTTP status codes to respond to each request. By preparing your application to utilize these status codes, you can often times solve common errors, or retry failed requests. ## Successful Requests (2xx) These are requests that are answered successfully. > [!CAUTION] > Some libraries are not prepared to handle HTTP 203 response codes as successful requests. Ensure the library you are using can accept a 203 response code the same way as a 200 response code. - `200 OK` - Successfully answered the request. - `203 NON-AUTHORITATIVE INFORMATION` - Successfully served the request from our caching server. Treat this result the same as STATUS 200. - `204 NO CONTENT` - Indicates a successful request for explicitly requested cached data, but our cache server lacks cached data for the symbol requested. Resend the request using the live mode. ## Client Errors (4xx) Client errors occur when Market Data cannot respond to a request due to a problem with the request. The request will need to be modified in order to get a different response. > [!TIP] > If you believe your request is correct and you received a 4xx reply in error, please ensure you log our complete response to your request, including the full response headers along with the complete JSON error message we deliver in our reply. Open a ticket at our help desk and provide this information to our support staff and we will investigate further. - `400 BAD REQUEST` - Input could not be parsed or validated (for example URL construction issues, invalid parameter values/types, invalid date usage, or invalid option symbols). See [400: Bad Request](https://www.marketdata.app/docs/api/troubleshooting/bad-request) for the complete troubleshooting guide and [URL Anatomy (Path vs Query Parameters)](https://www.marketdata.app/docs/api/troubleshooting/url-parameters) for URL construction basics. - `401 UNAUTHORIZED` - The token supplied with the request is missing, invalid, or cannot be used. See [Authentication](https://www.marketdata.app/docs/api/troubleshooting/authentication). - `402 PAYMENT REQUIRED` - The requested action cannot be performed with your current plan, such as requesting data older than your plan allows, using premium endpoints on Free/Trial, or using `mode=cached` on Free/Trial. See [402: Payment Required](https://www.marketdata.app/docs/api/troubleshooting/payment-required). - `403 FORBIDDEN` - Access denied. Only one device is permitted to access the API at a time. Your IP address has changed, and your account is temporarily blocked for security reasons. Please wait 5 minutes before trying again. See [Multiple IP Addresses](https://www.marketdata.app/docs/api/troubleshooting/multiple-ip-addresses). - `404 NOT FOUND` - No data exists for the requested symbol or time period. Consider trying a different symbol or time frame. - `413 PAYLOAD TOO LARGE` - The request payload is too large. This is often due to requesting more than 1 year of **intraday** candle data in a single request. Reduce the intraday time window (daily candles can span longer ranges). See [413: Payload Too Large](https://www.marketdata.app/docs/api/troubleshooting/payload-too-large). - `429 TOO MANY REQUESTS` - Credit limit exceeded for your current plan window (daily for Free/Starter/Trader, per-minute for Quant/Prime). See [429: Credit Limit Reached](https://www.marketdata.app/docs/api/troubleshooting/running-out-of-credits). - `429 TOO MANY REQUESTS` - Concurrent request limit reached (more than 50 simultaneous in-flight requests). See [429: Too Many Concurrent Requests](https://www.marketdata.app/docs/api/troubleshooting/too-many-concurrent-requests). ## Server Errors (5xx) Server errors are used to indicate problems with Market Data's service. They are requests that appear to be properly formed, but can't be responded to due to some kind of problem with our servers. ### Permanent Failures - `500 INTERNAL SERVER ERROR` - An unknown server issue prevents Market Data from responding to your request. Open a ticket with the helpdesk and include the Ray ID of the request. See [Service Outages](https://www.marketdata.app/docs/api/troubleshooting/service-outages). ### Temporary Failures Most 5xx errors are temporary and resolve themselves on their own. Please retry requests that receive 5xx errors at a later time and they will probably be successful. - `502 BAD GATEWAY` - Market Data's API server does not respond to the gateway, indicating the API is offline or unreachable. This error is normally sporadic and due to network issues and will typically resolve itself within 1-2 minutes. See [Service Outages](https://www.marketdata.app/docs/api/troubleshooting/service-outages). - `503 SERVICE UNAVAILABLE` - Market Data's API server is accessible but cannot fulfill the request, usually due to server overload. Retry in a few minutes. See [Service Outages](https://www.marketdata.app/docs/api/troubleshooting/service-outages). - `504 GATEWAY TIMEOUT` - Market Data's load balancer received no response from the API, suggesting the request is taking too long to resolve. Retry in 1-2 minutes and report to the helpdesk if the issue persists. See [Service Outages](https://www.marketdata.app/docs/api/troubleshooting/service-outages). - `509 API ENDPOINT OVERLOADED` - The endpoint is currently overloaded. Retry in a few minutes and report to the helpdesk if the issue continues for more than 15 minutes. See [Service Outages](https://www.marketdata.app/docs/api/troubleshooting/service-outages). - `524 A TIMEOUT OCCURRED` - The Market Data API failed to provide an HTTP response before the default 100-second connection timeout, possibly due to server overload or resource struggles. Contact support if this continues for more than 15 minutes. See [Service Outages](https://www.marketdata.app/docs/api/troubleshooting/service-outages). - `529 DATABASE OFFLINE` - The database is offline, overloaded, or not responding. Contact support or submit a ticket if this error persists for more than 15 minutes. See [Service Outages](https://www.marketdata.app/docs/api/troubleshooting/service-outages). - `530 DATABASE ERROR` - The request resulted in a database error. Contact support or submit a ticket with your API request details. See [Service Outages](https://www.marketdata.app/docs/api/troubleshooting/service-outages). - `540 API ENDPOINT OFFLINE` - The Market Data API endpoint expected to respond is offline. Report to the helpdesk if this persists for more than 15 minutes. See [Service Outages](https://www.marketdata.app/docs/api/troubleshooting/service-outages). - `598 API GATEWAY OFFLINE` - The gateway server is not responding or is unavailable. Report to the helpdesk if this issue continues for more than 15 minutes. See [Service Outages](https://www.marketdata.app/docs/api/troubleshooting/service-outages). --- # 401: Authentication Source: https://www.marketdata.app/docs/api/troubleshooting/authentication/index.md Authentication issues usually arise due to incorrect headers, omission of the authorization header, or problems with URL parameters. If you encounter a 401 error, it's usually related to issues with your `Authorization` header. The most common issues are: - Incorrect token - Invalid characters to separate the query string from the path - Invalid characters to separate query parameters from each other Troubleshooting authentication issues is crucial for ensuring uninterrupted access to our API services. Most authentication issues are related to header-based authentication, but URL parameter authentication can also be troublesome to users who are getting started with Market Data as their first REST API. This guide aims to provide you with steps to resolve common problems. > [!TIP] > Even though it is more complex to set-up, we encourage all users to take the extra time required to configure header-based authentication for our API, as this is the most secure method of authentication. ## Troubleshooting URL Parameter Authentication Usually URL parameter authentication goes wrong because customers use **invalid characters to separate the query string from the path**. The correct character to use is `?` and the correct character to use to separate query parameters is `&`. If you use the wrong characters, the API will not be able to parse the query string correctly and will be unable to authenticate your request. Learn more about the correct format of URL parameters [here](https://www.marketdata.app/docs/api/troubleshooting/url-parameters). For example, suppose your token was `token1234` and you were also using the `dateformat` parameter to request a timestamp as the output format for the time. For a stocks/quotes request, the correct URL would be: ```http https://api.marketdata.app/v1/stocks/quotes/SPY/?token=token1234&dateformat=timestamp ``` - Note how the token is separated from the path by a `?` and the dateformat parameter is separated from the token by a `&`. The ordering of the parameters is not important. You do not need to put `token` as the first parameter. It would also be perfectly valid to use the following URL: ```http https://api.marketdata.app/v1/stocks/quotes/SPY/?dateformat=timestamp&token=token1234 ``` No matter the order of the parameters, the API will be able to parse the query string and authenticate your request **as long as the correct characters are used to separate the query string from the path and the query parameters from each other.** ## Troubleshooting Header Authentication The most common issues customers face with header-based authentication are: - Omission of the header - Incorrect header name - Invalid header format - Incorrect token ### Steps for Troubleshooting 401 Errors 1. **Test the Token with URL Parameter Authentication** ```bash curl -X GET "https://api.marketdata.app/v1/stocks/quotes/SPY/?token=YOUR_TOKEN" ``` Use CURL to test your token using URL parameter authentication. If it works, you know that your token is valid. If you are using a token that is not valid, you will receive a 401 error. If you are using a token that is valid, you will receive a `200 OK` response along with a stock quote. 2. **Inspect Request Headers** To inspect the headers your application is sending, especially the `Authorization` header, use our dedicated [headers endpoint](https://www.marketdata.app/docs/api/utilities/headers). This will help you identify any discrepancies in the headers that might be causing authentication issues. ```bash curl -X GET "https://api.marketdata.app/headers/" -H "Authorization: Bearer YOUR_TOKEN" ``` Make a request to https://api.marketdata.app/headers/ from your application and save the headers. This endpoint will return a JSON response of the headers your application is sending, with sensitive headers like `Authorization` partially redacted for security. Compare the headers from your application's request to the expected headers. If your application's `Authorization` header is different from what you expect, there may be an issue with how your application is setting headers. If the headers match your expectations, the issue may lie elsewhere, possibly with the token itself. 3. **Log Response Headers and Submit a Helpdesk Ticket** ```bash curl -X GET "https://api.marketdata.app/v1/stocks/quotes/SPY/" -H "Authorization: Bearer YOUR_TOKEN" -i ``` Finally, we'll now make a header-authentication request using your token. Make a request using the CURL command above. If you receive a 401 error, the issue is with your token. If you receive a `200 OK` response, the issue is with your application's code. > [!TIP] > If the issue persists, include the log data and the Ray ID (listed in the CF-Ray header) when you submit a helpdesk ticket. This will help our support team locate your specific request in our server logs and assist you more effectively. ## Opening a Support Ticket ### When to Open a Ticket Open a ticket if you experience persistent issues or errors that cannot be resolved through this troubleshooting guide. ### What to Include Include the log data and the Ray ID for faster resolution. By attaching this information to your support ticket, it becomes much easier for our staff to understand and solve your ticket. --- # 400: Bad Request Source: https://www.marketdata.app/docs/api/troubleshooting/bad-request/index.md Use this page for **all `400 BAD REQUEST` errors**. A `400` means the request could not be parsed or validated as valid input. ## What Usually Causes 400 1. URL is constructed incorrectly (path vs query string mixups) - For path/query parsing issues caused by bad URL construction, see [400: URL Anatomy (Path vs Query Parameters)](https://www.marketdata.app/docs/api/troubleshooting/url-parameters). 2. Query parameters are malformed (`?` / `&` / `key=value` issues) 3. Parameter type or format is invalid (string where number/date is expected) 4. Parameter value is unsupported or out of range 5. Option symbol is not valid OCC format 6. `date` parameter is used in a current-data request (historical-only usage) 7. URL is too long — most often caused by packing too many tickers into a single multi-symbol request ## Case 1: URL Construction Problems (Path vs Query) Common examples: - Putting query parameters in the path segment - Missing `?` before query parameters - Using wrong separators between parameters Correct structure: ```text https://api.marketdata.app//?key=value&key2=value2 ``` If you need a step-by-step URL-building guide, use: - [400: URL Anatomy (Path vs Query Parameters)](https://www.marketdata.app/docs/api/troubleshooting/url-parameters) ## Case 2: Invalid Parameter Type/Format/Value You can get `400` when a parameter cannot be parsed correctly or violates endpoint rules. Examples: - Sending text where a numeric value is expected - Sending a malformed date - Sending an unsupported enum/value for a parameter - Sending a value outside allowed bounds Fast check: 1. Confirm parameter names exactly match the endpoint docs. 2. Confirm each parameter value type/format is valid. 3. Remove optional parameters and re-add one at a time. ## Case 3: Invalid Option Symbol (OCC) You may receive: ```json { "s": "error", "errmsg": "Invalid option symbol. Use valid OCC symbology for the option symbol." } ``` Meaning: request shape is valid, but `optionSymbol` is malformed. For detailed format rules, examples, and validation workflow, use: - [400: Invalid Option Symbol](https://www.marketdata.app/docs/api/troubleshooting/invalid-option-symbol) ## Case 4: `date` Parameter Is Historical-Only You may receive: ```json { "s": "error", "errmsg": "Invalid date. The date parameter is used for historical queries only." } ``` Meaning: - Request can be otherwise valid - `date` was used in a current-data context How to fix: 1. For current data: omit `date` 2. For historical data: provide a past trading date ```http # Current request (correct): no date parameter GET https://api.marketdata.app/v1/options/quotes/OPTION_SYMBOL/ # Historical request (correct): includes date GET https://api.marketdata.app/v1/options/quotes/OPTION_SYMBOL/?date=2025-01-15 ``` ## Case 5: URL Too Long (Multi-Symbol Requests) You may receive an HTML-formatted `400 BAD REQUEST` response that does **not** follow the usual JSON error shape: ```html Bad Request

Bad Request

Request Line is too large (XXXX > 4094) ``` This is returned by the web server **before** the request reaches the API, so it does not appear in your account's request log and does not consume credits. The cause is exceeding the **4094-byte HTTP request-line limit**. The most common trigger is packing too many tickers into a single multi-symbol call, for example: - `/v1/stocks/prices/?symbols=...` - `/v1/stocks/bulkcandles/D/?symbols=...` ### How many symbols per request There is no application-level cap on symbol count — the ceiling is set entirely by URL length. As a rule of thumb: - An average 4-character ticker plus a separator comma takes 5 bytes. - Path, `token`, `format`, `columns`, and other query parameters typically consume 150–250 bytes of overhead. - That leaves roughly 3,800 bytes for the symbols list — about **700–800 typical-length tickers** before requests begin to fail. To stay clear of the limit, **chunk multi-symbol requests into batches of 500 symbols or fewer.** That leaves headroom for longer tickers (BRK.B, 5-letter Nasdaq tickers) and any additional query parameters you may add later. ### How to fix 1. Reduce the number of symbols in the request. 2. Split your symbol list into multiple smaller calls. 3. If you must send a long list, drop any non-essential query parameters to free up URL space. ## 400 vs Other Status Codes - `400`: request input/format is invalid - `401`: authentication issue (token missing/invalid) - `402`: plan/entitlement limit reached - `403`: forbidden by policy (for example multiple IP addresses) - `404`: request format is valid, but requested resource was not found ## If You Still Get 400 1. Log full request URL and all query parameters 2. Compare request against endpoint docs field-by-field 3. Re-test with only required parameters 4. Add optional parameters back one at a time --- # 400: Invalid Option Symbol Source: https://www.marketdata.app/docs/api/troubleshooting/invalid-option-symbol/index.md Use this page when you receive a `400 BAD REQUEST` with: ```json { "s": "error", "errmsg": "Invalid option symbol. Use valid OCC symbology for the option symbol." } ``` If you need the broader `400` troubleshooting flow, start here: - [400: Bad Request](https://www.marketdata.app/docs/api/troubleshooting/bad-request) ## What This Error Means The endpoint and query string can be valid, but the `optionSymbol` itself is not valid OCC format. ## OCC Option Symbol Format Expected format: `UNDERLYING + YYMMDD + C/P + STRIKE` Example: - `AAPL271217C00250000` Field rules: 1. `UNDERLYING`: Root symbol (for example `AAPL`, `SPY`, `INTC`) 2. `YYMMDD`: Expiration date (2-digit year, month, day) 3. `C/P`: `C` for call or `P` for put 4. `STRIKE`: 8 digits, zero-padded, implied 3 decimal places ## Strike Encoding Reference The strike is represented as an integer with three implied decimal places. Examples: - `150` becomes `00150000` - `32.5` becomes `00032500` - `32.25` becomes `00032250` ## Common Construction Errors 1. Expiration is not in `YYMMDD` 2. Missing call/put flag (`C` or `P`) 3. Strike field is not 8 digits 4. Strike is not zero-padded 5. Decimal strike is encoded incorrectly 6. Legacy pre-2010 style symbol is used ## Validate Before Calling Quotes/History Use the lookup endpoint to build/validate a symbol first. ```http # Build/validate OCC symbol from human-readable contract GET https://api.marketdata.app/v1/options/lookup/AAPL%201/17/2025%20150%20Call # Then request data with the returned optionSymbol GET https://api.marketdata.app/v1/options/quotes/AAPL271217C00250000/ ``` ## 400 vs 404 for Options - `400`: the option symbol format is invalid - `404`: format is valid, but no matching contract/data was found ## Quick Debug Checklist 1. Log the exact `optionSymbol` your client sent 2. Check `YYMMDD`, `C/P`, and 8-digit strike encoding 3. Validate with `options/lookup` 4. Reuse the exact returned symbol in follow-up requests --- # Logging Source: https://www.marketdata.app/docs/api/troubleshooting/logging/index.md ## Why Logging is Important Logging our API's responses is crucial for monitoring the behavior of your application and troubleshooting problems, either with our API or your usage of the API. ### What Should Be Logged Market Data responds to successful requests with either a 200 or 203 status code. Therefore, we recommend you log any response that doesn't have a 2xx status code. Successful responses may be logged if you wish, but normally this is not necessary. When logging errors, the log should include the exact request made, Market Data's response, and the CF-Ray header. ## Logging Examples ### JavaScript ```js title="logger.js" 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 ```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); } ``` ### Python ```python title="logger.py" from marketdata import MarketDataClient, MarketDataClientErrorResult import json # Make the API request using the SDK client = MarketDataClient() result = client.stocks.quotes("AAPL") # Check if the result is an error (SDK handles HTTP errors internally) if isinstance(result, MarketDataClientErrorResult): log_data = { "request": f"GET https://api.marketdata.app/v1/stocks/quotes/AAPL/", "response": str(result.error), "cf_ray_header": "Not available" # CF-Ray is handled internally by the SDK } # Save to a logfile with open("api_error_log.json", "a") as logfile: logfile.write(json.dumps(log_data) + "\n") ``` ### Java ```java title="Logger.java" import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.exception.MarketDataException; import com.marketdata.sdk.stocks.StockQuotesRequest; // Enable DEBUG logging by setting MARKETDATA_LOGGING_LEVEL=DEBUG in your // environment. The SDK then logs full request/response diagnostics (including // the CF-Ray request id) on the "com.marketdata.sdk" java.util.logging logger. try (MarketDataClient client = new MarketDataClient()) { var quotes = client.stocks().quotes(StockQuotesRequest.of("AAPL")); System.out.println(quotes.values()); } catch (MarketDataException e) { // The SDK already logged the failing request/response. getSupportInfo() // bundles the status code, request URL and request id for a bug report. System.err.println(e.getSupportInfo()); } ``` ### Kotlin ```kotlin title="Logger.kt" import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.exception.MarketDataException import com.marketdata.sdk.stocks.StockQuotesRequest // Enable DEBUG logging via the MARKETDATA_LOGGING_LEVEL=DEBUG environment // variable; the SDK logs request/response diagnostics on "com.marketdata.sdk". MarketDataClient().use { client -> try { val quotes = client.stocks().quotes(StockQuotesRequest.of("AAPL")) println(quotes.values()) } catch (e: MarketDataException) { System.err.println(e.supportInfo) } } ``` ### C# ```csharp title="Logger.cs" using MarketDataApp; using MarketDataApp.Exceptions; using Microsoft.Extensions.Logging; // Enable DEBUG logging by setting MARKETDATA_LOGGING_LEVEL=DEBUG in your environment. // The SDK then emits full request/response diagnostics (including the CF-Ray request // id) as structured Microsoft.Extensions.Logging events through the logger you attach. using var loggerFactory = LoggerFactory.Create(builder => builder.AddMarketDataCanonicalConsole()); var options = MarketDataClientOptions.FromEnvironment() .WithLogger(loggerFactory.CreateLogger()); try { using var client = await MarketDataClient.CreateAsync(options); var quotes = await client.Stocks.GetQuotesAsync(["AAPL"]); Console.WriteLine(quotes); } catch (MarketDataException e) { // The SDK already logged the failing request/response. SupportInfo bundles the // status code, request URL and request id for a bug report. Console.Error.WriteLine(e.SupportInfo); } ``` ## The CF-Ray Header ### What is the CF-Ray Header The CF-Ray header (otherwise known as a Ray ID) is a hashed value that encodes information about the Cloudflare data center and the request. Every request that travels through the Cloudflare network is assigned a unique Ray ID for tracking. ### Why It's Important Since Market Data operates on the Cloudflare network, we log each of our API responses with Cloudflare's Ray ID. This allows us to have a unique identifier for each and every API request made to our systems. Additionally, we can also trace all requests through the Cloudflare network from our servers to your application. > [!TIP] > When opening a ticket at the customer helpdesk, if a Ray ID is provided to our support staff, we'll be able to identify the exact request you made and find why it produced an error. ## Opening a Support Ticket ### When to Open a Ticket Open a ticket if you experience persistent issues or errors that cannot be resolved through logging. For example, if you are making properly formatted requests to our systems and you are getting INTERNAL SERVER ERROR messages. ### What to Include Include the log data and the CF-Ray header value for faster resolution. By attaching your log data to your support ticket, it becomes much easier for our staff to understand and solve your ticket. --- # 403: Multiple IP Addresses Source: https://www.marketdata.app/docs/api/troubleshooting/multiple-ip-addresses/index.md > [!NOTE] > **[Automatic Resolution]** > > If your account is blocked, you do not need to contact support. The block will automatically resolve after 5 minutes if you stop making API requests. Simply wait 5 minutes without making any requests from the blocked IP address, then resume with a single device or IP address. > > During the block, you can continue making requests from the previously authorized IP address. If you encounter an issue where your account is temporarily blocked due to accessing the API from multiple devices, follow these steps to resolve it. For background on why this restriction exists, see the [Single IP Address Policy](https://www.marketdata.app/docs/docs/account/data-policies/single-ip/). ## Problem Your account is blocked because it was accessed from multiple IP addresses in a pattern that suggests simultaneous device usage. While **changing IP addresses is permitted**, rapidly switching back and forth between previously used IP addresses indicates multiple active devices, which is against our policy. When this occurs, you'll receive an error response that includes detailed information to help you identify the source of the conflict: ```json { "s": "error", "errmsg": "Access denied. Only one device is permitted. Your IP address has changed, and your account is temporarily blocked for security reasons. Please wait 5 minutes before trying again.", "authorizedIP": "107.178.202.2", "blockedIP": "44.116.21.32", "troubleshootingGuide": "https://www.marketdata.app/docs/api/troubleshooting/multiple-ip-addresses" } ``` The error response includes: - The IP address of your previous request (`authorizedIP`) - The IP address of your current request (`blockedIP`) You can use these IP addresses to: 1. Identify which devices or services are making the conflicting requests. 2. Check if the IPs match known ISPs, VPNs, cloud services, or other servers you're using. 3. Compare with your network configuration to determine if load balancing, round-robin outgoing requests, or failover is causing the issue. ### How It Works When you access the API: - Each new IP address you connect from is recorded - If you connect from IP address A, then switch to IP address B, that's permitted. - However, if you then switch back to IP address A while IP address B is still active within 5 minutes, this suggests two devices are being used simultaneously. - This pattern triggers an **automatic temporary block** on your account. - During the block, you can continue making requests from your previously authorized IP address. - After the block expires, you can switch to a different IP address again, but should avoid switching back to recently used IPs. ### Common Scenarios 1. **VPN Usage**: If you connect to a VPN, then disconnect, your IP address may change back and forth rapidly. This can happen if you frequently switch between a VPN and your regular network. 2. **Wi-Fi Network Changes**: Switching between different Wi-Fi networks in a short period can result in different IP addresses being used within a single five minute period. 3. **Load Balancing**: Servers making outgoing requests might use multiple IP addresses, especially if they are part of a load-balanced setup, round-robin configuration, or if they use multiple network interfaces. 4. **Proxy Usage**: Using proxies or rotating proxies can cause your requests to appear from different IP addresses even if you are using the same device. 5. **Cloud Functions**: Cloud services like Amazon Web Services, Google Cloud, or Azure may send requests from ephemeral instances that change IP addresses frequently. 6. **Google Apps Script**: Using UrlFetch in Google Apps Script can result in requests coming from different IP addresses simultaneously, as Google's infrastructure will rotate requests through various servers. ## Solution 1. **Wait for 5 Minutes**: The block is temporary. Wait for 5 minutes before attempting to access the API again. During this time, ensure all your devices stop their API access. 2. **Use a Single Active Device**: While you can change devices or IP addresses: - Make sure to completely stop API access from your previous device before switching. - Do not switch devices more than once every 5 minutes. - Do not attempt to connect using multiple devices. 3. **For Cloud Systems**: If you're running cloud systems that make API calls: - Use a single cloud instance to make API calls. - Ensure old instances are fully terminated before starting new ones. - Purchase a dedicated IP address for your cloud instance if it will be starting and stopping more than once every 5 minutes. 4. **Serverless Functions Are Not Supported (Lambda, Google Cloud Functions, Google Apps Script, etc.)**: Your subscription is licensed to run on a **single device**. Serverless platforms execute your code across a pool of ephemeral instances with rotating outbound IP addresses, which inherently bypasses the single-device licensing terms. For this reason, serverless functions are not compatible with the API and are not a supported deployment model. Run your workload on a single, persistent device or instance with a stable IP address instead. 5. **Multiple Environments (Development, Staging, Production)**: If you're running more than one environment that calls the API at the same time using the same token, you will hit this block. Sharing the token across environments is also a token-sharing violation under the [Single IP Address Policy](https://www.marketdata.app/docs/docs/account/data-policies/single-ip/). Compliant options: - **Serialize**: run only one environment at a time. For most testing and A/B workflows, running scenarios sequentially is sufficient. - **Use the no-auth demo symbols in development**: stock and option endpoints accept **AAPL** (and any AAPL option contract, e.g. `AAPL271217C00250000`) with no token at all. Development and integration tests that exercise the API surface — request shape, response parsing, error handling — can use these symbols without ever touching your account. See [Authentication › Demo The API With No Authentication](https://www.marketdata.app/docs/docs/api/authentication/#demo-the-api-with-no-authentication). Note that no-auth requests return historical data only. - **Commercial Plan**: if your use case genuinely requires simultaneous parallel data across multiple environments (not testing), the [Commercial Plan](https://www.marketdata.app/docs/docs/account/plans/commercial/) has no IP limit, so you can call the API from as many servers and IP addresses as your product needs. ## Important Note The system is designed to detect patterns of simultaneous access, not just IP changes. While you are permitted to change devices, attempting to maintain multiple active connections from different computers — even when egressed through a single shared IP — will result in account blocks and may result in suspension. For further assistance, please contact our support team. --- # 404: Not Found Source: https://www.marketdata.app/docs/api/troubleshooting/not-found/index.md A `404` can mean different things depending on endpoint and request context. This guide is intentionally detailed and will be built out case by case. ## Case 1: Market Closed on Requested Date One of the most helpful `404` responses is: ```json { "s": "no_data", "errmsg": "Market closed on this date." } ``` What this means: - Your symbol/request shape is valid. - The requested date is not a trading day for that market/session. - The API returns `no_data` to explain that the date context is invalid for quote/candle retrieval. What to do: 1. Check if the requested date is a weekend or market holiday. 2. Retry with the nearest trading day. 3. If using automated date logic, add trading-calendar validation before requesting. ## Case 2: Valid OCC Symbol Format, But Contract Does Not Exist Another common `404` response is: ```json { "s": "error", "errmsg": "No option found. No option was found for this strike and expiration." } ``` What this means: - The option symbol format is valid OCC symbology. - But no real option contract exists for that strike/expiration combination. - This is not a malformed-parameter case (`400`) and not a date-only no-data case. - The API treats this as a non-existent contract and returns `404`. How this differs from other errors: 1. `400 invalid option symbol`: OCC format itself is malformed. 2. `404 no_data` market/date cases: symbol can exist, but no data for that date/session. 3. This case: OCC format is valid, but the contract likely never existed. What to do: 1. Verify strike and expiration against available contracts for the underlying. 2. Use `options/chain` or `options/expirations` to confirm the contract exists. 3. Use `options/lookup` to avoid manually constructing symbols. ## Case 3: Valid Symbol, But Requested Time Has No Available Data Another common `404`/`no_data` response is: ```json { "s": "no_data", "nextTime": null, "prevTime": null } ``` What this means: - The symbol/request can be valid. - But there is no data available around the requested time in either direction. - This can happen for both stocks and options. Common reasons: 1. Requested date is outside available historical coverage for that symbol/endpoint. 2. Valid option contract was requested before it began quoting. 3. Requested window has no recorded quotes/candles and there is no adjacent data to return. Important context: - This is not necessarily customer error. - It can represent a legitimate \"data does not exist in this time region\" condition. What to do: 1. Move the request window forward in time and retry. 2. For options, confirm the contract had started trading by that date. 3. Use nearby date probes to find first/last available data for the symbol. --- # 413: Payload Too Large Source: https://www.marketdata.app/docs/api/troubleshooting/payload-too-large/index.md A `413 PAYLOAD TOO LARGE` means your request asked for more candle data than is allowed in a single call. For candles, this is most commonly triggered on **intraday resolutions** when you request **more than 1 year** in one request. ## Fast Fix For intraday candles, request less than 1 year per API call. ## Why This Happens The candles endpoints enforce request-size constraints to keep responses bounded and reliable. If your intraday candle range is too large, the API returns `413`. Daily candles are allowed beyond 1 year. ## What To Change 1. For intraday requests, reduce the date window to less than 1 year. 2. If you need more history, split your query into multiple requests. ## Example ```http # Too large (intraday example) GET https://api.marketdata.app/v1/stocks/candles/1/AAPL/?from=2022-01-01&to=2024-12-31 # Valid intraday: keep each request under 1 year GET https://api.marketdata.app/v1/stocks/candles/1/AAPL/?from=2024-01-01&to=2024-12-31 # Daily example: multi-year range is allowed GET https://api.marketdata.app/v1/stocks/candles/D/AAPL/?from=2015-01-01&to=2024-12-31 ``` ## If You Still Get 413 1. Double-check `from`/`to` ranges after timezone normalization. 2. Verify your client is not expanding ranges unexpectedly. 3. Contact support with your full request URL and parameters. --- # 402: Payment Required Source: https://www.marketdata.app/docs/api/troubleshooting/payment-required/index.md A `402 PAYMENT REQUIRED` means your request is valid, but your current plan does not include the specific data or feature you requested. This is a **plan entitlement** issue, not an authentication issue and not a rate-limit issue. ## Fast Triage You are likely hitting `402` when one of these is true: 1. You requested historical data older than your plan allows. 2. You requested a premium endpoint from Free/Trial. 3. You requested `mode=cached` from Free/Trial. ## What Triggers 402 by Plan ### Free Forever and Trial Plans Common 402 causes: 1. Historical query older than 1 year. 2. Premium endpoint request. 3. `mode=cached` request. ### Starter Plan Common 402 cause: 1. Historical query older than 5 years. ### Trader, Quant, Prime Trader, Quant, and Prime should not receive `402 PAYMENT REQUIRED` for standard plan usage. If you receive `402` on one of these plans, treat it as unexpected and contact support with the full request/response details. ## Historical Age Rules - Free Forever and Trial plans: up to 1 year of historical data - Starter: up to 5 years of historical data - Trader/Quant/Prime: no historical age limit ## mode=cached Rule `mode=cached` is a paid-plan feature. - Free/Trial: `mode=cached` is not available and can produce `402` - Paid plans: available on supported endpoints For mode details, see [Data Mode](https://www.marketdata.app/docs/api/universal-parameters/mode). ## Premium Endpoint Rule Free/Trial plans can access standard pricing endpoints but not premium endpoint features. Premium endpoint requests from Free/Trial can produce `402`. For plan-level capability details, see [Plan Limits](https://www.marketdata.app/docs/account/plan-limits). ## 30-Second Fix Checklist 1. Confirm your current plan in the dashboard. 2. If historical request: reduce date range to your plan limit. 3. If using Free/Trial: remove `mode=cached`. 4. If using Free/Trial: switch from premium endpoint to standard endpoint (or upgrade). 5. Retry after adjusting request scope/feature usage. ## Common Scenarios ### Scenario 1: "I requested 3 years of history on Free/Trial" What happened: - Plan allows only 1 year of historical data. What to do: - Reduce to `<= 1 year` or upgrade. ### Scenario 2: "I requested 10 years on Starter" What happened: - Starter allows up to 5 years of historical data. What to do: - Reduce to `<= 5 years` or upgrade to Trader/Quant/Prime. ### Scenario 3: "I used mode=cached on Trial" What happened: - Trial does not support cached mode. What to do: - Remove `mode=cached` on Trial or move to a paid plan. ### Scenario 4: "I called a premium endpoint on Free/Trial" What happened: - Premium endpoint access is not included on Free/Trial. What to do: - Use a standard endpoint or upgrade plan. ## When to Contact Support Contact support if you believe the request should be allowed on your current plan. Include: 1. Endpoint and full query parameters 2. Date range requested 3. Your current plan 4. Full response payload and headers --- # Real-Time Data Not Working Source: https://www.marketdata.app/docs/api/troubleshooting/real-time-data/index.md If you're expecting real-time data but receiving delayed or historical data instead, this guide will help you diagnose and fix the issue. The API doesn't throw errors when you're missing required agreements or profile information—instead, it silently provides you with the freshest data you're entitled to receive. ## Understanding the Problem ### What You're Experiencing When you request real-time data (or use `mode=live`), you might be receiving: - **15-minute delayed data** instead of real-time quotes - **Historical data (1 day old)** instead of current market data - Data that appears "stale" when you check the timestamps ### Why This Happens The API determines what type of data you can receive based on two key factors: 1. **Your Professional Status**: Whether you're classified as a professional or non-professional user 2. **Your Exchange Entitlements**: Whether you've signed the required exchange agreements (IEX, UTP, OPRA) If your account profile is incomplete or you haven't signed the required exchange agreements, the API cannot determine your proper status or activate your entitlements. In these cases, the API **silently falls back** to the most restrictive data type you're allowed to receive—typically historical data that's at least 1 day old. ### How to Recognize It The API response includes an `updated` field (or `date` field in some endpoints) that shows when the data was last updated. If this timestamp is more than a few minutes old during market hours, you're likely receiving delayed data instead of real-time data. ## Diagnosing the Issue ### Step 1: Check the Data Timestamp The first step is to verify whether you're actually receiving real-time data by checking the `updated` field in your API responses. ### Python ```python from marketdata import MarketDataClient, OutputFormat, MarketDataClientErrorResult from datetime import datetime # Make a request for stock quotes client = MarketDataClient() quotes = client.stocks.quotes("AAPL", output_format=OutputFormat.JSON) if isinstance(quotes, MarketDataClientErrorResult): print(f"Error: {quotes.error}") elif isinstance(quotes, dict) and quotes.get("s") == "ok" and "updated" in quotes: # Get the timestamp (Unix epoch seconds) updated_timestamp = quotes["updated"][0] updated_time = datetime.fromtimestamp(updated_timestamp) current_time = datetime.now() # Calculate the age of the data age_seconds = (current_time - updated_time).total_seconds() age_minutes = age_seconds / 60 print(f"Data timestamp: {updated_time}") print(f"Current time: {current_time}") print(f"Data age: {age_minutes:.1f} minutes") # Determine data type if age_minutes < 1: print("✅ Real-time data (less than 1 minute old)") elif age_minutes < 20: print("⚠️ 15-minute delayed data") else: print("❌ Historical data (1 day old or more)") ``` ### JavaScript ```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)"); } } } catch (error) { console.error(error); } ``` ### TypeScript ```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); } ``` ### cURL ```bash curl -X GET "https://api.marketdata.app/v1/stocks/quotes/AAPL/" \ -H "Authorization: Bearer YOUR_TOKEN" | jq '.updated' ``` The `updated` field contains a Unix timestamp. Compare this to the current time to determine if the data is real-time, delayed, or historical. ### Java ```java title="CheckDataAge.java" import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.Mode; import com.marketdata.sdk.stocks.StockQuote; import com.marketdata.sdk.stocks.StockQuoteRequest; import java.time.Duration; import java.time.ZonedDateTime; try (MarketDataClient client = new MarketDataClient()) { StockQuote quote = client.stocks() .mode(Mode.LIVE) .quote(StockQuoteRequest.of("AAPL")) .values().get(0); ZonedDateTime updated = quote.updated(); double ageMinutes = Duration.between(updated, ZonedDateTime.now(updated.getZone())).toSeconds() / 60.0; System.out.printf("Data timestamp: %s%n", updated); System.out.printf("Data age: %.1f minutes%n", ageMinutes); if (ageMinutes < 1) { System.out.println("✅ Real-time data (less than 1 minute old)"); } else if (ageMinutes < 20) { System.out.println("⚠️ 15-minute delayed data"); } else { System.out.println("❌ Historical data (1 day old or more)"); } } ``` ### Kotlin ```kotlin title="CheckDataAge.kt" import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.Mode import com.marketdata.sdk.stocks.StockQuoteRequest import java.time.Duration import java.time.ZonedDateTime MarketDataClient().use { client -> val quote = client.stocks() .mode(Mode.LIVE) .quote(StockQuoteRequest.of("AAPL")) .values()[0] val updated = quote.updated() ?: error("No timestamp in response") val ageMinutes = Duration.between(updated, ZonedDateTime.now(updated.zone)).toSeconds() / 60.0 println("Data timestamp: $updated") println("Data age: %.1f minutes".format(ageMinutes)) when { ageMinutes < 1 -> println("✅ Real-time data (less than 1 minute old)") ageMinutes < 20 -> println("⚠️ 15-minute delayed data") else -> println("❌ Historical data (1 day old or more)") } } ``` ### C# ```csharp title="CheckDataAge.cs" using MarketDataApp; using var client = await MarketDataClient.CreateAsync(); var quote = (await client.Stocks.GetQuoteAsync( "AAPL", options: new MarketDataRequestOptions { Mode = Mode.Live })).Values[0]; // Updated is already normalized to America/New_York; compare in UTC. var ageMinutes = (DateTimeOffset.UtcNow - quote.Updated!.Value.ToUniversalTime()).TotalMinutes; Console.WriteLine($"Data timestamp: {quote.Updated}"); Console.WriteLine($"Data age: {ageMinutes:F1} minutes"); if (ageMinutes < 1) { Console.WriteLine("✅ Real-time data (less than 1 minute old)"); } else if (ageMinutes < 20) { Console.WriteLine("⚠️ 15-minute delayed data"); } else { Console.WriteLine("❌ Historical data (1 day old or more)"); } ``` ### Step 2: Check Your Account Status After verifying that you're receiving delayed data, check your account status in the [Market Data Dashboard](https://dashboard.marketdata.app/marketdata/): 1. **Log into your account** at the dashboard 2. **Navigate to the Profile section** 3. **Check for incomplete information**: - Missing contact information (name, address, email, phone) - Missing employer information (company name, address, job title, LinkedIn) 4. **Check for unsigned agreements**: - IEX agreement (for real-time stock quotes) - UTP agreement (for 15-minute delayed stock candles) - OPRA agreement (for real-time options quotes) ## Root Causes ### 1. Incomplete Data Use Profile **Problem**: Your account profile is missing required information that prevents us from classifying you as either a professional or non-professional user. **Required Information**: - **Contact Information**: Name, Address, Email, Phone Number - **Employer Information**: Company Name, Address, Job Title, LinkedIn Profile URL **Impact**: When your profile is incomplete, your professional status is classified as "Unknown." Accounts with unknown status receive the most restrictive data type—historical data that's at least 1 day old—regardless of which exchange agreements you've signed. **How to Fix**: Complete all required fields in your profile at the [Market Data Dashboard](https://dashboard.marketdata.app/marketdata/). Once your profile is complete, we can classify your account and activate appropriate data access. ### 2. Missing Exchange Agreements **Problem**: You haven't signed the required exchange agreements that entitle you to real-time or delayed data. **Required Agreements**: - **IEX Agreement**: Required for real-time stock quotes - Without IEX: Non-professional users get 15-minute delayed quotes - Professional users do not receive real-time quotes with or without IEX. Delayed / historical access depends on the data policies of each exchange. - **UTP Agreement**: Required for 15-minute delayed stock candles - Without UTP: All users get historical (1 day old) candles - **OPRA Agreement**: Required for real-time options quotes - Without OPRA: Non-professional users get 15-minute delayed options quotes - Professional users do not receive real-time options quotes with or without OPRA. Delayed / historical access depends on the data policies of each exchange. **Impact**: Even if your profile is complete and you're classified as non-professional, missing exchange agreements will prevent you from receiving real-time data. **How to Fix**: Sign the appropriate exchange agreements in your dashboard: 1. Log into the [Market Data Dashboard](https://dashboard.marketdata.app/marketdata/) 2. Navigate to the Profile section 3. Review and sign the required agreements for the data you need 4. Wait for approval (typically processed within 1 business day) ### 3. Unknown Professional Status **Problem**: Your account cannot be classified as professional or non-professional due to incomplete information. **Impact**: Accounts with unknown status receive historical data (1 day old) regardless of: - Which exchange agreements you've signed - What plan you're on - Whether you're requesting real-time data **How to Fix**: Complete your data use profile with all required information. Once we can verify your information and classify your account, we can activate the appropriate data access level. ## Solutions ### Solution 1: Complete Your Data Use Profile **Steps**: 1. Log into the [Market Data Dashboard](https://dashboard.marketdata.app/marketdata/) 2. Navigate to **Profile** section 3. Ensure all required fields are completed: - **Contact Information**: Name, Address, Email, Phone Number - **Employer Information**: Company Name, Address, Job Title, LinkedIn Profile URL 4. Submit the profile for verification **Verification Process**: - We review your account profile and verify information against third-party databases (LinkedIn, FINRA BrokerCheck) - The goal is to classify your data use as either professional or non-professional - Verification typically takes **1 business day** - Until verification is complete, your account cannot be activated for real-time data **Note**: Customers employed by banks, insurance companies, or other financial institutions may require additional review. ### Solution 2: Sign Exchange Agreements **Steps**: 1. Log into the [Market Data Dashboard](https://dashboard.marketdata.app/marketdata/) 2. Navigate to **Profile** section 3. Review and sign the required agreements: - **IEX Agreement**: For real-time stock quotes - **UTP Agreement**: For 15-minute delayed stock candles - **OPRA Agreement**: For real-time options quotes 4. Wait for approval (typically **1 business day**) **Important**: You must have a complete profile before exchange agreements can be activated. If your profile is incomplete, signing agreements won't enable real-time data access. ### Solution 3: Wait for Verification After completing your profile and signing agreements: - Allow **1 business day** for verification and approval - Check your dashboard for status updates - Test your API requests after verification is complete ## Data Availability by Endpoint The data type you receive (real-time, 15-minute delayed, or historical) depends on your user type, exchange entitlements, and the specific endpoint you're using. **Professional users do not get real-time data on all endpoints**, but the exact data type available varies by endpoint. **To determine what data types are available for your user classification**, check the **Data Availability** section in the documentation for each endpoint you're using: **Stocks Endpoints:** - **[Stock Quotes](https://www.marketdata.app/docs/api/stocks/quotes#data-availability)** (`/v1/stocks/quotes/`) - **[Stock Prices](https://www.marketdata.app/docs/api/stocks/prices#data-availability)** (`/v1/stocks/prices/`) - **[Stock Candles](https://www.marketdata.app/docs/api/stocks/candles#data-availability)** (`/v1/stocks/candles/`) - **[Bulk Stock Candles](https://www.marketdata.app/docs/api/stocks/bulkcandles#data-availability)** (`/v1/stocks/bulkcandles/`) - **[Stock News](https://www.marketdata.app/docs/api/stocks/news#data-availability)** (`/v1/stocks/news/`) - **[Stock Earnings](https://www.marketdata.app/docs/api/stocks/earnings#data-availability)** (`/v1/stocks/earnings/`) **Options Endpoints:** - **[Options Quotes](https://www.marketdata.app/docs/api/options/quotes#data-availability)** (`/v1/options/quotes/`) - **[Option Chain](https://www.marketdata.app/docs/api/options/chain#data-availability)** (`/v1/options/chain/`) - **[Option Lookup](https://www.marketdata.app/docs/api/options/lookup#data-availability)** (`/v1/options/lookup/`) - **[Option Expirations](https://www.marketdata.app/docs/api/options/expirations#data-availability)** (`/v1/options/expirations/`) Each endpoint's Data Availability section provides the canonical source of truth for what data types are available for professional users, non-professional users, and users with unknown status, along with the required exchange entitlements. ## Verification Steps ### How to Verify Your Profile is Complete 1. Log into the [Market Data Dashboard](https://dashboard.marketdata.app/marketdata/) 2. Navigate to **Profile** section 3. Check that all fields are filled: - ✅ Name - ✅ Address - ✅ Email - ✅ Phone Number - ✅ Company Name (if applicable) - ✅ Company Address (if applicable) - ✅ Job Title (if applicable) - ✅ LinkedIn Profile URL (if applicable) If any fields are missing, complete them and submit your profile. ### How to Verify Agreements are Signed 1. Log into the [Market Data Dashboard](https://dashboard.marketdata.app/marketdata/) 2. Navigate to **Profile** section 3. Look for agreement status indicators: - ✅ IEX Agreement (for stock quotes) - ✅ UTP Agreement (for stock candles) - ✅ OPRA Agreement (for options quotes) If agreements show as unsigned, review and sign them. Note that agreements may take up to 1 business day to be approved and activated. ### How to Test if Real-Time Data is Working After completing your profile and signing agreements, wait 1 business day for verification, then test: ### Python ```python from marketdata import MarketDataClient, OutputFormat, MarketDataClientErrorResult from datetime import datetime, timezone def test_realtime_data(symbol="AAPL"): """Test if real-time data is working for a given symbol.""" client = MarketDataClient() quotes = client.stocks.quotes(symbol, output_format=OutputFormat.JSON) if isinstance(quotes, MarketDataClientErrorResult): print(f"Error: {quotes.error}") return False if not isinstance(quotes, dict) or quotes.get("s") != "ok": print(f"Error: {quotes.get('errmsg', 'Unknown error') if isinstance(quotes, dict) else 'Invalid response'}") return False if "updated" not in quotes: print("Warning: No timestamp in response") return False # Get timestamp updated_timestamp = quotes["updated"][0] updated_time = datetime.fromtimestamp(updated_timestamp, tz=timezone.utc) current_time = datetime.now(timezone.utc) # Calculate age age_seconds = (current_time - updated_time).total_seconds() age_minutes = age_seconds / 60 print(f"Symbol: {symbol}") print(f"Data timestamp: {updated_time}") print(f"Current time: {current_time}") print(f"Data age: {age_minutes:.2f} minutes") # Check if real-time (less than 1 minute old during market hours) if age_minutes < 1: print("✅ Real-time data is working!") return True elif age_minutes < 20: print("⚠️ Receiving 15-minute delayed data") print(" → Check if IEX agreement is signed and approved") return False else: print("❌ Receiving historical data (1 day old)") print(" → Check if profile is complete and agreements are signed") return False # Test during market hours for best results test_realtime_data("AAPL") ``` ### JavaScript ```js title="testRealtimeData.js" import { MarketDataClient, Mode } from "@marketdata/sdk"; const client = new MarketDataClient(); async function testRealtimeData(symbol = "AAPL") { 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; } // `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; } } // Test during market hours for best results testRealtimeData("AAPL"); ``` ### TypeScript ```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; } } // Test during market hours for best results testRealtimeData("AAPL"); ``` ### Java ```java title="TestRealtimeData.java" import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.Mode; import com.marketdata.sdk.stocks.StockQuote; import com.marketdata.sdk.stocks.StockQuoteRequest; import java.time.Duration; import java.time.ZonedDateTime; static boolean testRealtimeData(MarketDataClient client, String symbol) { StockQuote quote = client.stocks() .mode(Mode.LIVE) .quote(StockQuoteRequest.of(symbol)) .values().get(0); ZonedDateTime updated = quote.updated(); if (updated == null) { System.out.println("Warning: no timestamp in response"); return false; } double ageMinutes = Duration.between(updated, ZonedDateTime.now(updated.getZone())).toSeconds() / 60.0; System.out.printf("%s data age: %.2f minutes%n", symbol, ageMinutes); if (ageMinutes < 1) { System.out.println("✅ Real-time data is working!"); return true; } else if (ageMinutes < 20) { System.out.println("⚠️ Receiving 15-minute delayed data — check the IEX agreement"); return false; } else { System.out.println("❌ Receiving historical data — complete your profile and agreements"); return false; } } // Test during market hours for best results testRealtimeData(client, "AAPL"); ``` ### Kotlin ```kotlin title="TestRealtimeData.kt" import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.Mode import com.marketdata.sdk.stocks.StockQuoteRequest import java.time.Duration import java.time.ZonedDateTime fun testRealtimeData(client: MarketDataClient, symbol: String = "AAPL"): Boolean { val quote = client.stocks() .mode(Mode.LIVE) .quote(StockQuoteRequest.of(symbol)) .values()[0] val updated = quote.updated() ?: run { println("Warning: no timestamp in response") return false } val ageMinutes = Duration.between(updated, ZonedDateTime.now(updated.zone)).toSeconds() / 60.0 println("$symbol data age: %.2f minutes".format(ageMinutes)) return when { ageMinutes < 1 -> { println("✅ Real-time data is working!"); true } ageMinutes < 20 -> { println("⚠️ Receiving 15-minute delayed data — check the IEX agreement"); false } else -> { println("❌ Receiving historical data — complete your profile and agreements"); false } } } ``` ### C# ```csharp title="TestRealtimeData.cs" using MarketDataApp; static async Task TestRealtimeDataAsync(MarketDataClient client, string symbol) { var quote = (await client.Stocks.GetQuoteAsync( symbol, options: new MarketDataRequestOptions { Mode = Mode.Live })).Values[0]; if (quote.Updated is not { } updated) { Console.WriteLine("Warning: no timestamp in response"); return false; } var ageMinutes = (DateTimeOffset.UtcNow - updated.ToUniversalTime()).TotalMinutes; Console.WriteLine($"{symbol} data age: {ageMinutes:F2} minutes"); if (ageMinutes < 1) { Console.WriteLine("✅ Real-time data is working!"); return true; } if (ageMinutes < 20) { Console.WriteLine("⚠️ Receiving 15-minute delayed data — check the IEX agreement"); return false; } Console.WriteLine("❌ Receiving historical data — complete your profile and agreements"); return false; } // Test during market hours for best results await TestRealtimeDataAsync(client, "AAPL"); ``` ## Common Scenarios ### Scenario 1: "I just signed up but I'm still getting delayed data" **What's happening**: Your account profile is likely incomplete, or you haven't signed the required exchange agreements yet. **Solution**: 1. Complete your data use profile with all required information 2. Sign the appropriate exchange agreements (IEX for stock quotes, OPRA for options) 3. Wait 1 business day for verification and approval 4. Test your API requests after verification **Why it takes time**: We need to verify your information against third-party databases and get exchange approval for your agreements. This process typically takes 1 business day. ### Scenario 2: "I'm on a paid plan (Trader/Quant/Prime) but not getting real-time data" **What's happening**: Having a paid plan alone doesn't guarantee real-time data. You also need: - A complete data use profile (so we can classify your account) - Signed exchange agreements (IEX, UTP, OPRA) - Non-professional classification (professional users get delayed/historical data) **Solution**: 1. Check your profile in the dashboard—is it complete? 2. Check your agreements—are they signed and approved? 3. Check your professional status in the [customer dashboard](https://dashboard.marketdata.app/marketdata/profile)—if you're classified as professional, you'll receive delayed/historical data regardless of agreements, and real-time access requires exchange licensing through our [sales team](https://www.marketdata.app/contact/) **Note**: Professional users (corporations, investment advisors, money managers, etc.) do not receive real-time data for compliance reasons, even with signed agreements. No self-service plan and no combination of signed agreements grants a professional user real-time exchange data. Delayed / historical data access remains available, depending on the data policies of each exchange. Real-time access for professional use requires direct exchange licensing — [contact our sales team](https://www.marketdata.app/contact/) to discuss it. ### Scenario 3: "My data was real-time yesterday but delayed today" **What's happening**: This could indicate: - An agreement expired or was revoked - Your account status changed (e.g., reclassified as professional) - A verification issue that needs attention **Solution**: 1. Check your dashboard for any status changes or notifications 2. Verify your agreements are still signed and approved 3. Check if your profile information is still complete 4. Contact support if the issue persists ### Scenario 4: "How do I know if I'm professional or non-professional?" **Professional users** are typically: - Corporations or business entities - Investment advisors - Money managers - Users who trade on behalf of others **Non-professional users** are typically: - Individual investors trading for personal accounts - Not employed by financial institutions - Not trading on behalf of others **How to check**: You can check your professional status in the [customer dashboard](https://dashboard.marketdata.app/marketdata/profile). Your classification is determined during the verification process when you complete your profile. If you're unsure about your status, contact support for clarification. **Impact**: Professional users do not receive real-time data regardless of exchange agreements, while non-professional users can access real-time data with proper entitlements. Delayed / historical data access remains available, depending on the data policies of each exchange. If you are classified as professional and need real-time data, it requires direct exchange licensing — [contact our sales team](https://www.marketdata.app/contact/) to discuss it. ### Scenario 5: "I completed my profile and signed agreements, but still getting delayed data" **What's happening**: The verification and approval process may not be complete yet, or there may be an issue with your information. **Solution**: 1. **Wait 1 business day** after completing your profile and signing agreements 2. Check your dashboard for any notifications or status updates 3. Verify your information matches third-party databases (LinkedIn, etc.) 4. If information doesn't match, update your profile to match your LinkedIn or other professional profiles 5. Contact support if more than 1 business day has passed **Common issues**: - Profile information doesn't match LinkedIn or other databases - Missing required fields that weren't obvious - Additional verification needed for financial institution employees ## Getting Help ### When to Contact Support Contact our support team if: - You've completed your profile and signed agreements but still receiving delayed data after 1 business day - You're unsure about your professional status classification - Your profile information matches third-party databases but verification is still pending - You need clarification on which agreements you need for your use case - You've followed all steps but are still experiencing issues ### What to Include in Your Support Ticket To help us resolve your issue quickly, please include: 1. **Your account information**: - Email address associated with your account - Plan type (Free, Starter, Trader, Quant, Prime) 2. **Diagnosis information**: - Example API request that's returning delayed data - The `updated` timestamp from a recent response - Current time when you made the request - Calculated age of the data (in minutes) 3. **Account status**: - Whether your profile is complete (all fields filled) - Which agreements you've signed (IEX, UTP, OPRA) - When you completed your profile and signed agreements - Any error messages or notifications from the dashboard 4. **Test results**: - Output from the verification test code (if you ran it) - Screenshots of your dashboard profile/agreement status (if helpful) ### Support Resources - **Dashboard**: [Market Data Dashboard](https://dashboard.marketdata.app/marketdata/) - **Entitlements Documentation**: [Exchange Entitlements](https://www.marketdata.app/docs/account/entitlements) - **Verification Documentation**: [Account Verification](https://www.marketdata.app/docs/account/data-policies/account-verification) ## Summary Real-time data requires: 1. ✅ **Complete data use profile** (contact + employer information) 2. ✅ **Signed exchange agreements** (IEX for stocks, OPRA for options, UTP for candles) 3. ✅ **Non-professional classification** (professional users get delayed/historical data) 4. ✅ **Verification approval** (typically 1 business day) Professional users cannot satisfy requirement 3 on a self-service plan. Real-time exchange data for professional use requires direct exchange licensing — [contact our sales team](https://www.marketdata.app/contact/) to discuss it. The API doesn't throw errors when these requirements aren't met—it silently provides the best data you're entitled to receive. Check the `updated` timestamp in your API responses to verify you're getting real-time data, and use this guide to diagnose and fix any issues. --- # 429: Credit Limit Reached Source: https://www.marketdata.app/docs/api/troubleshooting/running-out-of-credits/index.md A `429 TOO MANY REQUESTS` can be returned for two different reasons: - Credit limit reached - Too many concurrent requests This page is only for the **credit limit** case. If your issue is concurrency, use [429: Too Many Concurrent Requests](https://www.marketdata.app/docs/api/troubleshooting/too-many-concurrent-requests). For full policy details, see [Rate Limits](https://www.marketdata.app/docs/api/rate-limiting). ## Fast Triage Use the response message and headers to identify which 429 you hit. Typical credit-limit error payload: ```json { "s": "error", "errmsg": "API credit limit reached for current window.", "troubleshootingGuide": "https://www.marketdata.app/docs/api/troubleshooting/running-out-of-credits" } ``` **Credit-limit 429 indicators:** - Error message indicates credit/rate budget exhaustion for your plan window - `X-Api-Ratelimit-Remaining` is `0` (or effectively exhausted) **Concurrency 429 indicators:** - Error message indicates too many simultaneous requests - Usually occurs during bursts with many in-flight requests - Official SDKs handle concurrency automatically and maximize throughput up to the 50-request limit without exceeding it ## 30-Second Action Checklist 1. Confirm this is a credit-limit 429 (not concurrency). 2. Read `X-Api-Ratelimit-Remaining` and `X-Api-Ratelimit-Reset`. 3. Find your plan group below and apply its primary workflow. 4. Check `X-Api-Ratelimit-Consumed` on recent heavy requests. 5. If still blocked repeatedly after optimization, escalate (support or plan change). ## Plan Groups Troubleshooting should follow your plan's optimization capabilities. | Plan Group | Plans | Limit Window | `mode=cached` Available | |------------------|-------------------------------------------|--------------|-------------------------| | Free + Trials | Free Forever, Starter Trial, Trader Trial | Daily | No | | Starter + Trader | Starter, Trader | Daily | Yes | | Quant + Prime | Quant, Prime | Per-minute | Yes | > [!WARNING] > **[Trial plan limitation]** > > `mode=cached` is **not available** on Starter Trial and Trader Trial. > [!CAUTION] > **[Reset Window Gotcha (All Daily-Reset Plans)]** > > All plans with daily credit windows (Free Forever, Starter Trial, Trader Trial, Starter, Trader) reset at **9:30 AM Eastern Time**, not midnight. > > This is intentional safety behavior: if a script runs out of control overnight, the reset timing limits impact to one trading day budget instead of potentially consuming two trading-day budgets across a midnight boundary. ## Plan Playbooks ### Free Forever + Trial Plans (No Cached Mode) These plans cannot use cached mode. For bulk option use, use one of these workflows: 1. **One-step server-side filtering:** keep quote columns and aggressively reduce contracts on the chain request using filters such as `strikeLimit`, `expiration`, `type`, `strike`, `minBid`, and `maxBid`. 2. **Two-step local filtering:** pull the full/large chain without quote columns, apply your own logic locally, then request quotes only for selected contracts using `options/quotes`. If current-day quote freshness is not required, a third strategy can be cheaper: use **historical option chains** with the `date` parameter. Historical chains are billed at **1 credit per 1,000 option symbols**, while real-time/15-minute delayed quote chains are billed per symbol. ```http # One-step: server-side liquidity + contract filters (quotes included) GET https://api.marketdata.app/v1/options/chain/UNDERLYING/?expiration=YYYY-MM-DD&type=call&strikeLimit=5&minBid=0.50&maxBid=10&columns=symbol,strike,expiration,type,bid,ask,mid,last,openInterest,volume # Two-step (Step 1): broad chain without costly quote columns GET https://api.marketdata.app/v1/options/chain/UNDERLYING/?expiration=all&columns=symbol,strike,expiration,type,openInterest,volume # Two-step (Step 2): quotes only for selected contracts GET https://api.marketdata.app/v1/options/quotes/OPTION_SYMBOL/ # Historical chain example (lower-cost bulk snapshot) GET https://api.marketdata.app/v1/options/chain/UNDERLYING/?date=YYYY-MM-DD&expiration=all ``` If you are still getting credit-limit 429 after these optimizations, the primary path is to upgrade to a paid plan. ### Starter + Trader (Daily Limits + Cached Mode) Primary optimization for this plan group is `mode=cached` on bulk requests. Recommended process: 1. Request bulk data with `mode=cached` (optionally with `maxage`). 2. Inspect the `updated` field in the response to measure snapshot age. 3. If fresh enough, use cached data. 4. If too old, re-request only required contracts/symbols in live mode instead of re-pulling the full chain. ```http # Step 1: bulk chain from cache (cheap, 1 credit) GET https://api.marketdata.app/v1/options/chain/UNDERLYING/?mode=cached&maxage=5min&expiration=YYYY-MM-DD # Step 2: only if selected contracts are too stale per `updated`, request live quotes for those contracts GET https://api.marketdata.app/v1/options/quotes/OPTION_SYMBOL/ ``` Additional controls for Starter + Trader: 1. Filter option chains aggressively (`strikeLimit`, `expiration`, `type`, `strike`, liquidity filters). 2. Exclude `bid`, `ask`, `mid`, `last` when current pricing is not needed. 3. Add daily credit guardrails to avoid getting blocked for the rest of the day. ### Quant + Prime (Per-Minute Limits + Cached Mode) Use cached-mode bulk strategy and filter optimization, plus minute-window pacing: 1. Smooth burst traffic (queue + worker pool) 2. Pace large chain pulls across the minute window 3. Prefer cached mode for full-chain or broad bulk pulls Quant/Prime have no daily credit cap. 429s here are the result of per-minute bursts, not daily depletion. ## How Pricing Actually Works The mental model `1 request = 1 credit` is often wrong for bulk and candles workloads. ### Common baseline - Many non-bulk responses: `1 request = 1 credit` ### High-usage endpoints where pricing scales 1. `options/chain` - Real-time or delayed quote chains can consume per option symbol returned when quote fields are included. - Historical chain requests (`date` parameter) are billed at 1 credit per 1,000 option symbols. 2. `stocks/quotes` - Multi-symbol quote responses with quote fields can consume per symbol returned. 3. `stocks/prices` - Multi-symbol price/quote responses with quote fields can consume per symbol returned. 4. `stocks/bulkcandles` - Bulk candle responses can consume per symbol returned. 5. `stocks/candles` - Candle responses are billed at 1 credit per 1,000 candles, so large ranges can consume multiple credits in one request. Practical rule: credit usage scales with response size on bulk quote endpoints and long candle responses. ### High-impact example ```http # Full live chain can consume very large credits GET https://api.marketdata.app/v1/options/chain/UNDERLYING/?expiration=all # Filtered chain reduces returned symbols GET https://api.marketdata.app/v1/options/chain/UNDERLYING/?expiration=all&strikeLimit=1 # Cached mode (paid plans only) reduces credits to 1 GET https://api.marketdata.app/v1/options/chain/UNDERLYING/?mode=cached ``` For cached mode details, see [mode parameter](https://www.marketdata.app/docs/api/universal-parameters/mode). ## Diagnose With Headers Check these headers on every response: - `X-Api-Ratelimit-Consumed`: credits consumed by current request - `X-Api-Ratelimit-Remaining`: credits remaining in current window - `X-Api-Ratelimit-Limit`: total credits for current window - `X-Api-Ratelimit-Reset`: reset time in UTC epoch seconds ```bash curl -i -X GET "https://api.marketdata.app/v1/stocks/quotes/AAPL/" \ -H "Authorization: Bearer YOUR_TOKEN" ``` ## Common Cost Traps 1. Pulling full option chains without filters 2. Including `bid/ask/mid/last` unnecessarily 3. Repeating expensive live requests without caching or deduplication 4. Ignoring rate-limit headers during production runs ## Common Scenarios ### Scenario 1: "I requested one option chain and ran out of credits" What happened: - A single `options/chain` request returned a very large number of contracts. - With quote fields included, credits scaled by returned contracts. What to do: - Free + Trials: use server-side filters or two-step local filtering; consider historical chains for bulk snapshots. - Starter + Trader and Quant + Prime: use `mode=cached` for bulk pulls, then selectively refresh only contracts you need live. ### Scenario 2: "I made a small number of requests but used far more credits than expected" What happened: - Requests hit bulk endpoints (`options/chain`, `stocks/quotes`, `stocks/prices`) with quote fields. - Credit usage scaled with symbols/contracts returned, not request count. What to do: - Inspect `X-Api-Ratelimit-Consumed` for high-cost requests. - Remove unnecessary quote fields or reduce returned symbols/contracts with filters. ### Scenario 3: "My credits did not reset at midnight" What happened: - You expected a calendar-day reset. - Daily-limit plans reset at **9:30 AM Eastern Time**, not midnight. What to do: - Use `X-Api-Ratelimit-Reset` as source of truth for exact reset timing. - Align scheduler logic to `America/New_York` and the 9:30 AM ET window. ### Scenario 4: "I need bulk option data every day on Free/Trial" What happened: - No access to `mode=cached` on Free/Trial makes full quote-chain pulls expensive. What to do: - Use one-step server-side liquidity/contract filters or two-step local filtering. - If intraday quote freshness is not required, prefer historical chain snapshots (`date`) for lower credit burn. - If this remains insufficient, upgrade to a paid plan. ### Scenario 5: "I am on Starter/Trader and still getting blocked daily" What happened: - Cached mode is not being used as primary bulk strategy, or stale cache fallback is too broad (full live repulls). What to do: - Make `mode=cached` the default bulk path. - Check `updated` freshness and only re-request live data for required contracts/symbols. - Add daily guardrails to stop or degrade workload before hard-limit exhaustion. ## When to Contact Support Open a support ticket if you have optimized for your plan group and still cannot operate within limits. Include: 1. Recent `X-Api-Ratelimit-*` header values 2. Sample expensive requests 3. Your current plan 4. Expected request volume and interval --- # Service Outages Source: https://www.marketdata.app/docs/api/troubleshooting/service-outages/index.md Market Data, as stated in our [terms of service](https://www.marketdata.app/terms/), makes no representation as to the reliability, availability, or timeliness of our service. **This is not just a standard disclaimer.** We have not yet been able to achieve 99.9% reliability, which is a metric we consider a minimum level of reliability that is needed to operate without a backup provider. **Market Data is a low cost provider** and we have determined that cost, rather than reliability, is our key driver. While we hope to achieve 99.9% reliability in the future, our focus will remain on keeping down costs and avoiding price increases for our users. > [!TIP] > **[Recommendation]** > > We highly encourage users with mission critical applications to have a backup provider or utilize Market Data as their secondary provider. ## How To Confirm Downtime Market Data provides real-time status monitoring for each of our API endpoints. You can check the status of individual endpoints either through our status webpage or programmatically via our status API endpoint. ### Status Webpage Visit our [status page](https://www.marketdata.app/status/) to view the current status of all Market Data API endpoints, including historical uptime statistics. - Status Page: [https://www.marketdata.app/status/](https://www.marketdata.app/status/) ### Confirm Downtime Programmatically Use the [status endpoint](https://www.marketdata.app/docs/api/utilities/status) at `https://api.marketdata.app/status/` to programmatically check the status of all Market Data API endpoints. This endpoint returns the current status for each endpoint (e.g., `/v1/stocks/quotes/`, `/v1/options/chain/`, etc.) and will remain online during outages, allowing you to determine which specific endpoints are affected. Unlike every other endpoint, the status endpoint is not versioned: it is served at `/status/`, not under `/v1/`. > [!TIP] > This endpoint is ideal to allow for automatic switching between Market Data and your backup provider when specific endpoints are down. ### JavaScript ```js title="status.js" 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], }; } 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"}`); } catch (error) { console.error("Error fetching API status:", error); } ``` ### TypeScript ```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); } ``` ### Python ```python title="status.py" # Importing the required library import requests # URL to the status endpoint url = "https://api.marketdata.app/status/" json_data = requests.get(url).json() # Function to check the status of a specific endpoint def check_endpoint_status(endpoint_path): try: index = json_data["service"].index(endpoint_path) return { "online": json_data["online"][index], "status": json_data["status"][index], "uptime30d": json_data["uptimePct30d"][index], "uptime90d": json_data["uptimePct90d"][index] } except ValueError: return None # Checking the status of specific endpoints stocks_quotes = check_endpoint_status("/v1/stocks/quotes/") options_chain = check_endpoint_status("/v1/options/chain/") stocks_candles = check_endpoint_status("/v1/stocks/candles/") print(f"Stocks Quotes: {'Online' if stocks_quotes and stocks_quotes['online'] else 'Offline' if stocks_quotes else 'Not found'}") print(f"Options Chain: {'Online' if options_chain and options_chain['online'] else 'Offline' if options_chain else 'Not found'}") print(f"Stocks Candles: {'Online' if stocks_candles and stocks_candles['online'] else 'Offline' if stocks_candles else 'Not found'}") ``` ### Java ```java title="Status.java" import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.utilities.ServiceStatus; import java.util.List; try (MarketDataClient client = new MarketDataClient()) { List services = client.utilities().status().values(); for (String path : List.of("/v1/stocks/quotes/", "/v1/options/chain/", "/v1/stocks/candles/")) { String state = services.stream() .filter(s -> s.service().equals(path)) .findFirst() .map(s -> s.online() ? "Online" : "Offline") .orElse("Not found"); System.out.println(path + ": " + state); } } ``` ### Kotlin ```kotlin title="Status.kt" import com.marketdata.sdk.MarketDataClient MarketDataClient().use { client -> val services = client.utilities().status().values() for (path in listOf("/v1/stocks/quotes/", "/v1/options/chain/", "/v1/stocks/candles/")) { val svc = services.firstOrNull { it.service() == path } val state = svc?.let { if (it.online()) "Online" else "Offline" } ?: "Not found" println("$path: $state") } } ``` ### C# ```csharp title="Status.cs" using MarketDataApp; using var client = await MarketDataClient.CreateAsync(); var services = (await client.Utilities.GetStatusAsync()).Values; foreach (var path in new[] { "/v1/stocks/quotes/", "/v1/options/chain/", "/v1/stocks/candles/" }) { var service = services.FirstOrDefault(s => s.Service == path); var state = service is null ? "Not found" : service.Online ? "Online" : "Offline"; Console.WriteLine($"{path}: {state}"); } ``` ## What To Do During Downtime It is not necessary to advise us of downtime or service outages. We monitor the status of our systems and we investigate and respond to all service outages. During Market Data service outages, we encourage you to switch your systems over to your back-up provider until our systems come back online. --- # 429: Too Many Concurrent Requests Source: https://www.marketdata.app/docs/api/troubleshooting/too-many-concurrent-requests/index.md A `429 TOO MANY REQUESTS` can also mean your account exceeded the concurrent request limit. This page is only for the **concurrency** case. If your issue is credit depletion, use [429: Credit Limit Reached](https://www.marketdata.app/docs/api/troubleshooting/running-out-of-credits). For full policy details, see [Rate Limits](https://www.marketdata.app/docs/api/rate-limiting). ## Fast Triage Use error context and rate-limit headers to confirm this is concurrency, not credits. Typical concurrency-limit error payload: ```json { "s": "error", "errmsg": "Too many concurrent requests. Please reduce parallel requests and retry.", "troubleshootingGuide": "https://www.marketdata.app/docs/api/troubleshooting/too-many-concurrent-requests" } ``` **Concurrency 429 indicators:** - 429 appears during bursts of parallel requests - You still have credit budget remaining - Workload fan-out is high (many simultaneous in-flight calls) **Credit-limit 429 indicators:** - `X-Api-Ratelimit-Remaining` is `0` (or effectively exhausted) - Error message indicates plan-window budget exhaustion ## 30-Second Action Checklist 1. Confirm this is a concurrency 429 (not credit depletion). 2. If possible, switch to an official Market Data SDK. 3. If not using SDK, reduce in-flight concurrency immediately. 4. Add retries with exponential backoff + jitter for 429. 5. Verify 429 rate falls after reducing burst fan-out. ## What This 429 Means All plans are limited to **50 simultaneous in-flight requests**. If your application exceeds that, additional requests return `429` until concurrency drops. ## Primary Fix: Use Official SDKs Official Market Data SDK clients are designed around the 50-request concurrency limit and automatically maximize throughput up to that limit without exceeding it. If you are using an SDK and still seeing concurrency 429, check for additional parallelism in your own app layer (multiple workers/processes/instances each issuing SDK calls). ## Non-SDK Playbook If you are using a custom HTTP client: 1. Cap worker/thread pool below 50. 2. Use bounded queueing to prevent fan-out bursts. 3. Ensure each worker handles one request at a time. 4. Add per-request timeout and retry budget. 5. Log in-flight concurrency and 429 counts. ### Recommended Non-SDK Pattern 1. Fixed max concurrency (for example, 20-40). 2. Shared request queue. 3. Retry with exponential backoff + jitter on 429. 4. Gradual ramp-up instead of burst starts. ## Non-SDK Backoff Examples These examples are only for custom HTTP clients. If you use an official SDK, skip this section. ### Python ```python import random import time import requests def request_with_backoff(url, headers, max_retries=5): for attempt in range(max_retries + 1): response = requests.get(url, headers=headers, timeout=30) if response.status_code != 429: return response backoff = 0.2 * (2 ** attempt) jitter = random.uniform(0, 0.1) time.sleep(backoff + jitter) raise RuntimeError("Exceeded retry budget for 429 concurrency responses") ``` ### JavaScript / TypeScript ```javascript async function requestWithBackoff(fn, maxRetries = 5) { for (let attempt = 0; attempt <= maxRetries; attempt++) { const response = await fn(); if (response.status !== 429) { return response; } const baseMs = 200; const backoffMs = baseMs * Math.pow(2, attempt); const jitterMs = Math.floor(Math.random() * 100); await new Promise((resolve) => setTimeout(resolve, backoffMs + jitterMs)); } throw new Error("Exceeded retry budget for 429 concurrency responses"); } ``` ### Go ```go package main import ( "errors" "math/rand" "net/http" "time" ) func requestWithBackoff(client *http.Client, req *http.Request, maxRetries int) (*http.Response, error) { for attempt := 0; attempt <= maxRetries; attempt++ { resp, err := client.Do(req) if err != nil { return nil, err } if resp.StatusCode != http.StatusTooManyRequests { return resp, nil } backoff := 200 * time.Millisecond * time.Duration(1< $status, 'body' => $body]; } $backoffMs = 200 * (2 ** $attempt); $jitterMs = random_int(0, 100); usleep(($backoffMs + $jitterMs) * 1000); } curl_close($ch); throw new RuntimeException('Exceeded retry budget for 429 concurrency responses'); } ``` ### Java ```java title="RequestWithBackoff.java" import com.marketdata.sdk.exception.RateLimitError; import java.util.concurrent.ThreadLocalRandom; import java.util.function.Supplier; // The SDK already retries with exponential backoff internally. For a 429 caused // by too many *concurrent* requests, wrap your call in an outer backoff and // reduce fan-out. RateLimitError is the SDK's 429; honour getRetryAfter() if set. static T requestWithBackoff(Supplier call, int maxRetries) throws InterruptedException { for (int attempt = 0; ; attempt++) { try { return call.get(); } catch (RateLimitError e) { if (attempt >= maxRetries) throw e; long backoffMs = (long) (200 * Math.pow(2, attempt)) + ThreadLocalRandom.current().nextInt(100); Thread.sleep(e.getRetryAfter().map(d -> d.toMillis()).orElse(backoffMs)); } } } ``` ### Kotlin ```kotlin title="RequestWithBackoff.kt" import com.marketdata.sdk.exception.RateLimitError import kotlin.random.Random // The SDK already retries internally. For a 429 from too many *concurrent* // requests, add outer backoff and reduce fan-out. fun requestWithBackoff(maxRetries: Int = 5, call: () -> T): T { var attempt = 0 while (true) { try { return call() } catch (e: RateLimitError) { if (attempt >= maxRetries) throw e val backoffMs = (200.0 * Math.pow(2.0, attempt.toDouble())).toLong() + Random.nextInt(100) Thread.sleep(e.retryAfter.map { it.toMillis() }.orElse(backoffMs)) attempt++ } } } ``` ### C# ```csharp title="RequestWithBackoff.cs" using MarketDataApp.Exceptions; // The SDK already retries with exponential backoff internally, and its // MaxConcurrentRequests option (default 50) bounds fan-out. For a 429 caused by // too many *concurrent* requests, wrap your call in an outer backoff and lower // the fan-out. RateLimitException is the SDK's 429; honour RetryAfter if set. static async Task RequestWithBackoffAsync(Func> call, int maxRetries = 3) { for (var attempt = 0; ; attempt++) { try { return await call(); } catch (RateLimitException e) when (attempt < maxRetries) { var backoff = TimeSpan.FromMilliseconds(200 * Math.Pow(2, attempt) + Random.Shared.Next(100)); await Task.Delay(e.RetryAfter ?? backoff); } } } ``` ## Common Concurrency Traps 1. Launching many jobs at once without queueing 2. Multiple app instances each using high internal concurrency 3. Immediate retries without backoff 4. Long request timeouts that keep too many requests in-flight ## When to Contact Support If concurrency 429 persists after reducing fan-out and applying backoff, contact support with: 1. Approximate in-flight concurrency at failure time 2. Retry policy and worker pool settings 3. Timestamped request logs around 429 bursts 4. Whether you are using official SDKs or a custom client --- # 400: URL Anatomy (Path vs Query Parameters) Source: https://www.marketdata.app/docs/api/troubleshooting/url-parameters/index.md ## What This Guide Is For This is a detailed guide for `400 BAD REQUEST` errors caused by incorrect URL construction. If you are troubleshooting a `400 BAD REQUEST`, use the dedicated guide: - [400: Bad Request](https://www.marketdata.app/docs/api/troubleshooting/bad-request) ## Introduction to URL Parameters URL parameters (query strings) pass optional input to an endpoint after the path. ### URL Anatomy: Path vs Query String A URL with parameters has the following structure:
https://api.marketdata.app/v1/stocks/quotes/SPY/?token=token1234&dateformat=timestamp
- **Protocol**: `https://` - **Host**: `api.marketdata.app` - **Path (or Endpoint)**: `/v1/stocks/quotes/SPY/` - **Query String**: Begins with a `?` and includes `token=token1234&dateformat=timestamp` - `token` and `dateformat` are the names of the parameters. - `token1234` and `timestamp` are the values assigned to those parameters. - `&` is used to separate multiple parameters. ### What Goes In The Path - Required route values like symbols, ids, or endpoint-specific path parts. - Example: `/v1/stocks/quotes/SPY/` ### What Goes In The Query String - Optional controls like `token`, `dateformat`, `limit`, and similar parameters. - Query values always use `key=value` form. - Multiple parameters are separated by `&`. ## How to Build URL Parameters When building URL parameters, follow these guidelines to ensure they are structured correctly: 1. **Start with the Endpoint URL**: Identify the base URL of the API endpoint you are interacting with. 2. **Add a Question Mark**: Follow the base URL with a `?` to start the query string. 3. **Append Parameters**: Add parameters in the format `key=value`. Use `&` to separate multiple parameters. 4. **Encode Special Characters**: Use URL encoding to handle special characters in keys or values. ### Example Suppose you want to request stock quotes for `SPY` with a specific date format and token authentication: ``` https://api.marketdata.app/v1/stocks/quotes/SPY/?token=token1234&dateformat=timestamp ``` ### Common Mistakes - **Incorrect Character Usage**: Ensure you use `?` to start and `&` to separate parameters. - **Unencoded Characters**: Encode special characters like spaces (`%20`), plus (`%2B`), etc, using URL encoding. --- # Universal Parameters Source: https://www.marketdata.app/docs/api/universal-parameters/index.md The following parameters are universal and can be used with any endpoint unless otherwise indicated in that endpoint's documentation. --- # Columns Source: https://www.marketdata.app/docs/api/universal-parameters/columns/index.md The columns parameter is used to limit the results of any endpoint to only the columns you need. ## Parameter ## Use Example ``` https://api.marketdata.app/v1/stocks/quotes/AAPL/?columns=ask,bid ``` ## Response Example ```json { "ask": [152.14], "bid": [152.12] } ``` ## Values ### string Use a list of columns names separated by commas to limit the response to just the columns requested. > [!CAUTION] > > When using the columns parameter the `s` status output is suppressed from the response. --- # Date Format Source: https://www.marketdata.app/docs/api/universal-parameters/date-format/index.md The dateformat parameter allows you to specify the format you wish to receive date and time information in. Regardless of format, all timestamps for US markets use the **America/New_York** timezone. See [Response Timezone](https://www.marketdata.app/docs/docs/api/dates-and-times#response-timezone) for full details on timezone handling. ## Parameter dateformat=\ ## Use Example /candles/D/AAPL/?dateformat=timestamp /candles/D/AAPL/?dateformat=unix /candles/D/AAPL/?dateformat=spreadsheet ## Values ### timestamp Receive dates and times as an ISO 8601 timestamp with timezone offset. All US market data is returned in America/New_York. For example, closing bell on Dec 30, 2020 for the NYSE would be: **2020-12-30 16:00:00 -05:00**. ### unix Receive dates and times in unix format (seconds after the unix epoch). For example, closing bell on Dec 30, 2020 for the NYSE would be: **1609362000**. Unix timestamps are anchored to America/New_York — see [Response Timezone](https://www.marketdata.app/docs/docs/api/dates-and-times#response-timezone) for how to interpret them correctly. ### spreadsheet Receive dates and times in spreadsheet format (days after the Excel epoch). For example, closing bell on Dec 30, 2020 for the NYSE would be: **44195.66667**. Spreadsheet format does not include a timezone offset. All times are expressed in America/New_York. --- # Format Source: https://www.marketdata.app/docs/api/universal-parameters/format/index.md The format parameter is used to specify the format for your data. We support JSON and CSV formats. The default format is JSON. ## Parameter format=\ ## Use Example /candles/daily/AAPL?format=json /candles/daily/AAPL?format=csv ## Values ### json (default) Use JSON to format the data in Javascript Object Notation (JSON) format. This format is ideal for programmatic use of the data. ### csv Use CSV to format the data in lightweight comma separated value (CSV) format. This format is ideal for importing the data into spreadsheets. --- # Headers Parameter Source: https://www.marketdata.app/docs/api/universal-parameters/headers/index.md The headers parameter is used to turn off headers when using CSV output. ## Parameter headers=\ ## Use Example /candles/daily/AAPL?headers=false&format=csv ## Values ### true (default) If the headers argument is not used, by default headers are turned on. ### false Turns headers off and returns just the data points. --- # Human Readable Source: https://www.marketdata.app/docs/api/universal-parameters/human-readable/index.md The human parameter will use human-readable attribute names in the JSON or CSV output instead of the standard camelCase attribute names. Use of this parameter will result in API output that can be loaded directly into a table or viewer and presented to an end-user with no further transformation required on the front-end. ## Parameter human=\ ## Use Example https://api.marketdata.app/v1/stocks/quotes/AAPL/?human=true ## Response Example ```json { "Symbol": ["AAPL"], "Ask": [152.63], "Ask Size": [400], "Bid": [152.61], "Bid Size": [600], "Mid": [152.62], "Last": [152.63], "Volume": [35021819], "Date": [1668531422] } ``` ## Values ### True The API will output human-readable attribute names instead of the standard camelCase attribute names. The output will be capitalized as a title, with the first letter of each major word capitalized. The `s` status response is also surpressed. ### False (default) Output of attribute names will be according to API specifications using camelCase. If the `human` attribute is omitted, the default behavior is `false`. --- # Limit Source: https://www.marketdata.app/docs/api/universal-parameters/limit/index.md The `limit` parameter allows you to limit the number of results for a particular API call or override an endpoint’s default limits to get more data. - Default Limit: 10,000 - Maximum Limit: 50,000 In the example below, the daily candle endpoint by default returns the last 252 daily bars. By using limit you could modify the behavior return the last two weeks or 10 years of data. ## Parameter limit=\ ## Use Example /candles/daily/AAPL?limit=10 /candles/daily/AAPL?limit=2520 ## Values ### integer (required) The limit parameter accepts any positive integer as an input. --- # Data Mode Source: https://www.marketdata.app/docs/api/universal-parameters/mode/index.md The `mode` parameter allows you to control **how an API request is fulfilled**, including data freshness guarantees and credit usage. The `mode` parameter determines the response behavior used to fulfill each request. Our API supports four freshness modes — `live`, `delayed`, `eod`, and `historical` — plus `cached`, which serves recently stored data at a reduced credit cost. These options balance immediacy, availability, and cost efficiency. Below is a detailed overview of each mode, including examples and recommended use-cases. > [!NOTE] > **[Premium Parameter]** > > This parameter is available only on paid plans. Free and trial plans cannot change the data mode. They receive historical data — the last fully-closed session — and a request for any fresher mode (`mode=live`, `mode=delayed`, or `mode=eod`) returns [402 Payment Required](https://www.marketdata.app/docs/api/troubleshooting/payment-required). See [Data Freshness](https://www.marketdata.app/docs/docs/account/data-freshness/) for what each plan receives. ## Live Mode The `live` mode returns real-time data, providing the most current market information available. This mode is intended for time-sensitive use cases that require up-to-the-second pricing. > [!NOTE] > `mode=live` returns the freshest data your entitlements allow. It does not shorten an exchange-imposed delay. See [Data Freshness](https://www.marketdata.app/docs/docs/account/data-freshness/) for the freshness of each endpoint. ### Pricing for Live Mode - Quotes: **1 credit per symbol** included in the response that has quote data (bid/ask/mid/last). - Candles: **1 credit per 1,000 candles** included in the response. - Bulk Candles: **1 credit per symbol*** included in the response. - Other Endpoints: **1 credit per response**. ### Requesting Live Data To request real-time data, append `mode=live` to your API call or omit the parameter entirely. If the `mode` parameter is not specified, `live` mode is used by default for paid accounts. ```http GET https://api.marketdata.app/v1/options/chain/SPY/?mode=live GET https://api.marketdata.app/v1/options/chain/SPY/ ```` Both requests are valid and return the most recent data available for the specified symbol. ## Cached Mode The `cached` mode returns recently stored data that may be a few seconds to several minutes old. This mode is designed to reduce credit usage when retrieving large amounts of quote data. Data freshness is not guaranteed. Symbols with higher usage across Market Data customers tend to be refreshed more frequently. ### Pricing for Cached Mode * Quotes: **1 credit per request**, regardless of the number of symbols. * Historical Quotes: Unavailable * Candles: Unavailable * Bulk Candles: Unavailable * Other Endpoints: Unavailable This makes cached mode particularly cost-effective for bulk quote retrieval using endpoints such as [Option Chain](https://www.marketdata.app/docs/api/options/chain) and [Bulk Stock Quotes](https://www.marketdata.app/docs/api/stocks/quotes). ### Requesting Cached Data ```http GET https://api.marketdata.app/v1/options/chain/SPY/?mode=cached ``` This request attempts to fulfill the response from Market Data’s cache. If cached data is available, the response is returned at a reduced credit cost. ### Using `maxage` with Cached Mode The `maxage` parameter can be combined with `mode=cached` to control how old cached data may be before it is considered unusable. #### How `maxage` Works * **Purpose**: Sets a maximum acceptable age for cached data * **Behavior**: If no cached data exists within the specified age window, the API returns an empty response with no credit charge * **Format**: Accepts absolute datetimes or relative durations (e.g. `1h`, `5min`) #### Example Scenarios Assume the most recent cached options chain for AAPL is 3 minutes old. **Scenario 1: Data within `maxage` limit** ```http GET https://api.marketdata.app/v1/options/chain/AAPL/?mode=cached&maxage=5min ``` * **Result**: `203` response with data (1 credit charged) * **Reason**: Cached data is newer than 5 minutes **Scenario 2: Data exceeds `maxage` limit** ```http GET https://api.marketdata.app/v1/options/chain/AAPL/?mode=cached&maxage=1min ``` * **Result**: `204` empty response (0 credits charged) * **Reason**: Cached data exceeds the specified age limit #### Strategic Usage Using `maxage` enables efficient fallback logic: 1. First attempt to retrieve cached data with a defined freshness threshold 2. If a `204` response is returned, selectively request live data for required symbols 3. This maximizes cost efficiency while preserving data freshness where it matters ### Cached Mode Response Codes 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**. It is available on paid plans (Starter and up), which may 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](https://www.marketdata.app/docs/docs/account/data-freshness). ### Pricing for Delayed Mode * Pricing is identical to live mode. ### Requesting Delayed Data ```http GET https://api.marketdata.app/v1/options/chain/SPY/?mode=delayed ``` This request returns market data that is delayed by a minimum of 15 minutes. ## EOD and Historical Modes Two further modes return a closed session rather than a delay on the current one. * `mode=eod` returns the most recent session whose 4:15 PM ET cutoff has passed. Requires a paid plan. * `mode=historical` returns the most recent fully-closed session — the API waits for the next session to open before it counts a session as closed. This is what free and trial accounts receive, and the only mode they may request explicitly. ### Pricing for EOD and Historical Modes * Pricing is identical to live mode. ### Requesting EOD or Historical Data ```http GET https://api.marketdata.app/v1/options/chain/SPY/?mode=eod GET https://api.marketdata.app/v1/options/chain/SPY/?mode=historical ``` ## Mode Comparison | Feature | Live Mode | Cached Mode | Delayed Mode | EOD Mode | Historical Mode | |--------------------------------|--------------------------------------|----------------------------|---------------------------------|------------------------------|---------------------------| | **Data Timeliness** | Real-time | Seconds to minutes old | ≥ 15 minutes delayed | Last session past 4:15 PM ET | Last fully-closed session | | **Pricing** | 1 credit per symbol with quote data | 1 credit per request | Same as live | Same as live | Same as live | | **Ideal Use-Case** | Time-sensitive decisions | Bulk quote retrieval | Non-time-sensitive applications | End-of-day reporting | Backtesting and research | | **Default Behavior** | Paid accounts (if `mode` is omitted) | Must specify `mode=cached` | Must specify `mode=delayed` | Must specify `mode=eod` | Free & trial accounts | | **Paid Accounts Access** | ✅ | ✅ | ✅ | ✅ | ✅ | | **Free/Trial Accounts Access** | ❌ | ❌ | ❌ | ❌ | ✅ | Free and trial accounts receive historical data on every request. See [Data Freshness](https://www.marketdata.app/docs/docs/account/data-freshness/) for the freshness of each endpoint on each plan. * 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. * Select **`mode=eod`** or **`mode=historical`** when a closed session is what you want. ## 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](https://www.marketdata.app/docs/api/troubleshooting/payment-required). For the full list of HTTP status codes returned by the API (including `4xx` and `5xx`), see [Troubleshooting](https://www.marketdata.app/docs/api/troubleshooting). --- # Offset Source: https://www.marketdata.app/docs/api/universal-parameters/offset/index.md The offset parameter is used together with limit to allow you to implement pagination in your application. Offset will allow you to return values starting at a certain value. ## Parameter offset=\ ## Use Example /candles/daily/AAPL?limit=10&offset=10 ## Values ### integer (required) The limit parameter accepts any positive integer as an input. --- # Token Source: https://www.marketdata.app/docs/api/universal-parameters/token/index.md The token parameter allows you to submit a read-only access token as a parameter. > [!CAUTION] > **[Security Warning]** > > When submitting your token in a URL, your token is exposed in server logs, cached in your browser, or otherwise made available. We do not recommend using your token as a parameter. This should only be used as a last resort in when you are unable to submit your token in a header. ## Parameter token=\ ## Use Example https://api.marketdata.app/v1/stocks/quotes/SPY/?token=put-your-token-here ## Values ### Token Submit your read-only access token as a value. --- # Utilities Source: https://www.marketdata.app/docs/api/utilities/index.md These endpoints are designed to assist with API-related service issues, including checking the online status and uptime. ## Root Endpoint For Utilities ``` https://api.marketdata.app/ ``` ## Utilities Endpoints --- # Request Headers Source: https://www.marketdata.app/docs/api/utilities/headers/index.md This endpoint allows users to retrieve a JSON response of the headers their application is sending, aiding in troubleshooting authentication issues, particularly with the Authorization header. > [!TIP] > The values in sensitive headers such as `Authorization` are partially redacted in the response for security purposes. ## Endpoint ``` https://api.marketdata.app/headers/ ``` ### Method ``` GET ``` ## Request Example ### HTTP **GET** [https://api.marketdata.app/headers/](https://api.marketdata.app/headers/) ### JavaScript ```js title="headersCheck.js" 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 ```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); } ``` ### Python ```python title="headersCheck.py" import requests url = "https://api.marketdata.app/headers/" response = requests.get(url) print(response.text) ``` ### PHP ```php title="headersCheck.php" use MarketDataApp\Client; $client = new Client(); $headers = $client->utilities->headers(); // Display all headers echo $headers; ``` ### Java ```java title="HeadersCheck.java" import com.marketdata.sdk.MarketDataClient; public class HeadersCheck { public static void main(String[] args) { try (MarketDataClient client = new MarketDataClient()) { System.out.println(client.utilities().headers().values()); } } } ``` ### Kotlin ```kotlin title="HeadersCheck.kt" import com.marketdata.sdk.MarketDataClient fun main() { MarketDataClient().use { client -> println(client.utilities().headers().values()) } } ``` ### C# ```csharp title="HeadersCheck.cs" using MarketDataApp; using var client = await MarketDataClient.CreateAsync(); foreach (var (name, value) in (await client.Utilities.GetHeadersAsync()).Values) { Console.WriteLine($"{name}: {value}"); } ``` ## Response Example ```json { "accept": "*/*", "accept-encoding": "gzip", "authorization": "Bearer *******************************************************YKT0", "cache-control": "no-cache", "cf-connecting-ip": "132.43.100.7", "cf-ipcountry": "US", "cf-ray": "85bc0c2bef389lo9", "cf-visitor": "{\"scheme\":\"https\"}", "connection": "Keep-Alive", "host": "api.marketdata.app", "postman-token": "09efc901-97q5-46h0-930a-7618d910b9f8", "user-agent": "PostmanRuntime/7.36.3", "x-forwarded-proto": "https", "x-real-ip": "53.43.221.49" } ``` ## Response Attributes ### Success - **Headers** `object` A JSON object representing the headers received from the user's request. This object includes standard and custom headers along with their respective values. ### Error - **s** `string` Status will be `error` if the request produces an error response. - **errmsg** `string` An error message. This endpoint is particularly useful for debugging issues related to authentication by allowing users to see exactly what headers are being sent to the API. --- # API Status Source: https://www.marketdata.app/docs/api/utilities/status/index.md Check the current status of Market Data services and historical uptime. The status of the Market Data API is updated every 5 minutes. Historical uptime is available for the last 30 and 90 days. > [!TIP] > This endpoint will continue to respond with the current status of the Market Data API, even if the API is offline. This endpoint is public and does not require a token. This endpoint is also unversioned: it is served at `/status/`, not under `/v1/` like every other endpoint. ## Endpoint ``` https://api.marketdata.app/status/ ``` ### Method ``` GET ``` ## Request Example ### HTTP **GET** [https://api.marketdata.app/status/](https://api.marketdata.app/status/) ### JavaScript ```js title="statusCheck.js" 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 ```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); } ``` ### Python ```python title="statusCheck.py" import requests url = "https://api.marketdata.app/status/" response = requests.get(url) print(response.text) ``` ### PHP ```php title="statusCheck.php" use MarketDataApp\Client; $client = new Client(); $status = $client->utilities->api_status(); // Display API status for all services echo $status; ``` ### Java ```java title="StatusCheck.java" import com.marketdata.sdk.MarketDataClient; public class StatusCheck { public static void main(String[] args) { try (MarketDataClient client = new MarketDataClient()) { client.utilities().status().values().forEach(System.out::println); } } } ``` ### Kotlin ```kotlin title="StatusCheck.kt" import com.marketdata.sdk.MarketDataClient fun main() { MarketDataClient().use { client -> client.utilities().status().values().forEach(::println) } } ``` ### C# ```csharp title="StatusCheck.cs" using MarketDataApp; using var client = await MarketDataClient.CreateAsync(); foreach (var service in (await client.Utilities.GetStatusAsync()).Values) { Console.WriteLine(service); } ``` ## Response Example ```json { "s": "ok", "service": [ "/v1/funds/candles/", "/v1/markets/status/", "/v1/options/chain/", "/v1/options/expirations/", "/v1/options/lookup/", "/v1/options/quotes/", "/v1/stocks/bulkcandles/", "/v1/stocks/bulkquotes/", "/v1/stocks/candles/", "/v1/stocks/earnings/", "/v1/stocks/news/", "/v1/stocks/quotes/" ], "status": [ "online", "online", "online", "online", "online", "online", "online", "online", "online", "online", "online", "online", "online", "online" ], "online": [true, true, true, true, true, true, true, true, true, true, true, true, true, true], "uptimePct30d": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], "uptimePct90d": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], "updated": [1734036832, 1734036832, 1734036832, 1734036832, 1734036832, 1734036832, 1734036832, 1734036832, 1734036832, 1734036832, 1734036832, 1734036832, 1734036832, 1734036832] } ``` ## Response Attributes ### Success - **s** `string` Will always be `ok` when the status information is successfully retrieved. - **service** `array[string]` The list of services being monitored. - **status** `array[string]` The current status of each service (`online` or `offline`). - **online** `array[boolean]` Boolean indicators for the online status of each service. - **uptimePct30d** `array[number]` The uptime percentage of each service over the last 30 days. - **uptimePct90d** `array[number]` The uptime percentage of each service over the last 90 days. - **updated** `array[date]` The timestamp of the last update for each service's status. ### Error - **s** `string` Status will be `error` if the request produces an error response. - **errmsg** `string` An error message. For more details on the API's status, visit the [Market Data API Status Page](https://www.marketdata.app/status/). --- # Market Data SDKs Source: https://www.marketdata.app/docs/sdk/index.md Welcome to the SDK documentation for Market Data. Our SDKs are designed to help you integrate our financial data into your applications seamlessly. Whether you're building a financial analysis tool, a stock market tracker, or any application that requires real-time or historical financial data, our SDKs are here to make your development process easier. ## Available SDKs Our SDKs are available for a multitude of programming languages and platforms, serving a diverse array of developers and their projects:
[![Postman Collection Logo](/img/postman-logo.svg)](https://www.marketdata.app/docs/sdk/postman) ### [Postman Collection](https://www.marketdata.app/docs/sdk/postman) [Comprehensive Postman Collection for easy API integration and testing.](https://www.marketdata.app/docs/sdk/postman)
[![Python SDK Logo](/img/python-logo-only.svg)](https://www.marketdata.app/docs/sdk/py) ### [Python SDK](https://www.marketdata.app/docs/sdk/py) [Perfect for data analysis, backend services, AI, ML, and automation scripts.](https://www.marketdata.app/docs/sdk/py)
[![JavaScript Logo](/img/javascript-logo.svg)![TypeScript Logo](/img/typescript-logo.svg)](https://www.marketdata.app/docs/sdk/js) ### [JavaScript / TypeScript SDK](https://www.marketdata.app/docs/sdk/js) [Type-safe Market Data integration for Node.js services and TypeScript applications.](https://www.marketdata.app/docs/sdk/js)
[![Java Logo](/img/java-logo.svg)![Kotlin Logo](/img/kotlin-logo.svg)](https://www.marketdata.app/docs/sdk/java) ### [Java / Kotlin SDK](https://www.marketdata.app/docs/sdk/java) [Typed Market Data integration for JVM applications, with first-class Kotlin interop.](https://www.marketdata.app/docs/sdk/java)
[![C# SDK Logo](/img/csharp-logo.svg)](https://www.marketdata.app/docs/sdk/csharp) ### [C# / .NET SDK](https://www.marketdata.app/docs/sdk/csharp) [Async-first, typed Market Data integration for .NET services, ASP.NET Core, and desktop apps.](https://www.marketdata.app/docs/sdk/csharp)
[![Go SDK Logo](/img/Go-Logo_Aqua.svg)](https://www.marketdata.app/docs/sdk/go) ### [Go SDK](https://www.marketdata.app/docs/sdk/go) [High performance Market Data SDK integration for enterprise-level backend systems.](https://www.marketdata.app/docs/sdk/go)
[![PHP SDK Logo](/img/php-logo.svg)](https://www.marketdata.app/docs/sdk/php) ### [PHP SDK](https://www.marketdata.app/docs/sdk/php) [Market Data integration for web applications and server-side processing.](https://www.marketdata.app/docs/sdk/php)
Every SDK is crafted for ease of use, guaranteeing a straightforward setup process with minimal configuration required. ## Getting Started To start using our SDKs, select the SDK for your preferred language or platform from the sidebar. Each SDK section includes a comprehensive guide on setting up, making requests, and handling responses. ## Support If you encounter any issues or have questions regarding our SDKs, please refer to the FAQ section or contact our support team through the [support portal](https://www.marketdata.app/dashboard/). Thank you for choosing Market Data for your financial data needs. We're excited to see what you build with our SDKs! --- # C# / .NET SDK Source: https://www.marketdata.app/docs/sdk/csharp/index.md Welcome to the Market Data C#/.NET SDK documentation. This SDK lets you integrate Market Data services into any .NET application. It ships typed, records-based responses, an async-first API where every endpoint returns a `Task` and accepts a `CancellationToken`, automatic retries and rate-limit tracking, and a focused exception hierarchy you can branch on. The SDK multi-targets **.NET 8 and .NET 10** (both LTS), integrates with `Microsoft.Extensions.Configuration`, `Microsoft.Extensions.Logging`, and dependency injection out of the box, and works equally well from a console app, a background service, or ASP.NET Core. ## Quick Start ```csharp using MarketDataApp; // With no HttpClient supplied, the SDK creates and owns one configured to its requirements. // It reads MARKETDATA_TOKEN from the environment (or a .env file), validates the token on // startup, and seeds the rate-limit snapshot before your first request. using var client = await MarketDataClient.CreateAsync(); // A single quote — GetQuoteAsync returns a list; a single symbol is row 0. var quote = (await client.Stocks.GetQuoteAsync("AAPL")).Values[0]; Console.WriteLine(quote); // AAPL mid=311.94 last=311.92 ``` ## Open Source The SDK is open source and available on GitHub. Feel free to contribute to the project, report bugs, or request new features. - [C#/.NET SDK on GitHub](https://github.com/MarketDataApp/sdk-csharp/) ## 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 all the tools you need to access real-time stock and options prices, historical data, and much more. ### Getting Started 1. [Install the SDK](https://www.marketdata.app/docs/sdk/csharp/installation) from NuGet. 2. Set up your [authentication token](https://www.marketdata.app/docs/sdk/csharp/authentication) to access the API. 3. Learn about the [client](https://www.marketdata.app/docs/sdk/csharp/client) and how to make your first API requests. 4. Configure [Settings](https://www.marketdata.app/docs/sdk/csharp/settings) to customize output format, date format, and other universal parameters. 5. Explore the available endpoints for [stocks](https://www.marketdata.app/docs/sdk/csharp/stocks), [options](https://www.marketdata.app/docs/sdk/csharp/options), [funds](https://www.marketdata.app/docs/sdk/csharp/funds), [markets](https://www.marketdata.app/docs/sdk/csharp/markets), and [utilities](https://www.marketdata.app/docs/sdk/csharp/utilities). ### 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-csharp/issues). Please only open issues if you find a bug. Use our helpdesk for general questions or implementation assistance. --- # Authentication (C# SDK) Source: https://www.marketdata.app/docs/sdk/csharp/authentication/index.md The Market Data API uses a **Bearer Token** for authentication. The token is required for almost every request. 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 four ways to set your token when using the C#/.NET SDK, in increasing order of precedence: 1. Store it in [.NET user secrets](https://learn.microsoft.com/aspnet/core/security/app-secrets) _(recommended for local development)_ 2. Load it from a `.env` file in the working directory 3. Set it from an environment variable _(recommended for production; highest precedence of the automatic sources)_ 4. Pass it directly through `MarketDataClientOptions` when creating the [client](https://www.marketdata.app/docs/sdk/csharp/client) When you create a client without explicit options, the SDK loads `MARKETDATA_TOKEN` (and every other `MARKETDATA_*` setting) from user secrets, then the `.env` file, then environment variables — later sources override earlier ones. Explicit options bypass that cascade entirely. > [!TIP] > When your code is running in a production environment, we recommend using an environment variable or a managed secret store 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 Environment Variable In The Console ### Mac / Linux This command sets the environment variable for the current session only. If you open a new terminal or restart your computer, it will not persist. ```bash export MARKETDATA_TOKEN="your_api_token" ``` #### Make The Variable Persistent Add the `export` line to your shell's profile script (`~/.zshrc`, `~/.bashrc`, `~/.bash_profile`, etc.), then restart your terminal or run `source ~/.zshrc` (adjusting for your shell). ### Windows `setx` sets the variable permanently, but it is not available in the current terminal session — open a new one after running it. ```powershell setx MARKETDATA_TOKEN "your_api_token" ``` ### Using .NET User Secrets For local development, user secrets keep the token out of your project directory entirely. From your executable project's directory: ```bash dotnet user-secrets init dotnet user-secrets set "MARKETDATA_TOKEN" "your_api_token" ``` The SDK reads user secrets automatically when you create a client without explicit options. ### Using a .env File The SDK also loads a `.env` file from your working directory at startup. Create a file named `.env` in your project root: ```ini 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 Verify your authentication is working by making a test request against `SPY` (or any symbol that requires authentication). Do **not** use `AAPL` to test authentication — `AAPL` is a free test symbol and returns data even when you are not authenticated. ```csharp using MarketDataApp; using MarketDataApp.Exceptions; try { // No need to pass a token here — the SDK reads MARKETDATA_TOKEN automatically, // and CreateAsync validates it against the API before returning the client. using var client = await MarketDataClient.CreateAsync(); var quote = (await client.Stocks.GetQuoteAsync("SPY")).Values[0]; Console.WriteLine(quote); // SPY mid=642.18 last=642.05 } catch (AuthenticationException e) { Console.WriteLine($"Authentication failed: {e.Message}"); } ``` ## Passing the Token Directly If you prefer to pass the token explicitly (not recommended for production code), set `ApiToken` on `MarketDataClientOptions`. Explicit options replace the automatic cascade, so nothing is read from the environment: ```csharp using MarketDataApp; var options = new MarketDataClientOptions { ApiToken = Environment.GetEnvironmentVariable("MY_APP_MARKETDATA_TOKEN") }; using var client = await MarketDataClient.CreateAsync(options); ``` ## Startup Token Validation When a token is configured, the client validates it at startup by default — on **both** construction paths. The startup `GET /user/`: 1. fails fast on an invalid token by throwing `AuthenticationException`, and 2. seeds the client-wide rate-limit snapshot (`client.LatestRateLimit`) from the `x-api-ratelimit-*` response headers before your first data request. In asynchronous applications, prefer the async factory, which runs the validation without blocking the calling thread: ```csharp using var client = await MarketDataClient.CreateAsync(options); ``` The plain constructor runs the same validation as a blocking request, which makes it the fail-fast choice for synchronous hosts and dependency-injection factories that cannot `await`: ```csharp // Validates the token with a blocking GET /user/ when a token is configured. using var client = new MarketDataClient(options); ``` Startup validation is governed by `MarketDataClientOptions.ValidateTokenOnStartup`. Set it to `false` for first-request (lazy) validation with no startup network I/O: ```csharp var options = new MarketDataClientOptions { ValidateTokenOnStartup = false }; ``` > [!NOTE] > **[Demo mode]** > > If no token is found anywhere in the cascade, the SDK runs in **demo mode** — startup validation is skipped and you can call the free, public endpoints (such as `AAPL` quotes and `client.Utilities.GetStatusAsync()`). Authenticated endpoints throw `AuthenticationException` on first use. ## Next Steps After successful authentication, read the overview of how the [client](https://www.marketdata.app/docs/sdk/csharp/client) works, then configure [Settings](https://www.marketdata.app/docs/sdk/csharp/settings) to customize output format, date format, and other universal parameters. --- # Client (C# SDK) Source: https://www.marketdata.app/docs/sdk/csharp/client/index.md The `MarketDataClient` is the entry point for the SDK. It groups the API into five resources — `Stocks`, `Options`, `Funds`, `Markets`, and `Utilities` — and owns the cross-cutting behavior: configuration loading, authentication, retries, timeouts, and rate-limit tracking. ## Get Started Quickly ```csharp using MarketDataApp; // Reads MARKETDATA_* from the environment, validates the token, seeds the rate limits. using var client = await MarketDataClient.CreateAsync(); var quote = (await client.Stocks.GetQuoteAsync("AAPL")).Values[0]; Console.WriteLine(quote); // AAPL mid=311.94 last=311.92 ``` ## MarketDataClient ### Two ways to construct Every entry point exists in two flavors: an **async factory** (`CreateAsync`) and a **constructor**. Both perform the same startup token validation when a token is configured; the difference is only whether that validation blocks the calling thread. | Entry point | Startup validation | Use when | |-------------------------------------------|--------------------|---------------------------------------------------------------------------| | `await MarketDataClient.CreateAsync(...)` | Asynchronous | Anywhere you can `await` — the recommended default. | | `new MarketDataClient(...)` | Blocking | Synchronous hosts and dependency-injection factories that cannot `await`. | Both accept an optional `MarketDataClientOptions`. When omitted, options are loaded from the [configuration cascade](https://www.marketdata.app/docs/sdk/csharp/settings#configuration-cascade) (user secrets, `.env`, environment variables). ### Two ways to own the HttpClient The SDK never disposes an `HttpClient` it did not create, and never reconfigures one you hand it. - **SDK-owned (default).** The overloads without an `HttpClient` parameter create one backed by `MarketDataClient.CreateDefaultHttpHandler()` (2-second connection timeout, pooled-connection rotation) with the `HttpClient`-level timeout disabled, because the SDK enforces its own fixed 99-second request timeout. `Dispose()` also disposes the owned client. - **Application-owned.** Supply your own `HttpClient` — for example one from `IHttpClientFactory` — and the application controls its lifetime. Its handler and `Timeout` are respected as-is. ```csharp // SDK-owned transport (simplest). using var client = await MarketDataClient.CreateAsync(options); // Application-owned transport. CreateDefaultHttpHandler() supplies the 2-second // connect timeout; a plain new HttpClient() works too. using var httpClient = new HttpClient(MarketDataClient.CreateDefaultHttpHandler()); var client2 = await MarketDataClient.CreateAsync(httpClient, options); ``` ### Dependency injection (ASP.NET Core) Register the client with one line. `AddMarketDataClient` lives in the `Microsoft.Extensions.DependencyInjection` namespace, so it is discoverable from `Program.cs` without an extra `using`: ```csharp // Program.cs builder.Services.AddMarketDataClient(builder.Configuration); ``` Then take `MarketDataClient` as a constructor-injected dependency or a minimal-API parameter: ```csharp app.MapGet("/quote/{symbol}", async (string symbol, MarketDataClient client, CancellationToken ct) => Results.Ok((await client.Stocks.GetQuoteAsync(symbol, cancellationToken: ct)).Values)); ``` `AddMarketDataClient` registers `MarketDataClient` as a **singleton** over an `IHttpClientFactory`-managed `HttpClient` backed by the SDK's default handler. Three overloads resolve the options for you: | Overload | Options source | |--------------------------------------------------------|-------------------------------------------------------------------------------------------| | `AddMarketDataClient()` | `MarketDataClientOptions.FromEnvironment()` — user secrets, `.env`, environment variables | | `AddMarketDataClient(IConfiguration configuration)` | `MarketDataClientOptions.FromConfiguration(configuration)` — the `MARKETDATA_*` keys | | `AddMarketDataClient(MarketDataClientOptions options)` | the supplied instance | Registrations use `TryAdd*`, so you can override either the options or the client by registering your own first. When your app has logging configured, the container's `ILogger` is auto-wired into the SDK. Because the DI path uses the constructor, a configured token is validated with a blocking `GET /user/` the first time the singleton is resolved — typically during startup — so an invalid token fails the application at startup rather than on the first request. Register options with `ValidateTokenOnStartup = false` to defer that. ## Async and Cancellation Every endpoint method is asynchronous, returns a `Task`, and accepts an optional `CancellationToken` as its last parameter. There are no synchronous endpoint methods. ```csharp using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(15)); var quotes = await client.Stocks.GetQuotesAsync( ["AAPL", "MSFT", "GOOG"], cancellationToken: timeout.Token); ``` Caller cancellation surfaces as `OperationCanceledException`, which is deliberately **not** part of the SDK's exception hierarchy — it means *you* stopped the request. An SDK timeout surfaces as `NetworkException`. ### Composing async calls Endpoint calls are ordinary tasks, so they compose with `Task.WhenAll`. All requests share the client's retry, rate-limit, and concurrency behavior; `MaxConcurrentRequests` (default 50) bounds how many are in flight at once. ```csharp var aaplTask = client.Stocks.GetQuoteAsync("AAPL"); var msftTask = client.Stocks.GetQuoteAsync("MSFT"); var statusTask = client.Markets.GetStatusAsync("US"); await Task.WhenAll(aaplTask, msftTask, statusTask); var aapl = aaplTask.Result.Values[0]; // safe: the tasks are already complete var msft = msftTask.Result.Values[0]; var status = statusTask.Result.Values[0]; ``` ## Scalar Parameters and Request Objects Every endpoint has two overloads. The **scalar** overload takes the common parameters directly and is the fastest way to make a call: ```csharp var candles = await client.Stocks.GetCandlesAsync(StockResolution.Daily, "AAPL", countback: 30); ``` The **request-object** overload takes an immutable request record. Required values are constructor arguments; optional values are `init` properties. Use it when several optional filters should be grouped or reused: ```csharp var request = new StockCandlesRequest(StockResolution.Daily, "AAPL") { Countback = 30, AdjustDividends = true }; var candles = await client.Stocks.GetCandlesAsync(request); ``` Both overloads also accept an optional [`MarketDataRequestOptions`](https://www.marketdata.app/docs/sdk/csharp/settings#universal-parameters) for the universal parameters (date format, mode, columns, limit, offset). ## The Response Object Every endpoint returns a typed response record derived from `MarketDataResponse`, wrapping the decoded data plus request metadata, with a uniform surface regardless of endpoint or format. ```csharp public abstract record MarketDataResponse { public T Values { get; } // the typed payload (e.g. IReadOnlyList) public int StatusCode { get; } // 200, 203, or 404 public bool IsNoData { get; } // true on a 404 "no_data" response public string? RequestId { get; } // server request id, for support tickets public Uri RequestUrl { get; } // the absolute request URL public RateLimitSnapshot? RateLimit { get; } // this request's rate limit public IReadOnlyList Parts { get; } // one per HTTP request public bool IsComposite { get; } // assembled from several HTTP responses public string RawBody { get; } // the raw response body, as sent public bool IsJson { get; } public bool IsCsv { get; } public bool IsHtml { get; } public string SaveToFile(string path); // write the body; returns the path public Task SaveToFileAsync(string path, CancellationToken cancellationToken = default); } ``` ```csharp var response = await client.Stocks.GetQuoteAsync("AAPL"); response.Values; // IReadOnlyList — the part you usually want response.StatusCode; // 200 response.RequestId; // e.g. for a support ticket response.RateLimit; // this request's rate-limit snapshot (may be null) await response.SaveToFileAsync("aapl.json"); // cache the raw body ``` `SaveToFile` chooses the representation from the file extension (`.json`, `.csv`, `.html`) when the response can provide it, and falls back to the raw body otherwise. ## Error Handling Everything the SDK throws is a `MarketDataException`. The seven subtypes below are the complete set. Each exception carries support context — `StatusCode`, `RequestId`, `RequestUrl`, `Timestamp`, and `ExceptionType` — plus a preformatted `SupportInfo` block ready to paste into a support ticket. | Subtype | When it's thrown | |---------------------------|--------------------------------------------------------------------------------------------------| | `AuthenticationException` | Missing or invalid token (HTTP 401 / 403) | | `BadRequestException` | Invalid parameters (HTTP 400) | | `NotFoundException` | The resource doesn't exist (HTTP 404 that is not a "no data" response) | | `RateLimitException` | Quota exceeded (HTTP 429), or the client-side rate-limit guard refused to send; see `RetryAfter` | | `ServerException` | API-side failure (HTTP 5xx); see `RetryAfter` | | `NetworkException` | Connection failure or timeout | | `ParseException` | The response could not be decoded | Request-shape mistakes caught **before** any HTTP call — an invalid date window, an unsupported option on a typed method — throw `ArgumentException`, the standard .NET signal for a bad argument. ```csharp using MarketDataApp.Exceptions; try { var quote = await client.Stocks.GetQuoteAsync("AAPL"); } catch (AuthenticationException) { Console.WriteLine("Check your token"); } catch (RateLimitException e) { var wait = e.RetryAfter is { } delay ? $"{delay.TotalSeconds:F0}s" : "a moment"; Console.WriteLine($"Rate limited — retry after {wait}"); } catch (MarketDataException e) { // Any other case. Attach SupportInfo to a bug report. Console.WriteLine(e.SupportInfo); } ``` ## Retries and Timeouts Transient failures — `NetworkException` and HTTP 501–599 — are retried automatically up to `MaxRetries` times (default 3) with a fixed exponential backoff (1s, 2s, 4s, capped at 30s). A server-supplied `Retry-After` header is honored. Authentication, validation, parsing, and rate-limit failures are never retried; before retrying a server error the SDK also consults the cached `/status/` reading and skips the retry if the API reports itself offline. Every HTTP attempt has a fixed **99-second request timeout**; SDK-owned and DI-registered transports add a **2-second connection timeout** through the default handler. Neither is configurable — `MaxRetries` is the only retry knob. ## Rate Limits The API reports your quota in `x-api-ratelimit-*` response headers. The SDK captures them two ways: - **Per response:** `response.RateLimit` — the snapshot returned with that request. - **Client-wide:** `client.LatestRateLimit` — the most recent complete snapshot the client has seen, seeded by the startup `GET /user/` when a token is configured. ```csharp if (client.LatestRateLimit is { } limit) { Console.WriteLine($"{limit.Remaining}/{limit.Limit} remaining, resets {limit.Reset:HH:mm}"); } ``` `RateLimitSnapshot` exposes `Limit`, `Remaining`, `Reset` (a `DateTimeOffset`), and `Consumed`. When the latest snapshot shows the quota exhausted and the reset time has not passed, the client refuses to send further requests and throws `RateLimitException` immediately, so you never burn calls that would fail anyway. ## Disposing the Client `MarketDataClient` implements `IDisposable`. Disposing it releases its internal resources and — only when the SDK created the transport — the owned `HttpClient`. An `HttpClient` you supplied is never disposed. A single client instance is safe to use concurrently from multiple threads or requests, so the typical pattern is one long-lived client per application: ```csharp using var client = await MarketDataClient.CreateAsync(); // ... use for the lifetime of the app ``` --- # Funds (C# SDK) Source: https://www.marketdata.app/docs/sdk/csharp/funds/index.md The C#/.NET SDK from Market Data provides methods to streamline your use of the Funds endpoints. These methods provide a typed interface over the underlying HTTP requests and responses; every method is asynchronous and accepts an optional `CancellationToken`. Reach the resource through `client.Funds`. For CSV output, call the paired `*CsvAsync` method of the same name (for example `GetCandlesCsvAsync`). ## Funds Endpoints --- # Fund Candles (C# SDK) Source: https://www.marketdata.app/docs/sdk/csharp/funds/candles/index.md Retrieve historical NAV candles (open/high/low/close) for a mutual fund symbol. Fund candles have no volume. ## Making Requests Use `GetCandlesAsync` on the `Funds` resource, passing a `FundResolution` and a symbol. ```csharp Task GetCandlesAsync( FundResolution resolution, string symbol, DateOnly? date = null, DateOnly? from = null, DateOnly? to = null, int? countback = null, MarketDataRequestOptions? options = null, CancellationToken cancellationToken = default) Task GetCandlesAsync(FundCandlesRequest request, ...) ``` ### FundCandlesRequest ```csharp new FundCandlesRequest(FundResolution resolution, string symbol) { Date = DateOnly, // a single trading day From = DateOnly, // window start (inclusive) To = DateOnly, // window end (inclusive) Countback = int // N candles back from `To` (or from today), instead of `From` } ``` The same date-window rules as [stock candles](https://www.marketdata.app/docs/sdk/csharp/stocks/candles) apply and are validated before any HTTP call. ### FundResolution Fund NAVs are published daily, so the resolutions are daily and coarser: ```csharp FundResolution.Daily // also Weekly, Monthly, Yearly FundResolution.Days(1) // also Weeks(n), Months(n), Years(n) FundResolution.Of("W") // any raw wire token ``` #### Returns `FundCandlesResponse` wrapping `IReadOnlyList`: ```csharp public record FundCandle( DateTimeOffset? Time, // America/New_York decimal? Open, decimal? High, decimal? Low, decimal? Close); ``` ## Examples ```csharp using MarketDataApp; using MarketDataApp.Funds; using var client = await MarketDataClient.CreateAsync(); // The last 30 daily NAVs. var candles = await client.Funds.GetCandlesAsync(FundResolution.Daily, "VFINX", countback: 30); foreach (var bar in candles.Values) { Console.WriteLine($"{bar.Time:yyyy-MM-dd} NAV close={bar.Close}"); } ``` For CSV output, call `client.Funds.GetCandlesCsvAsync(...)` and read `.Csv`. See [Settings](https://www.marketdata.app/docs/sdk/csharp/settings#csv-output). --- # Installation (C# SDK) Source: https://www.marketdata.app/docs/sdk/csharp/installation/index.md This guide will help you install the Market Data C#/.NET SDK and configure it for your project. | | | |-----------------------|------------------------------------------------------------------------------| | **Package** | [`MarketDataApp`](https://www.nuget.org/packages/MarketDataApp) on NuGet.org | | **Target frameworks** | `net8.0`, `net10.0` | | **License** | MIT | | **Source** | [MarketDataApp/sdk-csharp](https://github.com/MarketDataApp/sdk-csharp) | ## Prerequisites - **.NET 8.0 or newer.** The package multi-targets `net8.0` and `net10.0` (both LTS releases), so projects on either runtime get a native build. - The [.NET SDK](https://dotnet.microsoft.com/download) command line, or an IDE such as Visual Studio, Rider, or VS Code with the C# Dev Kit. ## Basic Installation Add the SDK as a NuGet package reference. From your project directory: ```bash dotnet add package MarketDataApp ``` `dotnet add package` resolves the newest stable version for you and writes it into your project file. If you maintain the reference by hand, take the version number from the package's [NuGet page](https://www.nuget.org/packages/MarketDataApp) or the [releases list](https://github.com/MarketDataApp/sdk-csharp/releases) — versions are derived from git tags, so there is no fixed number to copy from the docs: ```xml ``` > [!NOTE] > The NuGet **package id**, the assembly, and the root **namespace** are all `MarketDataApp` — you install `MarketDataApp` and then write `using MarketDataApp;`. Endpoint-specific types live under `MarketDataApp.Stocks`, `MarketDataApp.Options`, `MarketDataApp.Funds`, `MarketDataApp.Markets`, and `MarketDataApp.Utilities`. ## Requirements & Dependencies The SDK stays on the standard Microsoft stack, with one small helper: - **No third-party HTTP client.** It uses the built-in `System.Net.Http.HttpClient`, and can either create its own or use one your application manages (including clients from `IHttpClientFactory`, via `Microsoft.Extensions.Http`). - **`System.Text.Json`** for JSON decoding — no Newtonsoft dependency. - **`Microsoft.Extensions.Configuration`** (with the user-secrets provider), **`Microsoft.Extensions.Logging`**, and **`Microsoft.Extensions.DependencyInjection`** for configuration binding, structured logging, and the `AddMarketDataClient` service-collection extension. Environment variables are read by the SDK's own provider, which copies in only the `MARKETDATA_`-prefixed variables, so the rest of the machine environment never reaches the configuration object. - **`DotNetEnv`** to load an optional `.env` file from the working directory. ## Updating the SDK To move to the latest stable version: ```bash dotnet add package MarketDataApp ``` The SDK follows [semantic versioning](https://semver.org/) from `1.0.0`, so a `1.x` update never contains a breaking change. Review the [release notes](https://github.com/MarketDataApp/sdk-csharp/releases) before moving to a new major version. ## Next Steps After installation, you'll need to: 1. Set up your [authentication token](https://www.marketdata.app/docs/sdk/csharp/authentication). 2. Learn about the [client](https://www.marketdata.app/docs/sdk/csharp/client) and how to make your first API requests. 3. Configure [Settings](https://www.marketdata.app/docs/sdk/csharp/settings) to customize output format, date format, and other universal parameters. --- # Markets (C# SDK) Source: https://www.marketdata.app/docs/sdk/csharp/markets/index.md The C#/.NET SDK from Market Data provides methods to streamline your use of the Markets endpoints. These methods provide a typed interface over the underlying HTTP requests and responses; every method is asynchronous and accepts an optional `CancellationToken`. Reach the resource through `client.Markets`. For CSV output, call the paired `*CsvAsync` method of the same name (for example `GetStatusCsvAsync`). ## Markets Endpoints --- # Market Status (C# SDK) Source: https://www.marketdata.app/docs/sdk/csharp/markets/status/index.md Retrieve whether a country's stock market is open or closed on a given day, or over a range of days — the market calendar. ## Making Requests Use `GetStatusAsync` on the `Markets` resource. With no parameters it returns today's status for the US market. ```csharp Task GetStatusAsync( string? country = null, DateOnly? date = null, DateOnly? from = null, DateOnly? to = null, int? countback = null, MarketDataRequestOptions? options = null, CancellationToken cancellationToken = default) Task GetStatusAsync(MarketStatusRequest request, ...) ``` ### MarketStatusRequest ```csharp new MarketStatusRequest { Country = string, // ISO 3166 two-letter code; default US Date = DateOnly, // one day From = DateOnly, // range start To = DateOnly, // range end Countback = int // N most recent days } ``` #### Returns `MarketStatusResponse` wrapping `IReadOnlyList` — one row per day: ```csharp public record MarketStatus( DateTimeOffset? Date, string? Status); // "open" or "closed" ``` ## Examples ```csharp using MarketDataApp; using var client = await MarketDataClient.CreateAsync(); // Is the US market open today? var today = (await client.Markets.GetStatusAsync("US")).Values[0]; Console.WriteLine($"US market is {today.Status} on {today.Date:yyyy-MM-dd}"); // The last five trading-calendar days. var recent = await client.Markets.GetStatusAsync("US", countback: 5); foreach (var day in recent.Values) { Console.WriteLine(day); // 2026-08-14 open } ``` For CSV output, call `client.Markets.GetStatusCsvAsync(...)` and read `.Csv`. See [Settings](https://www.marketdata.app/docs/sdk/csharp/settings#csv-output). --- # Options (C# SDK) Source: https://www.marketdata.app/docs/sdk/csharp/options/index.md The C#/.NET SDK from Market Data provides methods to streamline your use of the Options endpoints. These methods provide a typed interface over the underlying HTTP requests and responses; every method is asynchronous and accepts an optional `CancellationToken`. Reach the resource through `client.Options`. For CSV output, call the paired `*CsvAsync` method of the same name (for example `GetChainCsvAsync`). ## Options Endpoints --- # Option Chain (C# SDK) Source: https://www.marketdata.app/docs/sdk/csharp/options/chain/index.md Retrieve a complete or filtered options chain for an underlying symbol. Every contract comes back as a full option quote — price, size, volume, open interest, greeks, and IV. ## Making Requests Use `GetChainAsync` on the `Options` resource. The scalar overload exposes every filter as a named optional parameter; the request-object overload groups them on `OptionsChainRequest`. ```csharp Task GetChainAsync( string symbol, ExpirationFilter? expiration = null, bool? weekly = null, bool? monthly = null, bool? quarterly = null, bool? am = null, bool? pm = null, bool? nonStandard = null, StrikeFilter? strike = null, double? delta = null, int? strikeLimit = null, StrikeRange? strikeRangeFilter = null, decimal? minBid = null, decimal? maxBid = null, decimal? minAsk = null, decimal? maxAsk = null, decimal? maxBidAskSpread = null, double? maxBidAskSpreadPct = null, long? minOpenInterest = null, long? minVolume = null, OptionSide? side = null, DateOnly? date = null, MarketDataRequestOptions? options = null, CancellationToken cancellationToken = default) Task GetChainAsync(OptionsChainRequest request, ...) ``` > [!CAUTION] > **[Quota]** > > A chain response bills roughly **one credit per contract returned**. An unfiltered chain for a liquid underlying can run to thousands of contracts. Always narrow with an expiration filter, `StrikeLimit`, or `Side` unless you really need the whole chain. ### OptionsChainRequest ```csharp new OptionsChainRequest(string symbol) { // Expiration selection (pick one) Expiration = ExpirationFilter, // see below Weekly = bool, Monthly = bool, Quarterly = bool, // expiration cycles Am = bool, Pm = bool, // settlement time NonStandard = bool, // include non-standard contracts // Strike selection Strike = StrikeFilter, // see below StrikeLimit = int, // N strikes closest to the money StrikeRangeFilter = StrikeRange, // StrikeRange.Itm / .Otm / .All Delta = double, // strikes nearest this delta // Liquidity filters MinBid = decimal, MaxBid = decimal, MinAsk = decimal, MaxAsk = decimal, MaxBidAskSpread = decimal, MaxBidAskSpreadPct = double, MinOpenInterest = long, MinVolume = long, Side = OptionSide.Call | OptionSide.Put, Date = DateOnly // historical chain as of this date } ``` ### ExpirationFilter A typed value for the `expiration` parameter: ```csharp ExpirationFilter.ForDate(DateOnly date) // one exact expiration ExpirationFilter.ForDte(int days) // the expiration closest to N days out ExpirationFilter.ForRange(DateOnly from, DateOnly to) // every expiration in a window ExpirationFilter.ForMonthYear(int year, int month) // all expirations in a month ``` ### StrikeFilter ```csharp StrikeFilter.ForExact(decimal price) // one strike StrikeFilter.ForRange(decimal min, decimal max) // strikes in a band StrikeFilter.ForComparison(StrikeFilter.ComparisonOperator.Gte, decimal price) // Gt / Gte / Lt / Lte ``` #### Returns `OptionsChainResponse` wrapping `IReadOnlyList` — the same record the [quotes](https://www.marketdata.app/docs/sdk/csharp/options/quotes) endpoint returns, one per contract: ```csharp public record OptionQuote( string? OptionSymbol, string? Underlying, DateTimeOffset? Expiration, string? Side, decimal? Strike, DateTimeOffset? FirstTraded, int? Dte, DateTimeOffset? Updated, decimal? Bid, long? BidSize, decimal? Mid, decimal? Ask, long? AskSize, decimal? Last, long? OpenInterest, long? Volume, bool? InTheMoney, decimal? IntrinsicValue, decimal? ExtrinsicValue, decimal? UnderlyingPrice, double? Iv, double? Delta, double? Gamma, double? Theta, double? Vega, double? Rho); ``` `OptionQuote.PresentGreeks` returns the set of greeks that are non-null on a given row (for example, `Rho` can be null). ## Examples ```csharp using MarketDataApp; using MarketDataApp.Options; using var client = await MarketDataClient.CreateAsync(); // Calls expiring closest to 45 days out, six strikes around the money. var chain = await client.Options.GetChainAsync( "AAPL", expiration: ExpirationFilter.ForDte(45), strikeLimit: 6, side: OptionSide.Call); foreach (var contract in chain.Values) { Console.WriteLine( $"{contract.OptionSymbol} strike={contract.Strike} bid/ask={contract.Bid}/{contract.Ask} " + $"OI={contract.OpenInterest} IV={contract.Iv:P1} delta={contract.Delta:F2}" + (contract.InTheMoney == true ? " ITM" : "")); } // A liquidity-filtered request object: monthly puts, in the money, tight spreads only. var puts = await client.Options.GetChainAsync(new OptionsChainRequest("SPY") { Monthly = true, Side = OptionSide.Put, StrikeRangeFilter = StrikeRange.Itm, MaxBidAskSpreadPct = 0.05, MinOpenInterest = 100 }); ``` For CSV output, call `client.Options.GetChainCsvAsync(...)` and read `.Csv`. See [Settings](https://www.marketdata.app/docs/sdk/csharp/settings#csv-output). --- # Expirations (C# SDK) Source: https://www.marketdata.app/docs/sdk/csharp/options/expirations/index.md Retrieve the list of expiration dates that have listed options for an underlying symbol. ## Making Requests Use `GetExpirationsAsync` on the `Options` resource. ```csharp Task GetExpirationsAsync( string symbol, decimal? strike = null, DateOnly? date = null, MarketDataRequestOptions? options = null, CancellationToken cancellationToken = default) Task GetExpirationsAsync(OptionsExpirationsRequest request, ...) ``` ### OptionsExpirationsRequest ```csharp new OptionsExpirationsRequest(string symbol) { Strike = decimal, // only expirations that list this strike Date = DateOnly // expirations available as of this historical date } ``` #### Returns `OptionsExpirationsResponse` wrapping `IReadOnlyList` — one entry per expiration date, in ascending order. ## Examples ```csharp using MarketDataApp; using var client = await MarketDataClient.CreateAsync(); var expirations = await client.Options.GetExpirationsAsync("AAPL"); Console.WriteLine($"{expirations.Values.Count} expirations; next: {expirations.Values[0]:yyyy-MM-dd}"); // Only expirations that list the 200 strike. var atStrike = await client.Options.GetExpirationsAsync("AAPL", strike: 200m); ``` For CSV output, call `client.Options.GetExpirationsCsvAsync(...)` and read `.Csv`. See [Settings](https://www.marketdata.app/docs/sdk/csharp/settings#csv-output). --- # Lookup (C# SDK) Source: https://www.marketdata.app/docs/sdk/csharp/options/lookup/index.md Turn a human-readable option description into a well-formed OCC option symbol. ## Making Requests Use `GetLookupAsync` on the `Options` resource. ```csharp Task GetLookupAsync( string userInput, MarketDataRequestOptions? options = null, CancellationToken cancellationToken = default) Task GetLookupAsync(OptionsLookupRequest request, ...) ``` ### OptionsLookupRequest ```csharp new OptionsLookupRequest(string userInput) ``` #### Returns `OptionsLookupResponse` wrapping a `string` — the OCC option symbol. Read it with `.Values`. ## Examples ```csharp using MarketDataApp; using var client = await MarketDataClient.CreateAsync(); var lookup = await client.Options.GetLookupAsync("AAPL 12/17/2027 250 Call"); Console.WriteLine(lookup.Values); // AAPL271217C00250000 ``` For CSV output, call `client.Options.GetLookupCsvAsync(...)` and read `.Csv`. See [Settings](https://www.marketdata.app/docs/sdk/csharp/settings#csv-output). --- # Option Quotes (C# SDK) Source: https://www.marketdata.app/docs/sdk/csharp/options/quotes/index.md Retrieve a real-time or historical quote for a specific option contract, identified by its OCC option symbol. ## Making Requests The `Options` resource offers `GetQuoteAsync` for one contract and `GetQuotesAsync` for several. Unlike stock quotes, the options backend does not batch symbols: `GetQuotesAsync` fans out one request per contract (bounded by the client's `MaxConcurrentRequests`) and returns a dictionary keyed by option symbol, so each contract keeps its own response metadata. ```csharp // One contract Task GetQuoteAsync( string optionSymbol, DateOnly? date = null, DateOnly? from = null, DateOnly? to = null, MarketDataRequestOptions? options = null, CancellationToken cancellationToken = default) Task GetQuoteAsync(OptionsQuoteRequest request, ...) // Several contracts, one request each Task> GetQuotesAsync( string[] optionSymbols, DateOnly? date = null, DateOnly? from = null, DateOnly? to = null, MarketDataRequestOptions? options = null, CancellationToken cancellationToken = default) Task> GetQuotesAsync(OptionsQuotesRequest request, ...) ``` ### Request types ```csharp new OptionsQuoteRequest(string optionSymbol) { Date = DateOnly, // a single historical date From = DateOnly, // historical window start To = DateOnly // historical window end } new OptionsQuotesRequest(params string[] optionSymbols) // or any IEnumerable { Date = DateOnly, From = DateOnly, To = DateOnly } ``` #### Returns `OptionsQuotesResponse` wrapping `IReadOnlyList` — a single row for a live quote, or one row per trading day for a historical window. See [chain](https://www.marketdata.app/docs/sdk/csharp/options/chain#returns) for the full `OptionQuote` record. ## Examples ```csharp using MarketDataApp; using var client = await MarketDataClient.CreateAsync(); // Live quote for one contract. var quote = (await client.Options.GetQuoteAsync("AAPL271217C00250000")).Values[0]; Console.WriteLine($"{quote.OptionSymbol}: bid/ask={quote.Bid}/{quote.Ask} IV={quote.Iv:P1} delta={quote.Delta:F2}"); // Several contracts — one response per symbol. var quotes = await client.Options.GetQuotesAsync(["AAPL271217C00250000", "AAPL271217P00250000"]); foreach (var (symbol, response) in quotes) { Console.WriteLine($"{symbol}: mid={response.Values[0].Mid} (request {response.RequestId})"); } ``` For CSV output, call `client.Options.GetQuoteCsvAsync(...)` and read `.Csv`. See [Settings](https://www.marketdata.app/docs/sdk/csharp/settings#csv-output). --- # Settings (C# SDK) Source: https://www.marketdata.app/docs/sdk/csharp/settings/index.md The SDK is configured at two levels: **client options** (`MarketDataClientOptions`, applied once when the client is created) and **per-request options** (`MarketDataRequestOptions`, passed to any endpoint call). Client options can be set programmatically or bound from configuration; per-request options override the client-level defaults field by field. ## Configuration Cascade When you create a client without explicit options, `MarketDataClientOptions.FromEnvironment()` loads every `MARKETDATA_*` key from these sources, in **increasing precedence order**: 1. .NET user secrets (lowest priority) 2. An optional `.env` file in the current working directory 3. `MARKETDATA_*` environment variables (highest priority) So a `MARKETDATA_*` environment variable overrides the same key in `.env`, which overrides user secrets. Only the `MARKETDATA_`-prefixed variables are read; every other variable in the environment is ignored. Explicit options — `new MarketDataClientOptions { ... }` — bypass the cascade entirely. In ASP.NET Core and generic-host apps, `MarketDataClientOptions.FromConfiguration(IConfiguration)` reads the same keys from your app's `IConfiguration` (appsettings, user secrets, environment variables — whatever the host has wired up). `AddMarketDataClient(builder.Configuration)` does this for you. The **per-request** layer sits on top: any `MarketDataRequestOptions` field you set on an endpoint call wins over the matching client-level default; fields you leave `null` fall back to the client default. ```csharp // MARKETDATA_DATE_FORMAT=timestamp and MARKETDATA_MODE=cached configured in the environment. using var client = await MarketDataClient.CreateAsync(); // dateformat=timestamp and mode=cached are applied from the client defaults. await client.Stocks.GetPricesAsync(["AAPL"]); // dateformat=unix wins from the per-request options; mode=cached still fills from the default. await client.Stocks.GetPricesAsync( ["AAPL"], new MarketDataRequestOptions { DateFormat = DateFormat.Unix }); ``` ## Universal Parameters `MarketDataRequestOptions` carries the parameters every data endpoint understands. Pass it as the `options` argument of any endpoint method: ```csharp var response = await client.Stocks.GetQuotesAsync( ["AAPL", "MSFT"], options: new MarketDataRequestOptions { DateFormat = DateFormat.Timestamp, Mode = Mode.Delayed, Limit = 50, Columns = ["symbol", "last"] }); ``` ### Date Format `DateFormat` controls how the API renders date/time fields in the raw response. Client-level default: `DefaultDateFormat` (`MARKETDATA_DATE_FORMAT`). | Value | Wire value | Notes | |--------------------------|---------------|--------------------------------------------------------------------------------------------------------------------------------------------------| | `DateFormat.Unix` | `unix` | Unix epoch seconds. | | `DateFormat.Timestamp` | `timestamp` | ISO-8601 timestamp strings. | | `DateFormat.Spreadsheet` | `spreadsheet` | Excel/Sheets serial dates. **CSV-only**: typed methods reject it with `ArgumentException`, because the typed decoders cannot parse serial dates. | Regardless of the wire format, typed responses always expose dates as `DateTimeOffset` values normalized to **America/New_York**. ### Data Mode `Mode` selects the data freshness. Client-level default: `DefaultMode` (`MARKETDATA_MODE`). | Value | Wire value | |----------------|------------| | `Mode.Live` | `live` | | `Mode.Delayed` | `delayed` | | `Mode.Cached` | `cached` | ### Columns `Columns` restricts the response to the named fields, which reduces both payload size and, on some endpoints, the credits consumed. It applies to both typed and CSV requests. Fields projected away come back as `null` on the typed models — which is why every field on the response records is nullable. Client-level default: `DefaultColumns` (`MARKETDATA_COLUMNS`, comma-separated). ### Limit and Offset `Limit` caps the number of rows and `Offset` skips rows, for endpoints that support paging. They have no client-level default and are taken only from the per-request options. ### Headers and Human `Headers` (include a header row) and `Human` (human-readable field names and values) apply to **CSV output only**. Typed JSON responses are always decoded into typed models keyed by property name, so neither flag is sent on the typed request path. Client-level defaults: `DefaultAddHeaders` (`MARKETDATA_ADD_HEADERS`) and `DefaultHuman` (`MARKETDATA_USE_HUMAN_READABLE`). ## CSV Output Every endpoint has a paired `*CsvAsync` method that returns the API's CSV output as text instead of typed models. The output format is chosen by **which method you call**, not by a setting: `GetQuotesAsync` returns typed models, `GetQuotesCsvAsync` returns CSV. ```csharp var csv = await client.Stocks.GetPricesCsvAsync( ["AAPL", "MSFT"], new MarketDataRequestOptions { Headers = true, Human = true }); Console.WriteLine(csv.Csv); // the CSV text (also available as Values / RawBody) await csv.SaveToFileAsync("prices.csv"); // write it to disk ``` `CsvResponse` carries the same metadata as every other response (`StatusCode`, `RequestId`, `RateLimit`, `IsNoData`, ...), with `IsCsv == true`. > [!NOTE] > **[`MARKETDATA_OUTPUT_FORMAT`]** > > This key exists for parity with the other Market Data SDKs and is **advisory only** in C#: it stores a hint on `OutputFormat` but never reroutes a typed method to CSV or vice versa. Call the `*CsvAsync` method when you want CSV. ## Logging The SDK emits structured `Microsoft.Extensions.Logging` events for its lifecycle, requests, responses, retries, and errors — tokens are always redacted. Attach any `ILogger` through `Logger` (or `options.WithLogger(logger)`); in ASP.NET Core, `AddMarketDataClient` auto-wires the container's `ILogger` when logging is configured. `MinimumLogLevel` (`MARKETDATA_LOGGING_LEVEL`) controls the SDK's own verbosity. The default `Information` suppresses the per-request Debug logs unless you set `MARKETDATA_LOGGING_LEVEL=DEBUG`. Accepted values are `DEBUG` / `INFO` / `WARNING` / `ERROR` or any .NET `LogLevel` name. For the canonical `{timestamp} - {logger_name} - {level} - {message}` console line shared by the Market Data SDKs, add the opt-in formatter to your logging builder: ```csharp builder.Logging.AddMarketDataCanonicalConsole(); // Output: 2025-02-21 12:00:00 - marketdata.client - INFO - Making request... ``` ## Programmatic Options Everything the cascade can bind is also settable in code, plus a few knobs that are code-only: ```csharp var options = new MarketDataClientOptions { ApiToken = token, MaxRetries = 2, // the only retry knob (default 3) MaxConcurrentRequests = 10, // 1–50, default 50 ValidateTokenOnStartup = false, // default true TimeProvider = TimeProvider.System, // inject a fake clock in tests UserAgent = "my-app/1.0", Logger = logger, DefaultDateFormat = DateFormat.Timestamp, DefaultMode = Mode.Cached }; ``` Retry timing (the 1-second base delay, exponential growth, 30-second cap, and the `Retry-After` ceiling) is fixed by the SDK requirements and is not configurable. ## Environment Variables Reference | Key | Property | Default | Notes | |--------------------------------------|-------------------------|-----------------------------------|-----------------------------------------| | `MARKETDATA_TOKEN` | `ApiToken` | `null` | Bearer token; `null` means demo mode. | | `MARKETDATA_BASE_URL` | `BaseAddress` | `https://api.marketdata.app/` | Absolute HTTP(S) URI. | | `MARKETDATA_API_VERSION` | `ApiVersion` | `v1` | Version path segment. | | `MARKETDATA_MAX_RETRIES` | `MaxRetries` | `3` | Retries after a transient failure. | | `MARKETDATA_MAX_CONCURRENT_REQUESTS` | `MaxConcurrentRequests` | `50` | In-flight request cap, 1–50. | | `MARKETDATA_USER_AGENT` | `UserAgent` | `marketdata-sdk-csharp/{version}` | Sent on every request. | | `MARKETDATA_DATE_FORMAT` | `DefaultDateFormat` | `null` | `unix` / `timestamp` / `spreadsheet`. | | `MARKETDATA_MODE` | `DefaultMode` | `null` | `live` / `delayed` / `cached`. | | `MARKETDATA_COLUMNS` | `DefaultColumns` | `null` | Comma-separated column list. | | `MARKETDATA_ADD_HEADERS` | `DefaultAddHeaders` | `null` | `true` / `false`; CSV only. | | `MARKETDATA_USE_HUMAN_READABLE` | `DefaultHuman` | `null` | `true` / `false`; CSV only. | | `MARKETDATA_LOGGING_LEVEL` | `MinimumLogLevel` | `Information` | `DEBUG` / `INFO` / `WARNING` / `ERROR`. | | `MARKETDATA_OUTPUT_FORMAT` | `OutputFormat` | `null` | `json` / `csv`; advisory only. | Invalid values for any key throw a `FormatException` naming the offending key. --- # Stocks (C# SDK) Source: https://www.marketdata.app/docs/sdk/csharp/stocks/index.md The C#/.NET SDK from Market Data provides methods to streamline your use of the Stocks endpoints. These methods provide a typed interface over the underlying HTTP requests and responses; every method is asynchronous and accepts an optional `CancellationToken`. Reach the resource through `client.Stocks`. For CSV output, call the paired `*CsvAsync` method of the same name (for example `GetQuotesCsvAsync`). ## Stocks Endpoints --- # Stock Candles (C# SDK) Source: https://www.marketdata.app/docs/sdk/csharp/stocks/candles/index.md Retrieve historical OHLCV (open/high/low/close/volume) candles for a stock symbol. ## Making Requests Use `GetCandlesAsync` on the `Stocks` resource, passing a `StockResolution` and a symbol. For large intraday ranges the SDK automatically splits the request into year-sized chunks, fetches them concurrently, and merges the results into a single response (`IsComposite == true`, one entry in `Parts` per HTTP request). ```csharp // Scalar overload — the common parameters directly: Task GetCandlesAsync( StockResolution resolution, string symbol, DateOnly? date = null, DateOnly? from = null, DateOnly? to = null, int? countback = null, string? exchange = null, bool? extended = null, string? country = null, bool? adjustSplits = null, bool? adjustDividends = null, MarketDataRequestOptions? options = null, CancellationToken cancellationToken = default) // Request-object overload: Task GetCandlesAsync( StockCandlesRequest request, MarketDataRequestOptions? options = null, CancellationToken cancellationToken = default) ``` ### StockCandlesRequest ```csharp new StockCandlesRequest(StockResolution resolution, string symbol) { Date = DateOnly, // a single trading day From = DateOnly, // window start (inclusive) To = DateOnly, // window end (inclusive) Countback = int, // N candles back from `To` (or from today), instead of `From` Exchange = string, // disambiguate exchange Extended = bool, // include extended-hours bars (intraday) Country = string, // exchange country (ISO 3166, 2-letter) AdjustSplits = bool, // default: true for daily AdjustDividends = bool // default: true for daily } ``` The date window is validated before any HTTP call: `Date` is exclusive with `From`/`To`/`Countback`, and `Countback` cannot be combined with `From`. An invalid combination throws `ArgumentException`. ### StockResolution A value type for the candle interval: ```csharp StockResolution.Daily // also Weekly, Monthly, Yearly StockResolution.Minutes(5) // 5-minute bars StockResolution.Hours(1) // hourly bars StockResolution.Days(1) // also Weeks(n), Months(n), Years(n) StockResolution.Of("1H") // any raw wire token ``` #### Returns `StockCandlesResponse` wrapping `IReadOnlyList`: ```csharp public record StockCandle( DateTimeOffset? Time, // bar opening moment (America/New_York) decimal? Open, decimal? High, decimal? Low, decimal? Close, long? Volume); ``` ## Examples ```csharp using MarketDataApp; using MarketDataApp.Stocks; using var client = await MarketDataClient.CreateAsync(); // Daily candles over a date range. var today = DateOnly.FromDateTime(DateTime.Today); var candles = await client.Stocks.GetCandlesAsync( StockResolution.Daily, "AAPL", from: today.AddDays(-7), to: today); foreach (var bar in candles.Values) { Console.WriteLine(bar); // 2026-08-14 O=228.10 H=230.44 L=227.55 C=229.88 V=41203118 } // The last 10 sessions, using countback instead of a left edge. var lastTen = await client.Stocks.GetCandlesAsync(StockResolution.Daily, "AAPL", countback: 10); // A long intraday range: the SDK chunks it by year and merges the parts. var intraday = await client.Stocks.GetCandlesAsync( new StockCandlesRequest(StockResolution.Minutes(30), "AAPL") { From = today.AddYears(-2), To = today }); Console.WriteLine($"{intraday.Values.Count} bars from {intraday.Parts.Count} requests"); ``` For CSV output, call `client.Stocks.GetCandlesCsvAsync(...)` with the same parameters and read `.Csv`. See [Settings](https://www.marketdata.app/docs/sdk/csharp/settings#csv-output). --- # Earnings (C# SDK) Source: https://www.marketdata.app/docs/sdk/csharp/stocks/earnings/index.md Retrieve historical and upcoming earnings reports for a stock symbol: fiscal period, report date and time, and reported vs. estimated EPS. ## Making Requests Use `GetEarningsAsync` on the `Stocks` resource. ```csharp Task GetEarningsAsync( string symbol, DateOnly? date = null, DateOnly? from = null, DateOnly? to = null, int? countback = null, string? report = null, MarketDataRequestOptions? options = null, CancellationToken cancellationToken = default) Task GetEarningsAsync(StockEarningsRequest request, ...) ``` ### StockEarningsRequest ```csharp new StockEarningsRequest(string symbol) { Date = DateOnly, // a single date From = DateOnly, // window start To = DateOnly, // window end Countback = int, // N most recent reports Report = string // a specific report, e.g. "2024-Q1" } ``` The same date-window rules as [candles](https://www.marketdata.app/docs/sdk/csharp/stocks/candles) apply and are validated before any HTTP call. #### Returns `StockEarningsResponse` wrapping `IReadOnlyList`: ```csharp public record StockEarning( string? Symbol, int? FiscalYear, int? FiscalQuarter, DateTimeOffset? Date, DateTimeOffset? ReportDate, string? ReportTime, // e.g. "before market open" / "after market close" string? Currency, decimal? ReportedEps, decimal? EstimatedEps, decimal? SurpriseEps, double? SurpriseEpsPct, DateTimeOffset? Updated); ``` ## Examples ```csharp using MarketDataApp; using var client = await MarketDataClient.CreateAsync(); // The four most recent reports. var earnings = await client.Stocks.GetEarningsAsync("AAPL", countback: 4); foreach (var report in earnings.Values) { Console.WriteLine( $"FY{report.FiscalYear} Q{report.FiscalQuarter}: reported {report.ReportedEps} vs est. {report.EstimatedEps} " + $"({report.SurpriseEpsPct:P1} surprise) on {report.ReportDate:yyyy-MM-dd}"); } ``` For CSV output, call `client.Stocks.GetEarningsCsvAsync(...)` and read `.Csv`. See [Settings](https://www.marketdata.app/docs/sdk/csharp/settings#csv-output). --- # News (C# SDK) Source: https://www.marketdata.app/docs/sdk/csharp/stocks/news/index.md Retrieve news articles for a stock symbol. ## Making Requests Use `GetNewsAsync` on the `Stocks` resource. ```csharp Task GetNewsAsync( string symbol, DateOnly? date = null, DateOnly? from = null, DateOnly? to = null, int? countback = null, MarketDataRequestOptions? options = null, CancellationToken cancellationToken = default) Task GetNewsAsync(StockNewsRequest request, ...) ``` ### StockNewsRequest ```csharp new StockNewsRequest(string symbol) { Date = DateOnly, // a single date From = DateOnly, // window start To = DateOnly, // window end Countback = int // N most recent articles } ``` The same date-window rules as [candles](https://www.marketdata.app/docs/sdk/csharp/stocks/candles) apply and are validated before any HTTP call. #### Returns `StockNewsResponse` wrapping `IReadOnlyList`: ```csharp public record StockNewsArticle( string Symbol, string Headline, string Content, string Source, DateTimeOffset PublicationDate); // America/New_York ``` ## Examples ```csharp using MarketDataApp; using var client = await MarketDataClient.CreateAsync(); var news = await client.Stocks.GetNewsAsync("AAPL", countback: 5); foreach (var article in news.Values) { Console.WriteLine($"[{article.PublicationDate:yyyy-MM-dd}] {article.Source}: {article.Headline}"); } ``` For CSV output, call `client.Stocks.GetNewsCsvAsync(...)` and read `.Csv`. See [Settings](https://www.marketdata.app/docs/sdk/csharp/settings#csv-output). --- # Prices (C# SDK) Source: https://www.marketdata.app/docs/sdk/csharp/stocks/prices/index.md Retrieve the latest price (mid, change) for one or more stock symbols — a lighter payload than a full quote. ## Making Requests The `Stocks` resource offers `GetPriceAsync` for a single symbol and `GetPricesAsync` for several symbols in one request. ```csharp Task GetPriceAsync( string symbol, MarketDataRequestOptions? options = null, CancellationToken cancellationToken = default) Task GetPriceAsync(StockPriceRequest request, ...) Task GetPricesAsync( string[] symbols, MarketDataRequestOptions? options = null, CancellationToken cancellationToken = default) Task GetPricesAsync(StockPricesRequest request, ...) ``` ### Request types ```csharp new StockPriceRequest(string symbol) new StockPricesRequest(params string[] symbols) // or any IEnumerable ``` #### Returns `StockPricesResponse` wrapping `IReadOnlyList`: ```csharp public record StockPrice( string? Symbol, decimal? Mid, decimal? Change, double? ChangePct, // a fraction: 0.0123 == +1.23% DateTimeOffset? Updated); // America/New_York ``` ## Examples ```csharp using MarketDataApp; using var client = await MarketDataClient.CreateAsync(); var prices = await client.Stocks.GetPricesAsync(["AAPL", "MSFT", "GOOG"]); foreach (var price in prices.Values) { Console.WriteLine($"{price.Symbol}: {price.Mid} ({price.ChangePct:P2})"); } ``` For CSV output, call `client.Stocks.GetPriceCsvAsync(...)` or `GetPricesCsvAsync(...)` and read `.Csv`. See [Settings](https://www.marketdata.app/docs/sdk/csharp/settings#csv-output). --- # Stock Quotes (C# SDK) Source: https://www.marketdata.app/docs/sdk/csharp/stocks/quotes/index.md Retrieve real-time quotes (bid, ask, mid, last, volume, etc.) for one or more stock symbols. ## Making Requests The `Stocks` resource offers two quote methods: - `GetQuoteAsync(...)` — a single symbol. - `GetQuotesAsync(...)` — several symbols in **one** request (the stocks backend batches a comma list). The result is a single response with one row per symbol. ```csharp // Single symbol Task GetQuoteAsync( string symbol, bool? extended = null, bool? candle = null, bool? week52 = null, MarketDataRequestOptions? options = null, CancellationToken cancellationToken = default) Task GetQuoteAsync(StockQuoteRequest request, ...) // Multiple symbols, one request Task GetQuotesAsync( string[] symbols, bool? extended = null, bool? candle = null, bool? week52 = null, MarketDataRequestOptions? options = null, CancellationToken cancellationToken = default) Task GetQuotesAsync(StockQuotesRequest request, ...) ``` ### Request types ```csharp // Single symbol new StockQuoteRequest(string symbol) { Extended = bool, // include extended-session prices Candle = bool, // add OHLC columns Week52 = bool // add 52-week high/low } // Multiple symbols, one request new StockQuotesRequest(params string[] symbols) // or any IEnumerable { Extended = bool, Candle = bool, Week52 = bool } ``` #### Returns `StockQuotesResponse` wrapping `IReadOnlyList` (one element for `GetQuoteAsync`, one per symbol for `GetQuotesAsync`). All fields are nullable: `Columns` can project any field away, and the backend maps NaN to `null` for closed or illiquid markets. ```csharp public record StockQuote( string? Symbol, decimal? Ask, long? AskSize, decimal? Bid, long? BidSize, decimal? Mid, decimal? Last, decimal? Change, double? ChangePct, // ChangePct is a fraction: 0.0123 == +1.23% long? Volume, DateTimeOffset? Updated, // Updated is America/New_York // opt-in via Candle = true: decimal? Open, decimal? High, decimal? Low, decimal? Close, // opt-in via Week52 = true: decimal? Week52High, decimal? Week52Low); ``` ## Examples ```csharp using MarketDataApp; using MarketDataApp.Stocks; using var client = await MarketDataClient.CreateAsync(); // A single quote — the response is a list; a single symbol is row 0. var quote = (await client.Stocks.GetQuoteAsync("AAPL")).Values[0]; Console.WriteLine($"{quote.Symbol}: bid={quote.Bid} ask={quote.Ask} last={quote.Last}"); // Several symbols in one request, with the 52-week range opted in. var quotes = await client.Stocks.GetQuotesAsync(["AAPL", "MSFT", "GOOG"], week52: true); foreach (var row in quotes.Values) { Console.WriteLine($"{row.Symbol}: last={row.Last} 52w {row.Week52Low}–{row.Week52High}"); } // The same call with a request object, grouping the optional flags. var detailed = await client.Stocks.GetQuotesAsync( new StockQuotesRequest("AAPL", "MSFT") { Candle = true, Extended = true }); ``` For CSV output, call `client.Stocks.GetQuoteCsvAsync(...)` or `GetQuotesCsvAsync(...)` and read `.Csv`. See [Settings](https://www.marketdata.app/docs/sdk/csharp/settings#csv-output). --- # Utilities (C# SDK) Source: https://www.marketdata.app/docs/sdk/csharp/utilities/index.md The C#/.NET SDK from Market Data provides methods for the Utilities endpoints: API service status, request-header inspection, and authenticated-user information. These are diagnostic endpoints, so they take no request parameters beyond a `CancellationToken` and have no CSV variants. Reach the resource through `client.Utilities`. ## Utilities Endpoints --- # Headers (C# SDK) Source: https://www.marketdata.app/docs/sdk/csharp/utilities/headers/index.md Echo back the HTTP request headers the API received. Useful for debugging what your application (or a proxy in between) is actually sending — user agent, authorization scheme, content negotiation. ## Making Requests Use `GetHeadersAsync` on the `Utilities` resource. ```csharp Task GetHeadersAsync(CancellationToken cancellationToken = default) ``` ### Returns `UtilitiesHeadersResponse` wrapping `IReadOnlyDictionary` — header name to value, as received by the API. The `Authorization` value is returned redacted by the API. ## Examples ```csharp using MarketDataApp; using var client = await MarketDataClient.CreateAsync(); var headers = await client.Utilities.GetHeadersAsync(); foreach (var (name, value) in headers.Values) { Console.WriteLine($"{name}: {value}"); } ``` --- # API Status (C# SDK) Source: https://www.marketdata.app/docs/sdk/csharp/utilities/status/index.md Retrieve the current operational status of the Market Data API services, with 30- and 90-day uptime. This is a public endpoint that works in demo mode. ## Making Requests Use `GetStatusAsync` on the `Utilities` resource. ```csharp Task GetStatusAsync(CancellationToken cancellationToken = default) ``` ### Returns `UtilitiesStatusResponse` wrapping `IReadOnlyList` — one row per service: ```csharp public record ServiceStatus( string Service, string Status, bool Online, double UptimePct30d, double UptimePct90d, DateTimeOffset Updated); ``` The SDK also uses this endpoint internally: before retrying a server error it consults a cached `/status/` reading and skips the retry when the API reports itself offline. Calling `GetStatusAsync` yourself refreshes that same cache. ## Examples ```csharp using MarketDataApp; using var client = await MarketDataClient.CreateAsync(); var status = await client.Utilities.GetStatusAsync(); foreach (var service in status.Values) { Console.WriteLine($"{service.Service}: {service.Status} ({service.UptimePct30d:F2}% over 30d)"); } Console.WriteLine($"{status.Values.Count(s => s.Online)}/{status.Values.Count} services online"); ``` --- # User (C# SDK) Source: https://www.marketdata.app/docs/sdk/csharp/utilities/user/index.md Retrieve the authenticated account's details: request quota and options-data entitlement. Requires a token. ## Making Requests Use `GetUserAsync` on the `Utilities` resource. ```csharp Task GetUserAsync(CancellationToken cancellationToken = default) ``` ### Returns `UtilitiesUserResponse` wrapping a single `User` record: ```csharp public record User( int RequestsRemaining, // requests left in the current billing period int RequestsLimit, // total quota for the billing period string OptionsDataPermissions); // "" == real-time; otherwise describes the delay ``` > [!NOTE] > **[Rate-limit state]** > > This endpoint is also what the client calls at startup to validate your token. The rate-limit numbers the SDK tracks — including the reset time and consumed count — come from the `x-api-ratelimit-*` response headers, not from this body, and surface through [`client.LatestRateLimit`](https://www.marketdata.app/docs/sdk/csharp/client#rate-limits) and `response.RateLimit`. `User` is the typed view of the account body only. ## Examples ```csharp using MarketDataApp; using var client = await MarketDataClient.CreateAsync(); var user = (await client.Utilities.GetUserAsync()).Values; Console.WriteLine($"Quota: {user.RequestsRemaining}/{user.RequestsLimit} remaining"); Console.WriteLine(string.IsNullOrEmpty(user.OptionsDataPermissions) ? "Options data: real-time" : $"Options data: {user.OptionsDataPermissions}"); ``` --- # Go SDK Source: https://www.marketdata.app/docs/sdk/go/index.md Welcome to the Market Data **Go SDK v2**. This is a complete, idiomatic rewrite of the SDK that gives you type-safe access to real-time and historical stock, option, fund, and market data with nothing but the Go standard library at runtime. ## Quick Start Create a client, make a call, and you are done. The client reads your token from the `MARKETDATA_TOKEN` environment variable (or a `.env` file) automatically, so no token needs to appear in your code: ```go package main import ( "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { // Reads MARKETDATA_TOKEN from the environment or a .env file. client, err := marketdata.NewClient() if err != nil { log.Fatal(err) } defer client.Close() quote, err := client.Stocks.GetQuote("AAPL") if err != nil { log.Fatal(err) } fmt.Printf("AAPL last: $%.2f bid: $%.2f ask: $%.2f\n", quote.Last, quote.Bid, quote.Ask) } ``` ``` AAPL last: $234.07 bid: $234.05 ask: $234.09 ``` Install it with `go get` (the import path includes the `/v2` suffix): ```bash go get github.com/MarketDataApp/sdk-go/v2 ``` The SDK requires **Go 1.22 or later** and has **zero third-party runtime dependencies**. ## Open Source The SDK is open source under the MIT license and is available on GitHub. Feel free to contribute to the project, report bugs, or request new features. - [Go SDK GitHub](https://github.com/MarketDataApp/sdk-go/) - [Go SDK on pkg.go.dev](https://pkg.go.dev/github.com/MarketDataApp/sdk-go/v2) ## 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](https://www.marketdata.app/docs/sdk/go/installation) into your Go module. 2. Set up your [authentication token](https://www.marketdata.app/docs/sdk/go/authentication) to access the API. 3. Learn about the [client](https://www.marketdata.app/docs/sdk/go/client) and how to make your first API requests. 4. Configure [parameters](https://www.marketdata.app/docs/sdk/go/parameters) to customize mode, columns, pagination, and other universal parameters. 5. Handle failures with the SDK's typed [errors](https://www.marketdata.app/docs/sdk/go/errors), and set up [logging](https://www.marketdata.app/docs/sdk/go/logging) for debugging. 6. Explore the available endpoints for [stocks](https://www.marketdata.app/docs/sdk/go/stocks), [options](https://www.marketdata.app/docs/sdk/go/options), [funds](https://www.marketdata.app/docs/sdk/go/funds), [markets](https://www.marketdata.app/docs/sdk/go/markets), and [utilities](https://www.marketdata.app/docs/sdk/go/utilities). ### 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-go/issues). Please only open issues if you find a bug. Use our helpdesk for general questions or implementation assistance. --- # Authentication (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/authentication/index.md The Market Data API authenticates every request with a **Bearer token**. You can obtain a token from the [Market Data Dashboard](https://www.marketdata.app/dashboard/). The Go SDK resolves your token from several sources so you never have to hard-code it in production. If no token is found, the client starts in **demo mode** with access limited to unauthenticated endpoints and free sample symbols (such as `AAPL`). ## Token Priority The SDK resolves the token in the following order, highest priority first: 1. **`WithToken(...)`** — an explicit token passed to `NewClient` 2. **`MARKETDATA_TOKEN`** — the process environment variable 3. **`.env` file** — a `MARKETDATA_TOKEN` entry loaded from a `.env` file in the working directory An explicit `WithToken` always wins over the environment, and a real environment variable always wins over a value loaded from `.env` (loading a `.env` file never overwrites variables already set in the process). > [!TIP] > In production, prefer the environment variable so your token is never committed with your source code. Reserve `WithToken` for local experiments and tests. ## Setting the Environment Variable (recommended) ### Mac / Linux Set the variable for the current shell session: ```bash export MARKETDATA_TOKEN="your_api_token" ``` To make it persistent, add that line to your shell profile (`~/.bashrc`, `~/.zshrc`, etc.), then reload it: ```bash source ~/.zshrc ``` Verify it is set: ```bash echo $MARKETDATA_TOKEN ``` ### Windows Using Command Prompt (persists for future sessions, not the current one): ```batch setx MARKETDATA_TOKEN "your_api_token" ``` Open a **new** Command Prompt and verify: ```batch echo %MARKETDATA_TOKEN% ``` With the variable set, create the client with no arguments and it picks up the token automatically: ```go client, err := marketdata.NewClient() ``` ## Using a `.env` File `NewClient` automatically loads a `.env` file from the current working directory. Create one in your project root: ```ini MARKETDATA_TOKEN=your_api_token ``` Values in `.env` are only applied when the corresponding variable is **not** already set in the real environment, so an exported `MARKETDATA_TOKEN` takes precedence. > [!WARNING] > Add `.env` to your `.gitignore` so your token never lands in version control. ## Passing the Token Explicitly You can supply the token directly with the `WithToken` option. This overrides both the environment variable and any `.env` value: ```go package main import ( "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient( marketdata.WithToken("your_api_token"), ) if err != nil { log.Fatal(err) } defer client.Close() } ``` ## Startup Validation When a token is present, `NewClient` validates it with a single synchronous request to the `/user/` endpoint. If the token is rejected, `NewClient` returns an error that wraps an [`AuthenticationError`](https://www.marketdata.app/docs/sdk/go/errors): ```go client, err := marketdata.NewClient(marketdata.WithToken("invalid-token")) if err != nil { var authErr *marketdata.AuthenticationError if errors.As(err, &authErr) { log.Fatalf("invalid API token: %v", authErr) } log.Fatal(err) } ``` If you want to skip this round trip (for example to shave startup latency), pass [`WithoutStartupValidation`](https://www.marketdata.app/docs/sdk/go/client). An invalid token then goes undetected until your first API call fails with an `AuthenticationError`. ## Testing Your Token Confirm your token works and inspect your current API credit usage with the Utilities service: ```go package main import ( "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient() if err != nil { log.Fatal(err) } defer client.Close() user, err := client.Utilities.GetUser() if err != nil { log.Fatal(err) } fmt.Printf("Credits remaining: %d / %d\n", user.CreditsRemaining, user.CreditLimit) fmt.Printf("Window resets at: %s\n", user.ResetAt) fmt.Printf("Options data: %s\n", user.OptionsDataPermissions) } ``` > [!TIP] > When you test authentication, use a symbol that requires it — such as `SPY` — rather than `AAPL`. `AAPL` is a free sample symbol and returns data even when your token is missing or invalid, so it will not reveal an auth problem. ## Security Notes - The SDK **never logs your full token**. In debug output it is redacted to its last four characters. See [Logging](https://www.marketdata.app/docs/sdk/go/logging). - The SDK **refuses to transmit your token over an insecure (non-HTTPS) connection** unless the host is a loopback address, guarding against accidental cleartext exposure. ## Next Steps With authentication configured, read the [Client](https://www.marketdata.app/docs/sdk/go/client) guide to learn how to construct clients and make requests. --- # Client (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/client/index.md The `marketdata.Client` is the entry point to the SDK. You create one with `NewClient`, configure it with functional options, and reach every endpoint through its five resource services. A `Client` is safe for concurrent use and caps itself at 50 in-flight requests; additional calls block until a slot frees. ## Creating a Client ```go func NewClient(opts ...Option) (*Client, error) ``` `NewClient` first loads a `.env` file from the working directory (without overriding real environment variables), resolves your token, and — when a token is present — validates it against the API. Always release the client with `Close` when you are done, typically via `defer`: ```go package main import ( "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { // Simplest form: reads MARKETDATA_TOKEN from the environment / .env. client, err := marketdata.NewClient() if err != nil { log.Fatal(err) } defer client.Close() } ``` ## Functional Options Pass any number of `Option` values to `NewClient`. Options are applied in order after defaults and environment variables, so a later option overrides an earlier one and any explicit option overrides the environment. | Option | Description | |----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **`WithToken(token string)`** | Sets the API token. Highest-priority token source; overrides `MARKETDATA_TOKEN` and `.env`. | | **`WithEnvironment(env Environment)`** | Selects the API environment: `marketdata.Production` (default), `marketdata.Test`, or `marketdata.Development`, each mapped to a base URL. | | **`WithBaseURL(url string)`** | Points the client at an arbitrary base URL (a proxy or mock server). Must be a valid `http`/`https` URL with a host, or `NewClient` returns a `ValidationError`. | | **`WithHTTPClient(c *http.Client)`** | Supplies a custom `*http.Client` (custom transport, proxy, instrumentation). The SDK's fixed timeouts still apply. | | **`WithLogger(l *slog.Logger)`** | Replaces `slog.Default()` as the logger. See [Logging](https://www.marketdata.app/docs/sdk/go/logging). | | **`WithDebug(enabled bool)`** | Enables verbose request/response debug logging. | | **`WithoutStartupValidation()`** | Skips the synchronous token-validation request during `NewClient`. | | **`WithMaxRetries(n int)`** | Sets the maximum retry attempts for failed requests (default 3; `0` disables retries). | | **`WithMode(m Mode)`** | Sets the universal `mode` parameter (`ModeLive`, `ModeCached`, `ModeDelayed`) on every request. | | **`WithMaxAge(maxAge string)`** | Sets the universal `maxage` parameter for cached mode (e.g. `"5min"`, `"1h"`). | | **`WithColumns(cols ...string)`** | Sets the universal `columns` parameter to trim the response. | | **`WithLimit(n int)`** | Sets the universal `limit` parameter (values ≤ 0 ignored). | | **`WithOffset(n int)`** | Sets the universal `offset` parameter for pagination (values ≤ 0 ignored). | | **`WithDateFormat(format string)`** | Sets the universal `dateformat` parameter. Advanced use — see [Parameters](https://www.marketdata.app/docs/sdk/go/parameters). | | **`WithHumanReadable(enabled bool)`** | Sets the universal `human` parameter. Advanced use — see [Parameters](https://www.marketdata.app/docs/sdk/go/parameters). | | **`WithAddHeaders(enabled bool)`** | Sets the universal `headers` parameter (affects CSV output only). | The universal-parameter options (`WithMode`, `WithMaxAge`, `WithColumns`, `WithLimit`, `WithOffset`, `WithDateFormat`, `WithHumanReadable`, `WithAddHeaders`) are documented in depth on the [Parameters](https://www.marketdata.app/docs/sdk/go/parameters) page. > [!NOTE] > **[Fixed timeouts and retry behavior]** > > Per-request timeout (99s) and connection dial timeout (2s) are **fixed and cannot be changed**. Only the retry *count* is configurable, via `WithMaxRetries`. Requests are retried only on 501–599 status codes and transient network errors, with exponential backoff starting at 1s and doubling each attempt. ### Example: a fully configured client ```go package main import ( "log" "log/slog" "os" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { logger := slog.New(slog.NewTextHandler(os.Stderr, nil)) client, err := marketdata.NewClient( marketdata.WithToken("your-token"), marketdata.WithEnvironment(marketdata.Production), marketdata.WithLogger(logger), marketdata.WithMaxRetries(5), marketdata.WithMode(marketdata.ModeCached), ) if err != nil { log.Fatal(err) } defer client.Close() } ``` ## Resource Services The client exposes five services, one per API resource: | Field | Type | Description | |--------------------|----------------------|------------------------------------------------------------------| | `client.Stocks` | `*stocks.Service` | Stock quotes, candles, bulk candles, prices, earnings, and news. | | `client.Options` | `*options.Service` | Option chains, expirations, quotes, and symbol lookup. | | `client.Funds` | `*funds.Service` | Mutual fund candles. | | `client.Markets` | `*markets.Service` | Market status (open/closed) and status history. | | `client.Utilities` | `*utilities.Service` | API status, response-header echo, and account/user details. | ```go quote, err := client.Stocks.GetQuote("AAPL") chain, err := client.Options.GetChain("AAPL") status, err := client.Markets.GetStatus() user, err := client.Utilities.GetUser() ``` ## Two Call Styles Every endpoint method comes in two forms. Choose based on whether you need a `context.Context` and the raw response metadata. | Method form | Signature shape | When to use | |------------------------|------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------| | **`Get(...)`** | `(T, error)` | Convenience form. Uses a background context. Best for scripts and simple calls. | | **`(ctx, ...)`** | `(T, *marketdata.Response, error)` | Context-aware form. Also returns the raw `*marketdata.Response` carrying HTTP status, headers, and per-request rate-limit metadata. | The `*marketdata.Response` returned by the context form embeds `*http.Response` and adds a `NoData` flag and a `RateLimit` field. (`marketdata.Response` is an exported alias for the SDK's response type, so you can name it in your own code.) ### Get (simple) ```go package main import ( "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient() if err != nil { log.Fatal(err) } defer client.Close() quote, err := client.Stocks.GetQuote("AAPL") if err != nil { log.Fatal(err) } fmt.Printf("AAPL last: $%.2f\n", quote.Last) } ``` ### Context + Response ```go package main import ( "context" "fmt" "log" "time" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient() if err != nil { log.Fatal(err) } defer client.Close() ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() quote, resp, err := client.Stocks.Quote(ctx, "AAPL") if err != nil { log.Fatal(err) } fmt.Printf("AAPL last: $%.2f\n", quote.Last) fmt.Printf("HTTP status: %s\n", resp.Status) fmt.Printf("Credits remaining: %d\n", resp.RateLimit.Remaining) fmt.Printf("No data: %t\n", resp.NoData) } ``` ## No-Data Responses A `404` (no data available for a valid request) or a `204` (a `mode=cached` cache miss) is **not** an error. The context form returns a nil result, a nil error, and a `*marketdata.Response` whose `NoData` field is `true`. Always check `NoData` (or a nil result) before dereferencing: ```go quote, resp, err := client.Stocks.Quote(ctx, "AAPL") if err != nil { log.Fatal(err) } if resp.NoData { fmt.Println("no quote data available") return } fmt.Printf("AAPL last: $%.2f\n", quote.Last) ``` ## Client Methods Besides the resource services, the client provides a few helper methods: | Method | Description | |-----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| | **`Close() error`** | Releases resources by closing idle HTTP connections. Safe to call multiple times; always returns `nil`. Do not reuse a client after `Close`. | | **`RateLimits() RateLimitState`** | Returns a client-level snapshot of rate-limit state from the most recently completed request. For exact, request-scoped values, read `resp.RateLimit` instead. | | **`DemoMode() bool`** | Reports whether the client is running in demo mode because no token was provided. | | **`Debug(enabled bool)`** | Turns debug logging on or off at runtime (equivalent to `WithDebug`). | ```go if client.DemoMode() { fmt.Println("running with limited demo access") } rl := client.RateLimits() fmt.Printf("credits: %d/%d, resets %s\n", rl.Remaining, rl.Limit, rl.ResetAt) ``` ## Next Steps - Configure [Parameters](https://www.marketdata.app/docs/sdk/go/parameters) to control mode, columns, and pagination. - Learn the [Error](https://www.marketdata.app/docs/sdk/go/errors) types and how to inspect them. - Set up [Logging](https://www.marketdata.app/docs/sdk/go/logging) for debugging. --- # Error Handling (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/errors/index.md The Go SDK returns typed errors that you inspect with the standard library's `errors.Is` and `errors.As`. Each API failure maps to a specific error type carrying request details for troubleshooting, and each type matches a sentinel value for quick classification. ## The `Error` Interface Every SDK error (except a couple of primitive ones noted below) implements `marketdata.Error`: ```go type Error interface { error Unwrap() error // Retryable reports whether the operation is safe to retry. Retryable() bool // SupportInfo formats the request details for a support ticket. SupportInfo() string } ``` Most typed errors embed a `SupportContext` with the fields that identify the failed request: ```go type SupportContext struct { RequestID string // the cf-ray response header RequestURL string // the full request URL StatusCode int // the HTTP status code Timestamp time.Time // when the error occurred (US/Eastern) Message string // the error description ExceptionType string // the error type name } ``` `SupportContext.SupportInfo()` returns a ready-to-paste block for [Market Data support tickets](https://www.marketdata.app/dashboard/). ## Error Types | HTTP status | Error type | Sentinel | Retried by SDK | |----------------------------|------------------------|----------------------|-------------------------------| | 400 | `BadRequestError` | `ErrBadRequest` | no | | 401 | `AuthenticationError` | `ErrAuthentication` | no | | 402 | `PaymentRequiredError` | `ErrPaymentRequired` | no | | 403 | `ForbiddenError` | `ErrForbidden` | no | | 404 | `NotFoundError` | `ErrNotFound` | no | | 413 | `PayloadTooLargeError` | `ErrPayloadTooLarge` | no | | 429 | `RateLimitError` | `ErrRateLimited` | no | | 500 | `InternalError` | `ErrInternal` | no | | 501–599 | `ServerError` | `ErrServer` | **yes** (exponential backoff) | | — (connection/timeout) | `NetworkError` | — | **yes** | | — (client-side validation) | `ValidationError` | `ErrInvalidRequest` | no | | — (body cannot be decoded) | `ParseError` | — | no | All of these types are exported from the `marketdata` package (they are aliases for the SDK's internal error types), so you refer to them as `marketdata.RateLimitError`, `marketdata.AuthenticationError`, and so on. > [!NOTE] > **[404 is not always an error]** > > A `404` that means "no data for this valid request" is **not** returned as an error. It is surfaced through the `NoData` field of the [`*marketdata.Response`](https://www.marketdata.app/docs/sdk/go/client#no-data-responses). A `NotFoundError` is only returned for a genuinely missing resource. ### Types with extra fields Some error types carry additional context beyond `SupportContext`: - **`RateLimitError`** — `Limit`, `Remaining`, `ResetAt`, and a `WaitDuration()` method returning how long to wait before retrying. - **`ForbiddenError`** — `AuthorizedIP`, `BlockedIP`, and `TroubleshootingGuide` (a 403 usually means your IP changed and access was temporarily blocked). - **`ValidationError`** — `Field` and `Message` identify the offending input. Because no request was made, its `SupportInfo()` returns an empty string. ### Primitive errors - **`APIError`** — returned when the HTTP status was successful but the response body reported an error (for example a status field other than `"ok"`). It carries only a `Message`. ## Classifying Errors Use `errors.As` when you need a typed error's fields, and `errors.Is` when you only need to know the category. ```go package main import ( "context" "errors" "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient() if err != nil { log.Fatal(err) } defer client.Close() quote, _, err := client.Stocks.Quote(context.Background(), "AAPL") if err != nil { handleError(err) return } fmt.Printf("AAPL last: $%.2f\n", quote.Last) } func handleError(err error) { var rateErr *marketdata.RateLimitError var authErr *marketdata.AuthenticationError var forbiddenErr *marketdata.ForbiddenError switch { case errors.As(err, &rateErr): fmt.Printf("rate limited; wait %s before retrying\n", rateErr.WaitDuration()) case errors.As(err, &authErr): fmt.Println("authentication failed. Support info:") fmt.Println(authErr.SupportInfo()) case errors.As(err, &forbiddenErr): fmt.Printf("IP %s is blocked; the authorized IP is %s\n", forbiddenErr.BlockedIP, forbiddenErr.AuthorizedIP) case errors.Is(err, marketdata.ErrBadRequest): fmt.Println("bad request:", err) default: fmt.Println("request failed:", err) } } ``` ## Sentinels with `errors.Is` When you only need to know the category, match the sentinel directly: ```go if errors.Is(err, marketdata.ErrNotFound) { // the requested resource does not exist } if errors.Is(err, marketdata.ErrPaymentRequired) { // your plan does not include this data — upgrade required } ``` The full set of sentinels: `ErrBadRequest`, `ErrAuthentication`, `ErrPaymentRequired`, `ErrForbidden`, `ErrNotFound`, `ErrPayloadTooLarge`, `ErrRateLimited`, `ErrInternal`, `ErrServer`, and `ErrInvalidRequest`. ## Deciding Whether to Retry The SDK already retries transient failures (`ServerError` and `NetworkError`) automatically before returning, so a returned error usually means retrying will not help. If you implement your own retry policy, ask the error whether it is safe to retry: ```go var sdkErr marketdata.Error if errors.As(err, &sdkErr) && sdkErr.Retryable() { // transient — safe to retry after a backoff } ``` ## Support Tickets Any error that embeds `SupportContext` can produce a support block. Include it when you open a ticket so the team can trace the exact request: ```go var apiErr *marketdata.InternalError if errors.As(err, &apiErr) { fmt.Println(apiErr.SupportInfo()) } ``` ``` --- MARKET DATA SUPPORT INFO --- request_id: 8a1b2c3d4e5f6a7b request_url: https://api.marketdata.app/v1/stocks/quotes/?symbols=AAPL status_code: 500 timestamp: 2026-07-12 09:30:00 message: internal server error exception_type: InternalError -------------------------------- ``` ## Next Steps - Review how [Logging](https://www.marketdata.app/docs/sdk/go/logging) records errors and redacts your token. - See the [Client](https://www.marketdata.app/docs/sdk/go/client) page for how no-data responses differ from errors. --- # Funds (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/funds/index.md Access historical net asset value (NAV) candles for mutual funds. The Go SDK exposes these endpoints through the `client.Funds` service. ## Mutual Funds Endpoints --- # Fund Candles (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/funds/candles/index.md Retrieve historical OHLC candles for a mutual fund. Each candle reports the fund's net asset value (NAV) for one period, with its `Time` normalized to US Eastern. ## Making Requests Fetch fund candles with the `Candles` method on the `client.Funds` service. It comes in two call styles: | Method | Return | Description | |-------------------------------------|-----------------------------------------------|------------------------------------------------------------------------| | **`GetCandles(symbol, ...opts)`** | `([]funds.Candle, error)` | Convenience; uses a background context and discards response metadata. | | **`Candles(ctx, symbol, ...opts)`** | `([]funds.Candle, *response.Response, error)` | Context-aware; also returns the raw response plus rate-limit metadata. | ## Candles ```go func (s *Service) Candles(ctx context.Context, symbol string, opts ...funds.CandleOption) ([]funds.Candle, *response.Response, error) func (s *Service) GetCandles(symbol string, opts ...funds.CandleOption) ([]funds.Candle, error) ``` Get historical OHLC candles for a mutual fund from the `/v1/funds/candles/{resolution}/{symbol}/` endpoint. Dates are sent to the API in `YYYY-MM-DD` form, so any time-of-day component is ignored. ### Parameters - `ctx` (`context.Context`) — request context for cancellation and deadlines (context method only). - `symbol` (`string`) — the fund's ticker, for example `"VFINX"`. Required; an empty symbol returns a validation error without making a request. - Options: - `funds.WithResolution(r funds.Resolution)` — the candle timeframe. Defaults to `funds.ResolutionDaily`. The funds endpoint supports `funds.ResolutionDaily` (`"D"`), `funds.ResolutionWeekly` (`"W"`), `funds.ResolutionMonthly` (`"M"`), and `funds.ResolutionYearly` (`"Y"`) only. The resolution becomes part of the request path, so an unsupported value causes the API to reject the request. - `funds.WithCandleWindow(w funds.DateWindow)` — the date range, expressed as a single `funds.DateWindow` value. Omitting it lets the API return its default recent window. ### Returns - `[]funds.Candle` — one [`Candle`](#candle) per period, oldest first. - `*response.Response` — raw response plus rate-limit metadata (context method only). - `error` — non-nil on request or decoding failure. A missing symbol returns a `*marketdata.ValidationError`. ### Notes - Mutual funds price once per day at market close, so daily resolution is the most common choice. - When the API responds `404` because no data exists for the requested symbol or range, both call styles return a `nil` slice and a `nil` error. With the context method, the returned `*response.Response` has its `NoData` field set to `true`. - The date parameters (`date`, `from`, `to`, `countback`) are mutually exclusive by construction: a `DateWindow` is a single sealed value, so an illegal pairing such as "from plus countback" cannot be expressed. ### Get (simple) ```go package main import ( "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" "github.com/MarketDataApp/sdk-go/v2/marketdata/funds" ) func main() { client, err := marketdata.NewClient() // reads MARKETDATA_TOKEN from env / .env if err != nil { log.Fatal(err) } defer client.Close() // The 5 most recent daily candles for the Vanguard 500 Index Fund. candles, err := client.Funds.GetCandles("VFINX", funds.WithCandleWindow(funds.LastN(5)), ) if err != nil { log.Fatal(err) } for _, candle := range candles { fmt.Println(candle) } } ``` **Output** ``` 2023-01-03 O: 352.76 H: 352.76 L: 352.76 C: 352.76 2023-01-04 O: 355.43 H: 355.43 L: 355.43 C: 355.43 2023-01-05 O: 351.35 H: 351.35 L: 351.35 C: 351.35 2023-01-06 O: 359.38 H: 359.38 L: 359.38 C: 359.38 ``` ### Context + Response ```go package main import ( "context" "fmt" "log" "time" "github.com/MarketDataApp/sdk-go/v2/marketdata" "github.com/MarketDataApp/sdk-go/v2/marketdata/funds" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() from := time.Date(2023, time.January, 1, 0, 0, 0, 0, time.UTC) to := time.Date(2023, time.January, 6, 0, 0, 0, 0, time.UTC) candles, resp, err := client.Funds.Candles(context.Background(), "VFINX", funds.WithResolution(funds.ResolutionDaily), funds.WithCandleWindow(funds.Between(from, to)), ) if err != nil { log.Fatal(err) } if resp.NoData { fmt.Println("No data for the requested range.") return } for _, candle := range candles { fmt.Println(candle) } // The Response exposes request-scoped rate-limit metadata. fmt.Printf("Credits remaining: %d\n", resp.RateLimit.Remaining) } ``` **Output** ``` 2023-01-03 O: 352.76 H: 352.76 L: 352.76 C: 352.76 2023-01-04 O: 355.43 H: 355.43 L: 355.43 C: 355.43 2023-01-05 O: 351.35 H: 351.35 L: 351.35 C: 351.35 2023-01-06 O: 359.38 H: 359.38 L: 359.38 C: 359.38 Credits remaining: 99847 ``` ### Date windows ```go // Every DateWindow constructor is a sealed value; pass exactly one to WithCandleWindow. funds.OnDate(day) // a single calendar day -> date=day funds.Between(from, to) // an explicit closed range -> from=from&to=to funds.Since(from) // everything since a day -> from=from funds.Until(to) // up to and including a day -> to=to funds.LastN(n) // the n most recent periods -> countback=n funds.LastNUntil(n, to) // the n periods ending at a day -> countback=n&to=to // Example: the 12 most recent monthly candles. candles, err := client.Funds.GetCandles("VFINX", funds.WithResolution(funds.ResolutionMonthly), funds.WithCandleWindow(funds.LastN(12)), ) ``` ## Candle ```go type Candle struct { Time time.Time `json:"t"` // Time is the candle timestamp (US Eastern). Open float64 `json:"o"` // Open is the opening price (NAV). High float64 `json:"h"` // High is the highest price. Low float64 `json:"l"` // Low is the lowest price. Close float64 `json:"c"` // Close is the closing price (NAV). } ``` `Candle` represents an OHLC candle for a fund. Because mutual funds report a single NAV per period, the open, high, low, and close are typically identical. ### Fields - `Time` (`time.Time`) — the candle timestamp, normalized to US Eastern. - `Open` (`float64`) — the opening NAV. - `High` (`float64`) — the highest price during the period. - `Low` (`float64`) — the lowest price during the period. - `Close` (`float64`) — the closing NAV. ### Methods - `String() string` — a concise one-line summary, e.g. `2023-01-03 O: 352.76 H: 352.76 L: 352.76 C: 352.76`. - `Range() float64` — the high-low range (`High - Low`). > [!NOTE] > **[Zero values and null data]** > > Numeric fields are value types, not pointers. When the API returns `null` for a field, it decodes as the zero value, so a `0` may represent either an actual zero or the absence of data. ## Resolution ```go type Resolution string const ( ResolutionDaily Resolution = "D" ResolutionWeekly Resolution = "W" ResolutionMonthly Resolution = "M" ResolutionYearly Resolution = "Y" ) ``` `Resolution` selects the candle timeframe passed to [`WithResolution`](#candles). The funds candles endpoint supports daily, weekly, monthly, and yearly only (quarterly is not accepted). `Resolution` has a `String()` method that returns the underlying code. --- # Installation (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/installation/index.md The Market Data Go SDK is installed with the standard `go get` command. It is a normal Go module with a `/v2` major-version suffix on its import path. ## Requirements - **Go 1.22 or later** (the module declares `go 1.22.5`) - No third-party runtime dependencies — the SDK is built entirely on the Go standard library ## Installing with `go get` From inside your Go module, run: ```bash go get github.com/MarketDataApp/sdk-go/v2 ``` This adds the SDK to your `go.mod` and downloads it into the module cache. > [!NOTE] > **[Version suffix matters]** > > Because this is a `/v2` module, the import path **must** include `/v2`. Dropping it would resolve to the older v1 SDK, which has a different (fluent-builder) API. ## Import Paths The public API lives in the `marketdata` package, and each resource has its own sub-package for its functional options and helper constructors: ```go import ( "github.com/MarketDataApp/sdk-go/v2/marketdata" // Import a resource package only when you use its options or helpers: "github.com/MarketDataApp/sdk-go/v2/marketdata/stocks" "github.com/MarketDataApp/sdk-go/v2/marketdata/options" "github.com/MarketDataApp/sdk-go/v2/marketdata/funds" "github.com/MarketDataApp/sdk-go/v2/marketdata/markets" "github.com/MarketDataApp/sdk-go/v2/marketdata/utilities" ) ``` You always import `marketdata` (for `NewClient` and the client-level options). You only need a resource package such as `stocks` when you pass one of its functional options, for example `stocks.WithResolution(...)`. ## Verifying the Installation Create a small program to confirm everything compiles and the SDK can reach the API. The `/status/` endpoint is public, so this works even before you configure a token: ```go package main import ( "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient() if err != nil { log.Fatal(err) } defer client.Close() status, err := client.Utilities.GetStatus() if err != nil { log.Fatal(err) } fmt.Println(status) } ``` Run it: ```bash go run . ``` ``` API online (30d: 99.99% 90d: 99.98%) Updated: 2026-07-12 09:30:00 ``` If you see the status line, the SDK is installed correctly. ## Updating the SDK To move to the latest release: ```bash go get -u github.com/MarketDataApp/sdk-go/v2 go mod tidy ``` ## Next Steps After installing, [set up your authentication token](https://www.marketdata.app/docs/sdk/go/authentication) so you can call the endpoints that require a paid or trial plan. --- # Logging (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/logging/index.md The Go SDK logs through the standard library's [`log/slog`](https://pkg.go.dev/log/slog) package. By default it uses `slog.Default()`, so SDK log records flow into whatever logger your application already configures. You can supply your own `*slog.Logger`, raise the verbosity for debugging, and rely on the SDK never printing your full API token. ## Default Behavior Out of the box the SDK logs at `slog.Default()`'s level. Setting the `MARKETDATA_LOGGING_LEVEL` environment variable makes the SDK install its own text handler (writing to stderr) at the level you choose: ```bash export MARKETDATA_LOGGING_LEVEL=DEBUG ``` Recognized values (case-insensitive): `DEBUG`, `INFO`, `WARNING` (or `WARN`), and `ERROR`. Setting it to `DEBUG` also turns on the SDK's verbose request/response debug logging automatically. ## Enabling Debug Logging In code, enable verbose logging with the `WithDebug` option, or toggle it at runtime with `client.Debug(...)`: ```go package main import ( "log/slog" "os" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ Level: slog.LevelDebug, })) client, err := marketdata.NewClient( marketdata.WithToken("your-token"), marketdata.WithLogger(logger), marketdata.WithDebug(true), ) if err != nil { logger.Error("client init failed", "err", err) os.Exit(1) } defer client.Close() // Turn debug logging off once you are past the section you are diagnosing. client.Debug(false) } ``` - **`WithLogger(l *slog.Logger)`** replaces `slog.Default()` for this client. - **`WithDebug(true)`** enables verbose request/response logging. - **`client.Debug(enabled bool)`** toggles debug logging at runtime. ## Log Levels The SDK emits records at standard `slog` levels: | Level | What the SDK logs | |-------------------|------------------------------------------------------------------------------------------| | `slog.LevelDebug` | Request URLs and parameters, response status, retry attempts, the redacted token in use. | | `slog.LevelInfo` | Normal operational messages. | | `slog.LevelWarn` | Demo-mode startup warning; transient-failure retries. | | `slog.LevelError` | Errors surfaced by the SDK. | ## Choosing a Handler Because the SDK uses `slog`, you have full control over formatting and destination through the handler you build. ### Text (stderr) ```go logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ Level: slog.LevelInfo, })) client, err := marketdata.NewClient(marketdata.WithLogger(logger)) ``` ### JSON (structured) ```go logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ Level: slog.LevelDebug, })) client, err := marketdata.NewClient(marketdata.WithLogger(logger)) ``` ## Token Redaction The SDK **never logs your full API token**. When debug logging is enabled and a token is present, the SDK logs the token redacted to its last four characters, with everything before them replaced by asterisks: ``` level=DEBUG msg="using API token" token=****************************abcd ``` This redaction applies everywhere the token would otherwise appear in output, so debug logs are safe to share in a support ticket. (The SDK additionally refuses to send the token at all over a non-HTTPS connection to a non-loopback host.) ## Disabling Logging To silence the SDK, pass a logger whose handler discards records or sits at a high level: ```go // Discard everything. logger := slog.New(slog.NewTextHandler(io.Discard, nil)) client, err := marketdata.NewClient(marketdata.WithLogger(logger)) ``` ## Production Recommendations - Use `slog.LevelWarn` or `slog.LevelError` in production to keep log volume down. - Reserve `slog.LevelDebug` for local development or active troubleshooting. - Prefer the JSON handler when shipping logs to an aggregation system. ## Next Steps - See [Error Handling](https://www.marketdata.app/docs/sdk/go/errors) for the structured error context that appears in error logs. - Review the [Client](https://www.marketdata.app/docs/sdk/go/client) options, including `WithLogger` and `WithDebug`. --- # Markets (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/markets/index.md Check whether a market was open, closed, or closed early on any trading day. The Go SDK exposes these endpoints through the `client.Markets` service. ## Markets Endpoints --- # Status History Source: https://www.marketdata.app/docs/sdk/go/markets/status-history/index.md Retrieve the market status for each day in a date range, returning one entry per day. ## Making Requests Fetch a range of days with the `StatusHistory` method on the `client.Markets` service. | Method | Return | Description | |-----------------------------------|-------------------------------------------------------|-----------------------------------------------------------------------------------------| | **`StatusHistory(ctx, ...opts)`** | `([]markets.MarketStatus, *response.Response, error)` | Context-aware; returns one entry per day plus the raw response and rate-limit metadata. | > [!NOTE] > **[Context method only]** > > Unlike most SDK endpoints, `StatusHistory` has no `Get*` convenience wrapper — it is only available as a context-aware method. For a single day, use [Status](https://www.marketdata.app/docs/sdk/go/markets/status), which offers both call styles. ## StatusHistory ```go func (s *Service) StatusHistory(ctx context.Context, opts ...markets.HistoryOption) ([]markets.MarketStatus, *response.Response, error) ``` Get the market status for a range of days from the `/v1/markets/status/` endpoint, returning one [`MarketStatus`](https://www.marketdata.app/docs/sdk/go/markets/status#marketstatus) per day. Dates are sent to the API in `YYYY-MM-DD` form, so any time-of-day component is ignored. ### Parameters - `ctx` (`context.Context`) — request context for cancellation and deadlines. - Options: - `markets.WithHistoryWindow(w markets.HistoryWindow)` — the date range, expressed as a single [`HistoryWindow`](#historywindow) value. Omitting it lets the API return its default recent range. This is a `StatusHistory`-only option; it cannot be passed to `Status`. - `markets.WithCountry(country string)` — the market to query, as a two-letter ISO 3166-1 alpha-2 code such as `"US"`. An empty string defaults to the United States. `WithCountry` applies to both `Status` and `StatusHistory`. ### Returns - `[]markets.MarketStatus` — one [`MarketStatus`](https://www.marketdata.app/docs/sdk/go/markets/status#marketstatus) per day in the range, oldest first. - `*response.Response` — raw response plus rate-limit metadata. - `error` — non-nil on request or decoding failure. ### Notes - When the API responds `404` because no data exists for the requested range, `StatusHistory` returns a `nil` slice and a `nil` error; the returned `*response.Response` has its `NoData` field set to `true`. - The range parameters (`from`, `to`, `countback`) are mutually exclusive by construction: a `HistoryWindow` is a single sealed value, so an illegal pairing such as "from plus countback" cannot be expressed. There is deliberately no single-date mode — a single calendar day is a [Status](https://www.marketdata.app/docs/sdk/go/markets/status) concept, selected with `WithDate`. ### Context + Response ```go package main import ( "context" "fmt" "log" "time" "github.com/MarketDataApp/sdk-go/v2/marketdata" "github.com/MarketDataApp/sdk-go/v2/marketdata/markets" ) func main() { client, err := marketdata.NewClient() // reads MARKETDATA_TOKEN from env / .env if err != nil { log.Fatal(err) } defer client.Close() from := time.Date(2024, time.January, 1, 0, 0, 0, 0, time.UTC) to := time.Date(2024, time.January, 10, 0, 0, 0, 0, time.UTC) statuses, resp, err := client.Markets.StatusHistory(context.Background(), markets.WithHistoryWindow(markets.Between(from, to)), markets.WithCountry("US"), ) if err != nil { log.Fatal(err) } if resp.NoData { fmt.Println("No status history for the requested range.") return } for _, status := range statuses { mark := "closed" if status.Open { mark = "open" } fmt.Printf("%s: %s\n", status.Date.Format("2006-01-02"), mark) } } ``` **Output** ``` 2024-01-01: closed 2024-01-02: open 2024-01-03: open 2024-01-04: open 2024-01-05: open 2024-01-06: closed 2024-01-07: closed 2024-01-08: open 2024-01-09: open 2024-01-10: open ``` ### History windows ```go // Every HistoryWindow constructor is a sealed value; pass exactly one to WithHistoryWindow. // Note: there is no OnDate mode here — a single day is a Status concept (use markets.WithDate). markets.Between(from, to) // an explicit closed range -> from=from&to=to markets.Since(from) // everything since a day -> from=from markets.Until(to) // up to and including a day -> to=to markets.LastN(n) // the n most recent days -> countback=n markets.LastNUntil(n, to) // the n days ending at a day -> countback=n&to=to // Example: the last 5 trading-calendar days. statuses, _, err := client.Markets.StatusHistory(context.Background(), markets.WithHistoryWindow(markets.LastN(5)), ) ``` ## HistoryWindow ```go type HistoryWindow interface { // contains filtered or unexported methods } ``` `HistoryWindow` selects the date range for `StatusHistory`. It is a sealed union: build it with exactly one of the constructors below. Because a `HistoryWindow` is a single value, the API's mutually-exclusive range parameters can never be combined by mistake. Only the calendar date of each `time.Time` is used; the time-of-day and zone are ignored. - `markets.Between(from, to time.Time) HistoryWindow` — an explicit closed range from `from` to `to` (inclusive). - `markets.Since(from time.Time) HistoryWindow` — everything from the given day onward. - `markets.Until(to time.Time) HistoryWindow` — data up to and including the given day. - `markets.LastN(n int) HistoryWindow` — the `n` most recent days (the API's `countback`). - `markets.LastNUntil(n int, to time.Time) HistoryWindow` — the `n` days ending at the given day. The returned entries use the same [`MarketStatus`](https://www.marketdata.app/docs/sdk/go/markets/status#marketstatus) type documented on the Status page. --- # Market Status (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/markets/status/index.md Retrieve the market status (open, closed, or early close) for a single trading day — past, present, or future. ## Making Requests Fetch a single day's status with the `Status` method on the `client.Markets` service. It comes in two call styles: | Method | Return | Description | |----------------------------|------------------------------------------------------|------------------------------------------------------------------------| | **`GetStatus(...opts)`** | `(*markets.MarketStatus, error)` | Convenience; uses a background context and discards response metadata. | | **`Status(ctx, ...opts)`** | `(*markets.MarketStatus, *response.Response, error)` | Context-aware; also returns the raw response plus rate-limit metadata. | For a range of days, use [Status History](https://www.marketdata.app/docs/sdk/go/markets/status-history) instead. ## Status ```go func (s *Service) Status(ctx context.Context, opts ...markets.StatusOption) (*markets.MarketStatus, *response.Response, error) func (s *Service) GetStatus(opts ...markets.StatusOption) (*markets.MarketStatus, error) ``` Get the market status for a single day from the `/v1/markets/status/` endpoint. With no options it reports today's status for the United States. The date is sent to the API in `YYYY-MM-DD` form, so any time-of-day component is ignored. ### Parameters - `ctx` (`context.Context`) — request context for cancellation and deadlines (context method only). - Options: - `markets.WithDate(d time.Time)` — the day whose status is requested. A zero time leaves the parameter unset and the API reports today's status. This is a `Status`-only option; it cannot be passed to `StatusHistory`. - `markets.WithCountry(country string)` — the market to query, as a two-letter ISO 3166-1 alpha-2 code such as `"US"`. An empty string defaults to the United States. `WithCountry` applies to both `Status` and `StatusHistory`. ### Returns - `*markets.MarketStatus` — the [`MarketStatus`](#marketstatus) for the requested day. - `*response.Response` — raw response plus rate-limit metadata (context method only). - `error` — non-nil on request or decoding failure. ### Notes - When the API responds `404` because no status exists for the requested date, both call styles return a `nil` `MarketStatus` and a `nil` error. With the context method, the returned `*response.Response` has its `NoData` field set to `true` — always guard against a `nil` result before dereferencing it. - `Status` accepts only single-day options. Its date parameter is a single `time.Time`, so the range parameters used by `StatusHistory` cannot be passed here by mistake. ### Get (simple) ```go package main import ( "fmt" "log" "time" "github.com/MarketDataApp/sdk-go/v2/marketdata" "github.com/MarketDataApp/sdk-go/v2/marketdata/markets" ) func main() { client, err := marketdata.NewClient() // reads MARKETDATA_TOKEN from env / .env if err != nil { log.Fatal(err) } defer client.Close() // Was the US market open on Independence Day 2024? status, err := client.Markets.GetStatus( markets.WithDate(time.Date(2024, time.July, 4, 0, 0, 0, 0, time.UTC)), ) if err != nil { log.Fatal(err) } if status == nil { fmt.Println("No status available for that date.") return } fmt.Println(status) } ``` **Output** ``` 2024-07-04 closed Open: false ``` ### Context + Response ```go package main import ( "context" "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() // No options: today's status for the United States. status, resp, err := client.Markets.Status(context.Background()) if err != nil { log.Fatal(err) } if resp.NoData || status == nil { fmt.Println("No status available.") return } if status.Open { fmt.Println("The market is open.") } else { fmt.Println("The market is closed.") } fmt.Printf("Credits remaining: %d\n", resp.RateLimit.Remaining) } ``` **Output** ``` The market is open. Credits remaining: 99846 ``` ### With options ```go // Query a specific market on a specific day. status, _, err := client.Markets.Status(context.Background(), markets.WithCountry("US"), markets.WithDate(time.Date(2024, time.November, 28, 0, 0, 0, 0, time.UTC)), ) if err != nil { log.Fatal(err) } if status != nil { fmt.Printf("early close: %t\n", status.IsEarlyClose()) } ``` ## MarketStatus ```go type MarketStatus struct { Date time.Time `json:"date"` // Date is the date of this status (US Eastern). Open bool `json:"open"` // Open indicates if the market was/is open on this date. Status string `json:"status"` // Status is a string description. } ``` `MarketStatus` represents the status of the market on a given day. `Status` is the raw string from the API (`"open"`, `"closed"`, or `"early-close"`), and `Open` is derived from it. ### Fields - `Date` (`time.Time`) — the date of this status, normalized to US Eastern. - `Open` (`bool`) — `true` when the market was or is open on this date. - `Status` (`string`) — a description: `"open"`, `"closed"`, or `"early-close"`. ### Methods - `String() string` — a one-line summary, e.g. `2024-07-04 closed Open: false`. - `IsOpen() bool` — reports whether the market is open. - `IsClosed() bool` — reports whether the market is closed. - `IsEarlyClose() bool` — reports whether this is an early-close day (`Status == "early-close"`). --- # Options (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/options/index.md Access options market data with the Go SDK: full option chains, expiration dates, single and multi-contract quotes, and OCC option symbol lookup. Each endpoint exposes a context-aware method that returns typed data plus a `*response.Response` (raw body and rate-limit metadata) and a convenience `Get*` wrapper that uses a background context. ## Options Endpoints --- # Option Chain (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/options/chain/index.md Fetch a current or historical option chain for an underlying symbol, with a rich set of composable filters for expiration, strike, side, moneyness, and liquidity. ## Making Requests `Chain` returns an `*OptionsChain` holding one `OptionQuote` per contract. The symbol is required; every filter is an optional `ChainOption`. Choose the call style that fits your code: | Method | Return | Description | |-----------------------------------|----------------------------------------------|-----------------------------------------------------------------------| | **`GetChain(symbol, ...opts)`** | `(*OptionsChain, error)` | Convenience; uses a background context. | | **`Chain(ctx, symbol, ...opts)`** | `(*OptionsChain, *response.Response, error)` | Context-aware; also returns the raw response and rate-limit metadata. | ## Chain ```go func (s *Service) Chain(ctx context.Context, symbol string, opts ...ChainOption) (*OptionsChain, *response.Response, error) func (s *Service) GetChain(symbol string, opts ...ChainOption) (*OptionsChain, error) ``` `Chain` fetches the options chain for an underlying stock symbol. Without any options the API returns every active contract across all expirations, so most callers narrow the result with filters. The two groups of mutually-exclusive API parameters — contract selection and expiration selection — are each collapsed into a single sealed-union option ([`WithStrike`](#strike-filters) and [`WithExpiry`](#expiration-filters)), so the API's silently-conflicting combinations cannot be written. ### Parameters - `symbol` (`string`) — the underlying stock or index symbol (for example `"AAPL"`, `"SPY"`, `"SPX"`). Required; an empty string is rejected with a `marketdata.ValidationError` before any request is made. #### Expiration filters - `options.WithExpiry(f ExpiryFilter)` — sets the expiration selector from a single `ExpiryFilter`. Replaces the API's mutually-exclusive `expiration`, `dte`, `month`, and `year` parameters with one value so they can never conflict. Build the filter with exactly one of: - `options.OnExpiration(t time.Time)` — a single expiration date (`expiration=YYYY-MM-DD`). Only the calendar date is used; must not be zero. - `options.InDTE(days int)` — the expiration closest to `days` days to expiry, counted from today (`dte=days`). `days` must be zero or greater. - `options.InMonth(month int)` — contracts expiring in the given calendar month across all years (`month=month`); `month` is 1 through 12. - `options.InYear(year int)` — contracts expiring in the given four-digit year across all months (`year=year`); `year` is at least 1900. - `options.InMonthOfYear(month, year int)` — a specific month of a specific year (`month=month&year=year`). - `options.ExpirationBetween(from, to time.Time)` — contracts expiring within an inclusive date range (`from=YYYY-MM-DD&to=YYYY-MM-DD`). Both dates are required and `from` must not be after `to`. This filters which expirations are returned, so it is an expiry mode, not a historical snapshot. - `options.WithChainDate(t time.Time)` — request the chain as it stood on a single historical trading day (`date=YYYY-MM-DD`). Only the calendar date is used; a zero time returns the current chain. Independent of the expiration selector, so a historical chain can still be narrowed by expiration or strike. #### Strike filters - `options.WithStrike(f StrikeFilter)` — sets the contract selector from a single `StrikeFilter`. Replaces the API's mutually-exclusive `strike` and `delta` parameters with one value. Build the filter with exactly one of: - `options.Strike(x float64)` — an exact strike (`strike=x`); `x` must be greater than zero. - `options.StrikeRange(lo, hi float64)` — an inclusive strike range (`strike=lo-hi`); both bounds greater than zero and `lo` must not exceed `hi`. - `options.MinStrike(x float64)` — strikes at or above `x` (`strike=>=x`). - `options.MaxStrike(x float64)` — strikes at or below `x` (`strike=<=x`). - `options.StrikeExpr(expr string)` — the API's raw strike expression, passed verbatim (`strike=expr`), such as `"150"`, `"140-160"`, `">=140"`, or `"<=160"`. An escape hatch for expressions the typed constructors do not cover; must not be empty. - `options.ByDelta(d float64)` — the strikes nearest delta `d` (`delta=d`). The API filters on the **absolute value** of delta and always returns **both sides** (calls and puts), so `ByDelta` does not choose a side — combine it with `WithSide` to keep only calls or puts. `d` must be non-zero and within `[-1, 1]` (puts have negative delta; a negative value behaves the same as its positive counterpart). - `options.WithStrikeLimit(limit int)` — cap the number of strikes returned, excluding the strikes furthest from the money. Values less than or equal to zero leave the limit unset. #### Side, moneyness, and contract type - `options.WithSide(side OptionSide)` — limit the chain to one side: `options.SideCall` (calls only) or `options.SidePut` (puts only). `options.SideBoth` (the default) leaves the parameter unset and returns both sides. - `options.WithRange(r string)` — filter by moneyness using the API's range keywords, such as `"itm"` (in the money), `"otm"` (out of the money), or `"all"`. An empty string leaves the filter unset. - `options.WithNonstandard(nonstandard bool)` — include (`true`) or exclude (`false`) nonstandard contracts, such as adjusted contracts created by splits or mergers. When the option is not used, the API default applies. - `options.WithExpirationTypes(f ExpirationTypeFilter)` — include or exclude expiration cadences via a single `ExpirationTypeFilter`. Build it with exactly one of: - `options.IncludeExpirationTypes(types ...ExpirationType)` — limit the chain to only the given cadences (for example `weekly=true&monthly=true`). - `options.ExcludeExpirationTypes(types ...ExpirationType)` — exclude the given cadences (for example `quarterly=false`). The cadence constants are `options.Weekly`, `options.Monthly`, and `options.Quarterly`. The API forbids mixing inclusion and exclusion in one request; because the choice is a single value, that illegal mix cannot be expressed. #### Settlement (index options only) - `options.WithAM(am bool)` — limit the chain to AM-settled contracts (`true`) or exclude them (`false`). Settlement style is meaningful only for **index options** (for example SPX, NDX); on single-stock and ETF options the API tolerates the parameter but it has no effect. When the option is not used, settlement is not filtered. - `options.WithPM(pm bool)` — the PM-settled counterpart of `WithAM`, with the same index-only semantics. #### Price and liquidity filters - `options.WithMinBid(min float64)` / `options.WithMaxBid(max float64)` — limit the chain to contracts whose bid price is at least `min` / at most `max`. - `options.WithMinAsk(min float64)` / `options.WithMaxAsk(max float64)` — limit the chain to contracts whose ask price is at least `min` / at most `max`. - `options.WithMaxBidAskSpread(max float64)` — maximum bid-ask spread in dollars. - `options.WithMaxBidAskSpreadPct(max float64)` — maximum bid-ask spread as a percentage of the midpoint price. - `options.WithMinOpenInterest(min int)` — minimum open interest, filtering out thinly held contracts. - `options.WithMinVolume(min int)` — minimum trading volume, filtering out thinly traded contracts. ### Returns - `*OptionsChain` — the chain and its contracts. `nil` when there is no data (see Notes). - `*response.Response` — the raw response plus rate-limit metadata (context method only). - `error` — non-nil on a validation failure, transport error, or unexpected API status. ### Notes - **Mutually-exclusive selectors are compile-time exclusive by design.** `WithStrike` and `WithExpiry` each take a single sealed-union value, so the API's silently-conflicting combinations (strike vs. delta; expiration vs. dte/month/year) cannot be written. Delta is a **mode of the strike filter** (`options.ByDelta`), not a separate option, because the API silently honors strike over delta when both are sent. - **Include vs. exclude expiration types is also compile-time exclusive.** A single `ExpirationTypeFilter` is either an include set or an exclude set, so the API's forbidden `weekly=true&monthly=false` mix cannot be produced. - **`am`/`pm` are index-only.** They filter settlement style only for index options; on stocks and ETFs the API tolerates them without effect. - **Historical snapshot vs. expiration range.** `WithChainDate` requests the chain *as of* a single day and is independent of the expiration selector. A `from`/`to` date range filters *expirations*, not history — use `options.ExpirationBetween` via `WithExpiry` for that. - **Validation happens before the network call.** A strike or delta out of range, a month outside 1..12, a year before 1900, or a malformed date range is rejected with a `marketdata.ValidationError` before any request is made. - **No-data behavior.** If the API has no data for the request (HTTP 404), `Chain` returns a `nil` chain, a response whose `NoData` field is `true`, and a `nil` error. Check for a `nil` chain before use. - Requesting a full chain for a large underlying (SPX, SPY, QQQ) can return tens of thousands of contracts; narrow with filters to control credit usage. ### Get (simple) ```go package main import ( "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" "github.com/MarketDataApp/sdk-go/v2/marketdata/options" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() // Calls expiring nearest 45 days out. chain, err := client.Options.GetChain("AAPL", options.WithExpiry(options.InDTE(45)), options.WithSide(options.SideCall), ) if err != nil { log.Fatal(err) } if chain == nil { fmt.Println("No data for the requested chain.") return } fmt.Printf("%s: %d contracts\n", chain.Underlying, len(chain.Options)) for _, o := range chain.Options { fmt.Printf("%s $%.2f Bid: %.2f Ask: %.2f OI: %d\n", o.OptionSymbol, o.Strike, o.Bid, o.Ask, o.OpenInterest) } } ``` **Output** ``` AAPL: 3 contracts AAPL271217C00280000 $180.00 Bid: 16.10 Ask: 16.35 OI: 4210 AAPL271217C00285000 $185.00 Bid: 12.30 Ask: 12.55 OI: 5678 AAPL271217C00290000 $190.00 Bid: 9.05 Ask: 9.25 OI: 3120 ``` ### Context + Response ```go ctx := context.Background() chain, resp, err := client.Options.Chain(ctx, "AAPL", options.WithExpiry(options.InDTE(45)), options.WithStrike(options.StrikeRange(150, 200)), options.WithSide(options.SideCall), options.WithMinOpenInterest(100), ) if err != nil { log.Fatal(err) } if chain == nil { fmt.Println("No data for the requested chain.") return } fmt.Println(chain) // OptionsChain{Underlying: AAPL, Contracts: 3, Updated: ...} for _, o := range chain.Options { fmt.Println(o) } // Rate-limit metadata from the raw response. fmt.Printf("Requests remaining: %d (resets %s)\n", resp.RateLimit.Remaining, resp.RateLimit.ResetAt.Format("2006-01-02 15:04:05")) ``` ### By delta ```go ctx := context.Background() // The ~30-delta calls near a target expiration. // ByDelta returns both sides, so pair it with WithSide to keep only calls. chain, _, err := client.Options.Chain(ctx, "AAPL", options.WithExpiry(options.OnExpiration(time.Date(2026, time.January, 16, 0, 0, 0, 0, time.UTC))), options.WithStrike(options.ByDelta(0.30)), options.WithSide(options.SideCall), ) if err != nil { log.Fatal(err) } for _, o := range chain.Options { fmt.Printf("$%.2f Delta: %.4f Mid: %.2f\n", o.Strike, o.Delta, o.Mid) } ``` ### Liquid contracts ```go ctx := context.Background() // Liquid, tight-spread contracts in a specific month, weeklies excluded. chain, _, err := client.Options.Chain(ctx, "AAPL", options.WithExpiry(options.InMonthOfYear(1, 2026)), options.WithExpirationTypes(options.ExcludeExpirationTypes(options.Weekly)), options.WithMinVolume(100), options.WithMinOpenInterest(1000), options.WithMaxBidAskSpread(0.10), options.WithStrikeLimit(20), ) if err != nil { log.Fatal(err) } for _, o := range chain.Options { fmt.Printf("%s Vol: %d OI: %d Spread: %.2f\n", o.OptionSymbol, o.Volume, o.OpenInterest, o.Spread()) } ``` ### Historical snapshot ```go ctx := context.Background() // The chain as it stood 30 days ago, narrowed to a strike range. chain, _, err := client.Options.Chain(ctx, "AAPL", options.WithChainDate(time.Now().AddDate(0, 0, -30)), options.WithStrike(options.StrikeRange(150, 200)), options.WithSide(options.SideCall), ) if err != nil { log.Fatal(err) } if chain != nil { fmt.Printf("%d contracts as of the snapshot date\n", len(chain.Options)) } ``` ## OptionsChain ```go type OptionsChain struct { Underlying string // the underlying stock symbol Options []OptionQuote // one quote per contract in the chain Updated time.Time // when the chain was last updated } ``` `OptionsChain` is the result of a `Chain` request: the set of contracts listed for a single underlying after any server-side filters have been applied. - `Underlying` (`string`) — the underlying stock symbol. - `Options` (`[]OptionQuote`) — the contracts in the chain, each an [`OptionQuote`](#optionquote). - `Updated` (`time.Time`) — when the chain was last updated (Eastern time). `OptionsChain` also provides `String() string`, which returns a summary such as `OptionsChain{Underlying: AAPL, Contracts: 3, Updated: 2026-07-11 16:00:00}`. ## OptionQuote ```go type OptionQuote struct { OptionSymbol string `json:"optionSymbol"` // OCC option symbol Underlying string `json:"underlying"` // underlying stock symbol Expiration time.Time `json:"expiration"` // expiration date Strike float64 `json:"strike"` // strike price Type OptionType `json:"side"` // "call" or "put" Bid float64 `json:"bid"` // bid price BidSize int `json:"bidSize"` // bid size Ask float64 `json:"ask"` // ask price AskSize int `json:"askSize"` // ask size Last float64 `json:"last"` // last trade price Volume int64 `json:"volume"` // trading volume OpenInterest int64 `json:"openInterest"` // open interest IV float64 `json:"iv"` // implied volatility Delta float64 `json:"delta"` // delta greek Gamma float64 `json:"gamma"` // gamma greek Theta float64 `json:"theta"` // theta greek Vega float64 `json:"vega"` // vega greek Mid float64 `json:"mid"` // midpoint price from the API UnderlyingPrice float64 `json:"underlyingPrice"` // current price of the underlying IntrinsicValue float64 `json:"intrinsicValue"` // intrinsic value ExtrinsicValue float64 `json:"extrinsicValue"` // extrinsic (time) value FirstTraded time.Time `json:"firstTraded"` // date the option was first traded DTE int `json:"dte"` // days to expiration InTheMoney bool `json:"inTheMoney"` // whether the option is ITM Updated time.Time `json:"updated"` // when this contract was last updated } ``` `OptionQuote` is a quote for a single option contract, including pricing, volume, open interest, implied volatility, and the Greeks. It is used both for the entries of an `OptionsChain` and for the responses of [`Quote` and `Quotes`](https://www.marketdata.app/docs/sdk/go/options/quotes). Timestamps are normalized to Eastern time. ### Methods - `String() string` — a one-line summary of the contract. - `Spread() float64` — the bid-ask spread (`Ask` minus `Bid`). - `CalcMid() float64` — the bid-ask midpoint computed locally from `Bid` and `Ask`, unlike the `Mid` field, which is the midpoint reported by the API. > [!NOTE] > **[Zero values and null data]** > > Numeric fields use Go value types (`float64`, `int64`), not pointers. When the > API returns null for a field it is decoded as the zero value (`0` or `0.0`). For > `IV` and the Greeks (`Delta`, `Gamma`, `Theta`, `Vega`), a zero value may mean > the figure was not calculable rather than a true zero. --- # Expirations (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/options/expirations/index.md Get the list of expiration dates that have listed option contracts for an underlying symbol, returned in Eastern time. ## Making Requests `Expirations` returns a `[]time.Time` of expiration dates. Two optional, independent filters narrow the list. Choose the call style that fits your code: | Method | Return | Description | |-----------------------------------------|--------------------------------------------|-----------------------------------------------------------------------| | **`GetExpirations(symbol, ...opts)`** | `([]time.Time, error)` | Convenience; uses a background context. | | **`Expirations(ctx, symbol, ...opts)`** | `([]time.Time, *response.Response, error)` | Context-aware; also returns the raw response and rate-limit metadata. | ## Expirations ```go func (s *Service) Expirations(ctx context.Context, symbol string, opts ...ExpirationOption) ([]time.Time, *response.Response, error) func (s *Service) GetExpirations(symbol string, opts ...ExpirationOption) ([]time.Time, error) ``` `Expirations` fetches the dates on which the underlying has listed option contracts. The symbol is required; without any options the endpoint returns every expiration in the chain. The dates are normalized to Eastern time (the exchange time zone). ### Parameters - `symbol` (`string`) — the underlying stock symbol (for example `"AAPL"`). Required; an empty string is rejected with a `marketdata.ValidationError` before any request is made. - Options (independent — may be combined): - `options.WithExpirationStrike(strike float64)` — limit the list to expirations that offer a contract at the given strike price. The value must be greater than zero to take effect. - `options.WithExpirationDate(d time.Time)` — return the expirations that were available on the given historical date instead of today's list. Only the calendar date is used; a zero time leaves the parameter unset. ### Returns - `[]time.Time` — the expiration dates, in Eastern time. Empty when there is no data. - `*response.Response` — the raw response plus rate-limit metadata (context method only). - `error` — non-nil on a validation failure, transport error, or unexpected API status. ### Notes - The two options are independent and combine freely (unlike the chain's strike and expiry selectors). - If the API has no data for the request (HTTP 404), `Expirations` returns a `nil` slice, a response whose `NoData` field is `true`, and a `nil` error. ### Get (simple) ```go package main import ( "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() expirations, err := client.Options.GetExpirations("AAPL") if err != nil { log.Fatal(err) } fmt.Println("AAPL option expirations:") for _, exp := range expirations { fmt.Println(exp.Format("2006-01-02")) } } ``` **Output** ``` AAPL option expirations: 2026-07-17 2026-07-24 2026-07-31 2026-08-21 2026-09-18 2026-12-18 2027-01-15 2027-06-17 ... ``` ### Context + Response ```go ctx := context.Background() expirations, resp, err := client.Options.Expirations(ctx, "AAPL") if err != nil { log.Fatal(err) } fmt.Printf("%d expirations\n", len(expirations)) fmt.Printf("Requests remaining: %d\n", resp.RateLimit.Remaining) ``` ### With options ```go ctx := context.Background() // Only expirations that list a $200 strike, as they stood one week ago. expirations, _, err := client.Options.Expirations(ctx, "AAPL", options.WithExpirationStrike(200), options.WithExpirationDate(time.Now().AddDate(0, 0, -7)), ) if err != nil { log.Fatal(err) } for _, exp := range expirations { fmt.Println(exp.Format("2006-01-02")) } ``` ## Return Value `Expirations` returns a plain `[]time.Time`. Each element is an expiration date normalized to Eastern time; call `Format("2006-01-02")` to render the calendar date. There is no wrapper struct for this endpoint. --- # Lookup (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/options/lookup/index.md Resolve an option contract to its standard OCC option symbol from the underlying symbol, expiration date, strike price, and option type. ## Making Requests `Lookup` takes the four contract attributes as ordinary Go arguments and returns the OCC option symbol as a string, suitable for use with [Quote and Quotes](https://www.marketdata.app/docs/sdk/go/options/quotes). Choose the call style that fits your code: | Method | Return | Description | |---------------------------------------------------------------|---------------------------------------|-----------------------------------------------------------------------| | **`GetLookup(underlying, expiration, strike, optionType)`** | `(string, error)` | Convenience; uses a background context. | | **`Lookup(ctx, underlying, expiration, strike, optionType)`** | `(string, *response.Response, error)` | Context-aware; also returns the raw response and rate-limit metadata. | ## Lookup ```go func (s *Service) Lookup(ctx context.Context, underlying string, expiration time.Time, strike float64, optionType OptionType) (string, *response.Response, error) func (s *Service) GetLookup(underlying string, expiration time.Time, strike float64, optionType OptionType) (string, error) ``` `Lookup` builds an OCC option symbol (for example `AAPL271217C00250000`) from a contract description. The underlying symbol, the expiration date, the strike price, and the option type are all required. The resolved symbol can then be passed to [`Quote`](https://www.marketdata.app/docs/sdk/go/options/quotes) or [`Quotes`](https://www.marketdata.app/docs/sdk/go/options/quotes). ### Parameters - `underlying` (`string`) — the underlying stock symbol (for example `"AAPL"`). Required; an empty string is rejected with a `marketdata.ValidationError` before any request is made. - `expiration` (`time.Time`) — the contract's expiration date. Only the calendar date is used (formatted as `2006-01-02`). - `strike` (`float64`) — the strike price (for example `150.0`). - `optionType` (`options.OptionType`) — the contract type: `options.Call` or `options.Put`. ### Returns - `string` — the OCC option symbol. Empty when the contract cannot be resolved (see Notes). - `*response.Response` — the raw response plus rate-limit metadata (context method only). - `error` — non-nil on a validation failure, transport error, or unexpected API status. ### Notes - The OCC symbol format is `SYMBOL + YYMMDD + C/P + STRIKE (8 digits)`. For example, `AAPL271217C00250000` is an AAPL January 17, 2025 $150 call. - If the API cannot resolve the contract (HTTP 404), `Lookup` returns an empty string, a response whose `NoData` field is `true`, and a `nil` error. Check for an empty string before using the result. - The lookup query is sent as a single URL path segment; there are no query parameters and no functional options for this endpoint. ### Get (simple) ```go package main import ( "fmt" "log" "time" "github.com/MarketDataApp/sdk-go/v2/marketdata" "github.com/MarketDataApp/sdk-go/v2/marketdata/options" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() expiration := time.Date(2025, time.January, 17, 0, 0, 0, 0, time.UTC) symbol, err := client.Options.GetLookup("AAPL", expiration, 150.0, options.Call) if err != nil { log.Fatal(err) } if symbol == "" { fmt.Println("Contract could not be resolved.") return } fmt.Printf("OCC symbol: %s\n", symbol) } ``` **Output** ``` OCC symbol: AAPL271217C00250000 ``` ### Context + Response ```go ctx := context.Background() expiration := time.Date(2025, time.January, 17, 0, 0, 0, 0, time.UTC) symbol, resp, err := client.Options.Lookup(ctx, "AAPL", expiration, 150.0, options.Call) if err != nil { log.Fatal(err) } fmt.Printf("OCC symbol: %s\n", symbol) fmt.Printf("Requests remaining: %d\n", resp.RateLimit.Remaining) ``` ### Lookup then Quote ```go ctx := context.Background() expiration := time.Date(2025, time.January, 17, 0, 0, 0, 0, time.UTC) // Resolve the OCC symbol, then fetch its quote. symbol, _, err := client.Options.Lookup(ctx, "AAPL", expiration, 150.0, options.Call) if err != nil { log.Fatal(err) } quote, _, err := client.Options.Quote(ctx, symbol) if err != nil { log.Fatal(err) } if quote != nil { fmt.Printf("%s Bid: %.2f Ask: %.2f\n", quote.OptionSymbol, quote.Bid, quote.Ask) } ``` ## OptionType ```go type OptionType string const ( Call OptionType = "call" Put OptionType = "put" ) ``` `OptionType` identifies a contract as a call or a put. It is the required contract-type argument to `Lookup` and the `Type` field of an [`OptionQuote`](https://www.marketdata.app/docs/sdk/go/options/quotes). Use the `options.Call` and `options.Put` constants. --- # Option Quotes (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/options/quotes/index.md Fetch current or historical end-of-day quotes for one option contract with `Quote`, or for many contracts at once with `Quotes`. ## Making Requests Both methods work with OCC option symbols (resolve one with [`Lookup`](https://www.marketdata.app/docs/sdk/go/options/lookup) or read them off a [`Chain`](https://www.marketdata.app/docs/sdk/go/options/chain)). Each has a context-aware form and a convenience `Get*` wrapper: | Method | Return | Description | |--------------------------------------------|----------------------------------------------|----------------------------------------------------------------------------| | **`GetQuote(optionSymbol, ...opts)`** | `(*OptionQuote, error)` | Single contract; convenience, background context. | | **`Quote(ctx, optionSymbol, ...opts)`** | `(*OptionQuote, *response.Response, error)` | Single contract; context-aware, plus raw response and rate-limit metadata. | | **`GetQuotes(optionSymbols ...string)`** | `([]OptionQuote, error)` | Multiple contracts; convenience, background context. | | **`Quotes(ctx, optionSymbols ...string)`** | `([]OptionQuote, *response.Response, error)` | Multiple contracts; context-aware, fetched concurrently. | ## Quote ```go func (s *Service) Quote(ctx context.Context, optionSymbol string, opts ...OptionQuoteOption) (*OptionQuote, *response.Response, error) func (s *Service) GetQuote(optionSymbol string, opts ...OptionQuoteOption) (*OptionQuote, error) ``` `Quote` fetches a real-time or historical quote for a single option contract, identified by its OCC option symbol (for example `AAPL271217C00250000`). The option symbol is required. ### Parameters - `optionSymbol` (`string`) — the OCC option symbol. Required; an empty string is rejected with a `marketdata.ValidationError` before any request is made. - Options: - `options.WithOptionQuoteWindow(w OptionQuoteWindow)` — request a historical quote from a single [`OptionQuoteWindow`](#optionquotewindow) value. Omitting it returns the current quote. Build the window with exactly one of: - `options.QuoteOnDate(t time.Time)` — the contract's quote on a single historical date (`date=YYYY-MM-DD`). - `options.QuoteRange(from, to time.Time)` — the contract's quotes across an explicit date range (`from=...&to=...`). ### Returns - `*OptionQuote` — the contract quote. `nil` when there is no data (see Notes). - `*response.Response` — the raw response plus rate-limit metadata (context method only). - `error` — non-nil on a validation failure, transport error, or unexpected API status. ### Notes - **The date window is compile-time exclusive.** Because `OptionQuoteWindow` is a single sealed-union value, the API's mutually-exclusive `date` and `from`/`to` parameters can never be combined by mistake (sending both returns HTTP 400). - **Validation happens before the network call.** A zero date, or a `from` after its `to`, is rejected with a `marketdata.ValidationError` before any request is made. - **No-data behavior.** If the API has no data for the request (HTTP 404), `Quote` returns a `nil` quote, a response whose `NoData` field is `true`, and a `nil` error. Check for a `nil` quote before use. ## Quotes ```go func (s *Service) Quotes(ctx context.Context, optionSymbols ...string) ([]OptionQuote, *response.Response, error) func (s *Service) GetQuotes(optionSymbols ...string) ([]OptionQuote, error) ``` `Quotes` fetches quotes for multiple contracts. It fans out one concurrent `Quote` request per symbol, drawing slots from the client's shared concurrency pool (at most 50 in-flight requests per client across all services), and merges the results into a single slice in the order the symbols were given. ### Parameters - `optionSymbols` (`...string`) — one or more OCC option symbols (variadic). At least one is required; passing none is rejected with a `marketdata.ValidationError`. ### Returns - `[]OptionQuote` — one quote per contract that returned data, in input order. - `*response.Response` — the raw response for one of the individual requests, not an aggregate (context method only). - `error` — non-nil if any request fails with a real error; in that case no quotes are returned. ### Notes - **Symbols with no data are omitted.** A contract that returns HTTP 404 is dropped from the result rather than producing an error, so the returned slice may be shorter than the input. - **`Quotes` does not accept a quote window.** The multi-symbol method fetches current quotes only; use `Quote` with `WithOptionQuoteWindow` for historical data on a single contract. ### Get (single) ```go package main import ( "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() quote, err := client.Options.GetQuote("AAPL271217C00250000") if err != nil { log.Fatal(err) } if quote == nil { fmt.Println("No data for the requested contract.") return } fmt.Printf("Symbol: %s\n", quote.OptionSymbol) fmt.Printf("Underlying: %s @ %.2f\n", quote.Underlying, quote.UnderlyingPrice) fmt.Printf("Strike: $%.2f %s\n", quote.Strike, quote.Type) fmt.Printf("Bid/Ask: %.2f x %d / %.2f x %d\n", quote.Bid, quote.BidSize, quote.Ask, quote.AskSize) fmt.Printf("Last: %.2f\n", quote.Last) fmt.Printf("Volume / OI: %d / %d\n", quote.Volume, quote.OpenInterest) fmt.Printf("IV / Delta: %.4f / %.4f\n", quote.IV, quote.Delta) } ``` **Output** ``` Symbol: AAPL271217C00250000 Underlying: AAPL @ 195.20 Strike: $150.00 call Bid/Ask: 45.30 x 45 / 45.65 x 30 Last: 45.50 Volume / OI: 1234 / 15678 IV / Delta: 0.2841 / 0.9210 ``` ### Context + Response ```go ctx := context.Background() quote, resp, err := client.Options.Quote(ctx, "AAPL271217C00250000") if err != nil { log.Fatal(err) } if quote == nil { fmt.Println("No data for the requested contract.") return } fmt.Println(quote) // OptionQuote.String() summary fmt.Printf("Requests remaining: %d\n", resp.RateLimit.Remaining) ``` ### Historical (single) ```go ctx := context.Background() // Quote on one historical date. onDate, _, err := client.Options.Quote(ctx, "AAPL271217C00250000", options.WithOptionQuoteWindow(options.QuoteOnDate( time.Date(2025, time.January, 2, 0, 0, 0, 0, time.UTC)))) if err != nil { log.Fatal(err) } if onDate != nil { fmt.Printf("Last: %.2f IV: %.4f\n", onDate.Last, onDate.IV) } // A series of end-of-day quotes across a range. series, _, err := client.Options.Quote(ctx, "AAPL271217C00250000", options.WithOptionQuoteWindow(options.QuoteRange( time.Date(2025, time.January, 1, 0, 0, 0, 0, time.UTC), time.Date(2025, time.January, 31, 0, 0, 0, 0, time.UTC)))) if err != nil { log.Fatal(err) } _ = series ``` ### Multiple symbols ```go ctx := context.Background() // Variadic list of OCC symbols, fetched concurrently. quotes, _, err := client.Options.Quotes(ctx, "AAPL271217C00280000", "AAPL271217C00290000", "AAPL271217C00300000", "AAPL271217P00280000", ) if err != nil { log.Fatal(err) } for _, q := range quotes { fmt.Printf("%s $%.0f %s Bid: %.2f Ask: %.2f Vol: %d\n", q.OptionSymbol, q.Strike, q.Type, q.Bid, q.Ask, q.Volume) } ``` ## OptionQuoteWindow ```go type OptionQuoteWindow interface { // contains filtered or unexported methods } func QuoteOnDate(t time.Time) OptionQuoteWindow func QuoteRange(from, to time.Time) OptionQuoteWindow ``` `OptionQuoteWindow` is the sealed-union date selector for a single-contract `Quote` request, passed via `options.WithOptionQuoteWindow`. Build it with exactly one of `options.QuoteOnDate` (a single day) or `options.QuoteRange` (an explicit range). Only the calendar date of each `time.Time` is used. Because it is one value, the API's mutually-exclusive `date` and `from`/`to` parameters cannot be combined. ## OptionQuote ```go type OptionQuote struct { OptionSymbol string `json:"optionSymbol"` // OCC option symbol Underlying string `json:"underlying"` // underlying stock symbol Expiration time.Time `json:"expiration"` // expiration date Strike float64 `json:"strike"` // strike price Type OptionType `json:"side"` // "call" or "put" Bid float64 `json:"bid"` // bid price BidSize int `json:"bidSize"` // bid size Ask float64 `json:"ask"` // ask price AskSize int `json:"askSize"` // ask size Last float64 `json:"last"` // last trade price Volume int64 `json:"volume"` // trading volume OpenInterest int64 `json:"openInterest"` // open interest IV float64 `json:"iv"` // implied volatility Delta float64 `json:"delta"` // delta greek Gamma float64 `json:"gamma"` // gamma greek Theta float64 `json:"theta"` // theta greek Vega float64 `json:"vega"` // vega greek Mid float64 `json:"mid"` // midpoint price from the API UnderlyingPrice float64 `json:"underlyingPrice"` // current price of the underlying IntrinsicValue float64 `json:"intrinsicValue"` // intrinsic value ExtrinsicValue float64 `json:"extrinsicValue"` // extrinsic (time) value FirstTraded time.Time `json:"firstTraded"` // date the option was first traded DTE int `json:"dte"` // days to expiration InTheMoney bool `json:"inTheMoney"` // whether the option is ITM Updated time.Time `json:"updated"` // when this contract was last updated } ``` `OptionQuote` is the quote for a single option contract, returned by both `Quote` (as `*OptionQuote`) and `Quotes` (as elements of `[]OptionQuote`), and also used for the entries of an [`OptionsChain`](https://www.marketdata.app/docs/sdk/go/options/chain). Timestamps are normalized to Eastern time. ### Methods - `String() string` — a one-line summary of the contract. - `Spread() float64` — the bid-ask spread (`Ask` minus `Bid`). - `CalcMid() float64` — the bid-ask midpoint computed locally from `Bid` and `Ask`, unlike the `Mid` field, which is the midpoint reported by the API. > [!NOTE] > **[Zero values and null data]** > > Numeric fields use Go value types (`float64`, `int64`), not pointers. When the > API returns null for a field it is decoded as the zero value (`0` or `0.0`). For > `IV` and the Greeks (`Delta`, `Gamma`, `Theta`, `Vega`), a zero value may mean > the figure was not calculable rather than a true zero. --- # Parameters (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/parameters/index.md The Market Data API supports a set of **universal parameters** that work on every endpoint. In the Go SDK these are configured as **client-level functional options** passed to `NewClient`; once set, they are attached to every request that client makes. ```go client, err := marketdata.NewClient( marketdata.WithMode(marketdata.ModeCached), marketdata.WithMaxAge("5min"), marketdata.WithLimit(50), marketdata.WithColumns("open", "high", "low", "close", "volume"), ) if err != nil { log.Fatal(err) } defer client.Close() ``` ## How Parameters Are Resolved Universal parameters are merged from three sources, lowest priority first: 1. **Environment variables** (lowest) — read once at `NewClient` time. Only four universal parameters have environment-variable support (see the table below). 2. **Client options** (`With*`) — override any value that came from the environment. 3. **Endpoint method-level parameters** (highest) — some endpoints set a specific value themselves (for example, candle endpoints pin the `dateformat` they need for correct decoding). A method-level value always wins over the client default. > [!NOTE] > **[Client-level, not per-call]** > > Unlike the PHP SDK, the Go SDK does not accept a universal-parameter object on each method call. Universal parameters are set on the client. If you need different values for different calls — for example a cached client for bulk quotes and a live client for time-sensitive calls — create separate clients. ## Environment Variables Only these four universal parameters can be set through the environment. They are read when the client is created and can be overridden by the corresponding `With*` option. | Variable | Values | Maps to | Option | |---------------------------------|------------------------------------|--------------|---------------------| | `MARKETDATA_DATE_FORMAT` | `timestamp`, `unix`, `spreadsheet` | `dateformat` | `WithDateFormat` | | `MARKETDATA_COLUMNS` | comma-separated list | `columns` | `WithColumns` | | `MARKETDATA_ADD_HEADERS` | `true`, `false` | `headers` | `WithAddHeaders` | | `MARKETDATA_USE_HUMAN_READABLE` | `true`, `false` | `human` | `WithHumanReadable` | > [!NOTE] > `mode`, `maxage`, `limit`, and `offset` have **no** environment-variable support in the Go SDK; set them with their `With*` options. ## Available Parameters ### Mode Controls how each request is fulfilled, trading data freshness against credit cost. - **Option:** `WithMode(mode Mode)` - **Values:** `marketdata.ModeLive` (real-time; default on paid plans), `marketdata.ModeCached` (recently cached data at reduced credit cost), `marketdata.ModeDelayed` (delayed at least 15 minutes; paid plans only) ```go client, err := marketdata.NewClient(marketdata.WithMode(marketdata.ModeLive)) ``` > [!NOTE] > **[Premium parameter]** > > `mode` is available only on paid plans. Free and trial plans receive historical data (the last fully-closed session) regardless of this setting. See [Data Freshness](https://www.marketdata.app/docs/account/data-freshness). When `ModeCached` misses the cache, the API returns HTTP `204`, which the SDK surfaces as a no-data response (nil result, `Response.NoData == true`, nil error) rather than an error. ### Max Age The maximum age of cached data accepted when `ModeCached` is in effect. It has no effect in other modes. - **Option:** `WithMaxAge(maxAge string)` - **Values:** a relative duration such as `"5min"` or `"1h"`, or an absolute datetime. If no cached data falls within the window, the API returns a no-data response at no credit cost. ```go client, err := marketdata.NewClient( marketdata.WithMode(marketdata.ModeCached), marketdata.WithMaxAge("5min"), ) ``` ### Columns Restricts responses to the named columns, which can reduce payload size. Missing columns simply decode as zero values, so filtering never causes a decode error. Passing no columns is a no-op. - **Option:** `WithColumns(columns ...string)` ```go client, err := marketdata.NewClient( marketdata.WithColumns("open", "close", "volume"), ) ``` ### Limit and Offset Cap and paginate results. - **Options:** `WithLimit(n int)`, `WithOffset(n int)` - Values ≤ 0 are ignored (offset 0 is the default first page). ```go client, err := marketdata.NewClient( marketdata.WithLimit(100), marketdata.WithOffset(100), // second page of 100 ) ``` ### Date Format Sets the wire representation of date/time fields. - **Option:** `WithDateFormat(format string)` - **Values:** `"timestamp"`, `"unix"`, `"spreadsheet"` > [!WARNING] > **[Advanced use only]** > > The SDK decodes dates from the API's default numeric (unix) representation. Overriding `dateformat` globally can change the wire format of date fields and cause typed responses (candles, quotes, earnings) to fail decoding. Endpoints that require a specific format set it themselves at the method level. Only set this when you consume the raw response yourself. ### Human Readable Requests human-readable field names and value formatting via the `human` parameter. - **Option:** `WithHumanReadable(enabled bool)` > [!WARNING] > **[Advanced use only]** > > Human-readable output changes field names and value formatting and is incompatible with the SDK's typed decoding. Enable it only when you read the raw `*marketdata.Response` body yourself instead of using the typed result. ### Add Headers Controls whether a header row is included in CSV output via the `headers` parameter. - **Option:** `WithAddHeaders(enabled bool)` This affects CSV output only and has no effect on the JSON responses the SDK decodes; it is provided for completeness. ## Reading the Raw Response The advanced parameters above are most useful when you access the raw response body rather than the typed result. Use the context-form call and the `*marketdata.Response` it returns (see [Client](https://www.marketdata.app/docs/sdk/go/client)): ```go _, resp, err := client.Stocks.Candles(ctx, "AAPL") if err != nil { log.Fatal(err) } fmt.Println("content-type:", resp.Header.Get("Content-Type")) ``` ## Next Steps - Review the [Client](https://www.marketdata.app/docs/sdk/go/client) options that set these parameters. - Learn how the SDK reports failures on the [Errors](https://www.marketdata.app/docs/sdk/go/errors) page. --- # Stocks (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/stocks/index.md Access real-time and historical stock market data — quotes, OHLCV candles, bulk candles, SmartMid prices, earnings reports, and news — through the `client.Stocks` service. --- # Bulk Candles (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/stocks/bulkcandles/index.md Retrieve a single daily candle for many stock symbols in one API request — ideal for market snapshots. ## Making Requests `BulkCandles` lives on the `Stocks` service (`client.Stocks`). Unlike the other stocks endpoints it has no `Get*` convenience wrapper; call the context method directly. | Method | Return | Description | |------------------------------------------|---------------------------------------------|---------------------------------------------------------------------| | **`BulkCandles(ctx, symbols, opts...)`** | `([]BulkCandle, *response.Response, error)` | Context-aware; also returns the raw response + rate-limit metadata. | ## BulkCandles ```go func (s *Service) BulkCandles(ctx context.Context, symbols []string, opts ...BulkCandleOption) ([]BulkCandle, *response.Response, error) ``` Fetch one daily candle per symbol. Unlike [Candles](https://www.marketdata.app/docs/sdk/go/stocks/candles), this endpoint returns a single candle for each symbol, not a time series. ### Parameters - `ctx` (`context.Context`) — controls cancellation and deadlines. - `symbols` (`[]string`) — the stock ticker symbols, e.g. `[]string{"AAPL", "MSFT", "GOOG"}`. At least one symbol is required; an empty slice returns a validation error without making a request. - Options (`stocks.BulkCandleOption`): - `stocks.WithBulkResolution(r Resolution)` — set the resolution. The endpoint only supports `stocks.ResolutionDaily`, which is also the default. - `stocks.WithBulkDate(t time.Time)` — request candles for a specific historical date instead of the most recent trading day. Only the calendar date is used. - `stocks.WithSnapshot(snapshot bool)` — control whether the API returns a snapshot of the latest candle. When omitted, the parameter is not sent and the API default applies. - `stocks.WithAdjustSplits(adjust bool)` — adjust for stock splits. When omitted, the parameter is not sent and the API default applies. - `stocks.WithAdjustDividends(adjust bool)` — adjust for dividends. The API adjusts for dividends by default; pass `false` for raw, unadjusted prices. When omitted, the parameter is not sent. ### Returns - `[]BulkCandle` — one candle per symbol. - `*response.Response` — raw response + rate-limit metadata. - `error` — non-nil on request/validation failure. ### Notes - Only `ResolutionDaily` is supported; `WithBulkResolution` exists for symmetry with the other candle endpoints. - The bulk candles endpoint accepts a single date only (no range or countback mode), so `WithBulkDate` is a plain option rather than a `DateWindow`. - If the API responds with 404 because no data is available, `BulkCandles` returns a `nil` slice, a `nil` error, and a non-nil `*response.Response` whose `NoData` field is `true`. ### Multiple symbols ```go package main import ( "context" "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() ctx := context.Background() candles, resp, err := client.Stocks.BulkCandles(ctx, []string{"AAPL", "MSFT", "GOOG"}) if err != nil { log.Fatal(err) } if resp.NoData { fmt.Println("no candles available") return } for _, c := range candles { fmt.Printf("%s: O:%.2f C:%.2f V:%d\n", c.Symbol, c.Open, c.Close, c.Volume) } } ``` **Output** ``` AAPL: O:185.64 C:186.19 V:48234500 MSFT: O:378.25 C:380.55 V:22156800 GOOG: O:142.35 C:143.12 V:18456200 ``` ### With options ```go package main import ( "context" "fmt" "log" "time" "github.com/MarketDataApp/sdk-go/v2/marketdata" "github.com/MarketDataApp/sdk-go/v2/marketdata/stocks" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() ctx := context.Background() date := time.Date(2024, 1, 15, 0, 0, 0, 0, time.UTC) candles, _, err := client.Stocks.BulkCandles(ctx, []string{"AAPL", "MSFT", "GOOG"}, stocks.WithBulkResolution(stocks.ResolutionDaily), stocks.WithBulkDate(date), stocks.WithAdjustSplits(true), ) if err != nil { log.Fatal(err) } for _, c := range candles { fmt.Println(c) // BulkCandle implements Stringer } } ``` ## BulkCandle ```go type BulkCandle struct { Symbol string `json:"symbol"` // Stock ticker symbol Time time.Time `json:"t"` // Candle timestamp (US Eastern) Open float64 `json:"o"` // Opening price High float64 `json:"h"` // Highest price Low float64 `json:"l"` // Lowest price Close float64 `json:"c"` // Closing price Volume int64 `json:"v"` // Trading volume } ``` Represents a single daily candle for one symbol. It carries the same OHLCV fields as [`Candle`](https://www.marketdata.app/docs/sdk/go/stocks/candles) plus the `Symbol` the candle belongs to, since bulk requests cover multiple symbols. Timestamps are normalized to US Eastern time. ### Fields - `Symbol` — the stock ticker symbol. - `Time` — the candle timestamp. - `Open` / `High` / `Low` / `Close` — the OHLC prices for the day. - `Volume` — trading volume for the day. ### Helper Methods - `String() string` — a concise one-line summary of the candle. --- # Stock Candles (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/stocks/candles/index.md Retrieve historical OHLCV (open, high, low, close, volume) candles for a stock symbol across intraday, daily, weekly, monthly, and yearly resolutions. ## Making Requests Both call styles live on the `Stocks` service (`client.Stocks`). Use `GetCandles` for the simplest call, or `Candles` when you also need the raw response and rate-limit metadata. | Method | Return | Description | |-------------------------------------|-----------------------------------------|--------------------------------------------------------------------------------------| | **`GetCandles(symbol, opts...)`** | `([]Candle, error)` | Convenience wrapper; uses `context.Background()` and discards the response metadata. | | **`Candles(ctx, symbol, opts...)`** | `([]Candle, *response.Response, error)` | Context-aware; also returns the raw response + rate-limit metadata. | ## Candles ```go func (s *Service) Candles(ctx context.Context, symbol string, opts ...CandleOption) ([]Candle, *response.Response, error) func (s *Service) GetCandles(symbol string, opts ...CandleOption) ([]Candle, error) ``` Fetch historical candles for one symbol. The resolution defaults to daily; the date range is expressed as a single [`DateWindow`](#date-windows) value. ### Parameters - `ctx` (`context.Context`) — controls cancellation and deadlines (context method only). - `symbol` (`string`) — the stock ticker symbol, e.g. `"AAPL"`. Required; an empty string returns a validation error without making a request. - Options (`stocks.CandleOption`): - `stocks.WithResolution(r Resolution)` — set the candle timeframe. Defaults to `stocks.ResolutionDaily`. See [Resolution values](#resolution) below. - `stocks.WithCandleWindow(w DateWindow)` — set the date range as a single `DateWindow` value (for example `stocks.Between(from, to)` or `stocks.LastN(30)`). When omitted, the API returns its default recent window. See [Date windows](#date-windows). - `stocks.WithCandleExtended(extended bool)` — include extended-hours data. When omitted, the parameter is not sent and the API default applies. Only meaningful for intraday resolutions. - `stocks.WithCandleAdjustSplits(adjust bool)` — adjust for stock splits. When omitted, the parameter is not sent and the API default applies. - `stocks.WithCandleAdjustDividends(adjust bool)` — adjust for dividends. The API adjusts for dividends by default; pass `false` for raw, unadjusted prices. When omitted, the parameter is not sent. ### Returns - `[]Candle` — candles in chronological order. - `*response.Response` — raw response + rate-limit metadata (context method only). - `error` — non-nil on request/validation failure. ### Notes - Only the calendar date of each `time.Time` in a window is used; the time-of-day and zone are ignored. - For intraday resolutions (`Resolution1Min` through `Resolution4Hour`), when both `from` and `to` are set and the range spans more than one year, `Candles` automatically splits the range into year-sized chunks, fetches them concurrently, and merges the results in chronological order. In that case the returned `*response.Response` reflects only one of the underlying requests. - The date range is a single `DateWindow` value, so the API's mutually-exclusive date parameters (`date`, `from`/`to`, `countback`) can never be combined by mistake. - If the API responds with 404 because no data is available, `Candles` returns a `nil` slice, a `nil` error, and a non-nil `*response.Response` whose `NoData` field is `true`. ### Get (simple) ```go package main import ( "fmt" "log" "time" "github.com/MarketDataApp/sdk-go/v2/marketdata" "github.com/MarketDataApp/sdk-go/v2/marketdata/stocks" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() from := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC) to := time.Date(2024, 1, 31, 0, 0, 0, 0, time.UTC) candles, err := client.Stocks.GetCandles("AAPL", stocks.WithResolution(stocks.ResolutionDaily), stocks.WithCandleWindow(stocks.Between(from, to)), ) if err != nil { log.Fatal(err) } for _, c := range candles { fmt.Printf("%s O:%.2f H:%.2f L:%.2f C:%.2f V:%d\n", c.Time.Format("2006-01-02"), c.Open, c.High, c.Low, c.Close, c.Volume) } } ``` **Output** ``` 2024-01-02 O:187.15 H:188.44 L:183.89 C:185.64 V:82488700 2024-01-03 O:184.22 H:185.88 L:183.43 C:184.25 V:58414500 ... ``` ### Context + Response ```go package main import ( "context" "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" "github.com/MarketDataApp/sdk-go/v2/marketdata/stocks" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() ctx := context.Background() // The 30 most recent daily candles (countback). candles, resp, err := client.Stocks.Candles(ctx, "AAPL", stocks.WithCandleWindow(stocks.LastN(30)), ) if err != nil { log.Fatal(err) } if resp.NoData { fmt.Println("no candles available") return } fmt.Printf("Fetched %d candles\n", len(candles)) fmt.Printf("Requests remaining: %d\n", resp.RateLimit.Remaining) } ``` ### With options ```go package main import ( "context" "fmt" "log" "time" "github.com/MarketDataApp/sdk-go/v2/marketdata" "github.com/MarketDataApp/sdk-go/v2/marketdata/stocks" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() ctx := context.Background() day := time.Date(2024, 1, 15, 0, 0, 0, 0, time.UTC) // 5-minute candles for a single day, extended hours, unadjusted for splits. candles, _, err := client.Stocks.Candles(ctx, "AAPL", stocks.WithResolution(stocks.Resolution5Min), stocks.WithCandleWindow(stocks.OnDate(day)), stocks.WithCandleExtended(true), stocks.WithCandleAdjustSplits(false), ) if err != nil { log.Fatal(err) } fmt.Printf("Found %d intraday candles\n", len(candles)) for _, c := range candles[:3] { fmt.Println(c) // Candle implements Stringer } } ``` ## Resolution `Resolution` is a string type selecting the candle timeframe. Pass a constant to `stocks.WithResolution`. ```go type Resolution string const ( Resolution1Min Resolution = "1" // 1 minute Resolution3Min Resolution = "3" // 3 minutes Resolution5Min Resolution = "5" // 5 minutes Resolution15Min Resolution = "15" // 15 minutes Resolution30Min Resolution = "30" // 30 minutes Resolution45Min Resolution = "45" // 45 minutes Resolution1Hour Resolution = "60" // 1 hour Resolution2Hour Resolution = "120" // 2 hours Resolution4Hour Resolution = "240" // 4 hours ResolutionDaily Resolution = "D" // daily (default) ResolutionWeekly Resolution = "W" // weekly ResolutionMonthly Resolution = "M" // monthly ResolutionYearly Resolution = "Y" // yearly ) ``` Resolutions `Resolution1Min` through `Resolution4Hour` are intraday; these are the resolutions eligible for automatic year-chunk splitting on large ranges. ## Date Windows `DateWindow` is a sealed union describing the date range for `Candles` (and shared by [Earnings](https://www.marketdata.app/docs/sdk/go/stocks/earnings) and [News](https://www.marketdata.app/docs/sdk/go/stocks/news)). Build one with exactly one of the constructors below and pass it to `stocks.WithCandleWindow`. Because a window is a single value, illegal combinations such as "from plus countback" are not expressible. | Constructor | Selects | API parameters | |----------------------------|--------------------------------------|---------------------| | `stocks.OnDate(d)` | a single calendar day | `date=d` | | `stocks.Between(from, to)` | an explicit closed range (inclusive) | `from=from&to=to` | | `stocks.Since(from)` | everything from a day onward | `from=from` | | `stocks.Until(to)` | up to and including a day | `to=to` | | `stocks.LastN(n)` | the `n` most recent periods | `countback=n` | | `stocks.LastNUntil(n, to)` | the `n` periods ending at a day | `countback=n&to=to` | Only the calendar date of each `time.Time` is used; the time-of-day and zone are ignored. ## Candle ```go type Candle struct { Time time.Time `json:"t"` // Candle timestamp (start of period, US Eastern) Open float64 `json:"o"` // Opening price High float64 `json:"h"` // Highest price Low float64 `json:"l"` // Lowest price Close float64 `json:"c"` // Closing price Volume int64 `json:"v"` // Trading volume } ``` Represents a single OHLCV candle. `Time` marks the start of the candle period and is normalized to US Eastern time. ### Fields - `Time` — the candle timestamp (start of the period). - `Open` / `High` / `Low` / `Close` — the OHLC prices for the period. - `Volume` — trading volume for the period. ### Helper Methods - `Range() float64` — the high-low range (`High - Low`). - `RangePercent() float64` — the range as a percentage of `Open`. - `IsBullish() bool` — true when `Close > Open`. - `IsBearish() bool` — true when `Close < Open`. - `String() string` — a concise one-line summary of the candle. --- # Earnings (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/stocks/earnings/index.md Get historical earnings-per-share data and the upcoming earnings calendar for a stock, one report per fiscal quarter. > [!NOTE] > **[Premium Endpoint]** > > The earnings endpoint requires a premium subscription. Free and trial accounts cannot access this data. ## Making Requests Both call styles live on the `Stocks` service (`client.Stocks`). Use `GetEarnings` for the simplest call, or `Earnings` when you also need the raw response and rate-limit metadata. | Method | Return | Description | |--------------------------------------|------------------------------------------|--------------------------------------------------------------------------------------| | **`GetEarnings(symbol, opts...)`** | `([]Earning, error)` | Convenience wrapper; uses `context.Background()` and discards the response metadata. | | **`Earnings(ctx, symbol, opts...)`** | `([]Earning, *response.Response, error)` | Context-aware; also returns the raw response + rate-limit metadata. | ## Earnings ```go func (s *Service) Earnings(ctx context.Context, symbol string, opts ...EarningsOption) ([]Earning, *response.Response, error) func (s *Service) GetEarnings(symbol string, opts ...EarningsOption) ([]Earning, error) ``` Fetch earnings reports for one symbol. With no options the API returns its default set of recent and upcoming reports. ### Parameters - `ctx` (`context.Context`) — controls cancellation and deadlines (context method only). - `symbol` (`string`) — the stock ticker symbol, e.g. `"AAPL"`. Required; an empty string returns a validation error without making a request. - Options (`stocks.EarningsOption`): - `stocks.WithEarningsWindow(w DateWindow)` — set the reporting date range as a single `DateWindow` value. Build the window with any of the shared [date-window constructors](#date-windows), for example `stocks.OnDate(d)`, `stocks.Between(from, to)`, or `stocks.LastN(4)`. ### Returns - `[]Earning` — earnings reports, one per fiscal quarter. - `*response.Response` — raw response + rate-limit metadata (context method only). - `error` — non-nil on request/validation failure. ### Notes - EPS fields on the returned `Earning` values are pointers (`*float64`) and are `nil` when the API reports no value, such as for earnings not yet reported. This distinguishes a missing value from a true `$0.00`. - Only the calendar date of each `time.Time` in a window is used; the time-of-day and zone are ignored. - If the API responds with 404 because no data is available, `Earnings` returns a `nil` slice, a `nil` error, and a non-nil `*response.Response` whose `NoData` field is `true`. ### Get (simple) ```go package main import ( "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() earnings, err := client.Stocks.GetEarnings("AAPL") if err != nil { log.Fatal(err) } for _, e := range earnings { reported := "n/a" if e.ReportedEPS != nil { reported = fmt.Sprintf("$%.2f", *e.ReportedEPS) } fmt.Printf("FY%d Q%d report %s EPS %s\n", e.FiscalYear, e.FiscalQuarter, e.ReportDate.Format("2006-01-02"), reported) } } ``` **Output** ``` FY2024 Q1 report 2024-01-25 EPS $2.18 FY2024 Q2 report 2024-04-25 EPS n/a ``` ### Context + Response ```go package main import ( "context" "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() ctx := context.Background() earnings, resp, err := client.Stocks.Earnings(ctx, "AAPL") if err != nil { log.Fatal(err) } if resp.NoData { fmt.Println("no earnings available") return } for _, e := range earnings { fmt.Println(e) // Earning implements Stringer } fmt.Printf("Requests remaining: %d\n", resp.RateLimit.Remaining) } ``` ### With options ```go package main import ( "context" "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" "github.com/MarketDataApp/sdk-go/v2/marketdata/stocks" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() ctx := context.Background() // The 4 most recent quarterly reports. earnings, _, err := client.Stocks.Earnings(ctx, "AAPL", stocks.WithEarningsWindow(stocks.LastN(4)), ) if err != nil { log.Fatal(err) } for _, e := range earnings { est, act := "n/a", "n/a" if e.EstimatedEPS != nil { est = fmt.Sprintf("$%.2f", *e.EstimatedEPS) } if e.ReportedEPS != nil { act = fmt.Sprintf("$%.2f", *e.ReportedEPS) } fmt.Printf("Q%d %d: estimate %s, actual %s\n", e.FiscalQuarter, e.FiscalYear, est, act) } } ``` ## Date Windows `stocks.WithEarningsWindow` takes a `DateWindow` — the same sealed-union value used by [Candles](https://www.marketdata.app/docs/sdk/go/stocks/candles) and [News](https://www.marketdata.app/docs/sdk/go/stocks/news). Build one with exactly one constructor: | Constructor | Selects | API parameters | |----------------------------|--------------------------------------|---------------------| | `stocks.OnDate(d)` | a single calendar day | `date=d` | | `stocks.Between(from, to)` | an explicit closed range (inclusive) | `from=from&to=to` | | `stocks.Since(from)` | everything from a day onward | `from=from` | | `stocks.Until(to)` | up to and including a day | `to=to` | | `stocks.LastN(n)` | the `n` most recent reports | `countback=n` | | `stocks.LastNUntil(n, to)` | the `n` reports ending at a day | `countback=n&to=to` | ## Earning ```go type Earning struct { Symbol string `json:"symbol"` // Stock ticker symbol FiscalYear int `json:"fiscalYear"` // Fiscal year of the report FiscalQuarter int `json:"fiscalQuarter"` // Fiscal quarter (1-4) Date time.Time `json:"date"` // Last calendar day of the fiscal period ReportDate time.Time `json:"reportDate"` // When earnings were / will be reported ReportTime string `json:"reportTime"` // When during the day (before/after market, during hours) Currency string `json:"currency"` // Currency of the report (may be empty for future earnings) ReportedEPS *float64 `json:"reportedEPS"` // Actual reported EPS (nil for future earnings) EstimatedEPS *float64 `json:"estimatedEPS"` // Consensus analyst estimate SurpriseEPS *float64 `json:"surpriseEPS"` // Difference between reported and estimated SurpriseEPSPercent *float64 `json:"surpriseEPSpct"` // Surprise as a percentage Updated time.Time `json:"updated"` // When this earnings data was last updated } ``` Represents a single earnings report, one per fiscal quarter. Timestamps are normalized to US Eastern time. ### Fields - `Symbol` — the stock ticker symbol. - `FiscalYear` / `FiscalQuarter` — the fiscal year and quarter (1-4) of the report. - `Date` — the last calendar day of the fiscal period. - `ReportDate` — the date the earnings were or will be reported. - `ReportTime` — when during the day the report is/was released (before market, after market, or during hours). - `Currency` — the currency of the report; may be empty for future earnings. - `ReportedEPS` — the actual reported EPS; `nil` for earnings not yet reported. - `EstimatedEPS` — the consensus analyst estimate; `nil` when unavailable. - `SurpriseEPS` — the difference between reported and estimated EPS; `nil` when unavailable. - `SurpriseEPSPercent` — the surprise as a percentage (JSON alias `surpriseEPSpct`); `nil` when unavailable. - `Updated` — when this data was last updated. ### Helper Methods - `String() string` — a one-line summary of the report (EPS fields render as `n/a` when `nil`). --- # News (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/stocks/news/index.md Retrieve news articles for a stock symbol. > [!CAUTION] > **[Beta Endpoint]** > > The news endpoint is currently in beta. Features and response formats may change. ## Making Requests Both call styles live on the `Stocks` service (`client.Stocks`). Use `GetNews` for the simplest call, or `News` when you also need the raw response and rate-limit metadata. | Method | Return | Description | |----------------------------------|----------------------------------------------|--------------------------------------------------------------------------------------| | **`GetNews(symbol, opts...)`** | `([]NewsArticle, error)` | Convenience wrapper; uses `context.Background()` and discards the response metadata. | | **`News(ctx, symbol, opts...)`** | `([]NewsArticle, *response.Response, error)` | Context-aware; also returns the raw response + rate-limit metadata. | ## News ```go func (s *Service) News(ctx context.Context, symbol string, opts ...NewsOption) ([]NewsArticle, *response.Response, error) func (s *Service) GetNews(symbol string, opts ...NewsOption) ([]NewsArticle, error) ``` Fetch news articles for one symbol. With no options the API returns its default set of recent articles. ### Parameters - `ctx` (`context.Context`) — controls cancellation and deadlines (context method only). - `symbol` (`string`) — the stock ticker symbol, e.g. `"AAPL"`. Required; an empty string returns a validation error without making a request. - Options (`stocks.NewsOption`): - `stocks.WithNewsWindow(w DateWindow)` — set the publication date range as a single `DateWindow` value. Build the window with any of the shared [date-window constructors](#date-windows), for example `stocks.OnDate(d)` or `stocks.Between(from, to)`. ### Returns - `[]NewsArticle` — the matching articles. - `*response.Response` — raw response + rate-limit metadata (context method only). - `error` — non-nil on request/validation failure. ### Notes - Only the calendar date of each `time.Time` in a window is used; the time-of-day and zone are ignored. - If the API responds with 404 because no data is available, `News` returns a `nil` slice, a `nil` error, and a non-nil `*response.Response` whose `NoData` field is `true`. ### Get (simple) ```go package main import ( "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() articles, err := client.Stocks.GetNews("AAPL") if err != nil { log.Fatal(err) } for _, a := range articles { fmt.Printf("%s %s\n", a.PublicationDate.Format("2006-01-02"), a.Headline) fmt.Printf(" %s\n", a.Source) } } ``` **Output** ``` 2024-01-25 Apple Reports Record Q1 Earnings https://www.reuters.com/technology/... 2024-01-24 Apple Unveils New Product Lineup https://www.bloomberg.com/... ``` ### Context + Response ```go package main import ( "context" "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() ctx := context.Background() articles, resp, err := client.Stocks.News(ctx, "AAPL") if err != nil { log.Fatal(err) } if resp.NoData { fmt.Println("no news available") return } fmt.Printf("Found %d articles\n", len(articles)) for _, a := range articles { fmt.Println(a) // NewsArticle implements Stringer } fmt.Printf("Requests remaining: %d\n", resp.RateLimit.Remaining) } ``` ### With options ```go package main import ( "context" "fmt" "log" "time" "github.com/MarketDataApp/sdk-go/v2/marketdata" "github.com/MarketDataApp/sdk-go/v2/marketdata/stocks" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() ctx := context.Background() from := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC) to := time.Date(2024, 1, 31, 0, 0, 0, 0, time.UTC) articles, _, err := client.Stocks.News(ctx, "AAPL", stocks.WithNewsWindow(stocks.Between(from, to)), ) if err != nil { log.Fatal(err) } for _, a := range articles { fmt.Printf("- %s (%s)\n", a.Headline, a.PublicationDate.Format("2006-01-02")) } } ``` ## Date Windows `stocks.WithNewsWindow` takes a `DateWindow` — the same sealed-union value used by [Candles](https://www.marketdata.app/docs/sdk/go/stocks/candles) and [Earnings](https://www.marketdata.app/docs/sdk/go/stocks/earnings). Build one with exactly one constructor: | Constructor | Selects | API parameters | |----------------------------|--------------------------------------|---------------------| | `stocks.OnDate(d)` | a single calendar day | `date=d` | | `stocks.Between(from, to)` | an explicit closed range (inclusive) | `from=from&to=to` | | `stocks.Since(from)` | everything from a day onward | `from=from` | | `stocks.Until(to)` | up to and including a day | `to=to` | | `stocks.LastN(n)` | the `n` most recent articles | `countback=n` | | `stocks.LastNUntil(n, to)` | the `n` articles ending at a day | `countback=n&to=to` | ## NewsArticle ```go type NewsArticle struct { Symbol string `json:"symbol"` // Stock ticker symbol Headline string `json:"headline"` // Article headline Content string `json:"content"` // Article content (may be partial) Source string `json:"source"` // URL where the article was published PublicationDate time.Time `json:"publicationDate"` // When the article was published (US Eastern) } ``` Represents a single news article about a stock. Timestamps are normalized to US Eastern time. ### Fields - `Symbol` — the stock ticker symbol the article relates to. - `Headline` — the article headline/title. - `Content` — the article content, which may be partial and may include captions or copyright notices. - `Source` — the URL where the article was published. - `PublicationDate` — when the article was published. ### Helper Methods - `String() string` — a one-line summary of the article. --- # Prices (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/stocks/prices/index.md Get real-time SmartMid midpoint prices for one or more stocks — a lightweight alternative to a full quote when only a single price per symbol is needed. ## Making Requests Both call styles live on the `Stocks` service (`client.Stocks`). Use `GetPrices` for the simplest call, or `Prices` when you need functional options or the raw response and rate-limit metadata. | Method | Return | Description | |-------------------------------------|----------------------------------------|------------------------------------------------------------------------------------------------------------| | **`GetPrices(symbols...)`** | `([]Price, error)` | Convenience wrapper; takes symbols variadically, uses `context.Background()`, and does not accept options. | | **`Prices(ctx, symbols, opts...)`** | `([]Price, *response.Response, error)` | Context-aware; accepts options and also returns the raw response + rate-limit metadata. | ## Prices ```go func (s *Service) Prices(ctx context.Context, symbols []string, opts ...PriceOption) ([]Price, *response.Response, error) func (s *Service) GetPrices(symbols ...string) ([]Price, error) ``` Fetch SmartMid midpoint prices for one or many symbols. ### Parameters - `ctx` (`context.Context`) — controls cancellation and deadlines (context method only). - `symbols` (`[]string` for `Prices`, variadic `...string` for `GetPrices`) — the stock ticker symbols, e.g. `[]string{"AAPL", "MSFT", "GOOG"}`. At least one symbol is required; an empty set returns a validation error without making a request. - Options (`stocks.PriceOption`, `Prices` only — `GetPrices` accepts no options): - `stocks.WithPriceExtended(extended bool)` — control whether prices include extended-hours data. When omitted, the parameter is not sent and the API default applies. ### Returns - `[]Price` — one `Price` per symbol. - `*response.Response` — raw response + rate-limit metadata (context method only). - `error` — non-nil on request/validation failure. ### Notes - The SmartMid model is Market Data's derived midpoint between bid and ask. - For a single symbol the SDK uses the path form `stocks/prices/{symbol}/`; for multiple symbols it uses the query form `stocks/prices/?symbols=...`. - To pass options and still request multiple symbols, use `Prices` — `GetPrices` is options-free by design. - If the API responds with 404 because no data is available, `Prices` returns a `nil` slice, a `nil` error, and a non-nil `*response.Response` whose `NoData` field is `true`. ### Get (simple) ```go package main import ( "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() prices, err := client.Stocks.GetPrices("AAPL", "MSFT", "GOOG") if err != nil { log.Fatal(err) } for _, p := range prices { fmt.Printf("%s: $%.2f (%.2f%%)\n", p.Symbol, p.Mid, p.ChangePercent) } } ``` **Output** ``` AAPL: $186.19 (0.71%) MSFT: $380.55 (1.25%) GOOG: $143.12 (-0.50%) ``` ### Context + Response ```go package main import ( "context" "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() ctx := context.Background() prices, resp, err := client.Stocks.Prices(ctx, []string{"AAPL", "MSFT", "GOOG"}) if err != nil { log.Fatal(err) } if resp.NoData { fmt.Println("no prices available") return } for _, p := range prices { fmt.Println(p) // Price implements Stringer } fmt.Printf("Requests remaining: %d\n", resp.RateLimit.Remaining) } ``` ### With options ```go package main import ( "context" "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" "github.com/MarketDataApp/sdk-go/v2/marketdata/stocks" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() ctx := context.Background() // Regular-session prices only. prices, _, err := client.Stocks.Prices(ctx, []string{"AAPL", "MSFT"}, stocks.WithPriceExtended(false), ) if err != nil { log.Fatal(err) } for _, p := range prices { fmt.Printf("%s: $%.2f (regular session)\n", p.Symbol, p.Mid) } } ``` ## Price ```go type Price struct { Symbol string `json:"symbol"` // Stock ticker symbol Mid float64 `json:"mid"` // SmartMid midpoint price Change float64 `json:"change"` // Price change from previous close ChangePercent float64 `json:"changepct"` // Percentage change from previous close Updated time.Time `json:"updated"` // When this price was last updated (US Eastern) } ``` Represents a SmartMid midpoint price for a stock. It is a lightweight alternative to [`Quote`](https://www.marketdata.app/docs/sdk/go/stocks/quote) when only a single price per symbol is needed. Timestamps are normalized to US Eastern time. ### Fields - `Symbol` — the stock ticker symbol. - `Mid` — the SmartMid midpoint price. - `Change` — price change in dollars from the previous close. - `ChangePercent` — the change from the previous close (JSON alias `changepct`). - `Updated` — timestamp of the price. ### Helper Methods - `String() string` — a one-line summary of the price. --- # Stock Quote (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/stocks/quote/index.md Get a real-time price quote for a single stock symbol, including bid/ask, last trade, change, and volume. ## Making Requests Both call styles live on the `Stocks` service (`client.Stocks`). Use `GetQuote` for the simplest call, or `Quote` when you also need the raw response and rate-limit metadata. | Method | Return | Description | |-----------------------------------|---------------------------------------|--------------------------------------------------------------------------------------| | **`GetQuote(symbol, opts...)`** | `(*Quote, error)` | Convenience wrapper; uses `context.Background()` and discards the response metadata. | | **`Quote(ctx, symbol, opts...)`** | `(*Quote, *response.Response, error)` | Context-aware; also returns the raw response + rate-limit metadata. | ## Quote ```go func (s *Service) Quote(ctx context.Context, symbol string, opts ...QuoteOption) (*Quote, *response.Response, error) func (s *Service) GetQuote(symbol string, opts ...QuoteOption) (*Quote, error) ``` Fetch a real-time quote for one stock symbol. ### Parameters - `ctx` (`context.Context`) — controls cancellation and deadlines (context method only). - `symbol` (`string`) — the stock ticker symbol, e.g. `"AAPL"`. Required; an empty string returns a validation error without making a request. - Options (`stocks.QuoteOption`): - `stocks.WithFiftyTwoWeek(enabled bool)` — request 52-week high/low data, populating the `FiftyTwoWeekHigh` and `FiftyTwoWeekLow` fields. Defaults to off; passing `false` is equivalent to omitting the option. - `stocks.WithExtended(extended bool)` — control whether the quote includes extended-hours (pre-market/after-hours) data. When omitted, the parameter is not sent and the API default applies. Pass `true` to return the most recent quote regardless of session, or `false` to restrict to the primary session. ### Returns - `*Quote` — the quote for the requested symbol. - `*response.Response` — raw response + rate-limit metadata (context method only). - `error` — non-nil on request/validation failure. ### Notes - If the API responds with 404 because no data is available, `Quote` returns a `nil` quote, a `nil` error, and a non-nil `*response.Response` whose `NoData` field is `true`. `GetQuote` discards that metadata, so a no-data result is simply a `nil` quote with a `nil` error. - If the API reports success but returns no quote for the symbol, `Quote` returns a `*stocks.QuoteNotFoundError` (detect it with `errors.As`). ### Get (simple) ```go package main import ( "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() quote, err := client.Stocks.GetQuote("AAPL") if err != nil { log.Fatal(err) } fmt.Printf("Symbol: %s\n", quote.Symbol) fmt.Printf("Last: $%.2f\n", quote.Last) fmt.Printf("Bid: $%.2f x %d\n", quote.Bid, quote.BidSize) fmt.Printf("Ask: $%.2f x %d\n", quote.Ask, quote.AskSize) fmt.Printf("Change: $%.2f (%.2f%%)\n", quote.Change, quote.ChangePercent) fmt.Printf("Volume: %d\n", quote.Volume) } ``` **Output** ``` Symbol: AAPL Last: $186.19 Bid: $186.18 x 200 Ask: $186.20 x 300 Change: $1.05 (0.57%) Volume: 48234500 ``` ### Context + Response ```go package main import ( "context" "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() ctx := context.Background() quote, resp, err := client.Stocks.Quote(ctx, "AAPL") if err != nil { log.Fatal(err) } if resp.NoData { fmt.Println("no quote available") return } fmt.Println(quote) // Quote implements Stringer // Inspect rate-limit metadata carried on the response. fmt.Printf("Requests remaining: %d\n", resp.RateLimit.Remaining) } ``` **Output** ``` AAPL Last: $186.19 Bid: 186.18 (200) Ask: 186.20 (300) Mid: 186.19 Chg: 1.05 (0.57%) Vol: 48234500 Updated: 2024-01-15 16:00:00 Requests remaining: 99998 ``` ### With options ```go package main import ( "context" "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" "github.com/MarketDataApp/sdk-go/v2/marketdata/stocks" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() ctx := context.Background() // 52-week data, regular session only. quote, _, err := client.Stocks.Quote(ctx, "AAPL", stocks.WithFiftyTwoWeek(true), stocks.WithExtended(false), ) if err != nil { log.Fatal(err) } fmt.Printf("%s: $%.2f\n", quote.Symbol, quote.Last) fmt.Printf("52-week high: $%.2f\n", quote.FiftyTwoWeekHigh) fmt.Printf("52-week low: $%.2f\n", quote.FiftyTwoWeekLow) } ``` **Output** ``` AAPL: $186.19 52-week high: $199.62 52-week low: $164.08 ``` ## Quote ```go type Quote struct { Symbol string `json:"symbol"` // Stock ticker symbol Ask float64 `json:"ask"` // Current ask price AskSize int `json:"askSize"` // Size of the ask Bid float64 `json:"bid"` // Current bid price BidSize int `json:"bidSize"` // Size of the bid Mid float64 `json:"mid"` // Midpoint between bid and ask Last float64 `json:"last"` // Last trade price Change float64 `json:"change"` // Price change from previous close ChangePercent float64 `json:"changepct"` // Percentage change from previous close Volume int64 `json:"volume"` // Trading volume Updated time.Time `json:"updated"` // When the quote was last updated FiftyTwoWeekHigh float64 `json:"52weekHigh,omitempty"` // 52-week high (WithFiftyTwoWeek) FiftyTwoWeekLow float64 `json:"52weekLow,omitempty"` // 52-week low (WithFiftyTwoWeek) } ``` Represents a real-time stock quote. Timestamps are normalized to US Eastern time. `FiftyTwoWeekHigh` and `FiftyTwoWeekLow` are populated only when the quote was requested with `stocks.WithFiftyTwoWeek(true)`. ### Fields - `Symbol` — the stock ticker symbol. - `Ask` / `AskSize` — the current ask price and the number of shares offered at it. - `Bid` / `BidSize` — the current bid price and the number of shares bid at it. - `Mid` — the midpoint between bid and ask. - `Last` — the last traded price. - `Change` — price change in dollars from the previous close. - `ChangePercent` — the change from the previous close (JSON alias `changepct`). - `Volume` — trading volume for the session. - `Updated` — timestamp of the quote (US Eastern). - `FiftyTwoWeekHigh` / `FiftyTwoWeekLow` — 52-week high/low, present only with `WithFiftyTwoWeek`. ### Helper Methods - `Spread() float64` — the bid-ask spread (`Ask - Bid`). - `SpreadPercent() float64` — the spread as a percentage of `Mid`. - `String() string` — a one-line summary of the quote. --- # Stock Quotes (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/stocks/quotes/index.md Get real-time price quotes for multiple stock symbols in a single API request. ## Making Requests Both call styles live on the `Stocks` service (`client.Stocks`). Use `GetQuotes` for the simplest call, or `Quotes` when you need functional options or the raw response and rate-limit metadata. | Method | Return | Description | |-------------------------------------|----------------------------------------|------------------------------------------------------------------------------------------------------------| | **`GetQuotes(symbols...)`** | `([]Quote, error)` | Convenience wrapper; takes symbols variadically, uses `context.Background()`, and does not accept options. | | **`Quotes(ctx, symbols, opts...)`** | `([]Quote, *response.Response, error)` | Context-aware; accepts options and also returns the raw response + rate-limit metadata. | ## Quotes ```go func (s *Service) Quotes(ctx context.Context, symbols []string, opts ...QuoteOption) ([]Quote, *response.Response, error) func (s *Service) GetQuotes(symbols ...string) ([]Quote, error) ``` Fetch real-time quotes for many symbols at once. ### Parameters - `ctx` (`context.Context`) — controls cancellation and deadlines (context method only). - `symbols` (`[]string` for `Quotes`, variadic `...string` for `GetQuotes`) — the stock ticker symbols, e.g. `[]string{"AAPL", "MSFT", "GOOG"}`. At least one symbol is required; an empty set returns a validation error without making a request. - Options (`stocks.QuoteOption`, `Quotes` only — `GetQuotes` accepts no options): - `stocks.WithFiftyTwoWeek(enabled bool)` — request 52-week high/low data for each quote. Defaults to off. - `stocks.WithExtended(extended bool)` — control whether quotes include extended-hours data. When omitted, the parameter is not sent and the API default applies. ### Returns - `[]Quote` — one `Quote` per symbol returned by the API. - `*response.Response` — raw response + rate-limit metadata (context method only). - `error` — non-nil on request/validation failure. ### Notes - If the API responds with 404 because no data is available, `Quotes` returns a `nil` slice, a `nil` error, and a non-nil `*response.Response` whose `NoData` field is `true`. `GetQuotes` discards that metadata, so a no-data result is simply a `nil` slice with a `nil` error. - To pass options and still request multiple symbols, use `Quotes` — `GetQuotes` is options-free by design. ### Get (simple) ```go package main import ( "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() quotes, err := client.Stocks.GetQuotes("AAPL", "MSFT", "GOOG") if err != nil { log.Fatal(err) } for _, q := range quotes { fmt.Printf("%s: $%.2f (%.2f%%)\n", q.Symbol, q.Last, q.ChangePercent) } } ``` **Output** ``` AAPL: $186.19 (0.57%) MSFT: $380.55 (0.61%) GOOG: $143.12 (-0.31%) ``` ### Context + Response ```go package main import ( "context" "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() ctx := context.Background() quotes, resp, err := client.Stocks.Quotes(ctx, []string{"AAPL", "MSFT", "GOOG"}) if err != nil { log.Fatal(err) } if resp.NoData { fmt.Println("no quotes available") return } for _, q := range quotes { fmt.Println(q) // Quote implements Stringer } fmt.Printf("Requests remaining: %d\n", resp.RateLimit.Remaining) } ``` ### With options ```go package main import ( "context" "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" "github.com/MarketDataApp/sdk-go/v2/marketdata/stocks" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() ctx := context.Background() watchlist := []string{"AAPL", "MSFT", "GOOG"} quotes, _, err := client.Stocks.Quotes(ctx, watchlist, stocks.WithFiftyTwoWeek(true), ) if err != nil { log.Fatal(err) } for _, q := range quotes { pctFromHigh := (q.Last - q.FiftyTwoWeekHigh) / q.FiftyTwoWeekHigh * 100 fmt.Printf("%-5s $%8.2f 52wk %.2f-%.2f (%+.1f%% from high)\n", q.Symbol, q.Last, q.FiftyTwoWeekLow, q.FiftyTwoWeekHigh, pctFromHigh) } } ``` ## Quote `Quotes` returns a slice of the same `Quote` value used by the single-symbol [Quote](https://www.marketdata.app/docs/sdk/go/stocks/quote) endpoint. ```go type Quote struct { Symbol string `json:"symbol"` // Stock ticker symbol Ask float64 `json:"ask"` // Current ask price AskSize int `json:"askSize"` // Size of the ask Bid float64 `json:"bid"` // Current bid price BidSize int `json:"bidSize"` // Size of the bid Mid float64 `json:"mid"` // Midpoint between bid and ask Last float64 `json:"last"` // Last trade price Change float64 `json:"change"` // Price change from previous close ChangePercent float64 `json:"changepct"` // Percentage change from previous close Volume int64 `json:"volume"` // Trading volume Updated time.Time `json:"updated"` // When the quote was last updated FiftyTwoWeekHigh float64 `json:"52weekHigh,omitempty"` // 52-week high (WithFiftyTwoWeek) FiftyTwoWeekLow float64 `json:"52weekLow,omitempty"` // 52-week low (WithFiftyTwoWeek) } ``` ### Fields - `Symbol` — the stock ticker symbol. - `Ask` / `AskSize` — the current ask price and the number of shares offered at it. - `Bid` / `BidSize` — the current bid price and the number of shares bid at it. - `Mid` — the midpoint between bid and ask. - `Last` — the last traded price. - `Change` — price change in dollars from the previous close. - `ChangePercent` — the change from the previous close (JSON alias `changepct`). - `Volume` — trading volume for the session. - `Updated` — timestamp of the quote (US Eastern). - `FiftyTwoWeekHigh` / `FiftyTwoWeekLow` — 52-week high/low, present only with `WithFiftyTwoWeek`. ### Helper Methods - `Spread() float64` — the bid-ask spread (`Ask - Bid`). - `SpreadPercent() float64` — the spread as a percentage of `Mid`. - `String() string` — a one-line summary of the quote. --- # Utilities (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/utilities/index.md Report on the Market Data API itself rather than on market data: check API health and uptime, echo back your request headers for debugging, and inspect the authenticated account's API credit state. The Go SDK exposes these endpoints through the `client.Utilities` service. ## Utilities Endpoints --- # API Status (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/utilities/api-status/index.md Check the current status of the Market Data API and its historical uptime. ## Making Requests Fetch API status with the `Status` method on the `client.Utilities` service. It takes no parameters and no options, and comes in two call styles: | Method | Return | Description | |-------------------|-----------------------------------------------------|------------------------------------------------------------------------| | **`GetStatus()`** | `(*utilities.APIStatus, error)` | Convenience; uses a background context and discards response metadata. | | **`Status(ctx)`** | `(*utilities.APIStatus, *response.Response, error)` | Context-aware; also returns the raw response plus rate-limit metadata. | ## Status ```go func (s *Service) Status(ctx context.Context) (*utilities.APIStatus, *response.Response, error) func (s *Service) GetStatus() (*utilities.APIStatus, error) ``` Get the current status of the Market Data API from the unversioned `/status/` endpoint. The API reports per-service status; the returned [`APIStatus`](#apistatus) aggregates it, with `Status` set to `"online"` only when every service is online. The status is updated every 5 minutes and remains accessible even when the API is experiencing issues. ### Parameters - `ctx` (`context.Context`) — request context for cancellation and deadlines (context method only). This endpoint takes no functional options. ### Returns - `*utilities.APIStatus` — the aggregated [`APIStatus`](#apistatus). - `*response.Response` — raw response plus rate-limit metadata (context method only). - `error` — non-nil on request or decoding failure. ### Notes - This endpoint is public and continues to respond even when the API is offline. - In the unlikely event the endpoint responds `404`, both call styles return a `nil` `APIStatus` and a `nil` error; with the context method the returned `*response.Response` has its `NoData` field set to `true`. ### Get (simple) ```go package main import ( "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient() // reads MARKETDATA_TOKEN from env / .env if err != nil { log.Fatal(err) } defer client.Close() status, err := client.Utilities.GetStatus() if err != nil { log.Fatal(err) } fmt.Println(status) } ``` **Output** ``` API online (30d: 99.98% 90d: 99.95%) Updated: 2026-07-12 09:30:00 ``` ### Context + Response ```go package main import ( "context" "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() status, resp, err := client.Utilities.Status(context.Background()) if err != nil { log.Fatal(err) } if resp.NoData || status == nil { fmt.Println("Status unavailable.") return } if status.IsOnline() { fmt.Println("API is online!") } fmt.Printf("30-day uptime: %.2f%%\n", status.Uptime30d) fmt.Printf("90-day uptime: %.2f%%\n", status.Uptime90d) } ``` **Output** ``` API is online! 30-day uptime: 99.98% 90-day uptime: 99.95% ``` ## APIStatus ```go type APIStatus struct { Status string `json:"s"` // Status indicates if the API is online. Uptime30d float64 `json:"uptime30d"` // Uptime30d is the 30-day uptime percentage. Uptime90d float64 `json:"uptime90d"` // Uptime90d is the 90-day uptime percentage. Updated time.Time `json:"updated"` // Updated is when the status was last checked. } ``` `APIStatus` represents the aggregated status of the Market Data API. `Status` is `"online"` only when every underlying service is online, otherwise `"offline"`. `Uptime30d` and `Uptime90d` are the average 30-day and 90-day uptime percentages across services. ### Fields - `Status` (`string`) — `"online"` or `"offline"`. - `Uptime30d` (`float64`) — the 30-day uptime percentage. - `Uptime90d` (`float64`) — the 90-day uptime percentage. - `Updated` (`time.Time`) — when the status was last checked, normalized to US Eastern. ### Methods - `String() string` — a one-line summary, e.g. `API online (30d: 99.98% 90d: 99.95%) Updated: 2026-07-12 09:30:00`. - `IsOnline() bool` — reports whether the API is currently online (`Status == "online"`). --- # Headers (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/utilities/headers/index.md Echo back the request headers your application is sending — useful for debugging authentication and proxy issues. ## Making Requests Fetch your request headers with the `Headers` method on the `client.Utilities` service. It takes no parameters and no options, and comes in two call styles: | Method | Return | Description | |--------------------|---------------------------------------------------|------------------------------------------------------------------------| | **`GetHeaders()`** | `(*utilities.Headers, error)` | Convenience; uses a background context and discards response metadata. | | **`Headers(ctx)`** | `(*utilities.Headers, *response.Response, error)` | Context-aware; also returns the raw response plus rate-limit metadata. | ## Headers ```go func (s *Service) Headers(ctx context.Context) (*utilities.Headers, *response.Response, error) func (s *Service) GetHeaders() (*utilities.Headers, error) ``` Get the request headers your application is sending, echoed back by the unversioned `/headers/` endpoint. The returned [`Headers`](#headers-type) maps header names to the values the server received, which is useful for debugging authentication and proxy problems. ### Parameters - `ctx` (`context.Context`) — request context for cancellation and deadlines (context method only). This endpoint takes no functional options. ### Returns - `*utilities.Headers` — the echoed [`Headers`](#headers-type). - `*response.Response` — raw response plus rate-limit metadata (context method only). - `error` — non-nil on request or decoding failure. ### Notes - Sensitive header values (such as `Authorization`) are partially redacted by the server for security. - In the unlikely event the endpoint responds `404`, both call styles return a `nil` result and a `nil` error; with the context method the returned `*response.Response` has its `NoData` field set to `true`. ### Get (simple) ```go package main import ( "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient() // reads MARKETDATA_TOKEN from env / .env if err != nil { log.Fatal(err) } defer client.Close() headers, err := client.Utilities.GetHeaders() if err != nil { log.Fatal(err) } if _, ok := headers.Headers["Authorization"]; ok { fmt.Println("Authorization header is being sent.") } else { fmt.Println("Authorization header is missing — check MARKETDATA_TOKEN.") } } ``` **Output** ``` Authorization: Bearer 1AB...***REDACTED*** Accept-Encoding: gzip User-Agent: sdk-go/2.0.0 Host: api.marketdata.app ``` ### Context + Response ```go package main import ( "context" "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() headers, resp, err := client.Utilities.Headers(context.Background()) if err != nil { log.Fatal(err) } if resp.NoData || headers == nil { fmt.Println("No headers returned.") return } for name, value := range headers.Headers { fmt.Printf("%s: %s\n", name, value) } } ``` **Output** ``` Authorization: Bearer 1AB...***REDACTED*** Accept-Encoding: gzip User-Agent: sdk-go/2.0.0 Host: api.marketdata.app ``` ## Headers ```go type Headers struct { Headers map[string]string `json:"headers"` // Headers maps header names to values. } ``` `Headers` represents the request headers echoed back by the API. It is useful for debugging authentication issues. ### Fields - `Headers` (`map[string]string`) — a map of header name to the value the server received. ### Methods - `String() string` — a concise summary of all header name/value pairs, e.g. `Headers{Authorization: Bearer 1AB...***REDACTED***, Host: api.marketdata.app}`. --- # User (Go SDK) Source: https://www.marketdata.app/docs/sdk/go/utilities/user/index.md Retrieve information about the authenticated account: its API credit state for the current window and its options data entitlement. ## Making Requests Fetch account information with the `User` method on the `client.Utilities` service. It takes no parameters and no options, but requires a valid API token. It comes in two call styles: | Method | Return | Description | |-----------------|----------------------------------------------------|------------------------------------------------------------------------| | **`GetUser()`** | `(*utilities.UserInfo, error)` | Convenience; uses a background context and discards response metadata. | | **`User(ctx)`** | `(*utilities.UserInfo, *response.Response, error)` | Context-aware; also returns the raw response plus rate-limit metadata. | ## User ```go func (s *Service) User(ctx context.Context) (*utilities.UserInfo, *response.Response, error) func (s *Service) GetUser() (*utilities.UserInfo, error) ``` Get information about the authenticated user and their account from the unversioned `/user/` endpoint. The returned [`UserInfo`](#userinfo) carries the account's API credit state for the current window — taken from the `x-api-ratelimit-*` response headers — and the account's options data entitlement, taken from the response body. ### Parameters - `ctx` (`context.Context`) — request context for cancellation and deadlines (context method only). This endpoint takes no functional options, but it requires a valid API token. ### Returns - `*utilities.UserInfo` — the account's [`UserInfo`](#userinfo). - `*response.Response` — raw response plus rate-limit metadata (context method only). - `error` — non-nil on request or decoding failure. ### Notes - Rate limits track **credits**. Most requests consume 1 credit, but bulk or options requests may consume several. - In the unlikely event the endpoint responds `404`, both call styles return a `nil` result and a `nil` error; with the context method the returned `*response.Response` has its `NoData` field set to `true`. ### Get (simple) ```go package main import ( "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient() // reads MARKETDATA_TOKEN from env / .env if err != nil { log.Fatal(err) } defer client.Close() user, err := client.Utilities.GetUser() if err != nil { log.Fatal(err) } fmt.Println(user) } ``` **Output** ``` User{Credits: 99847/100000, Resets: 2026-07-13 00:00:00, Options: OPRA data delayed 15 minutes} ``` ### Context + Response ```go package main import ( "context" "fmt" "log" "github.com/MarketDataApp/sdk-go/v2/marketdata" ) func main() { client, err := marketdata.NewClient(marketdata.WithToken("YOUR_TOKEN")) if err != nil { log.Fatal(err) } defer client.Close() user, resp, err := client.Utilities.User(context.Background()) if err != nil { log.Fatal(err) } if resp.NoData || user == nil { fmt.Println("No account information returned.") return } fmt.Printf("Credits: %d/%d\n", user.CreditsRemaining, user.CreditLimit) fmt.Printf("Consumed by this request: %d\n", user.CreditsConsumed) fmt.Printf("Window resets at: %s\n", user.ResetAt.Format("2006-01-02 15:04:05")) fmt.Printf("Options data: %s\n", user.OptionsDataPermissions) } ``` **Output** ``` Credits: 99847/100000 Consumed by this request: 1 Window resets at: 2026-07-13 00:00:00 Options data: OPRA data delayed 15 minutes ``` ## UserInfo ```go type UserInfo struct { CreditLimit int // Total API credit allowance for the current window. CreditsRemaining int // API credits left in the current window. CreditsConsumed int // API credits consumed by the user info request itself. ResetAt time.Time // When the current credit window resets. OptionsDataPermissions string // The account's options data entitlement. } ``` `UserInfo` describes the authenticated account: the API credit state for the current window (taken from the `x-api-ratelimit-*` response headers) and the account's options data entitlement (taken from the response body). ### Fields - `CreditLimit` (`int`) — the account's total API credit allowance for the current window. - `CreditsRemaining` (`int`) — the number of API credits left in the current window. - `CreditsConsumed` (`int`) — the number of API credits consumed by the user info request itself (not cumulative). - `ResetAt` (`time.Time`) — when the current credit window resets. - `OptionsDataPermissions` (`string`) — the account's options data entitlement, e.g. `"OPRA data delayed 15 minutes"`. ### Methods - `String() string` — a one-line summary, e.g. `User{Credits: 99847/100000, Resets: 2026-07-13 00:00:00, Options: OPRA data delayed 15 minutes}`. --- # Java SDK Source: https://www.marketdata.app/docs/sdk/java/index.md Welcome to the Market Data Java SDK documentation. This SDK lets you integrate Market Data services into any JVM application. It ships typed, records-based responses, sync **and** async (`CompletableFuture`) variants for every endpoint, automatic retries and rate-limit tracking, and a sealed exception hierarchy you can branch on exhaustively. The SDK is written in Java (JDK 17+) with no Kotlin runtime dependency, but it is designed to be **idiomatic from Kotlin too**: nullability is annotated (no platform types), the client is `AutoCloseable` (so `use {}` works), and async methods return `CompletableFuture`, which Kotlin interops with directly. ## Quick Start ### Java ```java import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.stocks.StockQuoteRequest; // The no-arg constructor reads MARKETDATA_TOKEN from the environment (or a .env file) // and validates it on startup. The client is AutoCloseable. try (MarketDataClient client = new MarketDataClient()) { // A single quote — quote(...) returns a list; a single symbol is row 0. var quote = client.stocks().quote(StockQuoteRequest.of("AAPL")).values().get(0); System.out.println(quote.symbol() + " last=" + quote.last()); } ``` ### Kotlin ```kotlin import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.stocks.StockQuoteRequest // `use {}` closes the client when the block ends — the Kotlin equivalent of try-with-resources. MarketDataClient().use { client -> val quote = client.stocks().quote(StockQuoteRequest.of("AAPL")).values()[0] println("${quote.symbol()} last=${quote.last()}") } ``` ## Open Source The SDK is open source and available on GitHub. Feel free to contribute to the project, report bugs, or request new features. - [Java SDK on GitHub](https://github.com/MarketDataApp/sdk-java/) ## 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 all the tools you need to access real-time stock and options prices, historical data, and much more. ### Getting Started 1. [Install the SDK](https://www.marketdata.app/docs/sdk/java/installation) into a Gradle or Maven project. 2. Set up your [authentication token](https://www.marketdata.app/docs/sdk/java/authentication) to access the API. 3. Learn about the [client](https://www.marketdata.app/docs/sdk/java/client) and how to make your first API requests. 4. Configure [Settings](https://www.marketdata.app/docs/sdk/java/settings) to customize output format, date format, and other universal parameters. 5. Explore the available endpoints for [stocks](https://www.marketdata.app/docs/sdk/java/stocks), [options](https://www.marketdata.app/docs/sdk/java/options), [funds](https://www.marketdata.app/docs/sdk/java/funds), [markets](https://www.marketdata.app/docs/sdk/java/markets), and [utilities](https://www.marketdata.app/docs/sdk/java/utilities). ### 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-java/issues). Please only open issues if you find a bug. Use our helpdesk for general questions or implementation assistance. --- # Authentication (Java SDK) Source: https://www.marketdata.app/docs/sdk/java/authentication/index.md The Market Data API uses a **Bearer Token** for authentication. The token is required for almost every request. 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 Java 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](https://www.marketdata.app/docs/sdk/java/client) On startup, the SDK looks for the `MARKETDATA_TOKEN` environment variable. If found, it uses that token for all requests. The SDK also loads a `.env` file automatically from the working directory if one is present. > [!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 Environment Variable In The Console ### Mac / Linux This command sets the environment variable for the current session only. If you open a new terminal or restart your computer, it will not persist. ```bash export MARKETDATA_TOKEN="your_api_token" ``` #### Make The Variable Persistent Add the `export` line to your shell's profile script (`~/.zshrc`, `~/.bashrc`, `~/.bash_profile`, etc.), then restart your terminal or run `source ~/.zshrc` (adjusting for your shell). ### Windows `setx` sets the variable permanently, but it is not available in the current Command Prompt session — open a new one after running it. ```bash setx MARKETDATA_TOKEN "your_api_token" ``` ### Using a .env File The SDK automatically loads a `.env` file from your working directory at startup. Create a file named `.env` in your project root: ```ini 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 Verify your authentication is working by making a test request against `SPY` (or any symbol that requires authentication). Do **not** use `AAPL` to test authentication — `AAPL` is a free test symbol and returns data even when you are not authenticated. ### Java ```java import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.stocks.StockQuoteRequest; import com.marketdata.sdk.exception.AuthenticationError; // No need to pass a token here — the SDK reads MARKETDATA_TOKEN automatically. try (MarketDataClient client = new MarketDataClient()) { var quote = client.stocks().quote(StockQuoteRequest.of("SPY")).values().get(0); System.out.println(quote.symbol() + " last=" + quote.last()); } catch (AuthenticationError e) { System.out.println("Authentication failed: " + e.getMessage()); } ``` ### Kotlin ```kotlin import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.stocks.StockQuoteRequest import com.marketdata.sdk.exception.AuthenticationError MarketDataClient().use { client -> try { val quote = client.stocks().quote(StockQuoteRequest.of("SPY")).values()[0] println("${quote.symbol()} last=${quote.last()}") } catch (e: AuthenticationError) { println("Authentication failed: ${e.message}") } } ``` ## Passing the Token Directly If you prefer to pass the token explicitly (not recommended for production code), use the four-argument constructor `(apiKey, baseUrl, apiVersion, validateOnStartup)`. Pass `null` for any slot you want resolved from the cascade or left at its default. ### Java ```java import com.marketdata.sdk.MarketDataClient; try (MarketDataClient client = new MarketDataClient("your_token_here", null, null, true)) { // ... make requests } ``` ### Kotlin ```kotlin import com.marketdata.sdk.MarketDataClient MarketDataClient("your_token_here", null, null, true).use { client -> // ... make requests } ``` > [!NOTE] > **[Demo mode]** > > If no token is found anywhere in the cascade, the SDK runs in **demo mode** — startup validation is skipped and you can call the free, public endpoints (such as `AAPL` quotes and `utilities().status()`). ## Next Steps After successful authentication, read the overview of how the [client](https://www.marketdata.app/docs/sdk/java/client) works, then configure [Settings](https://www.marketdata.app/docs/sdk/java/settings) to customize output format, date format, and other universal parameters. --- # Client (Java SDK) Source: https://www.marketdata.app/docs/sdk/java/client/index.md The `MarketDataClient` is the entry point to the SDK. It handles API requests, response parsing, rate-limit tracking, retries with exponential backoff, and logging. A single client gives you access to all five resources: stocks, options, funds, markets, and utilities. The client is immutable, thread-safe, and `AutoCloseable`. Create one per application (it holds a shared HTTP client and a 50-request concurrency pool) and close it when you're done. ## Get Started Quickly 1. Review the [authentication documentation](https://www.marketdata.app/docs/sdk/java/authentication) to learn how to set your API token. 2. Create a [`MarketDataClient`](#marketdataclient) and use it to make requests. 3. Reach a resource through `client.stocks()`, `client.options()`, etc. 4. Track your [rate limit](#rate-limits) to see how many API credits remain. 5. Configure [Settings](https://www.marketdata.app/docs/sdk/java/settings) to customize output format, date format, and other universal parameters. ## MarketDataClient ```java public final class MarketDataClient implements AutoCloseable { // Production: resolves everything from the configuration cascade and // validates the token on startup. public MarketDataClient(); // Full control: any null falls back to the cascade / default for that slot. public MarketDataClient( @Nullable String apiKey, @Nullable String baseUrl, // default: https://api.marketdata.app @Nullable String apiVersion, // default: v1 boolean validateOnStartup); // Resources public StocksResource stocks(); public OptionsResource options(); public FundsResource funds(); public MarketsResource markets(); public UtilitiesResource utilities(); // Latest client-wide rate-limit snapshot (or null before the first request) public @Nullable RateLimitSnapshot getRateLimits(); // Releases the shared HTTP client @Override public void close(); } ``` ### Constructors The **no-arg constructor** is what you'll use in production. It resolves the token and settings from the [configuration cascade](https://www.marketdata.app/docs/sdk/java/settings#configuration-cascade) and fires a single `GET /user/` request to validate the token before returning. The **four-arg constructor** `(apiKey, baseUrl, apiVersion, validateOnStartup)` lets you set everything explicitly — handy for tests, short-lived runtimes, and serverless platforms where you want to skip the startup round-trip (`validateOnStartup = false`). ### Java ```java import com.marketdata.sdk.MarketDataClient; // Production: token from the cascade, validated on startup. try (MarketDataClient client = new MarketDataClient()) { // ... } // Explicit, no startup validation (e.g. on a Lambda cold start): try (MarketDataClient client = new MarketDataClient("your_token", null, null, false)) { // ... } ``` ### Kotlin ```kotlin import com.marketdata.sdk.MarketDataClient MarketDataClient().use { client -> // ... } MarketDataClient("your_token", null, null, false).use { client -> // ... } ``` > [!NOTE] > **[Fail-fast configuration]** > > A misconfigured base URL or API version is rejected with an `IllegalArgumentException` **at construction**, not later on the first request. Tokens are never printed in full — the client's `toString()` redacts them (long tokens show only the last four characters; short ones are hidden entirely). ## Sync and Async Every endpoint comes in two variants: a blocking call (e.g. `status()`) and an async one (e.g. `statusAsync()`) returning a `CompletableFuture`. They share the same validation, retry, and rate-limit logic — pick whichever fits your code. Internally the SDK is async-first; the sync method is a thin wrapper that joins the future and unwraps the underlying exception. Async pays off when you fan out several requests: total time is about the slowest single call, not the sum. ### Java ```java import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.UtilitiesStatusResponse; import java.util.concurrent.CompletableFuture; try (MarketDataClient client = new MarketDataClient()) { // Sync — blocks and returns the typed response. var sync = client.utilities().status(); System.out.println(sync.values().size() + " services"); // Async — returns immediately with a future. Attach a callback, or join(). CompletableFuture future = client.utilities().statusAsync(); future.thenAccept(resp -> System.out.println(resp.values().size() + " services")); // Fan out several calls in parallel and wait for all of them. var a = client.utilities().statusAsync(); var b = client.utilities().statusAsync(); CompletableFuture.allOf(a, b).join(); } ``` ### Kotlin ```kotlin import com.marketdata.sdk.MarketDataClient import java.util.concurrent.CompletableFuture MarketDataClient().use { client -> // Sync val sync = client.utilities().status() println("${sync.values().size} services") // Async — returns a CompletableFuture (coroutine users can await() it // via kotlinx-coroutines-jdk8). client.utilities().statusAsync() .thenAccept { resp -> println("${resp.values().size} services") } .join() } ``` ### Composing async calls The point of the async variants is to **stay non-blocking** — chain off the future instead of calling `join()` in the middle. Use `thenApply` to transform a result, `thenCompose` to chain a dependent call, and fire independent calls together when they don't depend on each other. ### Java ```java import com.marketdata.sdk.options.OptionsLookupRequest; import com.marketdata.sdk.options.OptionsQuoteRequest; import com.marketdata.sdk.stocks.StockQuoteRequest; import java.util.concurrent.CompletableFuture; // thenApply — transform the result without blocking. CompletableFuture lastPrice = client.stocks() .quoteAsync(StockQuoteRequest.of("AAPL")) .thenApply(resp -> resp.values().get(0).last()); // thenCompose — chain a dependent async call (resolve a symbol, then quote it). var optionQuote = client.options() .lookupAsync(OptionsLookupRequest.of("AAPL 1/16/2026 $200 Call")) .thenCompose(sym -> client.options().quoteAsync(OptionsQuoteRequest.of(sym.values()))); // Independent calls in parallel, then use every result. var aapl = client.stocks().quoteAsync(StockQuoteRequest.of("AAPL")); var msft = client.stocks().quoteAsync(StockQuoteRequest.of("MSFT")); CompletableFuture.allOf(aapl, msft).join(); // wait for both double spread = aapl.join().values().get(0).last() - msft.join().values().get(0).last(); ``` ### Kotlin ```kotlin import com.marketdata.sdk.options.OptionsLookupRequest import com.marketdata.sdk.options.OptionsQuoteRequest import com.marketdata.sdk.stocks.StockQuoteRequest import java.util.concurrent.CompletableFuture // thenApply — transform the result without blocking. val lastPrice = client.stocks() .quoteAsync(StockQuoteRequest.of("AAPL")) .thenApply { resp -> resp.values()[0].last() } // thenCompose — chain a dependent async call. val optionQuote = client.options() .lookupAsync(OptionsLookupRequest.of("AAPL 1/16/2026 \$200 Call")) .thenCompose { sym -> client.options().quoteAsync(OptionsQuoteRequest.of(sym.values())) } // Independent calls in parallel, then use every result. val aapl = client.stocks().quoteAsync(StockQuoteRequest.of("AAPL")) val msft = client.stocks().quoteAsync(StockQuoteRequest.of("MSFT")) CompletableFuture.allOf(aapl, msft).join() val spread = aapl.join().values()[0].last()!! - msft.join().values()[0].last()!! ``` ### Handling errors in async When a request fails, the future completes **exceptionally**. Inside `exceptionally()`, `handle()`, or `whenComplete()`, the throwable you receive is a `java.util.concurrent.CompletionException` — call `getCause()` to reach the underlying `MarketDataException`. (The blocking variants do this unwrapping for you, which is why `try/catch` around a sync call catches the `MarketDataException` directly.) ### Java ```java import com.marketdata.sdk.exception.RateLimitError; import com.marketdata.sdk.stocks.StockQuoteRequest; import java.util.concurrent.CompletionException; client.stocks().quoteAsync(StockQuoteRequest.of("AAPL")) .thenApply(resp -> resp.values().get(0).last()) .exceptionally(ex -> { // `ex` is a CompletionException; the real cause is the MarketDataException. Throwable cause = (ex instanceof CompletionException && ex.getCause() != null) ? ex.getCause() : ex; if (cause instanceof RateLimitError) { System.out.println("Rate limited — backing off"); } return null; // fallback value the rest of the chain will see }) .join(); // handle((value, throwable) -> ...) sees both outcomes in one place; use it when // you want to map success and failure to the same result type. ``` ### Kotlin ```kotlin import com.marketdata.sdk.exception.RateLimitError import com.marketdata.sdk.stocks.StockQuoteRequest import java.util.concurrent.CompletionException client.stocks().quoteAsync(StockQuoteRequest.of("AAPL")) .thenApply { resp -> resp.values()[0].last() } .exceptionally { ex -> val cause = if (ex is CompletionException && ex.cause != null) ex.cause else ex if (cause is RateLimitError) println("Rate limited — backing off") null } .join() ``` > [!NOTE] > **[`join()` vs `get()`]** > > To block on a future, prefer **`join()`** — it throws an unchecked `CompletionException`. `get()` does the same thing but throws the checked `ExecutionException` (plus `InterruptedException`), forcing a `try/catch`. Both wrap the underlying cause identically, so `join()` is usually the friendlier choice. ## The Response Object Every endpoint returns a typed response that wraps the decoded data plus request metadata, with a uniform surface regardless of endpoint or format. ```java public interface MarketDataResponse { T values(); // the typed payload (e.g. List) int statusCode(); // 200, 203, or 404 boolean isNoData(); // true on a 404 "no_data" response @Nullable String requestId(); // server request id, for support tickets java.net.URI requestUrl(); // the absolute request URL @Nullable RateLimitSnapshot rateLimit(); // this request's rate limit String json(); // the raw response body, as sent boolean isJson(); boolean isCsv(); boolean isHtml(); void saveToFile(java.nio.file.Path path); // write the raw body verbatim } ``` ```java var response = client.stocks().quote(StockQuoteRequest.of("AAPL")); response.values(); // List — the part you usually want response.statusCode(); // 200 response.requestId(); // e.g. for a support ticket response.rateLimit(); // this request's rate-limit snapshot (may be null) response.saveToFile(Path.of("aapl.json")); // cache the raw body ``` ## Error Handling Everything the SDK throws is a `MarketDataException`. It is a **sealed** hierarchy — the seven subtypes below are the complete set, so you can branch on them exhaustively and the compiler will tell you if a future major version adds one. Each exception carries support context: `getStatusCode()`, `getRequestId()`, `getRequestUrl()`, and `getSupportInfo()`. | Subtype | When it's thrown | |-----------------------|--------------------------------------------------| | `AuthenticationError` | Missing or invalid token (HTTP 401) | | `BadRequestError` | Invalid parameters (HTTP 4xx) | | `NotFoundError` | The resource doesn't exist (HTTP 404) | | `RateLimitError` | Quota exceeded (HTTP 429); see `getRetryAfter()` | | `ServerError` | API-side failure (HTTP 5xx) | | `NetworkError` | Connection failure or timeout | | `ParseError` | The response could not be decoded | Async calls surface the same exceptions through the `CompletableFuture` (wrapped in a `CompletionException`); the sync wrappers unwrap them so you catch the cause directly. ### Java (JDK 17) ```java import com.marketdata.sdk.exception.*; try { var quote = client.stocks().quote(StockQuoteRequest.of("AAPL")); } catch (AuthenticationError e) { System.out.println("Check your token"); } catch (RateLimitError e) { System.out.println("Rate limited — retry after " + e.getRetryAfter().map(d -> d.toSeconds() + "s").orElse("a moment")); } catch (MarketDataException e) { // Any other case. Attach getSupportInfo() to a bug report. System.out.println(e.getSupportInfo()); } ``` ### Java (JDK 21+) ```java import com.marketdata.sdk.exception.*; // On JDK 21+ the sealed hierarchy enables an exhaustive switch — the compiler // rejects the code if any subtype is left unhandled. try { var quote = client.stocks().quote(StockQuoteRequest.of("AAPL")); } catch (MarketDataException e) { String message = switch (e) { case AuthenticationError a -> "Authentication failed — check your token"; case BadRequestError b -> "Bad request — check your parameters"; case NotFoundError n -> "Not found"; case RateLimitError r -> "Rate limited"; case ServerError s -> "Server error (HTTP " + s.getStatusCode() + ")"; case NetworkError n -> "Network problem — is the API reachable?"; case ParseError p -> "Could not parse the response"; }; System.out.println(message); } ``` ### Kotlin ```kotlin import com.marketdata.sdk.exception.* try { val quote = client.stocks().quote(StockQuoteRequest.of("AAPL")) } catch (e: AuthenticationError) { println("Check your token") } catch (e: RateLimitError) { println("Rate limited") } catch (e: MarketDataException) { println(e.supportInfo) } ``` ## Rate Limits The SDK tracks your rate limit from the `x-api-ratelimit-*` headers on every response. - **Per-request:** `response.rateLimit()` reflects the headers of that specific response. - **Client-wide:** `client.getRateLimits()` returns the latest snapshot seen across all requests. ```java public record RateLimitSnapshot(int limit, int remaining, Instant reset, int consumed) {} ``` ```java var response = client.stocks().quote(StockQuoteRequest.of("AAPL")); var rl = response.rateLimit(); if (rl != null) { System.out.println(rl.remaining() + " / " + rl.limit() + " requests left"); } ``` The SDK also fails fast: if a prior response reported `remaining = 0` and the reset window has not yet elapsed, the next request raises a `RateLimitError` before hitting the network. ## Closing the Client `MarketDataClient` is `AutoCloseable`. Prefer try-with-resources (Java) or `use {}` (Kotlin) so the underlying HTTP client is released. For a long-lived, application-scoped client, call `close()` on shutdown. --- # Funds (Java SDK) Source: https://www.marketdata.app/docs/sdk/java/funds/index.md The Java SDK from Market Data provides methods to streamline your use of the Funds endpoints. These methods provide a typed interface over the underlying HTTP requests and responses, with both sync and async variants. Reach the resource through `client.funds()`. For CSV output use `client.funds().asCsv()`. ## Funds Endpoints --- # Fund Candles (Java SDK) Source: https://www.marketdata.app/docs/sdk/java/funds/candles/index.md Retrieve a mutual fund's NAV (net asset value) OHLC series. ## Making Requests Use the `candles()` method on the `funds` resource, built with `FundCandlesRequest`. Funds report NAV, not traded volume, so there is **no volume column**, and only daily-and-coarser resolutions are available (no intraday). ```java FundCandlesResponse candles(FundCandlesRequest request) CompletableFuture candlesAsync(FundCandlesRequest request) ``` ### FundCandlesRequest ```java FundCandlesRequest.of(FundResolution resolution, String symbol) FundCandlesRequest.builder(FundResolution resolution, String symbol) .date(LocalDate date) // a single day .from(LocalDate from) // window start (inclusive) .to(LocalDate to) // window end (exclusive) .countback(int n) // N candles before `to` .build() ``` ### FundResolution A value type for the candle interval — daily and coarser only: ```java FundResolution.DAILY // also WEEKLY, MONTHLY, YEARLY FundResolution.days(1) FundResolution.weeks(1) FundResolution.of("D") // any raw wire token ``` #### Returns `FundCandlesResponse` wrapping `List`: ```java public record FundCandle( @Nullable ZonedDateTime time, // bar opening moment (America/New_York) @Nullable Double open, // NAV at open @Nullable Double high, @Nullable Double low, @Nullable Double close) // NAV at close — note: no volume ``` ## Examples ### Java ```java import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.funds.FundCandle; import com.marketdata.sdk.funds.FundCandlesRequest; import com.marketdata.sdk.funds.FundResolution; import java.time.LocalDate; try (MarketDataClient client = new MarketDataClient()) { var candles = client.funds().candles( FundCandlesRequest.builder(FundResolution.DAILY, "VFINX") .from(LocalDate.now().minusWeeks(2)) .to(LocalDate.now()) .build()); for (FundCandle bar : candles.values()) { System.out.printf("%s O=%.2f H=%.2f L=%.2f C=%.2f%n", bar.time(), bar.open(), bar.high(), bar.low(), bar.close()); } } ``` ### Kotlin ```kotlin import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.funds.FundCandlesRequest import com.marketdata.sdk.funds.FundResolution import java.time.LocalDate MarketDataClient().use { client -> val candles = client.funds().candles( FundCandlesRequest.builder(FundResolution.DAILY, "VFINX") .from(LocalDate.now().minusWeeks(2)) .to(LocalDate.now()) .build()) for (bar in candles.values()) { println("${bar.time()} close=${bar.close()}") } } ``` --- # Installation (Java SDK) Source: https://www.marketdata.app/docs/sdk/java/installation/index.md This guide will help you install the Market Data Java SDK and configure it for your project. ## Prerequisites - **JDK 17 or newer.** The SDK is compiled for Java 17 and is tested on JDK 17, 21, and 25. - A build tool: [Gradle](https://gradle.org/) or [Maven](https://maven.apache.org/). ## Basic Installation Add the SDK as a dependency. The published artifact is a single JAR; it does **not** pull in a Kotlin standard library or a third-party HTTP client. ### Gradle (Kotlin DSL) ```kotlin // build.gradle.kts dependencies { implementation("app.marketdata:marketdata-sdk-java:1.0.0") } ``` ### Gradle (Groovy DSL) ```groovy // build.gradle dependencies { implementation 'app.marketdata:marketdata-sdk-java:1.0.0' } ``` ### Maven ```xml app.marketdata marketdata-sdk-java 1.0.0 ``` > [!TIP] > Always check the [latest release](https://github.com/MarketDataApp/sdk-java/releases) for the current version number. > [!NOTE] > The Maven **groupId** is `app.marketdata` (the published Maven Central namespace), while the Java **package** you import from is `com.marketdata.sdk` — for example `import com.marketdata.sdk.MarketDataClient;`. The two are intentionally different. ## Kotlin Support Although the SDK is written in Java, Kotlin consumers are a first-class audience: - The public API is null-annotated, so Kotlin sees real nullable/non-null types — not platform types (`String!`). - `MarketDataClient` is `AutoCloseable`, so you can use `client.use { ... }`. - Async methods return `java.util.concurrent.CompletableFuture`. If you use coroutines, bridge it with `kotlinx-coroutines-jdk8`'s `await()` — the SDK does **not** depend on coroutines itself. ## Requirements & Dependencies The SDK keeps its dependency footprint small: - **No third-party HTTP client.** It uses the JDK's built-in `java.net.http.HttpClient` (HTTP/2). - **Jackson** (`jackson-databind`) for JSON decoding. - **JSpecify** nullability annotations — compile-time only, with no runtime cost. ## Next Steps After installation, you'll need to: 1. Set up your [authentication token](https://www.marketdata.app/docs/sdk/java/authentication). 2. Learn about the [client](https://www.marketdata.app/docs/sdk/java/client) and how to make your first API requests. 3. Configure [Settings](https://www.marketdata.app/docs/sdk/java/settings) to customize output format, date format, and other universal parameters. --- # Markets (Java SDK) Source: https://www.marketdata.app/docs/sdk/java/markets/index.md The Java SDK from Market Data provides methods to streamline your use of the Markets endpoints. These methods provide a typed interface over the underlying HTTP requests and responses, with both sync and async variants. Reach the resource through `client.markets()`. For CSV output use `client.markets().asCsv()`. ## Markets Endpoints --- # Market Status (Java SDK) Source: https://www.marketdata.app/docs/sdk/java/markets/status/index.md Retrieve the exchange open/closed calendar — whether the market was (or will be) open on a given day. > [!NOTE] > This is distinct from [`utilities().status()`](https://www.marketdata.app/docs/sdk/java/utilities/status), which reports the Market Data **API's** own service health. ## Making Requests Use the `status()` method on the `markets` resource, built with `MarketStatusRequest`. Every parameter is optional; a bare request returns today's status for the US market. ```java MarketStatusResponse status(MarketStatusRequest request) CompletableFuture statusAsync(MarketStatusRequest request) ``` ### MarketStatusRequest ```java MarketStatusRequest.of() // today, US MarketStatusRequest.builder() .country(String country) // ISO 3166, 2-letter (default: US) .date(LocalDate date) // a single day .from(LocalDate from) // range start (inclusive) .to(LocalDate to) // range end (inclusive) .countback(int n) // N days before `to` .build() ``` #### Returns `MarketStatusResponse` wrapping `List`: ```java public record MarketStatus( @Nullable ZonedDateTime date, @Nullable String status) // "open" / "closed" / null (outside coverage) // also: boolean isOpen() ``` ## Examples ### Java ```java import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.markets.MarketStatus; import com.marketdata.sdk.markets.MarketStatusRequest; import java.time.LocalDate; try (MarketDataClient client = new MarketDataClient()) { var status = client.markets().status( MarketStatusRequest.builder() .from(LocalDate.now().minusDays(7)) .to(LocalDate.now()) .build()); for (MarketStatus day : status.values()) { System.out.println(day.date().toLocalDate() + " " + day.status() + (day.isOpen() ? " (trading)" : "")); } } ``` ### Kotlin ```kotlin import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.markets.MarketStatusRequest import java.time.LocalDate MarketDataClient().use { client -> val status = client.markets().status( MarketStatusRequest.builder() .from(LocalDate.now().minusDays(7)) .to(LocalDate.now()) .build()) for (day in status.values()) { println("${day.date().toLocalDate()} ${day.status()}") } } ``` --- # Options (Java SDK) Source: https://www.marketdata.app/docs/sdk/java/options/index.md The Java SDK from Market Data provides methods to streamline your use of the Options endpoints. These methods provide a typed interface over the underlying HTTP requests and responses, with both sync and async variants. Reach the resource through `client.options()`. For CSV output use `client.options().asCsv()`. ## Options Endpoints --- # Option Chain (Java SDK) Source: https://www.marketdata.app/docs/sdk/java/options/chain/index.md Retrieve a full option chain for an underlying, with a rich set of filters. The chain is where most option workflows start. ## Making Requests Use the `chain()` method on the `options` resource, built with `OptionsChainRequest`. ```java OptionsChainResponse chain(OptionsChainRequest request) CompletableFuture chainAsync(OptionsChainRequest request) ``` ### OptionsChainRequest The chain has two mutually-exclusive filter axes modeled as **sealed types**, so the compiler lets you pick exactly one variant of each: ```java OptionsChainRequest.builder(String symbol) // Expiration selection (sealed ExpirationFilter — pick one): .expirationFilter(ExpirationFilter.onDate(LocalDate date)) .expirationFilter(ExpirationFilter.dte(int days)) // days-to-expiration .expirationFilter(ExpirationFilter.between(LocalDate from, LocalDate to)) .expirationFilter(ExpirationFilter.all()) // every expiration // (if omitted, the API narrows to the front month) // Strike selection (sealed StrikeFilter — pick one): .strikeFilter(StrikeFilter.exact(double price)) .strikeFilter(StrikeFilter.range(double min, double max)) // Additive filters: .side(OptionSide.CALL) // CALL or PUT .strikeRange(StrikeRange.ITM) // ITM / OTM / ATM .strikeLimit(int limit) // max strikes per side .delta(double delta) .minOpenInterest(long oi) .minVolume(long vol) .date(LocalDate date) // historical chain .build() ``` #### Returns `OptionsChainResponse` wrapping `List`: ```java public record OptionQuote( @Nullable String optionSymbol, @Nullable String underlying, @Nullable ZonedDateTime expiration, @Nullable String side, @Nullable Double strike, @Nullable ZonedDateTime firstTraded, @Nullable Integer dte, @Nullable ZonedDateTime updated, @Nullable Double bid, @Nullable Long bidSize, @Nullable Double mid, @Nullable Double ask, @Nullable Long askSize, @Nullable Double last, @Nullable Long openInterest, @Nullable Long volume, @Nullable Boolean inTheMoney, @Nullable Double intrinsicValue, @Nullable Double extrinsicValue, @Nullable Double underlyingPrice, @Nullable Double iv, // greeks: @Nullable Double delta, @Nullable Double gamma, @Nullable Double theta, @Nullable Double vega, @Nullable Double rho) ``` ## Examples ### Java ```java import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.options.ExpirationFilter; import com.marketdata.sdk.options.OptionQuote; import com.marketdata.sdk.options.OptionSide; import com.marketdata.sdk.options.OptionsChainRequest; import com.marketdata.sdk.options.StrikeFilter; import java.util.List; try (MarketDataClient client = new MarketDataClient()) { // The 5 calls nearest the money. List chain = client.options().chain( OptionsChainRequest.builder("AAPL") .side(OptionSide.CALL) .strikeLimit(5) .build()) .values(); for (OptionQuote c : chain) { System.out.println(c.optionSymbol() + " strike=" + c.strike() + " bid/ask=" + c.bid() + "/" + c.ask() + " delta=" + c.delta()); } // Sealed filters: contracts within 45 days, strikes between 150 and 250. var filtered = client.options().chain( OptionsChainRequest.builder("AAPL") .expirationFilter(ExpirationFilter.dte(45)) .strikeFilter(StrikeFilter.range(150, 250)) .side(OptionSide.CALL) .build()); } ``` ### Kotlin ```kotlin import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.options.OptionSide import com.marketdata.sdk.options.OptionsChainRequest MarketDataClient().use { client -> val chain = client.options().chain( OptionsChainRequest.builder("AAPL") .side(OptionSide.CALL) .strikeLimit(5) .build()) .values() for (c in chain) { println("${c.optionSymbol()} strike=${c.strike()} delta=${c.delta()}") } } ``` --- # Expirations (Java SDK) Source: https://www.marketdata.app/docs/sdk/java/options/expirations/index.md List the available expiration dates for an underlying. ## Making Requests Use the `expirations()` method on the `options` resource, built with `OptionsExpirationsRequest`. ```java OptionsExpirationsResponse expirations(OptionsExpirationsRequest request) CompletableFuture expirationsAsync(OptionsExpirationsRequest request) ``` ### OptionsExpirationsRequest ```java OptionsExpirationsRequest.of(String symbol) OptionsExpirationsRequest.builder(String symbol) .strike(double strike) // only expirations that list this strike .date(LocalDate date) // historical: the calendar as it stood on this date .build() ``` #### Returns `OptionsExpirationsResponse` wrapping `List` — the expiration dates. ## Examples ### Java ```java import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.options.OptionsExpirationsRequest; import java.time.ZonedDateTime; try (MarketDataClient client = new MarketDataClient()) { var expirations = client.options().expirations(OptionsExpirationsRequest.of("AAPL")); System.out.println("AAPL has " + expirations.values().size() + " expirations"); for (ZonedDateTime exp : expirations.values()) { System.out.println(" " + exp.toLocalDate()); } } ``` ### Kotlin ```kotlin import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.options.OptionsExpirationsRequest MarketDataClient().use { client -> val expirations = client.options().expirations(OptionsExpirationsRequest.of("AAPL")) println("AAPL has ${expirations.values().size} expirations") } ``` --- # Lookup (Java SDK) Source: https://www.marketdata.app/docs/sdk/java/options/lookup/index.md Turn a human-readable option description into a well-formed OCC option symbol. ## Making Requests Use the `lookup()` method on the `options` resource, built with `OptionsLookupRequest`. ```java OptionsLookupResponse lookup(OptionsLookupRequest request) CompletableFuture lookupAsync(OptionsLookupRequest request) ``` ### OptionsLookupRequest ```java OptionsLookupRequest.of(String userInput) ``` #### Returns `OptionsLookupResponse` wrapping a `String` — the OCC option symbol. Read it with `.values()`. ## Examples ### Java ```java import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.options.OptionsLookupRequest; try (MarketDataClient client = new MarketDataClient()) { String occSymbol = client.options() .lookup(OptionsLookupRequest.of("AAPL 1/16/2026 $200 Call")) .values(); System.out.println("Resolved to: " + occSymbol); // AAPL271217C00300000 } ``` ### Kotlin ```kotlin import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.options.OptionsLookupRequest MarketDataClient().use { client -> val occSymbol = client.options() .lookup(OptionsLookupRequest.of("AAPL 1/16/2026 \$200 Call")) .values() println("Resolved to: $occSymbol") } ``` --- # Option Quotes (Java SDK) Source: https://www.marketdata.app/docs/sdk/java/options/quotes/index.md Retrieve quotes (bid, ask, last, greeks, etc.) for one or more option contracts by OCC symbol. ## Making Requests The `options` resource offers two quote methods: - `quote(...)` — a single contract, built with `OptionsQuoteRequest`. - `quotes(...)` — several contracts. Unlike stocks, options quotes **fan out one request per symbol concurrently**, so the result is a `Map` keyed by OCC symbol (insertion order preserved). ```java OptionsQuotesResponse quote(OptionsQuoteRequest request) CompletableFuture quoteAsync(OptionsQuoteRequest request) Map quotes(OptionsQuotesRequest request) CompletableFuture> quotesAsync(OptionsQuotesRequest request) ``` ### Request types ```java // Single contract OptionsQuoteRequest.of(String optionSymbol) OptionsQuoteRequest.builder(String optionSymbol) .date(LocalDate date) // historical quote .from(LocalDate from) // date range .to(LocalDate to) .countback(int n) .build() // Multiple contracts (fan-out) OptionsQuotesRequest.of(String first, String... rest) // shortcut: symbols only OptionsQuotesRequest.builder(String first, String... rest) .addSymbol(String optionSymbol) .date(LocalDate date) .from(LocalDate from) .to(LocalDate to) .countback(int n) .build() ``` #### Returns `OptionsQuotesResponse` wraps `List`. See [Chain](https://www.marketdata.app/docs/sdk/java/options/chain) for the full `OptionQuote` record (price fields plus greeks). ## Examples ### Java ```java import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.options.OptionQuote; import com.marketdata.sdk.options.OptionsQuoteRequest; import com.marketdata.sdk.options.OptionsQuotesRequest; try (MarketDataClient client = new MarketDataClient()) { // Single contract. OptionQuote q = client.options() .quote(OptionsQuoteRequest.of("AAPL271217C00300000")) .values().get(0); System.out.println("last=" + q.last() + " iv=" + q.iv() + " delta=" + q.delta()); // Multiple contracts — one request each, keyed by symbol. var quotes = client.options().quotes( OptionsQuotesRequest.of("AAPL271217C00300000", "AAPL271217C00310000")); quotes.forEach((sym, resp) -> System.out.println(sym + " → " + resp.values().size() + " row(s)")); } ``` ### Kotlin ```kotlin import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.options.OptionsQuoteRequest MarketDataClient().use { client -> val q = client.options() .quote(OptionsQuoteRequest.of("AAPL271217C00300000")) .values()[0] println("last=${q.last()} iv=${q.iv()} delta=${q.delta()}") } ``` --- # Settings (Java SDK) Source: https://www.marketdata.app/docs/sdk/java/settings/index.md The Java SDK lets you customize API requests through **universal parameters** — settings such as date format, data mode, column projection, and CSV shaping that apply across endpoints. These are configured per resource, and some can also be set globally through environment variables. ## Configuration Cascade Client-level configuration (token, base URL, API version) is resolved in priority order, first match wins: 1. **Explicit constructor argument** (highest priority) 2. **`MARKETDATA_*` environment variable** 3. **`.env` file** in the working directory 4. **Built-in default** (lowest priority) ```java // Explicit wins over the environment; null falls through to the cascade. new MarketDataClient("explicit-token", null, null, true); ``` ## Universal Parameters Universal parameters are set on the **resource**, before you call the endpoint. Each setter returns a configured copy of the resource, so they chain. They apply to every call you make through that configured resource. ### Java ```java import com.marketdata.sdk.DateFormat; import com.marketdata.sdk.Mode; import com.marketdata.sdk.stocks.StockCandlesRequest; import com.marketdata.sdk.stocks.StockResolution; var candles = client.stocks() .dateFormat(DateFormat.TIMESTAMP) // how dates are sent on the wire .mode(Mode.DELAYED) // live vs delayed vs cached data .columns("symbol", "close") // project only the columns you need .candles(StockCandlesRequest.builder(StockResolution.DAILY, "AAPL") .from(LocalDate.now().minusMonths(1)) .to(LocalDate.now()) .build()); ``` ### Kotlin ```kotlin import com.marketdata.sdk.DateFormat import com.marketdata.sdk.Mode import com.marketdata.sdk.stocks.StockCandlesRequest import com.marketdata.sdk.stocks.StockResolution val candles = client.stocks() .dateFormat(DateFormat.TIMESTAMP) .mode(Mode.DELAYED) .columns("symbol", "close") .candles( StockCandlesRequest.builder(StockResolution.DAILY, "AAPL") .from(LocalDate.now().minusMonths(1)) .to(LocalDate.now()) .build()) ``` ### Date Format Controls how dates and times are represented on the wire. ```java import com.marketdata.sdk.DateFormat; // DateFormat.UNIX, DateFormat.TIMESTAMP, DateFormat.SPREADSHEET client.stocks().dateFormat(DateFormat.TIMESTAMP); ``` | Value | Description | |---------------|--------------------------------------------------------| | `UNIX` | Unix timestamp in seconds (e.g. `1609362000`) | | `TIMESTAMP` | ISO-8601 timestamp (e.g. `2020-12-30 16:00:00 -05:00`) | | `SPREADSHEET` | Excel/Sheets serial date number (e.g. `44195.66667`) | For more details, see the [API Date Format documentation](https://www.marketdata.app/docs/api/universal-parameters/date-format). ### Data Mode Controls whether the API returns live, delayed, or cached data. ```java import com.marketdata.sdk.Mode; // Mode.LIVE, Mode.DELAYED, Mode.CACHED client.stocks().mode(Mode.CACHED); ``` | Value | Description | |-----------|--------------------------------------| | `LIVE` | Real-time data (paid plans) | | `DELAYED` | 15+ minute delayed data | | `CACHED` | Cached data — lower cost per request | For more details, see the [API Data Mode documentation](https://www.marketdata.app/docs/api/universal-parameters/mode). ### Columns Limits the response to only the columns you need, reducing payload size. Columns you didn't request come back `null` on the typed model — there's no error. ```java client.stocks().columns("symbol", "last"); ``` For more details, see the [API Columns documentation](https://www.marketdata.app/docs/api/universal-parameters/columns). ### Limit and Offset Cap the number of rows returned (`limit`) and skip rows from the start (`offset`). ```java client.stocks().limit(100).offset(0); ``` ## CSV Output Every resource exposes an `asCsv()` facet that switches the whole resource to CSV. The response's `csv()` accessor returns the raw CSV text. CSV-only shaping parameters live on this facet: - `human(boolean)` — render human-friendly field names and values. - `headers(boolean)` — include the CSV header row (default `true`). ### Java ```java import com.marketdata.sdk.stocks.StockQuotesRequest; var csv = client.stocks().asCsv() .columns("symbol", "last") .human(true) .headers(true) .quotes(StockQuotesRequest.of("AAPL", "MSFT")); System.out.println(csv.csv()); // raw CSV text csv.saveToFile(Path.of("quotes.csv")); ``` ### Kotlin ```kotlin import com.marketdata.sdk.stocks.StockQuotesRequest val csv = client.stocks().asCsv() .columns("symbol", "last") .human(true) .headers(true) .quotes(StockQuotesRequest.of("AAPL", "MSFT")) println(csv.csv()) csv.saveToFile(Path.of("quotes.csv")) ``` ## 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_DATE_FORMAT` | Default date format | _(API default)_ | | `MARKETDATA_LOGGING_LEVEL` | SDK logging level (`java.util.logging`) | _(consumer default)_ | --- # Stocks (Java SDK) Source: https://www.marketdata.app/docs/sdk/java/stocks/index.md The Java SDK from Market Data provides methods to streamline your use of the Stocks endpoints. These methods provide a typed interface over the underlying HTTP requests and responses, with both sync and async variants. Reach the resource through `client.stocks()`. For CSV output use `client.stocks().asCsv()`. ## Stocks Endpoints --- # Stock Candles (Java SDK) Source: https://www.marketdata.app/docs/sdk/java/stocks/candles/index.md Retrieve historical OHLCV (open/high/low/close/volume) candles for a stock symbol. ## Making Requests Use the `candles()` method on the `stocks` resource. Build the request with `StockCandlesRequest`, passing a `StockResolution` and a symbol. For large intraday ranges the SDK automatically splits the request into year-sized chunks, fetches them concurrently, and merges the results. ```java StockCandlesResponse candles(StockCandlesRequest request) CompletableFuture candlesAsync(StockCandlesRequest request) ``` ### StockCandlesRequest ```java // Factory — all-defaults form: StockCandlesRequest.of(StockResolution resolution, String symbol) // Builder — for date windows and adjustments: StockCandlesRequest.builder(StockResolution resolution, String symbol) .date(LocalDate date) // a single trading day .from(LocalDate from) // window start (inclusive) .to(LocalDate to) // window end (exclusive) .countback(int n) // N candles before `to` (instead of `from`) .exchange(String exchange) // disambiguate exchange .extended(boolean extended) // include extended-hours bars (intraday) .country(String country) // exchange country (ISO 3166, 2-letter) .adjustSplits(boolean adjust) // default: true for daily .adjustDividends(boolean adjust) // default: true for daily .build() ``` ### StockResolution A value type for the candle interval: ```java StockResolution.DAILY // also WEEKLY, MONTHLY, YEARLY StockResolution.minutes(5) // 5-minute bars StockResolution.hours(1) // hourly bars StockResolution.days(1) StockResolution.of("1H") // any raw wire token ``` #### Returns `StockCandlesResponse` wrapping `List`: ```java public record StockCandle( @Nullable ZonedDateTime time, // bar opening moment (America/New_York) @Nullable Double open, @Nullable Double high, @Nullable Double low, @Nullable Double close, @Nullable Long volume) ``` ## Examples ### Java ```java import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.stocks.StockCandle; import com.marketdata.sdk.stocks.StockCandlesRequest; import com.marketdata.sdk.stocks.StockResolution; import java.time.LocalDate; try (MarketDataClient client = new MarketDataClient()) { // Daily candles over a date range. var candles = client.stocks().candles( StockCandlesRequest.builder(StockResolution.DAILY, "AAPL") .from(LocalDate.now().minusWeeks(1)) .to(LocalDate.now()) .build()); for (StockCandle bar : candles.values()) { System.out.printf("%s O=%.2f H=%.2f L=%.2f C=%.2f V=%d%n", bar.time(), bar.open(), bar.high(), bar.low(), bar.close(), bar.volume()); } // The last 10 sessions, using countback instead of a left edge. var lastTen = client.stocks().candles( StockCandlesRequest.builder(StockResolution.DAILY, "AAPL") .to(LocalDate.now()) .countback(10) .build()); } ``` ### Kotlin ```kotlin import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.stocks.StockCandlesRequest import com.marketdata.sdk.stocks.StockResolution import java.time.LocalDate MarketDataClient().use { client -> val candles = client.stocks().candles( StockCandlesRequest.builder(StockResolution.DAILY, "AAPL") .from(LocalDate.now().minusWeeks(1)) .to(LocalDate.now()) .build()) for (bar in candles.values()) { println("${bar.time()} O=${bar.open()} C=${bar.close()} V=${bar.volume()}") } } ``` For CSV output, call `client.stocks().asCsv().candles(...)` and read `.csv()`. See [Settings](https://www.marketdata.app/docs/sdk/java/settings#csv-output). --- # Earnings (Java SDK) Source: https://www.marketdata.app/docs/sdk/java/stocks/earnings/index.md Retrieve historical and upcoming earnings data (EPS actuals, estimates, surprises) for a stock symbol. ## Making Requests Use the `earnings()` method on the `stocks` resource, built with `StockEarningsRequest`. ```java StockEarningsResponse earnings(StockEarningsRequest request) CompletableFuture earningsAsync(StockEarningsRequest request) ``` ### StockEarningsRequest ```java StockEarningsRequest.of(String symbol) StockEarningsRequest.builder(String symbol) .date(LocalDate date) // a single report date .from(LocalDate from) // window start .to(LocalDate to) // window end .countback(int n) // N reports before `to` .report(String report) // "actual" or "estimated" .build() ``` #### Returns `StockEarningsResponse` wrapping `List`: ```java public record StockEarning( @Nullable String symbol, @Nullable Integer fiscalYear, @Nullable Integer fiscalQuarter, @Nullable ZonedDateTime date, @Nullable ZonedDateTime reportDate, @Nullable String reportTime, // "pre" / "post" @Nullable String currency, @Nullable Double reportedEPS, @Nullable Double estimatedEPS, @Nullable Double surpriseEPS, @Nullable Double surpriseEPSpct, @Nullable ZonedDateTime updated) ``` ## Examples ### Java ```java import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.stocks.StockEarning; import com.marketdata.sdk.stocks.StockEarningsRequest; try (MarketDataClient client = new MarketDataClient()) { var earnings = client.stocks().earnings(StockEarningsRequest.of("AAPL")); for (StockEarning e : earnings.values()) { System.out.printf("%d Q%d reported=%s estimated=%s%n", e.fiscalYear(), e.fiscalQuarter(), e.reportedEPS(), e.estimatedEPS()); } } ``` ### Kotlin ```kotlin import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.stocks.StockEarningsRequest MarketDataClient().use { client -> val earnings = client.stocks().earnings(StockEarningsRequest.of("AAPL")) for (e in earnings.values()) { println("${e.fiscalYear()} Q${e.fiscalQuarter()} reported=${e.reportedEPS()}") } } ``` --- # News (Java SDK) Source: https://www.marketdata.app/docs/sdk/java/stocks/news/index.md Retrieve news articles for a stock symbol. ## Making Requests Use the `news()` method on the `stocks` resource, built with `StockNewsRequest`. ```java StockNewsResponse news(StockNewsRequest request) CompletableFuture newsAsync(StockNewsRequest request) ``` ### StockNewsRequest ```java StockNewsRequest.of(String symbol) StockNewsRequest.builder(String symbol) .date(LocalDate date) // a single day .from(LocalDate from) // window start .to(LocalDate to) // window end .countback(int n) // N articles before `to` .build() ``` #### Returns `StockNewsResponse` wrapping `List`: ```java public record StockNewsArticle( String symbol, String headline, String content, String source, ZonedDateTime publicationDate) ``` ## Examples ### Java ```java import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.stocks.StockNewsArticle; import com.marketdata.sdk.stocks.StockNewsRequest; import java.time.LocalDate; try (MarketDataClient client = new MarketDataClient()) { var news = client.stocks().news( StockNewsRequest.builder("AAPL") .from(LocalDate.now().minusDays(7)) .to(LocalDate.now()) .build()); for (StockNewsArticle a : news.values()) { System.out.println(a.publicationDate() + " " + a.headline() + " (" + a.source() + ")"); } } ``` ### Kotlin ```kotlin import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.stocks.StockNewsRequest import java.time.LocalDate MarketDataClient().use { client -> val news = client.stocks().news( StockNewsRequest.builder("AAPL") .from(LocalDate.now().minusDays(7)) .to(LocalDate.now()) .build()) for (a in news.values()) { println("${a.publicationDate()} ${a.headline()} (${a.source()})") } } ``` > [!NOTE] > On the typed path, all article fields are populated. For column projection use the CSV facet: `client.stocks().asCsv().news(...)`. --- # Prices (Java SDK) Source: https://www.marketdata.app/docs/sdk/java/stocks/prices/index.md Retrieve the latest price (mid, change) for one or more stock symbols — a lighter payload than a full quote. ## Making Requests Use the `prices()` method on the `stocks` resource, built with `StockPricesRequest`. ```java StockPricesResponse prices(StockPricesRequest request) CompletableFuture pricesAsync(StockPricesRequest request) ``` ### StockPricesRequest ```java StockPricesRequest.of(String first, String... rest) StockPricesRequest.builder(String first, String... rest) .addSymbol(String symbol) .build() ``` #### Returns `StockPricesResponse` wrapping `List`: ```java public record StockPrice( @Nullable String symbol, @Nullable Double mid, @Nullable Double change, @Nullable Double changepct, @Nullable ZonedDateTime updated) ``` ## Examples ### Java ```java import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.stocks.StockPrice; import com.marketdata.sdk.stocks.StockPricesRequest; try (MarketDataClient client = new MarketDataClient()) { var prices = client.stocks().prices(StockPricesRequest.of("AAPL", "MSFT")); for (StockPrice p : prices.values()) { System.out.printf("%-6s mid=%.2f change=%.2f%n", p.symbol(), p.mid(), p.change()); } } ``` ### Kotlin ```kotlin import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.stocks.StockPricesRequest MarketDataClient().use { client -> val prices = client.stocks().prices(StockPricesRequest.of("AAPL", "MSFT")) for (p in prices.values()) { println("${p.symbol()} mid=${p.mid()} change=${p.change()}") } } ``` --- # Stock Quotes (Java SDK) Source: https://www.marketdata.app/docs/sdk/java/stocks/quotes/index.md Retrieve real-time quotes (bid, ask, mid, last, volume, etc.) for one or more stock symbols. ## Making Requests The `stocks` resource offers two quote methods: - `quote(...)` — a single symbol, built with `StockQuoteRequest`. - `quotes(...)` — several symbols in **one** request (the stocks backend batches a comma list), built with `StockQuotesRequest`. The result is a single response with one row per symbol. ```java StockQuotesResponse quote(StockQuoteRequest request) CompletableFuture quoteAsync(StockQuoteRequest request) StockQuotesResponse quotes(StockQuotesRequest request) CompletableFuture quotesAsync(StockQuotesRequest request) ``` ### Request types ```java // Single symbol StockQuoteRequest.of(String symbol) StockQuoteRequest.builder(String symbol) .extended(boolean extended) // include extended-session prices .candle(boolean candle) // add OHLC columns .week52(boolean week52) // add 52-week high/low .build() // Multiple symbols, one request StockQuotesRequest.of(String first, String... rest) // shortcut: symbols only StockQuotesRequest.builder(String first, String... rest) .addSymbol(String symbol) .extended(boolean extended) .candle(boolean candle) .week52(boolean week52) .build() ``` #### Returns `StockQuotesResponse` wrapping `List` (one element for `quote`, one per symbol for `quotes`): ```java public record StockQuote( @Nullable String symbol, @Nullable Double ask, @Nullable Long askSize, @Nullable Double bid, @Nullable Long bidSize, @Nullable Double mid, @Nullable Double last, @Nullable Double change, @Nullable Double changepct, @Nullable Long volume, @Nullable ZonedDateTime updated, // opt-in via .candle(true): @Nullable Double open, @Nullable Double high, @Nullable Double low, @Nullable Double close, // opt-in via .week52(true): @Nullable Double week52High, @Nullable Double week52Low) ``` ## Examples ### Java ```java import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.stocks.StockQuote; import com.marketdata.sdk.stocks.StockQuoteRequest; import com.marketdata.sdk.stocks.StockQuotesRequest; try (MarketDataClient client = new MarketDataClient()) { // A single symbol — row 0 of the list. StockQuote q = client.stocks().quote(StockQuoteRequest.of("AAPL")).values().get(0); System.out.printf("%s last=%.2f bid/ask=%.2f/%.2f%n", q.symbol(), q.last(), q.bid(), q.ask()); // Several symbols in one request. var quotes = client.stocks().quotes( StockQuotesRequest.of("AAPL", "MSFT", "GOOGL")); for (StockQuote row : quotes.values()) { System.out.printf("%-6s last=%.2f%n", row.symbol(), row.last()); } } ``` ### Kotlin ```kotlin import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.stocks.StockQuoteRequest import com.marketdata.sdk.stocks.StockQuotesRequest MarketDataClient().use { client -> val q = client.stocks().quote(StockQuoteRequest.of("AAPL")).values()[0] println("${q.symbol()} last=${q.last()}") val quotes = client.stocks().quotes( StockQuotesRequest.of("AAPL", "MSFT", "GOOGL")) for (row in quotes.values()) { println("${row.symbol()} last=${row.last()}") } } ``` --- # Utilities (Java SDK) Source: https://www.marketdata.app/docs/sdk/java/utilities/index.md The Java SDK from Market Data provides utility methods for checking API health, inspecting your account quota, and debugging requests. These methods provide a typed interface over the underlying HTTP requests and responses, with both sync and async variants. Reach the resource through `client.utilities()`. ## Utilities Endpoints --- # Headers (Java SDK) Source: https://www.marketdata.app/docs/sdk/java/utilities/headers/index.md Echo back the HTTP headers the server received for your request — useful for confirming your `Authorization` header actually reached the API. Sensitive values are redacted in the response. ## Making Requests Use the `headers()` method on the `utilities` resource. It requires a valid token. ```java UtilitiesHeadersResponse headers() CompletableFuture headersAsync() ``` ### Returns `UtilitiesHeadersResponse` wrapping `Map` — the headers the server received, lower-cased keys to values (sensitive values redacted server-side). ## Examples ### Java ```java import com.marketdata.sdk.MarketDataClient; try (MarketDataClient client = new MarketDataClient()) { var headers = client.utilities().headers().values(); System.out.println("Server saw " + headers.size() + " request headers"); } ``` ### Kotlin ```kotlin import com.marketdata.sdk.MarketDataClient MarketDataClient().use { client -> val headers = client.utilities().headers().values() println("Server saw ${headers.size} request headers") } ``` --- # API Status (Java SDK) Source: https://www.marketdata.app/docs/sdk/java/utilities/status/index.md Retrieve the health of the Market Data API's services — a handy liveness check. > [!NOTE] > This reports the **API's** own service health. For the exchange open/closed calendar, see [`markets().status()`](https://www.marketdata.app/docs/sdk/java/markets/status). ## Making Requests Use the `status()` method on the `utilities` resource. It takes no parameters and is **public** — it works without a token. ```java UtilitiesStatusResponse status() CompletableFuture statusAsync() ``` ### Returns `UtilitiesStatusResponse` wrapping `List`. Each `ServiceStatus` exposes the service name, a status string, an `online()` flag, uptime percentiles, and an updated timestamp. The server refreshes this data every few minutes, so polling more often than that is wasted work. ## Examples ### Java ```java import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.utilities.ServiceStatus; try (MarketDataClient client = new MarketDataClient()) { var status = client.utilities().status(); long online = status.values().stream().filter(ServiceStatus::online).count(); System.out.println(online + " of " + status.values().size() + " services online"); } ``` ### Kotlin ```kotlin import com.marketdata.sdk.MarketDataClient import com.marketdata.sdk.utilities.ServiceStatus MarketDataClient().use { client -> val status = client.utilities().status() val online = status.values().count { it.online() } println("$online of ${status.values().size} services online") } ``` --- # User (Java SDK) Source: https://www.marketdata.app/docs/sdk/java/utilities/user/index.md Retrieve your account details — including how much of your daily request quota remains. This is the same endpoint the SDK calls on startup to validate your token. ## Making Requests Use the `user()` method on the `utilities` resource. It requires a valid token. ```java UtilitiesUserResponse user() CompletableFuture userAsync() ``` ### Returns `UtilitiesUserResponse` wrapping a `User`, which exposes your plan and quota — for example `requestsRemaining()` and `requestsLimit()`. ## Examples ### Java ```java import com.marketdata.sdk.MarketDataClient; import com.marketdata.sdk.utilities.User; try (MarketDataClient client = new MarketDataClient()) { User me = client.utilities().user().values(); System.out.println("Quota: " + me.requestsRemaining() + " of " + me.requestsLimit() + " requests left today"); } ``` ### Kotlin ```kotlin import com.marketdata.sdk.MarketDataClient MarketDataClient().use { client -> val me = client.utilities().user().values() println("Quota: ${me.requestsRemaining()} of ${me.requestsLimit()} left today") } ``` --- # JavaScript SDK Source: https://www.marketdata.app/docs/sdk/js/index.md 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. ## 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. - [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](https://www.marketdata.app/docs/sdk/js/installation) into a Node.js or TypeScript project. 2. Set up your [authentication token](https://www.marketdata.app/docs/sdk/js/authentication) to access the API. 3. Learn about the [client](https://www.marketdata.app/docs/sdk/js/client) and how to make your first API requests. 4. Configure [Settings](https://www.marketdata.app/docs/sdk/js/settings) to customize output format, date format, and other universal parameters. 5. Explore the available endpoints for [stocks](https://www.marketdata.app/docs/sdk/js/stocks), [options](https://www.marketdata.app/docs/sdk/js/options), [funds](https://www.marketdata.app/docs/sdk/js/funds), [markets](https://www.marketdata.app/docs/sdk/js/markets), and [utilities](https://www.marketdata.app/docs/sdk/js/utilities). ### 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. --- # Authentication (JavaScript SDK) Source: https://www.marketdata.app/docs/sdk/js/authentication/index.md 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](https://www.marketdata.app/docs/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 ### Mac / Linux 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. ### Windows 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: ```ini 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"; // No need to pass a token here — the SDK reads it from // the MARKETDATA_TOKEN environment variable automatically. const client = new MarketDataClient(); 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. ## 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"; const token = "your_token_here"; const client = new MarketDataClient({ token }); try { const quotes = await client.stocks.quotes("SPY"); console.log(quotes); } catch (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](https://www.marketdata.app/docs/sdk/js/client) works, so you can get familiar with interacting with the SDK. You may also want to configure [Settings](https://www.marketdata.app/docs/sdk/js/settings) to customize output format, date format, and other universal parameters. --- # Client (JavaScript SDK) Source: https://www.marketdata.app/docs/sdk/js/client/index.md 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 1. Review the [documentation on authentication](https://www.marketdata.app/docs/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](#accessing-rate-limits) in the client to track credit usage and how many API credits remain. 5. Configure [Settings](https://www.marketdata.app/docs/sdk/js/settings) to customize output format, date format, and other universal parameters. ## MarketDataClient ```typescript class MarketDataClient { constructor(config?: MarketDataConfig); readonly ready: Promise; } interface MarketDataConfig { token?: string; baseUrl?: string; apiVersion?: string; maxRetries?: number; 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, utilities) and handles authentication, rate limiting, and request management. ### Properties - `token` (string, optional): The authentication token for API requests. See [authentication documentation](https://www.marketdata.app/docs/sdk/js/authentication) for details. - `rateLimits` ([UserRateLimits](#userratelimits), 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`. - `logger` ([Logger](#logging)): The logger instance used for diagnostic output. - `settings` ([MarketDataSettings](https://www.marketdata.app/docs/sdk/js/settings)): Resolved configuration including env-var defaults. See [Settings](https://www.marketdata.app/docs/sdk/js/settings) for details. ### Resources - `stocks` ([StocksResource](https://www.marketdata.app/docs/sdk/js/stocks)): Access to stocks endpoints (prices, quotes, candles, earnings, news) - `options` ([OptionsResource](https://www.marketdata.app/docs/sdk/js/options)): Access to options endpoints (chain, expirations, quotes, lookup) - `funds` ([FundsResource](https://www.marketdata.app/docs/sdk/js/funds)): Access to funds endpoints (candles) - `markets` ([MarketsResource](https://www.marketdata.app/docs/sdk/js/markets)): Access to markets endpoints (status) - `utilities` ([UtilitiesResource](https://www.marketdata.app/docs/sdk/js/utilities)): Access to utility endpoints (`status`, `headers`) ### 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, prepares the resource namespaces, and kicks off eager `/user/` validation (unless `skipStartupValidation: true`). #### 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`. - `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](#logging)): 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-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`. - Configuration properties can also be provided via environment variables — see [Settings](https://www.marketdata.app/docs/sdk/js/settings) for the full list and their resolution order. #### Example ```typescript import { MarketDataClient } from "@marketdata/sdk"; // 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" }); // 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"; const logger = new DefaultLogger(LogLevel.WARN); const quietClient = new MarketDataClient({ logger }); ``` ## Error Handling 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. ### try / catch The idiomatic approach. ```typescript import { MarketDataClient, AuthenticationError, RateLimitError, NotFoundError, } from "@marketdata/sdk"; 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); } } ``` ### .catch() Fluent chaining without `try`/`catch`. ```typescript const prices = await client.stocks .prices("AAPL") .catch((error) => { console.error("Failed:", error.message); return []; }); ``` ### Asserting rejections in tests ```typescript import { AuthenticationError } from "@marketdata/sdk"; await expect(client.stocks.prices("AAPL")) .rejects.toBeInstanceOf(AuthenticationError); ``` Every thrown error is a subclass of `MarketDataClientError` and carries HTTP context for support tickets: ```typescript try { 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. The SDK translates 404 into an empty response with `no_data: true`; see [no-data handling](#no-data-responses). 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 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 pending = client.stocks.prices("UNKNOWN"); const prices = await pending; if (!(await pending.hasData())) { console.log("No data for that symbol"); } else { console.log(prices); } ``` ### MarketDataPromise ```typescript 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 `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`: ```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 by the eager `/user/` call during construction (awaited via `client.ready`). ```typescript const client = new MarketDataClient(); await client.ready; // ensures rateLimits is populated 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"; // 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`. 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 The SDK supports flexible configuration of universal parameters through environment variables, constructor arguments, and per-method overrides. See the [Settings](https://www.marketdata.app/docs/sdk/js/settings) documentation for complete details on all available configuration options and how they interact. --- # Funds (JavaScript SDK) Source: https://www.marketdata.app/docs/sdk/js/funds/index.md 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 --- # Fund Candles (JavaScript SDK) Source: https://www.marketdata.app/docs/sdk/js/funds/candles/index.md 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. > [!NOTE] > 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, ): MarketDataPromise // Object form candles

( params: P & { symbol: string }, ): MarketDataPromise ``` 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`](https://www.marketdata.app/docs/sdk/js/settings#output-format) (optional): The format of the returned data. Alias: `format`. - [`dateFormat`](https://www.marketdata.app/docs/sdk/js/settings#date-format) (optional): Date format. Alias: `dateformat`. - [`columns`](https://www.marketdata.app/docs/sdk/js/settings#columns) (optional): Columns to include. - [`addHeaders`](https://www.marketdata.app/docs/sdk/js/settings#headers) (optional): Whether to include headers in CSV output. Alias: `headers`. - [`useHumanReadable`](https://www.marketdata.app/docs/sdk/js/settings#human-readable) (optional): Use human-readable field names. Alias: `human`. - [`mode`](https://www.marketdata.app/docs/sdk/js/settings#data-mode) (optional): The data mode to use. ### Returns - [`MarketDataPromise`](https://www.marketdata.app/docs/sdk/js/client#marketdatapromise) ### Default ```typescript 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); } ``` ### Date Range ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); 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); } ``` ### Human Readable ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); 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); } ``` ## 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. --- # Installation (JavaScript SDK) Source: https://www.marketdata.app/docs/sdk/js/installation/index.md 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 ### pnpm ```bash pnpm add @marketdata/sdk ``` ### npm ```bash npm install @marketdata/sdk ``` ### yarn ```bash yarn add @marketdata/sdk ``` ## 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");` ## 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 used internally for error composition. The public API surfaces standard Promises — see [client error handling](https://www.marketdata.app/docs/sdk/js/client#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](https://www.marketdata.app/docs/sdk/js/authentication) 2. Learn about the [client](https://www.marketdata.app/docs/sdk/js/client) and how to make your first API requests 3. Configure [settings](https://www.marketdata.app/docs/sdk/js/settings) to customize output format, date format, and other universal parameters --- # Markets (JavaScript SDK) Source: https://www.marketdata.app/docs/sdk/js/markets/index.md 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 --- # Market Status (JavaScript SDK) Source: https://www.marketdata.app/docs/sdk/js/markets/status/index.md 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, ): MarketDataPromise ``` 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`](https://www.marketdata.app/docs/sdk/js/settings#output-format) (optional): The format of the returned data. Alias: `format`. - [`dateFormat`](https://www.marketdata.app/docs/sdk/js/settings#date-format) (optional): Date format. Alias: `dateformat`. - [`columns`](https://www.marketdata.app/docs/sdk/js/settings#columns) (optional): Columns to include. - [`addHeaders`](https://www.marketdata.app/docs/sdk/js/settings#headers) (optional): Whether to include headers in CSV output. Alias: `headers`. - [`useHumanReadable`](https://www.marketdata.app/docs/sdk/js/settings#human-readable) (optional): Use human-readable field names. Alias: `human`. - [`mode`](https://www.marketdata.app/docs/sdk/js/settings#data-mode) (optional): The data mode to use. ### Returns - [`MarketDataPromise`](https://www.marketdata.app/docs/sdk/js/client#marketdatapromise) ### Today ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); 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); } ``` ### Date Range ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); 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); } ``` ### Countback ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); try { // Last 7 trading-status days const days = await client.markets.status({ countback: 7 }); console.log(days); } catch (error) { console.error(error); } ``` ### Human Readable ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); 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); } ``` ## 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. --- # Options (JavaScript SDK) Source: https://www.marketdata.app/docs/sdk/js/options/index.md 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 --- # Option Chain (JavaScript SDK) Source: https://www.marketdata.app/docs/sdk/js/options/chain/index.md 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, ): MarketDataPromise // Object form chain

( params: P & { symbol: string }, ): MarketDataPromise ``` 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. - `dte` (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. - `strikeLimit` (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"`). - `minBid` / `maxBid` / `minAsk` / `maxAsk` (number, optional) Filter by quote price thresholds. - `maxBidAskSpread` (number, optional), `maxBidAskSpreadPct` (number, optional) Filter by spread thresholds. - `minOpenInterest` (number, optional), `minVolume` (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`](https://www.marketdata.app/docs/sdk/js/settings#output-format) (optional): The format of the returned data. Alias: `format`. - [`dateFormat`](https://www.marketdata.app/docs/sdk/js/settings#date-format) (optional): Date format. Alias: `dateformat`. - [`columns`](https://www.marketdata.app/docs/sdk/js/settings#columns) (optional): Columns to include. - [`addHeaders`](https://www.marketdata.app/docs/sdk/js/settings#headers) (optional): Whether to include headers in CSV output. Alias: `headers`. - [`useHumanReadable`](https://www.marketdata.app/docs/sdk/js/settings#human-readable) (optional): Use human-readable field names. Alias: `human`. - [`mode`](https://www.marketdata.app/docs/sdk/js/settings#data-mode) (optional): The data mode to use. ### Returns - [`MarketDataPromise`](https://www.marketdata.app/docs/sdk/js/client#marketdatapromise) ### Full Chain ```typescript 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); } ``` ### Filtered ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); try { // Calls only, narrow strike window, high open interest const contracts = await client.options.chain("AAPL", { side: "call", strikeLimit: 10, minOpenInterest: 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); } ``` ### Historical ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); 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); } ``` ### Human Readable ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); 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); } ``` ## 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. --- # Expirations (JavaScript SDK) Source: https://www.marketdata.app/docs/sdk/js/options/expirations/index.md 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, ): MarketDataPromise // Object form expirations

( params: P & { symbol: string }, ): MarketDataPromise ``` 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`](https://www.marketdata.app/docs/sdk/js/settings#output-format) (optional): The format of the returned data. Alias: `format`. - [`dateFormat`](https://www.marketdata.app/docs/sdk/js/settings#date-format) (optional): Date format. Alias: `dateformat`. - [`columns`](https://www.marketdata.app/docs/sdk/js/settings#columns) (optional): Columns to include. - [`useHumanReadable`](https://www.marketdata.app/docs/sdk/js/settings#human-readable) (optional): Use human-readable field names. Alias: `human`. ### Returns - [`MarketDataPromise`](https://www.marketdata.app/docs/sdk/js/client#marketdatapromise) ### Default ```typescript 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); } ``` ### Filter By Strike ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); 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); } ``` ### Human Readable ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); try { const data = await client.options.expirations("AAPL", { human: true }); console.log(data.Expirations); } catch (error) { console.error(error); } ``` ## 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. --- # Lookup (JavaScript SDK) Source: https://www.marketdata.app/docs/sdk/js/options/lookup/index.md Convert a human-readable option description (e.g. `"AAPL 12/17/2027 250 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, ): MarketDataPromise // Object form lookup

( params: P & { lookup: string }, ): MarketDataPromise ``` Resolves a human-readable option description to its OCC symbol. ### Parameters - `lookupStr` (string) A natural-language description of the option contract (e.g. `"AAPL 12/17/2027 250 Call"`, `"TSLA Jan 2025 300 Put"`). - [`outputFormat`](https://www.marketdata.app/docs/sdk/js/settings#output-format) (optional): The format of the returned data. Alias: `format`. - [`useHumanReadable`](https://www.marketdata.app/docs/sdk/js/settings#human-readable) (optional): Use human-readable field names. Alias: `human`. ### Returns - [`MarketDataPromise`](https://www.marketdata.app/docs/sdk/js/client#marketdatapromise) ### Default ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); try { const data = await client.options.lookup("AAPL 12/17/2027 250 Call"); console.log(data.optionSymbol); // "AAPL271217C00250000" } catch (error) { console.error(error); } ``` ### Human Readable ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); try { const data = await client.options.lookup("AAPL 12/17/2027 250 Call", { human: true, }); console.log(data.Symbol); } catch (error) { console.error(error); } ``` ## 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. --- # Option Quotes (JavaScript SDK) Source: https://www.marketdata.app/docs/sdk/js/options/quotes/index.md 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, ): MarketDataPromise // Object form quotes

( params: P & { symbols: string | string[] }, ): MarketDataPromise ``` 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`](https://www.marketdata.app/docs/sdk/js/settings#output-format) (optional): The format of the returned data. Alias: `format`. - [`dateFormat`](https://www.marketdata.app/docs/sdk/js/settings#date-format) (optional): Date format. Alias: `dateformat`. - [`columns`](https://www.marketdata.app/docs/sdk/js/settings#columns) (optional): Columns to include. - [`addHeaders`](https://www.marketdata.app/docs/sdk/js/settings#headers) (optional): Whether to include headers in CSV output. Alias: `headers`. - [`useHumanReadable`](https://www.marketdata.app/docs/sdk/js/settings#human-readable) (optional): Use human-readable field names. Alias: `human`. - [`mode`](https://www.marketdata.app/docs/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](https://www.marketdata.app/docs/api/options/quotes) for the full list. ### Returns - [`MarketDataPromise`](https://www.marketdata.app/docs/sdk/js/client#marketdatapromise) ### Single Contract ```typescript 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); } ``` ### Multiple Contracts ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); 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); } ``` ### Human Readable ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); try { const quotes = await client.options.quotes("AAPL271217C00250000", { human: true, }); console.log(quotes[0]); } catch (error) { console.error(error); } ``` ## OptionsQuotes `OptionsQuotes` uses the same field shape as [`OptionsChain`](https://www.marketdata.app/docs/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](https://www.marketdata.app/docs/sdk/js/options/chain#optionschain) for the complete list of fields. ## OptionsQuotesHuman `OptionsQuotesHuman` is returned when `human: true` is set. Field shape mirrors [`OptionsChainHuman`](https://www.marketdata.app/docs/sdk/js/options/chain#optionschainhuman) — same columns, `Title_Case` names. --- # Settings (JavaScript SDK) Source: https://www.marketdata.app/docs/sdk/js/settings/index.md 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 ### Environment Variables 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"; const client = new MarketDataClient(); // All calls will use the env-var defaults unless overridden. const result = await client.stocks.prices("AAPL"); ``` ### Constructor Arguments Set client-level configuration via the constructor: ```typescript import { MarketDataClient } from "@marketdata/sdk"; 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. ### Direct Method Parameters Pass parameters directly to resource methods: ```typescript import { MarketDataClient } from "@marketdata/sdk"; 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"; // 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"; 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](https://www.marketdata.app/docs/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"; // 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"; const client = new MarketDataClient(); const candles = await client.stocks.candles("AAPL", { dateFormat: DateFormat.TIMESTAMP }); ``` For more details, see the [API Date Format documentation](https://www.marketdata.app/docs/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"; const client = new MarketDataClient(); const quotes = await client.stocks.quotes("AAPL", { columns: ["ask", "bid"] }); ``` For more details, see the [API Columns documentation](https://www.marketdata.app/docs/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"; const client = new MarketDataClient(); const csv = await client.stocks.candles("AAPL", { outputFormat: OutputFormat.CSV, addHeaders: false, }); ``` For more details, see the [API Headers documentation](https://www.marketdata.app/docs/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"; const client = new MarketDataClient(); 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](https://www.marketdata.app/docs/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"; // 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"; const client = new MarketDataClient(); const quotes = await client.stocks.quotes("AAPL", { mode: Mode.CACHED }); ``` For more details, see the [API Data Mode documentation](https://www.marketdata.app/docs/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` | --- # Stocks (JavaScript SDK) Source: https://www.marketdata.app/docs/sdk/js/stocks/index.md 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 --- # Stock Candles (JavaScript SDK) Source: https://www.marketdata.app/docs/sdk/js/stocks/candles/index.md 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, ): MarketDataPromise // Object form candles

( params: P & { symbol: string }, ): MarketDataPromise ``` 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`](https://www.marketdata.app/docs/sdk/js/settings#output-format) (optional): The format of the returned data. Alias: `format`. - [`dateFormat`](https://www.marketdata.app/docs/sdk/js/settings#date-format) (optional): Date format for response timestamps. Alias: `dateformat`. - [`columns`](https://www.marketdata.app/docs/sdk/js/settings#columns) (optional): Columns to include. - [`addHeaders`](https://www.marketdata.app/docs/sdk/js/settings#headers) (optional): Whether to include headers in CSV output. Alias: `headers`. - [`useHumanReadable`](https://www.marketdata.app/docs/sdk/js/settings#human-readable) (optional): Use human-readable field names. Alias: `human`. - [`mode`](https://www.marketdata.app/docs/sdk/js/settings#data-mode) (optional): The data mode to use. ### Returns - [`MarketDataPromise`](https://www.marketdata.app/docs/sdk/js/client#marketdatapromise) ### 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). ### Daily Candles ```typescript 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); } ``` ### Intraday Range ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); 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); } ``` ### Human Readable ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); 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); } ``` ### CSV ```typescript import { MarketDataClient, OutputFormat } from "@marketdata/sdk"; const client = new MarketDataClient(); const csv = await client.stocks.candles("AAPL", { resolution: "D", countback: 100, outputFormat: OutputFormat.CSV, }); const filename = await csv.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. --- # Earnings (JavaScript SDK) Source: https://www.marketdata.app/docs/sdk/js/stocks/earnings/index.md 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, ): MarketDataPromise // Object form earnings

( params: P & { symbol: string }, ): MarketDataPromise ``` Fetches earnings data for a single symbol. ### Parameters - `symbol` (string) The stock symbol to fetch earnings for. - [`outputFormat`](https://www.marketdata.app/docs/sdk/js/settings#output-format) (optional): The format of the returned data. Alias: `format`. - [`dateFormat`](https://www.marketdata.app/docs/sdk/js/settings#date-format) (optional): Date format for response timestamps. Alias: `dateformat`. - [`columns`](https://www.marketdata.app/docs/sdk/js/settings#columns) (optional): Columns to include. - [`addHeaders`](https://www.marketdata.app/docs/sdk/js/settings#headers) (optional): Whether to include headers in CSV output. Alias: `headers`. - [`useHumanReadable`](https://www.marketdata.app/docs/sdk/js/settings#human-readable) (optional): Use human-readable field names. Alias: `human`. - [`mode`](https://www.marketdata.app/docs/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](https://www.marketdata.app/docs/api/stocks/earnings) for the complete list. ### Returns - [`MarketDataPromise`](https://www.marketdata.app/docs/sdk/js/client#marketdatapromise) > [!NOTE] > Earnings data is a premium endpoint on the Market Data API. Your plan must include earnings access for this method to return data. ### Default ```typescript 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); } ``` ### Human Readable ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); 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); } ``` ## 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`. --- # News (JavaScript SDK) Source: https://www.marketdata.app/docs/sdk/js/stocks/news/index.md 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, ): MarketDataPromise // Object form news

( params: P & { symbol: string }, ): MarketDataPromise ``` 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`](https://www.marketdata.app/docs/sdk/js/settings#output-format) (optional): The format of the returned data. Alias: `format`. - [`dateFormat`](https://www.marketdata.app/docs/sdk/js/settings#date-format) (optional): Date format. Alias: `dateformat`. - [`columns`](https://www.marketdata.app/docs/sdk/js/settings#columns) (optional): Columns to include. - [`addHeaders`](https://www.marketdata.app/docs/sdk/js/settings#headers) (optional): Whether to include headers in CSV output. Alias: `headers`. - [`useHumanReadable`](https://www.marketdata.app/docs/sdk/js/settings#human-readable) (optional): Use human-readable field names. Alias: `human`. - [`mode`](https://www.marketdata.app/docs/sdk/js/settings#data-mode) (optional): The data mode to use. ### Returns - [`MarketDataPromise`](https://www.marketdata.app/docs/sdk/js/client#marketdatapromise) ### Default ```typescript 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); } ``` ### Date Range ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); 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); } ``` ## 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. --- # Prices (JavaScript SDK) Source: https://www.marketdata.app/docs/sdk/js/stocks/prices/index.md 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 [`MarketDataPromise`](https://www.marketdata.app/docs/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 | |------------------------|---------------------------------------|-----------------------------------------------------------------------| | **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, ): MarketDataPromise // Object form prices

( params: P & { symbols: string | string[] }, ): MarketDataPromise ``` 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`](https://www.marketdata.app/docs/sdk/js/settings#output-format) (optional) The format of the returned data. See [Settings](https://www.marketdata.app/docs/sdk/js/settings#output-format) for details. Also accepts the alias `format`. - [`dateFormat`](https://www.marketdata.app/docs/sdk/js/settings#date-format) (optional) The date format to use in the response. Also accepts the alias `dateformat`. See [Settings](https://www.marketdata.app/docs/sdk/js/settings#date-format) for details. - [`columns`](https://www.marketdata.app/docs/sdk/js/settings#columns) (optional) Specify which columns to include in the response. See [Settings](https://www.marketdata.app/docs/sdk/js/settings#columns) for details. - [`addHeaders`](https://www.marketdata.app/docs/sdk/js/settings#headers) (optional) Whether to include headers in CSV output. Also accepts the alias `headers`. See [Settings](https://www.marketdata.app/docs/sdk/js/settings#headers) for details. - [`useHumanReadable`](https://www.marketdata.app/docs/sdk/js/settings#human-readable) (optional) Whether to use human-readable field names. Also accepts the alias `human`. See [Settings](https://www.marketdata.app/docs/sdk/js/settings#human-readable) for details. - [`mode`](https://www.marketdata.app/docs/sdk/js/settings#data-mode) (optional) The data mode to use (`"live"`, `"cached"`, `"delayed"`). See [Settings](https://www.marketdata.app/docs/sdk/js/settings#data-mode) for details. ### Returns - [`MarketDataPromise`](https://www.marketdata.app/docs/sdk/js/client#marketdatapromise) 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`. ### Single Symbol ```typescript 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); } ``` ### Multiple Symbols ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); 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); } ``` ### Human Readable ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); 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); } ``` ### Raw JSON ```typescript import { MarketDataClient, OutputFormat } from "@marketdata/sdk"; const client = new MarketDataClient(); try { const json = await client.stocks.prices(["AAPL", "MSFT"], { outputFormat: OutputFormat.JSON, }); console.log(json); } catch (error) { console.error(error); } ``` ### CSV ```typescript import { MarketDataClient, OutputFormat } from "@marketdata/sdk"; const client = new MarketDataClient(); const csv = await client.stocks.prices(["AAPL", "MSFT"], { outputFormat: OutputFormat.CSV, }); // Save the Blob to disk (Node.js) const filename = await csv.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. --- # Stock Quotes (JavaScript SDK) Source: https://www.marketdata.app/docs/sdk/js/stocks/quotes/index.md 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 [`MarketDataPromise`](https://www.marketdata.app/docs/sdk/js/client#marketdatapromise) that resolves to decoded records by default, or rejects with a `MarketDataClientError` subclass on failure. | 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, ): MarketDataPromise // Object form quotes

( params: P & { symbols: string | string[] }, ): MarketDataPromise ``` 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`](https://www.marketdata.app/docs/sdk/js/settings#output-format) (optional): The format of the returned data. Alias: `format`. - [`dateFormat`](https://www.marketdata.app/docs/sdk/js/settings#date-format) (optional): The date format to use. Alias: `dateformat`. - [`columns`](https://www.marketdata.app/docs/sdk/js/settings#columns) (optional): Columns to include. - [`addHeaders`](https://www.marketdata.app/docs/sdk/js/settings#headers) (optional): Whether to include headers in CSV output. Alias: `headers`. - [`useHumanReadable`](https://www.marketdata.app/docs/sdk/js/settings#human-readable) (optional): Use human-readable field names. Alias: `human`. - [`mode`](https://www.marketdata.app/docs/sdk/js/settings#data-mode) (optional): The data mode to use. ### Returns - [`MarketDataPromise`](https://www.marketdata.app/docs/sdk/js/client#marketdatapromise) ### Single Symbol ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); try { const quotes = await client.stocks.quotes("AAPL"); console.log(quotes[0]); } catch (error) { console.error(error); } ``` ### Multiple Symbols ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); 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); } ``` ### 52-Week High/Low ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); try { const quotes = await client.stocks.quotes("AAPL", { "52week": true }); console.log(quotes[0]); } catch (error) { console.error(error); } ``` ### Human Readable ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); 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); } ``` ## 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`. --- # Utilities (JavaScript SDK) Source: https://www.marketdata.app/docs/sdk/js/utilities/index.md 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. 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`](https://www.marketdata.app/docs/sdk/js/client#accessing-rate-limits), which the SDK populates automatically at startup from the same `/user/` response headers. ## Utilities Endpoints --- # Headers (JavaScript SDK) Source: https://www.marketdata.app/docs/sdk/js/utilities/headers/index.md 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`](https://www.marketdata.app/docs/sdk/js/client#marketdatapromise) ### Default ```typescript import { MarketDataClient } from "@marketdata/sdk"; const client = new MarketDataClient(); try { const h = await client.utilities.headers(); console.log(h["user-agent"]); // "marketdata-sdk-js/1.0.0" 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. --- # API Status (JavaScript SDK) Source: https://www.marketdata.app/docs/sdk/js/utilities/status/index.md 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`](https://www.marketdata.app/docs/sdk/js/client#marketdatapromise) ### Default ```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); } ``` ### With Uptime ```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. > [!NOTE] > 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. --- # PHP SDK Source: https://www.marketdata.app/docs/sdk/php/index.md Welcome to the Market Data PHP SDK documentation. This SDK allows you to easily integrate Market Data services into your PHP applications. ## Open Source The SDK is completely open source under the MIT license and is available on GitHub. Feel free to contribute to the project, report bugs, or request new features. - [PHP SDK GitHub](https://github.com/MarketDataApp/sdk-php/) - [PHP SDK Packagist](https://packagist.org/packages/marketdataapp/sdk-php) ## 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](https://www.marketdata.app/docs/sdk/php/installation) with Composer. 2. Set up your [authentication token](https://www.marketdata.app/docs/sdk/php/authentication) to access the API. 3. Learn about the [client](https://www.marketdata.app/docs/sdk/php/client) and how to make your first API requests. 4. Configure [Parameters](https://www.marketdata.app/docs/sdk/php/parameters) to customize output format, date format, and other universal parameters. 5. Explore the available endpoints for [stocks](https://www.marketdata.app/docs/sdk/php/stocks), [options](https://www.marketdata.app/docs/sdk/php/options), [funds](https://www.marketdata.app/docs/sdk/php/funds), and [markets](https://www.marketdata.app/docs/sdk/php/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-php/issues). Please only open issues if you find a bug. Use our helpdesk for general questions or implementation assistance. --- # Authentication (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/authentication/index.md The Market Data PHP SDK requires an API token to authenticate requests. You can obtain a token from the [Market Data Dashboard](https://www.marketdata.app/dashboard/). ## Token Configuration The SDK supports multiple methods for providing your API token, with the following priority order (highest to lowest): 1. **Explicit token parameter** - Passed directly to the Client constructor 2. **Environment variable** - `MARKETDATA_TOKEN` 3. **.env file** - Using phpdotenv 4. **Empty string** - For accessing free endpoints only ### Setting the Environment Variable ### Mac/Linux Add to your shell profile (`.bashrc`, `.zshrc`, etc.): ```bash export MARKETDATA_TOKEN="your_api_token_here" ``` Then reload your shell: ```bash source ~/.bashrc # or source ~/.zshrc ``` ### Windows Using Command Prompt: ```batch setx MARKETDATA_TOKEN "your_api_token_here" ``` Using PowerShell: ```powershell [Environment]::SetEnvironmentVariable("MARKETDATA_TOKEN", "your_api_token_here", "User") ``` ### Using a .env File Create a `.env` file in your project root: ```ini MARKETDATA_TOKEN=your_api_token_here ``` The SDK automatically loads `.env` files using phpdotenv. ### Explicit Token Parameter Pass the token directly to the Client constructor: ```php getMessage(); } ``` ## Testing Your Token Verify your token is working correctly: ```php utilities->user(); echo "Requests Used Today: " . $user->requests_made_today . "\n"; echo "Daily Request Limit: " . $user->daily_requests . "\n"; echo "Requests Remaining: " . ($user->daily_requests - $user->requests_made_today) . "\n"; ``` ## Security Best Practices - **Never commit your API token** to version control - Add `.env` to your `.gitignore` file - Use environment variables in production environments - Rotate tokens regularly if compromised ## Next Steps After configuring authentication, learn about the [Client](https://www.marketdata.app/docs/sdk/php/client) class to start making API requests. --- # Client (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/client/index.md The `Client` class is the main entry point for the Market Data PHP SDK. It provides access to all API endpoints through resource properties. ## Creating a Client ```php stocks` | `Stocks` | Stock data including candles, quotes, earnings, and news | | `$client->options` | `Options` | Options chains, expirations, and quotes | | `$client->markets` | `Markets` | Market status and trading calendar | | `$client->mutual_funds` | `MutualFunds` | Mutual fund candles | | `$client->utilities` | `Utilities` | API status, headers, and rate limits | ## Using Resources Each resource provides methods for accessing specific API endpoints: ```php stocks->candles('AAPL', '2024-01-01', '2024-01-31'); $quote = $client->stocks->quote('AAPL'); $quotes = $client->stocks->quotes(['AAPL', 'MSFT', 'GOOGL']); // Options $expirations = $client->options->expirations('AAPL'); $chain = $client->options->option_chain('AAPL', expiration: '2025-01-17'); // Markets $status = $client->markets->status(); // Mutual Funds $fundCandles = $client->mutual_funds->candles('VFINX', '2024-01-01', '2024-01-31'); // Utilities $apiStatus = $client->utilities->api_status(); $user = $client->utilities->user(); ``` ## Default Parameters Configure universal parameters that apply to all API calls: ```php default_params = new Parameters( format: Format::JSON, mode: Mode::CACHED ); // These calls will use the default parameters $candles = $client->stocks->candles('AAPL', '2024-01-01', '2024-01-31'); $quote = $client->stocks->quote('AAPL'); ``` See [Parameters](https://www.marketdata.app/docs/sdk/php/parameters) for all available options. ## Rate Limits Access your current rate limit information: ```php stocks->quote('AAPL'); // Access rate limits from the last response $rateLimits = $client->rate_limits; if ($rateLimits) { echo "Requests consumed: " . $rateLimits->consumed . "\n"; echo "Daily limit: " . $rateLimits->limit . "\n"; echo "Remaining: " . $rateLimits->remaining . "\n"; echo "Reset time: " . $rateLimits->reset . "\n"; } ``` ## Parallel Requests Execute multiple API calls concurrently for improved performance: ```php execute_in_parallel([ ['stocks/quotes/', ['symbol' => 'AAPL']], ['stocks/quotes/', ['symbol' => 'MSFT']], ['stocks/quotes/', ['symbol' => 'GOOGL']], ]); ``` For simpler multi-symbol requests, use the built-in batch methods like `quotes()` which handle parallelization automatically. ## Error Handling The Client throws exceptions for various error conditions: ```php stocks->quote('INVALID_SYMBOL'); } catch (UnauthorizedException $e) { echo "Authentication failed: " . $e->getMessage(); } catch (ApiException $e) { echo "API error: " . $e->getMessage(); echo "\nSupport info: " . $e->getSupportInfo(); } catch (BadStatusCodeError $e) { echo "Request failed: " . $e->getMessage(); } ``` See [Error Handling](https://www.marketdata.app/docs/sdk/php/errors) for details on exception types. ## Custom HTTP Client Inject a custom Guzzle client for advanced use cases: ```php 30, 'connect_timeout' => 5, ]); $client->setGuzzle($guzzle); ``` ## Next Steps - Configure [Parameters](https://www.marketdata.app/docs/sdk/php/parameters) for customizing output formats - Set up [Logging](https://www.marketdata.app/docs/sdk/php/logging) for debugging - Learn about [Error Handling](https://www.marketdata.app/docs/sdk/php/errors) - Explore endpoint documentation: [Stocks](https://www.marketdata.app/docs/sdk/php/stocks), [Options](https://www.marketdata.app/docs/sdk/php/options), [Markets](https://www.marketdata.app/docs/sdk/php/markets), [Funds](https://www.marketdata.app/docs/sdk/php/funds) --- # Error Handling (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/errors/index.md The Market Data PHP SDK uses a hierarchical exception system to handle different types of errors. Understanding these exceptions helps you build robust applications that gracefully handle failures. ## Exception Hierarchy ``` MarketDataException (base) ├── ApiException - Business logic errors (invalid symbol, no data) ├── RequestError - Transient errors (5xx, timeouts) - triggers retry └── BadStatusCodeError - Permanent errors (4xx) - no retry └── UnauthorizedException - 401 authentication errors ``` ## Exception Types ### MarketDataException Base exception class for all SDK exceptions. All other exceptions extend this class. ```php stocks->quote('AAPL'); } catch (MarketDataException $e) { // Catches all SDK exceptions echo "SDK Error: " . $e->getMessage(); } ``` ### ApiException Thrown for business logic errors returned by the API, such as invalid symbols or no data available. ```php stocks->quote('INVALID_SYMBOL_XYZ'); } catch (ApiException $e) { echo "API Error: " . $e->getMessage() . "\n"; echo "HTTP Code: " . $e->getCode() . "\n"; // Get detailed support information echo "Support Info:\n" . $e->getSupportInfo() . "\n"; // Get structured context for logging $context = $e->getSupportContext(); print_r($context); } ``` **Common causes:** - Invalid or unknown symbol - No data available for the requested date range - Invalid parameter values - Premium endpoint accessed without subscription ### RequestError Thrown for transient errors that may succeed on retry, such as server errors (5xx) or network timeouts. ```php stocks->quote('AAPL'); } catch (RequestError $e) { echo "Transient Error: " . $e->getMessage() . "\n"; echo "This error may be resolved by retrying.\n"; } ``` **Common causes:** - Server errors (500, 502, 503, 504) - Network timeouts - Connection failures **Note:** The SDK automatically retries these errors with exponential backoff before throwing the exception. ### BadStatusCodeError Thrown for permanent HTTP errors (4xx) that will not succeed on retry. ```php stocks->quote('AAPL'); } catch (BadStatusCodeError $e) { echo "HTTP Error: " . $e->getCode() . "\n"; echo "Message: " . $e->getMessage() . "\n"; } ``` **Common causes:** - 400 Bad Request (invalid parameters) - 403 Forbidden (endpoint requires higher subscription tier) - 404 Not Found (invalid endpoint) - 429 Too Many Requests (rate limit exceeded) ### UnauthorizedException Thrown specifically for 401 authentication errors. Extends `BadStatusCodeError`. ```php getMessage() . "\n"; echo "Please check your API token.\n"; } ``` **Common causes:** - Invalid API token - Expired API token - Missing API token ## Support Information All SDK exceptions provide methods for getting detailed error context: ### getSupportInfo() Returns a human-readable string with request details for support tickets: ```php getSupportInfo(); // Returns formatted string like: // Request URL: https://api.marketdata.app/v1/stocks/quotes/?symbol=INVALID // HTTP Status: 400 // Error Message: Symbol not found echo $supportInfo; } ``` ### getSupportContext() Returns an associative array with structured error context for logging: ```php getSupportContext(); // Returns array like: // [ // 'request_url' => 'https://api.marketdata.app/v1/stocks/quotes/?symbol=INVALID', // 'http_status' => 400, // 'error_message' => 'Symbol not found' // ] // Log to your monitoring system $logger->error('API call failed', $context); } ``` ## Handling Multiple Exception Types Use a try-catch chain to handle different error types appropriately: ```php stocks->quote('AAPL'); echo "Quote received: $" . $quote->last . "\n"; } catch (UnauthorizedException $e) { // Handle authentication errors echo "Authentication failed. Please check your API token.\n"; } catch (ApiException $e) { // Handle business logic errors echo "API Error: " . $e->getMessage() . "\n"; // Log details for troubleshooting error_log("API Exception: " . $e->getSupportInfo()); } catch (RequestError $e) { // Handle transient errors (already retried) echo "Service temporarily unavailable. Please try again later.\n"; } catch (BadStatusCodeError $e) { // Handle other HTTP errors echo "Request failed with HTTP " . $e->getCode() . "\n"; } catch (MarketDataException $e) { // Catch-all for any SDK exception echo "Unexpected SDK error: " . $e->getMessage() . "\n"; } ``` ## Automatic Retry Behavior The SDK automatically retries transient errors (`RequestError`) with exponential backoff: - **Initial delay:** 1 second - **Maximum retries:** 3 attempts - **Backoff multiplier:** 2x per retry - **Jitter:** Random variation to prevent thundering herd If all retries fail, the exception is thrown to your code. ## Best Practices 1. **Always catch exceptions** - API calls can fail for various reasons 2. **Handle specific exceptions first** - Catch more specific exceptions before general ones 3. **Log error context** - Use `getSupportContext()` for structured logging 4. **Provide user feedback** - Show appropriate messages for different error types 5. **Don't retry manually** - The SDK handles retries for transient errors ## Example: Robust API Call ```php stocks->quote($symbol); } catch (UnauthorizedException $e) { // Critical error - cannot recover without new token throw new RuntimeException('API authentication failed', 0, $e); } catch (ApiException $e) { // Symbol-specific error - log and return null error_log("Failed to get quote for {$symbol}: " . $e->getMessage()); return null; } catch (MarketDataException $e) { // Other SDK errors - log and return null error_log("SDK error for {$symbol}: " . $e->getMessage()); return null; } } // Usage $quote = getStockQuote('AAPL'); if ($quote) { echo "AAPL: $" . $quote->last . "\n"; } else { echo "Could not fetch quote for AAPL\n"; } ``` --- # Mutual Funds (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/funds/index.md Access historical price data for mutual funds. ## Mutual Funds Endpoints --- # Fund Candles (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/funds/candles/index.md Get historical price candles (OHLCV data) for mutual funds. ## Making Requests Use the `candles()` method on the `mutual_funds` resource to fetch fund candles: | Output Format | Return Type | Description | |---------------|-------------|---------------------------------------------------------------------------| | **JSON** | `Candles` | Returns a Candles object containing an array of Candle objects (default). | | **CSV** | `Candles` | Returns a Candles object with CSV data accessible via `getCsv()`. | | **HTML** | `Candles` | Returns a Candles object with HTML data accessible via `getHtml()`. | > [!WARNING] > **[HTML Not Yet Available]** > > `Format::HTML` is included for forward compatibility, but HTML responses are not currently implemented by the Market Data API. ## candles ```php public function candles( string $symbol, string $from, ?string $to = null, string $resolution = 'D', ?int $countback = null, ?Parameters $parameters = null ): Candles ``` Get historical price candles for a mutual fund. Supports various timeframes from minutely to yearly resolutions. ### Parameters - `symbol` (string) The mutual fund ticker symbol (e.g., "VFINX", "FXAIX"). - `from` (string) The start date for the candle data. Accepted formats: ISO 8601 (`2024-01-01`), Unix timestamp, or spreadsheet serial number. - `to` (string, optional) The end date for the candle data. If omitted, you must specify `countback`. - `resolution` (string, optional) The granularity of the candle data. Defaults to `"D"` (daily). - **Minutely:** `1`, `3`, `5`, `15`, `30`, `45`, `minutely` - **Hourly:** `H`, `1H`, `2H`, `hourly` - **Daily:** `D`, `1D`, `2D`, `daily` - **Weekly:** `W`, `1W`, `2W`, `weekly` - **Monthly:** `M`, `1M`, `2M`, `monthly` - **Yearly:** `Y`, `1Y`, `2Y`, `yearly` - `countback` (int, optional) Number of candles to return, counting backwards from `to`. - `parameters` ([Parameters](https://www.marketdata.app/docs/sdk/php/parameters), optional) Universal parameters for customizing the output format. ### Returns - `Candles` A Candles response object containing the candle data. ### Notes - Mutual funds typically only trade once per day at market close, so daily resolution is most common. - The `from` date is inclusive (leftmost candle on a chart). - The `to` date is inclusive (rightmost candle on a chart). ### Example (Daily) ```php mutual_funds->candles( symbol: 'VFINX', from: '2024-01-01', to: '2024-01-31' ); echo "VFINX Daily Prices:\n"; echo "===================\n"; foreach ($candles->candles as $candle) { echo sprintf( "%s: Open $%.2f, High $%.2f, Low $%.2f, Close $%.2f\n", $candle->timestamp->format('Y-m-d'), $candle->open, $candle->high, $candle->low, $candle->close ); } ``` **Output** ``` VFINX Daily Prices: =================== 2024-01-02: Open $438.21, High $440.15, Low $436.89, Close $439.45 2024-01-03: Open $439.45, High $441.22, Low $437.56, Close $438.12 2024-01-04: Open $438.12, High $439.88, Low $435.44, Close $436.78 ... ``` ### Example (Weekly) ```php mutual_funds->candles( symbol: 'VFINX', from: '2023-01-01', to: '2023-12-31', resolution: 'W' ); echo "VFINX Weekly Summary (2023):\n"; echo "============================\n"; foreach ($candles->candles as $candle) { $change = $candle->close - $candle->open; $pctChange = ($change / $candle->open) * 100; $indicator = $change >= 0 ? '▲' : '▼'; echo sprintf( "Week of %s: $%.2f %s%.1f%%\n", $candle->timestamp->format('M j'), $candle->close, $indicator, abs($pctChange) ); } ``` ### Example (Compare Funds) ```php mutual_funds->candles( symbol: $symbol, from: '2024-01-01', to: '2024-01-31' ); $first = $candles->candles[0]; $last = end($candles->candles); $return = (($last->close - $first->open) / $first->open) * 100; $results[$symbol] = [ 'start' => $first->open, 'end' => $last->close, 'return' => $return ]; } echo "Fund Comparison (January 2024):\n"; echo "===============================\n"; foreach ($results as $symbol => $data) { echo sprintf( "%s: $%.2f → $%.2f (%+.2f%%)\n", $symbol, $data['start'], $data['end'], $data['return'] ); } ``` ### Example (CSV) ```php mutual_funds->candles( symbol: 'VFINX', from: '2024-01-01', to: '2024-01-31', parameters: new Parameters(format: Format::CSV) ); echo $candles->getCsv(); ``` ## Candle ```php class Candle { public float $open; public float $high; public float $low; public float $close; public int $volume; public Carbon $timestamp; } ``` Represents a single mutual fund candle (OHLCV data). ### Properties - `open` (float): The opening price (NAV). - `high` (float): The highest price during the period. - `low` (float): The lowest price during the period. - `close` (float): The closing price (NAV). - `volume` (int): The trading volume. - `timestamp` (Carbon): The timestamp for the candle. ## Candles ```php class Candles extends ResponseBase { public string $status; public ?int $next_time; public array $candles; } ``` Represents a collection of mutual fund candles data. ### Properties - `status` (string): The status of the response (`"ok"` or `"no_data"`). - `next_time` (int|null): Unix timestamp of the next available quote if no data in the requested period. - `candles` (Candle[]): Array of Candle objects. ### Methods - `getCsv()`: Returns the raw CSV data (when using `Format::CSV`). - `getHtml()`: Returns the raw HTML data (when using `Format::HTML`). - `isJson()`: Returns `true` if the response contains JSON data. --- # Installation (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/installation/index.md The Market Data PHP SDK can be installed via Composer, the standard PHP dependency manager. ## Requirements - PHP 8.2 or higher - Composer ## Installing with Composer Install the SDK using Composer: ```bash composer require marketdataapp/sdk-php ``` This will install the SDK and all required dependencies. ## Dependencies The SDK depends on the following packages, which are automatically installed by Composer: - **guzzlehttp/guzzle** - HTTP client for making API requests - **vlucas/phpdotenv** - Environment variable loading from `.env` files - **psr/log** - PSR-3 logger interface - **nesbot/carbon** - Date/time handling - **monolog/monolog** - PSR-3 compatible logging (optional) ## Verifying Installation After installation, you can verify the SDK is working by creating a simple test script: ```php utilities->api_status(); echo "API Status Response: {$status->status}\n"; if (!empty($status->services)) { $service = $status->services[0]; echo "Service Check: {$service->service} is {$service->status}\n"; } ``` ## Updating the SDK To update to the latest version: ```bash composer update marketdataapp/sdk-php ``` ## Next Steps After installing the SDK, [set up your authentication token](https://www.marketdata.app/docs/sdk/php/authentication) to start making API calls. --- # Logging (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/logging/index.md The Market Data PHP SDK supports PSR-3 compatible logging for debugging and monitoring API interactions. ## Default Logging The SDK creates a default logger based on the `MARKETDATA_LOGGING_LEVEL` environment variable: ```php stocks->quote('AAPL'); ``` ### Available Log Levels The SDK supports standard PSR-3 log levels: | Level | Description | |-------------|-------------------------------------| | `emergency` | System is unusable | | `alert` | Action must be taken immediately | | `critical` | Critical conditions | | `error` | Error conditions | | `warning` | Warning conditions | | `notice` | Normal but significant conditions | | `info` | Informational messages | | `debug` | Debug-level messages (most verbose) | ## Custom Logger Inject your own PSR-3 compatible logger: ### Monolog ```php pushHandler(new RotatingFileHandler( '/var/log/marketdata/api.log', maxFiles: 7, level: Logger::DEBUG )); // Also log errors to stderr $logger->pushHandler(new StreamHandler( 'php://stderr', level: Logger::ERROR )); // Pass logger to client $client = new Client(logger: $logger); $quote = $client->stocks->quote('AAPL'); ``` ### Custom PSR-3 Logger ```php stocks->quote('AAPL'); ``` ## What Gets Logged The SDK logs the following events: ### Debug Level - API request URLs and parameters - Response status codes - Retry attempts ### Info Level - Successful API responses - Rate limit information ### Warning Level - Retry attempts after transient failures - Deprecated endpoint usage ### Error Level - API errors and exceptions - Authentication failures - Network errors ## Example Log Output ``` [2024-01-15 10:30:00] [debug] Making request: GET https://api.marketdata.app/v1/stocks/quotes/?symbol=AAPL [2024-01-15 10:30:01] [debug] Response received: 200 OK [2024-01-15 10:30:01] [info] Rate limits - Used: 1, Remaining: 9999, Limit: 10000 ``` ## Disabling Logging To disable logging, set the log level to a high threshold or provide a null logger: ```php [!WARNING] > **[HTML Not Yet Available]** > > `Format::HTML` is included for forward compatibility, but HTML responses are not currently implemented by the Market Data API. ## status ```php public function status( string $country = "US", ?string $date = null, ?string $from = null, ?string $to = null, ?int $countback = null, ?Parameters $parameters = null ): Statuses ``` Get the past, present, or future status for a stock market. The endpoint will respond with "open" for trading days or "closed" for weekends or market holidays. ### Parameters - `country` (string, optional) The country code using the two-digit ISO 3166 format. Defaults to `"US"`. Currently, only the United States is supported. - `date` (string, optional) Check whether the market was open or closed on a specific date. Accepted formats: ISO 8601, Unix timestamp, spreadsheet serial number. - `from` (string, optional) The earliest date (inclusive) for a date range query. Combine with `to` or `countback`. - `to` (string, optional) The last date (inclusive) for a date range query. - `countback` (int, optional) Number of dates to return before `to`. Use instead of `from`. - `parameters` ([Parameters](https://www.marketdata.app/docs/sdk/php/parameters), optional) Universal parameters for customizing the output format. ### Returns - `Statuses` A Statuses response object containing the market status for the requested date(s). ### Example (Current Status) ```php markets->status(); foreach ($statuses->statuses as $status) { echo "Date: " . $status->date->format('Y-m-d') . "\n"; echo "Status: " . $status->status . "\n"; } ``` **Output** ``` Date: 2024-01-15 Status: open ``` ### Example (Specific Date) ```php markets->status(date: '2024-01-01'); foreach ($statuses->statuses as $status) { $isOpen = $status->status === 'open'; echo "January 1, 2024: " . ($isOpen ? "Market Open" : "Market Closed") . "\n"; } ``` **Output** ``` January 1, 2024: Market Closed ``` ### Example (Date Range) ```php markets->status( from: '2024-01-01', to: '2024-01-15' ); echo "Market Calendar: January 2024\n"; echo "=============================\n"; foreach ($statuses->statuses as $status) { $indicator = $status->status === 'open' ? '✓' : '✗'; echo sprintf( "%s %s: %s\n", $indicator, $status->date->format('D, M j'), ucfirst($status->status) ); } ``` **Output** ``` Market Calendar: January 2024 ============================= ✗ Mon, Jan 1: Closed ✓ Tue, Jan 2: Open ✓ Wed, Jan 3: Open ✓ Thu, Jan 4: Open ✓ Fri, Jan 5: Open ✗ Sat, Jan 6: Closed ✗ Sun, Jan 7: Closed ✓ Mon, Jan 8: Open ... ``` ### Example (Trading Days Count) ```php markets->status( to: 'today', countback: 20 ); $tradingDays = 0; foreach ($statuses->statuses as $status) { if ($status->status === 'open') { $tradingDays++; } } echo "Last " . count($statuses->statuses) . " days included " . $tradingDays . " trading days.\n"; ``` ### Example (CSV) ```php markets->status( from: '2024-01-01', to: '2024-01-31', parameters: new Parameters(format: Format::CSV) ); echo $statuses->getCsv(); ``` ## Status ```php class Status { public Carbon $date; public string $status; } ``` Represents the market status for a single date. ### Properties - `date` (Carbon): The date. - `status` (string): The market status (`"open"` or `"closed"`). ## Statuses ```php class Statuses extends ResponseBase { public string $status; public array $statuses; } ``` Represents a collection of market status entries. ### Properties - `status` (string): Response status (`"ok"` or `"no_data"`). - `statuses` (Status[]): Array of Status objects. ### Methods - `getCsv()`: Returns the raw CSV data (when using `Format::CSV`). - `getHtml()`: Returns the raw HTML data (when using `Format::HTML`). - `isJson()`: Returns `true` if the response contains JSON data. --- # Options (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/options/index.md Access options market data including chains, expirations, and quotes for any underlying symbol. ## Options Endpoints --- # Option Chain (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/options/chain/index.md Get a current or historical end-of-day options chain for an underlying ticker symbol with extensive filtering options. ## Making Requests Use the `option_chain()` method on the `options` resource to fetch option chains: | Output Format | Return Type | Description | |---------------|----------------|---------------------------------------------------------------------------| | **JSON** | `OptionChains` | Returns an OptionChains object containing option contracts (default). | | **CSV** | `OptionChains` | Returns an OptionChains object with CSV data accessible via `getCsv()`. | | **HTML** | `OptionChains` | Returns an OptionChains object with HTML data accessible via `getHtml()`. | > [!WARNING] > **[HTML Not Yet Available]** > > `Format::HTML` is included for forward compatibility, but HTML responses are not currently implemented by the Market Data API. ## option_chain ```php public function option_chain( string $symbol, ?string $date = null, string|Expiration|null $expiration = null, ?string $from = null, ?string $to = null, ?int $month = null, ?int $year = null, bool $weekly = true, bool $monthly = true, bool $quarterly = true, ?bool $non_standard = null, ?int $dte = null, string|float|null $delta = null, ?Side $side = null, Range $range = Range::ALL, ?string $strike = null, ?int $strike_limit = null, ?float $min_bid = null, ?float $max_bid = null, ?float $min_ask = null, ?float $max_ask = null, ?float $max_bid_ask_spread = null, ?float $max_bid_ask_spread_pct = null, ?int $min_open_interest = null, ?int $min_volume = null, ?bool $am = null, ?bool $pm = null, ?Parameters $parameters = null ): OptionChains ``` Get a current or historical end-of-day options chain for an underlying ticker symbol. Use the `optionSymbol` returned from this endpoint to get quotes, greeks, or other information using other endpoints. ### Parameters - `symbol` (string) The underlying ticker symbol (e.g., "AAPL", "SPY", "SPX"). - `date` (string, optional) Look up a historical end-of-day options chain from a specific trading day. If omitted, returns the current chain during market hours or the last trading day's chain when closed. - `expiration` (string|Expiration, optional) Limit to a specific expiration date, or use `Expiration::ALL` to return the complete chain. :::caution Using `Expiration::ALL` with large chains (SPX, SPY, QQQ) can consume API credits quickly. The full SPX chain has 20,000+ contracts. ::: - `from` (string, optional) Limit to expiration dates after this date (inclusive). Combine with `to` for a range. - `to` (string, optional) Limit to expiration dates before this date (not inclusive). Combine with `from` for a range. - `month` (int, optional) Limit to options expiring in a specific month (1-12). - `year` (int, optional) Limit to options expiring in a specific year. - `weekly` (bool, optional) Include weekly expirations. Defaults to `true`. - `monthly` (bool, optional) Include standard monthly expirations. Defaults to `true`. - `quarterly` (bool, optional) Include quarterly expirations. Defaults to `true`. - `non_standard` (bool, optional) Include non-standard contracts. Defaults to `false`. - `dte` (int, optional) Days to expiration. Returns the single expiration closest to this DTE. - `delta` (string|float, optional) Filter by delta: - Single value: `.50` (closest to delta) - Multiple values: `.60,.30` - Open interval: `>.50` - Closed interval: `.30-.60` - `side` (Side, optional) Limit to `Side::CALL` or `Side::PUT`. If omitted, both sides are returned. - `range` (Range, optional) Filter by moneyness: `Range::IN_THE_MONEY`, `Range::OUT_OF_THE_MONEY`, or `Range::ALL` (default). - `strike` (string, optional) Filter by strike price: - Single value: `400` - Multiple values: `400,405` - Open interval: `>400` - Closed interval: `400-410` - `strike_limit` (int, optional) Limit total strikes returned. Excludes strikes furthest from the money. - `min_bid`, `max_bid`, `min_ask`, `max_ask` (float, optional) Filter by bid/ask price ranges. - `max_bid_ask_spread` (float, optional) Maximum bid-ask spread in dollars. - `max_bid_ask_spread_pct` (float, optional) Maximum bid-ask spread as percent of underlying (e.g., 0.5 = 0.5%). - `min_open_interest` (int, optional) Minimum open interest. - `min_volume` (int, optional) Minimum volume. - `am`, `pm` (bool, optional) For index options only (SPX): filter by AM or PM settlement. - `parameters` ([Parameters](https://www.marketdata.app/docs/sdk/php/parameters), optional) Universal parameters for customizing the output format. ### Returns - `OptionChains` An OptionChains response object containing the filtered option contracts. ### Example (Basic) ```php options->option_chain('AAPL'); echo "AAPL Options Chain\n"; echo "==================\n"; echo "Total contracts: " . count($chain->options) . "\n\n"; // Display first few contracts foreach (array_slice($chain->options, 0, 5) as $option) { echo sprintf( "%s: Bid $%.2f / Ask $%.2f (Vol: %d, OI: %d)\n", $option->option_symbol, $option->bid, $option->ask, $option->volume, $option->open_interest ); } ``` ### Example (Filtered by Side) ```php options->option_chain( symbol: 'AAPL', expiration: '2025-01-17', side: Side::CALL ); echo "AAPL Calls for Jan 17, 2025:\n"; echo "============================\n"; foreach ($chain->options as $option) { echo sprintf( "Strike $%.2f: Bid $%.2f / Ask $%.2f\n", $option->strike, $option->bid, $option->ask ); } ``` ### Example (ATM Options) ```php options->option_chain( symbol: 'SPY', expiration: '2025-01-17', delta: 0.50, strike_limit: 5 ); echo "SPY ATM Options (near 50 delta):\n"; echo "================================\n"; foreach ($chain->options as $option) { $type = str_contains($option->option_symbol, 'C') ? 'CALL' : 'PUT'; echo sprintf( "%s $%.2f: $%.2f (Delta: %.2f)\n", $type, $option->strike, $option->mid, $option->delta ?? 0 ); } ``` ### Example (Liquid Options) ```php options->option_chain( symbol: 'AAPL', expiration: '2025-01-17', min_volume: 100, min_open_interest: 1000, max_bid_ask_spread: 0.10 ); echo "Liquid AAPL Options:\n"; echo "====================\n"; foreach ($chain->options as $option) { $spread = $option->ask - $option->bid; echo sprintf( "%s: Vol %d, OI %d, Spread $%.2f\n", $option->option_symbol, $option->volume, $option->open_interest, $spread ); } ``` ### Example (CSV) ```php options->option_chain( symbol: 'AAPL', expiration: '2025-01-17', side: Side::CALL, strike_limit: 10, parameters: new Parameters(format: Format::CSV) ); echo $chain->getCsv(); ``` ## OptionChains ```php class OptionChains extends ResponseBase { public string $status; public array $options; } ``` Represents an options chain with all matching contracts. ### Properties - `status` (string): Response status (`"ok"` or `"no_data"`). - `options` (array): Array of option contracts, each containing: - `option_symbol` (string): OCC-formatted option symbol - `underlying` (string): Underlying ticker symbol - `expiration` (Carbon): Expiration date - `side` (string): "call" or "put" - `strike` (float): Strike price - `bid` (float): Bid price - `bid_size` (int): Bid size - `ask` (float): Ask price - `ask_size` (int): Ask size - `mid` (float): Midpoint price - `last` (float): Last trade price - `volume` (int): Daily volume - `open_interest` (int): Open interest - `delta` (float|null): Delta greek - `gamma` (float|null): Gamma greek - `theta` (float|null): Theta greek - `vega` (float|null): Vega greek - `iv` (float|null): Implied volatility - `updated` (Carbon): Last update timestamp ### Methods - `getCsv()`: Returns the raw CSV data (when using `Format::CSV`). - `getHtml()`: Returns the raw HTML data (when using `Format::HTML`). - `isJson()`: Returns `true` if the response contains JSON data. --- # Expirations (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/options/expirations/index.md Get a list of current or historical option expiration dates for an underlying symbol. ## Making Requests Use the `expirations()` method on the `options` resource to fetch expiration dates: | Output Format | Return Type | Description | |---------------|---------------|--------------------------------------------------------------------------| | **JSON** | `Expirations` | Returns an Expirations object containing expiration dates (default). | | **CSV** | `Expirations` | Returns an Expirations object with CSV data accessible via `getCsv()`. | | **HTML** | `Expirations` | Returns an Expirations object with HTML data accessible via `getHtml()`. | > [!WARNING] > **[HTML Not Yet Available]** > > `Format::HTML` is included for forward compatibility, but HTML responses are not currently implemented by the Market Data API. ## expirations ```php public function expirations( string $symbol, int|float|null $strike = null, ?string $date = null, ?Parameters $parameters = null ): Expirations ``` Get a list of current or historical option expiration dates for an underlying symbol. If no optional parameters are used, the endpoint returns all expiration dates in the option chain. ### Parameters - `symbol` (string) The underlying ticker symbol for the options chain (e.g., "AAPL", "SPY"). - `strike` (int|float, optional) Limit the lookup of expiration dates to a specific strike price. Only returns expiration dates that include this strike. Accepts decimal values for non-standard strikes (e.g., `12.5`). - `date` (string, optional) Look up a historical list of expiration dates from a specific previous trading day. If omitted, returns current trading day expirations (or last trading day when market is closed). Accepted formats: ISO 8601, Unix timestamp, spreadsheet serial number. - `parameters` ([Parameters](https://www.marketdata.app/docs/sdk/php/parameters), optional) Universal parameters for customizing the output format. See [Parameters](https://www.marketdata.app/docs/sdk/php/parameters) for details. ### Returns - `Expirations` An Expirations response object containing the list of expiration dates. ### Example (All Expirations) ```php options->expirations('AAPL'); echo "AAPL Option Expirations:\n"; echo "========================\n"; foreach ($expirations->expirations as $date) { echo $date->format('Y-m-d') . "\n"; } ``` **Output** ``` AAPL Option Expirations: ======================== 2024-01-19 2024-01-26 2024-02-02 2024-02-09 2024-02-16 2024-03-15 2024-06-21 2024-09-20 2025-01-17 ... ``` ### Example (By Strike) ```php options->expirations( symbol: 'AAPL', strike: 200 ); echo "Expirations with $200 strike:\n"; foreach ($expirations->expirations as $date) { echo $date->format('Y-m-d') . "\n"; } ``` ### Example (Historical) ```php options->expirations( symbol: 'AAPL', date: '2024-01-02' ); echo "Expirations available on 2024-01-02:\n"; foreach ($expirations->expirations as $date) { echo $date->format('Y-m-d') . "\n"; } ``` ### Example (CSV) ```php options->expirations( symbol: 'AAPL', parameters: new Parameters(format: Format::CSV) ); echo $expirations->getCsv(); ``` ## Expirations ```php class Expirations extends ResponseBase { public string $status; public array $expirations; } ``` Represents a list of option expiration dates. ### Properties - `status` (string): Response status (`"ok"` or `"no_data"`). - `expirations` (Carbon[]): Array of expiration dates as Carbon datetime objects. ### Methods - `getCsv()`: Returns the raw CSV data (when using `Format::CSV`). - `getHtml()`: Returns the raw HTML data (when using `Format::HTML`). - `isJson()`: Returns `true` if the response contains JSON data. --- # Lookup (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/options/lookup/index.md Convert a human-readable option description to the standard OCC option symbol format. ## Making Requests Use the `lookup()` method on the `options` resource to convert option descriptions: | Output Format | Return Type | Description | |---------------|-------------|--------------------------------------------------------------------| | **JSON** | `Lookup` | Returns a Lookup object containing the OCC symbol (default). | | **CSV** | `Lookup` | Returns a Lookup object with CSV data accessible via `getCsv()`. | | **HTML** | `Lookup` | Returns a Lookup object with HTML data accessible via `getHtml()`. | > [!WARNING] > **[HTML Not Yet Available]** > > `Format::HTML` is included for forward compatibility, but HTML responses are not currently implemented by the Market Data API. ## lookup ```php public function lookup( string $input, ?Parameters $parameters = null ): Lookup ``` Generate a properly formatted OCC option symbol based on a human-readable description of an option. This endpoint converts text such as "AAPL 12/17/27 $250 Call" to OCC option symbol format: `AAPL271217C00250000`. ### Parameters - `input` (string) A human-readable string containing: 1. Stock symbol 2. Strike price 3. Expiration date 4. Option side (put or call) Example: `"AAPL 12/17/27 $250 Call"` - `parameters` ([Parameters](https://www.marketdata.app/docs/sdk/php/parameters), optional) Universal parameters for customizing the output format. See [Parameters](https://www.marketdata.app/docs/sdk/php/parameters) for details. ### Returns - `Lookup` A Lookup response object containing the OCC-formatted option symbol. ### Notes - The OCC (Options Clearing Corporation) symbol format is: `SYMBOL + YYMMDD + C/P + STRIKE(8 digits)` - Example: `AAPL271217C00250000` = AAPL July 28, 2023 $200 Call - The input parser is flexible and accepts various date formats and orderings. ### Example (Basic) ```php options->lookup('AAPL 12/17/27 $250 Call'); echo "Input: AAPL 12/17/27 $250 Call\n"; echo "OCC Symbol: " . $lookup->option_symbol . "\n"; ``` **Output** ``` Input: AAPL 12/17/27 $250 Call OCC Symbol: AAPL271217C00250000 ``` ### Example (Various Formats) ```php options->lookup($input); echo sprintf("%-35s → %s\n", $input, $lookup->option_symbol); } ``` **Output** ``` AAPL Jan 17, 2025 $180 Call → AAPL271217C00280000 MSFT 2025-01-17 400 put → MSFT281215P00485000 GOOGL $150 call 1/17/25 → GOOGL281215C00345000 SPY 500 C 01/19/24 → SPY281215C00770000 ``` ### Example (Use with Quotes) ```php options->lookup('AAPL Jan 2025 $180 Call'); $occSymbol = $lookup->option_symbol; // Use the OCC symbol to get option quotes $quotes = $client->options->quotes($occSymbol); echo "Option: {$occSymbol}\n"; foreach ($quotes->quotes as $quote) { echo "Bid: $" . $quote->bid . "\n"; echo "Ask: $" . $quote->ask . "\n"; echo "Last: $" . $quote->last . "\n"; } ``` ### Example (CSV) ```php options->lookup( input: 'AAPL 12/17/27 $250 Call', parameters: new Parameters(format: Format::CSV) ); echo $lookup->getCsv(); ``` ## Lookup ```php class Lookup extends ResponseBase { public string $status; public string $option_symbol; } ``` Represents the result of an option symbol lookup. ### Properties - `status` (string): Response status (`"ok"` or `"error"`). - `option_symbol` (string): The OCC-formatted option symbol. ### Methods - `getCsv()`: Returns the raw CSV data (when using `Format::CSV`). - `getHtml()`: Returns the raw HTML data (when using `Format::HTML`). - `isJson()`: Returns `true` if the response contains JSON data. --- # Option Quotes (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/options/quotes/index.md Get current or historical end-of-day quotes for one or more options contracts. ## Making Requests Use the `quotes()` method on the `options` resource to fetch option quotes: | Output Format | Return Type | Description | |---------------|-------------|--------------------------------------------------------------------| | **JSON** | `Quotes` | Returns a Quotes object containing option quote data (default). | | **CSV** | `Quotes` | Returns a Quotes object with CSV data accessible via `getCsv()`. | | **HTML** | `Quotes` | Returns a Quotes object with HTML data accessible via `getHtml()`. | > [!WARNING] > **[HTML Not Yet Available]** > > `Format::HTML` is included for forward compatibility, but HTML responses are not currently implemented by the Market Data API. ## quotes ```php public function quotes( string|array $option_symbols, ?string $date = null, ?string $from = null, ?string $to = null, ?Parameters $parameters = null ): Quotes ``` Get current or historical end-of-day quotes for one or more options contracts. When multiple symbols are provided, requests are made concurrently using a sliding window of up to 50 concurrent requests. ### Parameters - `option_symbols` (string|array) The OCC-formatted option symbol(s). Can be a single string or an array of strings for multiple symbols. Use `option_chain()` or `lookup()` to find OCC symbols. - `date` (string, optional) Look up a historical end-of-day quote from a specific trading day. If omitted, returns the current quote during market hours or last trading day's quote when closed. - `from` (string, optional) Return a series of end-of-day quotes starting from this date (inclusive). - `to` (string, optional) Return a series of end-of-day quotes ending at this date (exclusive). - `parameters` ([Parameters](https://www.marketdata.app/docs/sdk/php/parameters), optional) Universal parameters for customizing the output format. ### Returns - `Quotes` A Quotes response object containing the option quote data. ### Notes - For multiple symbols, the SDK uses concurrent requests for optimal throughput. - Failed requests for individual symbols are tolerated - the response includes data from successful requests. ### Example (Single Symbol) ```php options->quotes('AAPL271217C00300000'); foreach ($quotes->quotes as $quote) { echo "Symbol: " . $quote->option_symbol . "\n"; echo "Underlying: " . $quote->underlying . "\n"; echo "Strike: $" . $quote->strike . "\n"; echo "Expiration: " . $quote->expiration->format('Y-m-d') . "\n"; echo "Bid: $" . $quote->bid . " x " . $quote->bid_size . "\n"; echo "Ask: $" . $quote->ask . " x " . $quote->ask_size . "\n"; echo "Last: $" . $quote->last . "\n"; echo "Volume: " . number_format($quote->volume) . "\n"; echo "Open Interest: " . number_format($quote->open_interest) . "\n"; } ``` **Output** ``` Symbol: AAPL271217C00300000 Underlying: AAPL Strike: $200 Expiration: 2025-01-17 Bid: $5.80 x 45 Ask: $5.95 x 30 Last: $5.85 Volume: 1,234 Open Interest: 15,678 ``` ### Example (Multiple Symbols) ```php options->quotes($symbols); echo "Option Quotes:\n"; echo "==============\n"; foreach ($quotes->quotes as $quote) { $type = str_contains($quote->option_symbol, 'C00') ? 'C' : 'P'; echo sprintf( "%s $%.0f: Bid $%.2f / Ask $%.2f (Vol: %d)\n", $type, $quote->strike, $quote->bid, $quote->ask, $quote->volume ); } ``` ### Example (Historical) ```php options->quotes( option_symbols: 'AAPL271217C00300000', date: '2024-01-15' ); echo "Historical Quote (2024-01-15):\n"; foreach ($quotes->quotes as $quote) { echo "Last: $" . $quote->last . "\n"; echo "IV: " . number_format($quote->iv * 100, 1) . "%\n"; } ``` ### Example (Historical Range) ```php options->quotes( option_symbols: 'AAPL271217C00300000', from: '2024-01-01', to: '2024-01-31' ); echo "Historical Price Series:\n"; echo "========================\n"; foreach ($quotes->quotes as $quote) { echo sprintf( "%s: Close $%.2f, IV %.1f%%\n", $quote->updated->format('Y-m-d'), $quote->last, ($quote->iv ?? 0) * 100 ); } ``` ### Example (CSV) ```php options->quotes( option_symbols: ['AAPL271217C00280000', 'AAPL271217C00300000'], parameters: new Parameters(format: Format::CSV) ); echo $quotes->getCsv(); ``` ## Quotes ```php class Quotes extends ResponseBase { public string $status; public array $quotes; public ?Carbon $next_time; public ?Carbon $prev_time; public array $errors; } ``` Represents option quote data for one or more contracts. ### Properties - `status` (string): Response status (`"ok"` or `"no_data"`). - `quotes` (array): Array of option quotes, each containing: - `option_symbol` (string): OCC-formatted option symbol - `underlying` (string): Underlying ticker symbol - `expiration` (Carbon): Expiration date - `side` (string): "call" or "put" - `strike` (float): Strike price - `bid` (float): Bid price - `bid_size` (int): Bid size - `ask` (float): Ask price - `ask_size` (int): Ask size - `mid` (float): Midpoint price - `last` (float): Last trade price - `volume` (int): Daily volume - `open_interest` (int): Open interest - `delta` (float|null): Delta greek - `gamma` (float|null): Gamma greek - `theta` (float|null): Theta greek - `vega` (float|null): Vega greek - `iv` (float|null): Implied volatility - `updated` (Carbon): Last update timestamp - `next_time` (Carbon|null): Next available data time (when status is `no_data`). - `prev_time` (Carbon|null): Previous available data time (when status is `no_data`). - `errors` (array): Map of symbol => error message for failed requests (multi-symbol only). ### Methods - `getCsv()`: Returns the raw CSV data (when using `Format::CSV`). - `getHtml()`: Returns the raw HTML data (when using `Format::HTML`). - `isJson()`: Returns `true` if the response contains JSON data. --- # Parameters (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/parameters/index.md The Market Data PHP SDK provides flexible configuration options 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 multiple ways to configure universal parameters. These configuration methods follow a priority hierarchy, where higher priority settings override lower priority ones. ### Priority Order (Lowest to Highest) 1. **Environment Variables** (Lowest Priority) - Set via environment variables like `MARKETDATA_FORMAT`, `MARKETDATA_DATE_FORMAT`, etc. - Applied globally to all API calls unless overridden 2. **Client Default Parameters** (`$client->default_params`) - Set via the `default_params` property - Applied to all API calls made with that client instance unless overridden 3. **Direct Method Parameters** (Highest Priority) - Passed via the `parameters` argument to resource methods - Applied only to that specific API call ### How It Works When making an API call, the SDK merges parameters in the following order: 1. **Start with environment variables** (lowest priority) - Parameters are read from environment variables like `MARKETDATA_FORMAT`, `MARKETDATA_DATE_FORMAT`, etc. 2. **Override with `$client->default_params`** (medium priority) - Parameters set via `$client->default_params` override environment variables 3. **Override with direct method parameters** (highest priority) - Parameters passed via the `parameters` argument override both environment variables and `$client->default_params` ### Examples ### Environment Variables Set universal parameters via environment variables: ```php stocks->quote('AAPL'); // Uses: format=CSV, date_format=TIMESTAMP ``` ### Client Default Params Set universal parameters via `$client->default_params`: ```php default_params = new Parameters( format: Format::JSON, date_format: DateFormat::TIMESTAMP ); // All calls will use JSON format and timestamp date format // (unless overridden by method parameters) $quote = $client->stocks->quote('AAPL'); // Uses: format=JSON, date_format=TIMESTAMP $quotes = $client->stocks->quotes(['AAPL', 'MSFT']); // Uses: format=JSON, date_format=TIMESTAMP ``` **Note:** `$client->default_params` overrides environment variables. ### Direct Method Parameters Pass parameters directly to resource methods: ```php default_params = new Parameters( format: Format::JSON, date_format: DateFormat::TIMESTAMP ); // Direct method parameters override default_params $candles = $client->stocks->candles( 'AAPL', '2024-01-01', '2024-01-31', parameters: new Parameters( format: Format::CSV, // Overrides JSON date_format: DateFormat::UNIX // Overrides TIMESTAMP ) ); // Uses: format=CSV, date_format=UNIX // This call still uses default_params $quote = $client->stocks->quote('AAPL'); // Uses: format=JSON, date_format=TIMESTAMP ``` **Note:** Direct method parameters override both environment variables and `$client->default_params`. ## Parameters Constructor The `Parameters` class accepts the following options: ```php ### Format Controls the format of the API response data. **Available Values:** - `Format::JSON` (default): Returns data as typed PHP objects - `Format::CSV`: Returns CSV data (writes to file if `filename` specified) - `Format::HTML`: Reserved for future API support (not yet implemented) > [!WARNING] > **[HTML Not Yet Available]** > > The PHP SDK includes `Format::HTML` so your integration is ready once API support is released, but HTML responses are not currently implemented by the Market Data API. **Configuration Methods:** 1. **Environment Variable:** `MARKETDATA_FORMAT` (values: `json`, `csv`, `html`) 2. **Client Default:** `$client->default_params = new Parameters(format: Format::CSV)` 3. **Direct Parameter:** `parameters: new Parameters(format: Format::CSV)` **Example:** ```php default_params = new Parameters(format: Format::CSV); // Method 3: Direct parameter (highest priority) $candles = $client->stocks->candles( 'AAPL', '2024-01-01', '2024-01-31', parameters: new Parameters(format: Format::JSON) ); ``` For more details, see the [API Format documentation](https://www.marketdata.app/docs/api/universal-parameters/format). ### Date Format Specifies the format for date and time information in CSV/HTML responses. **Available Values:** - `DateFormat::TIMESTAMP`: ISO 8601 timestamp format (e.g., `2020-12-30 16:00:00 -05:00`) - `DateFormat::UNIX`: Unix timestamp in seconds (e.g., `1609362000`) - `DateFormat::SPREADSHEET`: Excel spreadsheet format (e.g., `44195.66667`) **Configuration Methods:** 1. **Environment Variable:** `MARKETDATA_DATE_FORMAT` (values: `timestamp`, `unix`, `spreadsheet`) 2. **Client Default:** `$client->default_params = new Parameters(date_format: DateFormat::UNIX)` 3. **Direct Parameter:** `parameters: new Parameters(date_format: DateFormat::UNIX)` **Example:** ```php stocks->candles( 'AAPL', '2024-01-01', '2024-01-31', parameters: new Parameters( format: Format::CSV, date_format: DateFormat::TIMESTAMP ) ); ``` For more details, see the [API Date Format documentation](https://www.marketdata.app/docs/api/universal-parameters/date-format). ### Columns Limits the response to only the columns you need (CSV/HTML only). **Type:** `array` of column names **Configuration Methods:** 1. **Environment Variable:** `MARKETDATA_COLUMNS` (comma-separated list, e.g., `open,close,volume`) 2. **Client Default:** `$client->default_params = new Parameters(columns: ['open', 'close'])` 3. **Direct Parameter:** `parameters: new Parameters(columns: ['open', 'close'])` **Example:** ```php stocks->candles( 'AAPL', '2024-01-01', '2024-01-31', parameters: new Parameters( format: Format::CSV, columns: ['open', 'close', 'volume'] ) ); ``` For more details, see the [API Columns documentation](https://www.marketdata.app/docs/api/universal-parameters/columns). ### Headers Controls whether headers are included in CSV/HTML output. **Type:** `bool` **Default:** `true` **Configuration Methods:** 1. **Environment Variable:** `MARKETDATA_ADD_HEADERS` (values: `true`, `false`) 2. **Client Default:** `$client->default_params = new Parameters(add_headers: false)` 3. **Direct Parameter:** `parameters: new Parameters(add_headers: false)` **Example:** ```php stocks->candles( 'AAPL', '2024-01-01', '2024-01-31', parameters: new Parameters( format: Format::CSV, add_headers: false ) ); ``` For more details, see the [API Headers documentation](https://www.marketdata.app/docs/api/universal-parameters/headers). ### Human Readable Uses human-readable attribute names in responses instead of camelCase. **Type:** `bool` **Default:** `false` When enabled, response objects use capitalized property names (e.g., `$quote->Ask` instead of `$quote->ask`). **Configuration Methods:** 1. **Environment Variable:** `MARKETDATA_USE_HUMAN_READABLE` (values: `true`, `false`) 2. **Client Default:** `$client->default_params = new Parameters(use_human_readable: true)` 3. **Direct Parameter:** `parameters: new Parameters(use_human_readable: true)` **Example:** ```php stocks->quote( 'AAPL', parameters: new Parameters(use_human_readable: true) ); // Access using capitalized names echo "Ask: " . $quote->Ask . "\n"; echo "Bid: " . $quote->Bid . "\n"; echo "Last Price: " . $quote->Last . "\n"; ``` For more details, see the [API Human Readable documentation](https://www.marketdata.app/docs/api/universal-parameters/human-readable). ### Mode Controls how an API request is fulfilled, including data freshness guarantees and credit usage. **Available Values:** - `Mode::LIVE`: Real-time data (default for paid accounts) - `Mode::CACHED`: Recently cached data (reduces credit usage for bulk quotes) - `Mode::DELAYED`: Data delayed by at least 15 minutes (default for free/trial accounts) **Configuration Methods:** 1. **Environment Variable:** `MARKETDATA_MODE` (values: `live`, `cached`, `delayed`) 2. **Client Default:** `$client->default_params = new Parameters(mode: Mode::CACHED)` 3. **Direct Parameter:** `parameters: new Parameters(mode: Mode::CACHED)` **Example:** ```php options->option_chain( 'SPY', expiration: '2025-01-17', parameters: new Parameters(mode: Mode::CACHED) ); // Use live mode for real-time quotes $quote = $client->stocks->quote( 'AAPL', parameters: new Parameters(mode: Mode::LIVE) ); ``` > [!NOTE] > **[Premium Parameter]** > > The `mode` parameter is available only on paid plans. Free and trial plans cannot change the data mode and receive historical data (the last fully-closed session). See [Data Freshness](https://www.marketdata.app/docs/account/data-freshness). For more details, see the [API Mode documentation](https://www.marketdata.app/docs/api/universal-parameters/mode). ### Max Age Controls the maximum age of cached data in seconds. Only applies when `mode` is `CACHED`. **Type:** `int` (seconds) **Configuration Methods:** 1. **Client Default:** `$client->default_params = new Parameters(mode: Mode::CACHED, maxage: 300)` 2. **Direct Parameter:** `parameters: new Parameters(mode: Mode::CACHED, maxage: 300)` **Example:** ```php stocks->quote( 'AAPL', parameters: new Parameters( mode: Mode::CACHED, maxage: 300 // 5 minutes ) ); ``` ### Filename Specifies the output file path for CSV/HTML format responses. **Type:** `string` **Validation Rules:** - Must end with `.csv` (for CSV format) or `.html` (for HTML format) - Parent directory must exist (SDK does not create directories) - File must not already exist (prevents accidental overwrites) - Cannot be used with parallel requests **Example:** ```php stocks->candles( 'AAPL', '2024-01-01', '2024-01-31', parameters: new Parameters( format: Format::CSV, filename: '/path/to/candles.csv' ) ); ``` ## Available Environment Variables You can set the following environment variables to configure universal parameters globally: | Variable | Values | Description | |---------------------------------|------------------------------------|-----------------------------| | `MARKETDATA_FORMAT` | `json`, `csv`, `html` | Output format | | `MARKETDATA_DATE_FORMAT` | `timestamp`, `unix`, `spreadsheet` | Date format for CSV/HTML | | `MARKETDATA_COLUMNS` | Comma-separated list | Columns to include | | `MARKETDATA_ADD_HEADERS` | `true`, `false` | Include headers in CSV/HTML | | `MARKETDATA_USE_HUMAN_READABLE` | `true`, `false` | Human-readable format | | `MARKETDATA_MODE` | `live`, `cached`, `delayed` | Data mode | ## Best Practices - **Use environment variables** for global defaults that apply across your entire application - **Use `$client->default_params`** when you want different defaults for different client instances - **Use direct method parameters** when you need to override defaults for specific API calls This flexible configuration system allows you to set sensible defaults while still having fine-grained control over individual API calls. --- # Stocks (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/stocks/index.md Access real-time and historical stock market data including price candles, quotes, earnings reports, and news articles. ## Stocks Endpoints --- # Bulk Candles (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/stocks/bulkcandles/index.md Retrieve bulk daily candle data for multiple stocks in a single request. This endpoint returns a single daily candle for each symbol, ideal for market snapshots. ## Making Requests Use the `bulkCandles()` method on the `stocks` resource to fetch bulk candle data: | Output Format | Return Type | Description | |---------------|---------------|-------------------------------------------------------------------------------| | **JSON** | `BulkCandles` | Returns a BulkCandles object containing an array of Candle objects (default). | | **CSV** | `BulkCandles` | Returns a BulkCandles object with CSV data accessible via `getCsv()`. | | **HTML** | `BulkCandles` | Returns a BulkCandles object with HTML data accessible via `getHtml()`. | > [!WARNING] > **[HTML Not Yet Available]** > > `Format::HTML` is included for forward compatibility, but HTML responses are not currently implemented by the Market Data API. ## bulkCandles ```php public function bulkCandles( array $symbols = [], string $resolution = 'D', bool $snapshot = false, ?string $date = null, ?bool $adjust_splits = null, ?Parameters $parameters = null ): BulkCandles ``` Get bulk candle data for multiple stocks. Unlike the standard candles endpoint, this endpoint returns a single daily candle for each symbol provided. The typical use-case is to get a complete market snapshot during trading hours, though it can also be used for bulk snapshots of historical daily candles. ### Parameters - `symbols` (array, optional) Array of ticker symbols to return. Can be omitted if `snapshot` is `true`. - `resolution` (string, optional) The duration of each candle. Only daily candles are supported. Defaults to `"D"`. - **Daily:** `D`, `1D`, `2D`, `daily` - `snapshot` (bool, optional) When `true`, returns candles for all available symbols for the specified date. The `symbols` parameter can be omitted when using snapshot mode. Defaults to `false`. - `date` (string, optional) The date of the candles to return. If omitted during market hours, returns current session data. If the market is closed, returns data from the most recent session. Accepted formats: ISO 8601, Unix timestamp, spreadsheet serial number. - `adjust_splits` (bool, optional) Whether to adjust historical data for stock splits. Daily candles default to `true`. Uses API alias `adjustsplits`. - `parameters` ([Parameters](https://www.marketdata.app/docs/sdk/php/parameters), optional) Universal parameters for customizing the output format. See [Parameters](https://www.marketdata.app/docs/sdk/php/parameters) for details. ### Returns - `BulkCandles` A BulkCandles response object containing the candle data for all requested symbols. ### Notes - Only daily resolutions are supported for bulk candles. - Either `symbols` or `snapshot` must be provided. - When using `snapshot: true`, all available symbols are returned, which can be a large dataset. ### Example (Multiple Symbols) ```php stocks->bulkCandles( symbols: ['AAPL', 'MSFT', 'GOOGL', 'AMZN'] ); // Access candle data for each symbol foreach ($bulkCandles->candles as $candle) { echo sprintf( "%s: Open $%.2f, Close $%.2f, Volume %s\n", $candle->symbol, $candle->open, $candle->close, number_format($candle->volume) ); } ``` **Output** ``` AAPL: Open $185.64, Close $186.19, Volume 48,234,500 MSFT: Open $378.25, Close $380.55, Volume 22,156,800 GOOGL: Open $142.35, Close $143.12, Volume 18,456,200 AMZN: Open $155.82, Close $157.45, Volume 35,678,900 ``` ### Example (Historical Date) ```php stocks->bulkCandles( symbols: ['AAPL', 'MSFT', 'GOOGL'], date: '2024-01-15' ); echo "Candles for 2024-01-15:\n"; foreach ($bulkCandles->candles as $candle) { echo sprintf( "%s: $%.2f (%.2f%%)\n", $candle->symbol, $candle->close, (($candle->close - $candle->open) / $candle->open) * 100 ); } ``` ### Example (Market Snapshot) ```php stocks->bulkCandles( snapshot: true ); echo "Total symbols: " . count($bulkCandles->candles) . "\n"; // Find top gainers $gainers = array_filter($bulkCandles->candles, function($candle) { return $candle->close > $candle->open; }); echo "Gainers today: " . count($gainers) . "\n"; ``` ### Example (CSV) ```php stocks->bulkCandles( symbols: ['AAPL', 'MSFT', 'GOOGL'], parameters: new Parameters(format: Format::CSV) ); // Access the raw CSV data echo $bulkCandles->getCsv(); ``` ## BulkCandles ```php class BulkCandles extends ResponseBase { public string $status; public array $candles; } ``` Represents a collection of bulk candle data for multiple symbols. ### Properties - `status` (string): The status of the response (`"ok"` or `"no_data"`). - `candles` (Candle[]): Array of Candle objects, one per symbol requested. ### Methods - `getCsv()`: Returns the raw CSV data (when using `Format::CSV`). - `getHtml()`: Returns the raw HTML data (when using `Format::HTML`). - `isJson()`: Returns `true` if the response contains JSON data. --- # Stock Candles (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/stocks/candles/index.md Retrieve historical price candles (OHLCV data) for any supported stock symbol. ## Making Requests Use the `candles()` method on the `stocks` resource to fetch stock candles. The method supports multiple output formats and automatically handles large date ranges by splitting them into year-long chunks and fetching them concurrently: | Output Format | Return Type | Description | |---------------|-------------|---------------------------------------------------------------------------| | **JSON** | `Candles` | Returns a Candles object containing an array of Candle objects (default). | | **CSV** | `Candles` | Returns a Candles object with CSV data accessible via `getCsv()`. | | **HTML** | `Candles` | Returns a Candles object with HTML data accessible via `getHtml()`. | > [!WARNING] > **[HTML Not Yet Available]** > > `Format::HTML` is included for forward compatibility, but HTML responses are not currently implemented by the Market Data API. ## candles ```php public function candles( string $symbol, string $from, ?string $to = null, string $resolution = 'D', ?int $countback = null, bool $extended = false, ?bool $adjust_splits = null, ?Parameters $parameters = null ): Candles ``` Fetches historical candles (OHLCV) data for a stock symbol. Supports various timeframes (minutely, hourly, daily, weekly, monthly, yearly) and automatically handles large date ranges by splitting them into year-long chunks and fetching them concurrently. ### Parameters - `symbol` (string) The stock symbol for which to fetch candles data (e.g., "AAPL", "MSFT"). - `from` (string) The start date for the candle data. Accepted formats: ISO 8601 (`2024-01-01`), Unix timestamp, or spreadsheet serial number. - `to` (string, optional) The end date for the candle data. If omitted, you must specify `countback`. Accepted formats: ISO 8601, Unix timestamp, or spreadsheet serial number. - `resolution` (string, optional) The granularity of the candle data. Defaults to `"D"` (daily). - **Minutely:** `1`, `3`, `5`, `15`, `30`, `45`, `minutely` - **Hourly:** `H`, `1H`, `2H`, `hourly` - **Daily:** `D`, `1D`, `2D`, `daily` - **Weekly:** `W`, `1W`, `2W`, `weekly` - **Monthly:** `M`, `1M`, `2M`, `monthly` - **Yearly:** `Y`, `1Y`, `2Y`, `yearly` - `countback` (int, optional) Number of candles to return, counting backwards from `to`. If specified, `to` is required and `from` is ignored. - `extended` (bool, optional) Whether to include extended hours (pre-market and after-hours) data. Defaults to `false`. Only applies to intraday resolutions. - `adjust_splits` (bool, optional) Whether to adjust for stock splits. Daily candles default to `true`, intraday candles default to `false`. Uses API alias `adjustsplits`. - `parameters` ([Parameters](https://www.marketdata.app/docs/sdk/php/parameters), optional) Universal parameters for customizing the output format. See [Parameters](https://www.marketdata.app/docs/sdk/php/parameters) for details. ### Returns - `Candles` A Candles response object containing the candle data, or an error status if no data is available. ### Notes - For intraday resolutions (minutely/hourly), large date ranges spanning more than 1 year are automatically split into year-long chunks and fetched concurrently (up to 50 concurrent requests). - The `from` date is inclusive (leftmost candle on a chart). - The `to` date is inclusive (rightmost candle on a chart). - If requesting CSV format with date ranges > 1 year, the `filename` parameter cannot be used. ### Example (JSON) ```php stocks->candles( symbol: 'AAPL', from: '2024-01-01', to: '2024-01-31' ); // Access individual candle data foreach ($candles->candles as $candle) { echo "Date: " . $candle->timestamp->format('Y-m-d') . "\n"; echo "Open: $" . $candle->open . "\n"; echo "High: $" . $candle->high . "\n"; echo "Low: $" . $candle->low . "\n"; echo "Close: $" . $candle->close . "\n"; echo "Volume: " . number_format($candle->volume) . "\n\n"; } ``` **Output** ``` Date: 2024-01-02 Open: $187.15 High: $188.44 Low: $183.89 Close: $185.64 Volume: 82,488,700 Date: 2024-01-03 Open: $184.22 High: $185.88 Low: $183.43 Close: $184.25 Volume: 58,414,500 ... ``` ### Example (Intraday) ```php stocks->candles( symbol: 'AAPL', from: '2024-01-15', to: '2024-01-15', resolution: '5', extended: true ); echo "Found " . count($candles->candles) . " candles\n"; // Access the first few candles foreach (array_slice($candles->candles, 0, 3) as $candle) { echo $candle->timestamp->format('Y-m-d H:i') . ": $" . $candle->close . "\n"; } ``` ### Example (CSV) ```php stocks->candles( symbol: 'AAPL', from: '2024-01-01', to: '2024-01-31', parameters: new Parameters(format: Format::CSV) ); // Access the raw CSV data $csv = $candles->getCsv(); echo $csv; ``` **Output** ``` t,o,h,l,c,v 1704204000,187.15,188.44,183.89,185.64,82488700 1704290400,184.22,185.88,183.43,184.25,58414500 ... ``` ### Example (Countback) ```php stocks->candles( symbol: 'AAPL', from: '2024-01-01', // Ignored when countback is used to: '2024-01-31', countback: 10 ); echo "Retrieved " . count($candles->candles) . " candles\n"; ``` ### Example (Multi-Year) ```php stocks->candles( symbol: 'AAPL', from: '2019-01-01', to: '2024-01-01', resolution: 'H' ); echo "Retrieved " . count($candles->candles) . " hourly candles\n"; echo "First candle: " . $candles->candles[0]->timestamp->format('Y-m-d') . "\n"; echo "Last candle: " . end($candles->candles)->timestamp->format('Y-m-d') . "\n"; ``` ## Candle ```php class Candle { public float $open; public float $high; public float $low; public float $close; public int $volume; public Carbon $timestamp; public ?string $symbol; } ``` Represents a single stock candle (OHLCV data), encapsulating price and volume information for a specific time period. ### Properties - `open` (float): The opening price. - `high` (float): The highest price during the period. - `low` (float): The lowest price during the period. - `close` (float): The closing price. - `volume` (int): The trading volume. - `timestamp` (Carbon): The timestamp for the candle. - `symbol` (string|null): The stock symbol (may be null for single-symbol requests). ## Candles ```php class Candles extends ResponseBase { public string $status; public ?int $next_time; public array $candles; } ``` Represents a collection of stock candles data. ### Properties - `status` (string): The status of the response (`"ok"` or `"no_data"`). - `next_time` (int|null): Unix timestamp of the next available quote if no data in the requested period. - `candles` (Candle[]): Array of Candle objects. ### Methods - `getCsv()`: Returns the raw CSV data (when using `Format::CSV`). - `getHtml()`: Returns the raw HTML data (when using `Format::HTML`). - `isJson()`: Returns `true` if the response contains JSON data. --- # Earnings (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/stocks/earnings/index.md Get historical earnings per share data or a future earnings calendar for a stock. > [!NOTE] > **[Premium Endpoint]** > > The earnings endpoint requires a premium subscription. Free and trial accounts cannot access this data. ## Making Requests Use the `earnings()` method on the `stocks` resource to fetch earnings data: | Output Format | Return Type | Description | |---------------|-------------|-----------------------------------------------------------------------| | **JSON** | `Earnings` | Returns an Earnings object containing earnings report data (default). | | **CSV** | `Earnings` | Returns an Earnings object with CSV data accessible via `getCsv()`. | | **HTML** | `Earnings` | Returns an Earnings object with HTML data accessible via `getHtml()`. | > [!WARNING] > **[HTML Not Yet Available]** > > `Format::HTML` is included for forward compatibility, but HTML responses are not currently implemented by the Market Data API. ## earnings ```php public function earnings( string $symbol, ?string $from = null, ?string $to = null, ?int $countback = null, ?string $date = null, ?Parameters $parameters = null ): Earnings ``` Get historical earnings per share data or a future earnings calendar for a stock. ### Parameters - `symbol` (string) The stock ticker symbol (e.g., "AAPL"). - `from` (string, optional) The earliest earnings report to include. If omitted without `countback`, returns recent and upcoming earnings. Accepted formats: ISO 8601, Unix timestamp. - `to` (string, optional) The latest earnings report to include. Accepted formats: ISO 8601, Unix timestamp. - `countback` (int, optional) Number of earnings reports to return before `to`. Use instead of `from`. - `date` (string, optional) Retrieve a specific earnings report by date. Accepted formats: ISO 8601, Unix timestamp. - `parameters` ([Parameters](https://www.marketdata.app/docs/sdk/php/parameters), optional) Universal parameters for customizing the output format. See [Parameters](https://www.marketdata.app/docs/sdk/php/parameters) for details. ### Returns - `Earnings` An Earnings response object containing the earnings data. ### Example (Recent Earnings) ```php stocks->earnings('AAPL'); foreach ($earnings->earnings as $report) { echo "Date: " . $report->report_date->format('Y-m-d') . "\n"; echo "Fiscal Year: " . $report->fiscal_year . " Q" . $report->fiscal_quarter . "\n"; echo "EPS Estimate: $" . $report->estimated_eps . "\n"; echo "EPS Actual: $" . ($report->reported_eps ?? 'N/A') . "\n"; echo "Surprise: " . ($report->surprise_eps ?? 'N/A') . "\n\n"; } ``` **Output** ``` Date: 2024-01-25 Fiscal Year: 2024 Q1 EPS Estimate: $2.10 EPS Actual: $2.18 Surprise: 0.08 Date: 2024-04-25 Fiscal Year: 2024 Q2 EPS Estimate: $1.50 EPS Actual: N/A Surprise: N/A ``` ### Example (Historical Range) ```php stocks->earnings( symbol: 'AAPL', from: '2023-01-01', to: '2023-12-31' ); echo "AAPL Earnings for 2023:\n"; echo "========================\n"; foreach ($earnings->earnings as $report) { $surprise = $report->surprise_eps ?? 0; $surpriseIndicator = $surprise > 0 ? '↑' : ($surprise < 0 ? '↓' : '-'); echo sprintf( "Q%d %d: EPS $%.2f (Est: $%.2f) %s\n", $report->fiscal_quarter, $report->fiscal_year, $report->reported_eps ?? 0, $report->estimated_eps ?? 0, $surpriseIndicator ); } ``` ### Example (Last N Reports) ```php stocks->earnings( symbol: 'AAPL', to: 'today', countback: 4 ); echo "Last 4 Quarterly Earnings:\n"; foreach ($earnings->earnings as $report) { echo sprintf( "%s: Q%d FY%d - $%.2f EPS\n", $report->report_date->format('Y-m-d'), $report->fiscal_quarter, $report->fiscal_year, $report->reported_eps ?? 0 ); } ``` ### Example (CSV) ```php stocks->earnings( symbol: 'AAPL', from: '2023-01-01', parameters: new Parameters(format: Format::CSV) ); echo $earnings->getCsv(); ``` ## Earning ```php class Earning { public string $symbol; public int $fiscal_year; public int $fiscal_quarter; public Carbon $date; public Carbon $report_date; public string $report_time; public ?string $currency; public ?float $reported_eps; public ?float $estimated_eps; public ?float $surprise_eps; public ?float $surprise_eps_pct; public Carbon $updated; } ``` Represents a single earnings report. ### Properties - `symbol` (string): The stock ticker symbol. - `fiscal_year` (int): The fiscal year of the earnings report. - `fiscal_quarter` (int): The fiscal quarter (1-4). - `date` (Carbon): The last calendar day that corresponds to this earnings report (period end date). - `report_date` (Carbon): The date the earnings report was released or is projected to be released. - `report_time` (string): Time of day for the report ("before market open", "after market close", or "during market hours"). - `currency` (string|null): The currency of the earnings report. - `reported_eps` (float|null): Actual reported EPS. Typically non-GAAP unless the company does not report non-GAAP earnings. Null for future reports. - `estimated_eps` (float|null): The average consensus EPS estimate by Wall Street analysts. - `surprise_eps` (float|null): The difference between reported EPS and estimated EPS. - `surprise_eps_pct` (float|null): The percentage difference between reported and estimated EPS. - `updated` (Carbon): The date/time the earnings data was last updated. ## Earnings ```php class Earnings extends ResponseBase { public string $status; public array $earnings; } ``` Represents a collection of earnings reports. ### Properties - `status` (string): Response status (`"ok"` or `"no_data"`). - `earnings` (Earning[]): Array of Earning objects. ### Methods - `getCsv()`: Returns the raw CSV data (when using `Format::CSV`). - `getHtml()`: Returns the raw HTML data (when using `Format::HTML`). - `isJson()`: Returns `true` if the response contains JSON data. --- # News (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/stocks/news/index.md Retrieve news articles for a stock symbol. > [!CAUTION] > **[Beta Endpoint]** > > The news endpoint is currently in beta. Features and response formats may change. ## Making Requests Use the `news()` method on the `stocks` resource to fetch news articles: | Output Format | Return Type | Description | |---------------|-------------|------------------------------------------------------------------| | **JSON** | `News` | Returns a News object containing news articles (default). | | **CSV** | `News` | Returns a News object with CSV data accessible via `getCsv()`. | | **HTML** | `News` | Returns a News object with HTML data accessible via `getHtml()`. | > [!WARNING] > **[HTML Not Yet Available]** > > `Format::HTML` is included for forward compatibility, but HTML responses are not currently implemented by the Market Data API. ## news ```php public function news( string $symbol, ?string $from = null, ?string $to = null, ?int $countback = null, ?string $date = null, ?Parameters $parameters = null ): News ``` Retrieve news articles for a given stock symbol. ### Parameters - `symbol` (string) The stock ticker symbol (e.g., "AAPL"). - `from` (string, optional) The earliest news to include. If omitted without `countback`, returns recent news. Accepted formats: ISO 8601, Unix timestamp. - `to` (string, optional) The latest news to include. Accepted formats: ISO 8601, Unix timestamp. - `countback` (int, optional) Number of news articles to return before `to`. Use instead of `from`. - `date` (string, optional) Retrieve news for a specific day. Accepted formats: ISO 8601, Unix timestamp. - `parameters` ([Parameters](https://www.marketdata.app/docs/sdk/php/parameters), optional) Universal parameters for customizing the output format. See [Parameters](https://www.marketdata.app/docs/sdk/php/parameters) for details. ### Returns - `News` A News response object containing an array of Article objects. ### Example (Quick Display) ```php stocks->news('AAPL', from: '2024-01-01'); echo $news; // Or display individual articles foreach ($news->articles as $article) { echo $article . "\n\n"; } ``` **Output** ``` News: 3 articles (status: ok) AAPL: Apple Reports Record Q1 Earnings Published: Jan 25, 2024 4:30 PM Source: https://www.reuters.com/... Content: Apple Inc reported record quarterly earnings on Thursday... AAPL: Apple Reports Record Q1 Earnings Published: Jan 25, 2024 4:30 PM Source: https://www.reuters.com/... Content: Apple Inc reported record quarterly earnings on Thursday... ``` ### Example (Recent News) ```php stocks->news('AAPL'); foreach ($news->articles as $article) { echo "Symbol: " . $article->symbol . "\n"; echo "Title: " . $article->headline . "\n"; echo "Source: " . $article->source . "\n"; echo "Published: " . $article->publication_date->format('Y-m-d H:i') . "\n"; echo "Content: " . substr($article->content, 0, 100) . "...\n\n"; } ``` **Output** ``` Symbol: AAPL Title: Apple Reports Record Q1 Earnings Source: https://www.reuters.com/technology/... Published: 2024-01-25 16:30 Content: Apple Inc reported record quarterly earnings on Thursday... ``` ### Example (Date Range) ```php stocks->news( symbol: 'AAPL', from: '2024-01-01', to: '2024-01-31' ); if ($news->status === 'ok') { echo "Found " . count($news->articles) . " articles:\n\n"; foreach ($news->articles as $article) { echo "- " . $article->headline . "\n"; echo " Published: " . $article->publication_date->format('Y-m-d H:i') . "\n\n"; } } ``` ### Example (Specific Date) ```php stocks->news( symbol: 'AAPL', date: '2024-01-25' ); echo "News for January 25, 2024:\n"; echo "==========================\n\n"; foreach ($news->articles as $article) { echo "* " . $article->headline . "\n"; echo " " . $article->source . " | " . $article->publication_date->format('H:i') . "\n\n"; } ``` ### Example (CSV) ```php stocks->news( symbol: 'AAPL', from: '2024-01-01', parameters: new Parameters(format: Format::CSV) ); echo $news->getCsv(); ``` ## News ```php class News extends ResponseBase { public string $status; /** @var Article[] */ public array $articles; } ``` Represents news articles for a stock. Contains an array of `Article` objects, each representing a single news article. ### Properties - `status` (string): Response status (`"ok"` or `"no_data"`). - `articles` (Article[]): Array of Article objects containing individual news article data. ### Methods - `getCsv()`: Returns the raw CSV data (when using `Format::CSV`). - `getHtml()`: Returns the raw HTML data (when using `Format::HTML`). - `isJson()`: Returns `true` if the response contains JSON data. ## Article ```php class Article { public string $symbol; public string $headline; public string $content; public string $source; public Carbon $publication_date; } ``` Represents a single news article. ### Properties - `symbol` (string): The stock ticker symbol this article relates to. - `headline` (string): The article headline/title. - `content` (string): The content of the article, if available. May include captions, copyright notices, and other elements that may need filtering. - `source` (string): The source URL where the news appeared. - `publication_date` (Carbon): The date the news was published on the source website. --- # Prices (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/stocks/prices/index.md Get real-time midpoint prices for one or more stocks using the SmartMid model. ## Making Requests Use the `prices()` method on the `stocks` resource to fetch midpoint prices: | Output Format | Return Type | Description | |---------------|-------------|--------------------------------------------------------------------| | **JSON** | `Prices` | Returns a Prices object containing price data (default). | | **CSV** | `Prices` | Returns a Prices object with CSV data accessible via `getCsv()`. | | **HTML** | `Prices` | Returns a Prices object with HTML data accessible via `getHtml()`. | > [!WARNING] > **[HTML Not Yet Available]** > > `Format::HTML` is included for forward compatibility, but HTML responses are not currently implemented by the Market Data API. ## prices ```php public function prices( string|array $symbols, bool $extended = true, ?Parameters $parameters = null ): Prices ``` Get real-time midpoint prices for stocks using the SmartMid model. Supports both single and multiple symbol requests. ### Parameters - `symbols` (string|array) A single stock ticker symbol as a string (e.g., `"AAPL"`), or an array of symbols (e.g., `['AAPL', 'MSFT']`). - `extended` (bool, optional) Control the inclusion of extended hours (pre-market and after-hours) data. Defaults to `true`. - When `true`: Returns the most recent price regardless of trading session. - When `false`: Returns only prices from the primary trading session. - `parameters` ([Parameters](https://www.marketdata.app/docs/sdk/php/parameters), optional) Universal parameters for customizing the output format. See [Parameters](https://www.marketdata.app/docs/sdk/php/parameters) for details. ### Returns - `Prices` A Prices response object containing an array of Price objects. ### Notes - The SmartMid model calculates the midpoint price between the bid and ask, weighted by size and market conditions. - For single symbols, the API uses the path format `prices/{symbol}/`. - For multiple symbols, the API uses the query format `prices/?symbols={comma-separated}`. ### Example (Quick Display) ```php stocks->prices(['AAPL', 'MSFT', 'GOOGL']); echo $prices; // Or display individual prices foreach ($prices->prices as $price) { echo $price . "\n"; } ``` **Output** ``` Prices: 3 symbols (status: ok) AAPL: $186.19 (+0.71%) Change: +$1.31 Updated: Jan 15, 2024 4:00 PM MSFT: $380.55 (+1.25%) Change: +$4.70 Updated: Jan 15, 2024 4:00 PM GOOGL: $143.12 (-0.50%) Change: -$0.72 Updated: Jan 15, 2024 4:00 PM AAPL: $186.19 (+0.71%) Change: +$1.31 Updated: Jan 15, 2024 4:00 PM MSFT: $380.55 (+1.25%) Change: +$4.70 Updated: Jan 15, 2024 4:00 PM GOOGL: $143.12 (-0.50%) Change: -$0.72 Updated: Jan 15, 2024 4:00 PM ``` ### Example (Single Symbol) ```php stocks->prices('AAPL'); foreach ($prices->prices as $price) { echo "Symbol: " . $price->symbol . "\n"; echo "Price: $" . number_format($price->mid, 2) . "\n"; echo "Change: $" . number_format($price->change, 2) . "\n"; echo "Updated: " . $price->updated->format('Y-m-d H:i:s') . "\n"; } ``` **Output** ``` Symbol: AAPL Price: $186.19 Updated: 2024-01-15 16:00:00 ``` ### Example (Multiple Symbols) ```php stocks->prices(['AAPL', 'MSFT', 'GOOGL', 'AMZN']); echo "Current Prices\n"; echo "==============\n"; foreach ($prices->prices as $price) { echo sprintf( "%s: $%.2f (Change: %+.2f%%)\n", $price->symbol, $price->mid, $price->changepct * 100 ); } ``` **Output** ``` Current Prices ============== AAPL: $186.19 MSFT: $380.55 GOOGL: $143.12 AMZN: $157.45 ``` ### Example (Regular Hours) ```php stocks->prices( symbols: ['AAPL', 'MSFT'], extended: false ); // During extended hours, returns closing prices from regular session foreach ($prices->prices as $price) { echo sprintf( "%s: $%.2f (Regular Session)\n", $price->symbol, $price->mid ); } ``` ### Example (CSV) ```php stocks->prices( symbols: ['AAPL', 'MSFT', 'GOOGL'], parameters: new Parameters(format: Format::CSV) ); echo $prices->getCsv(); ``` ## Prices ```php class Prices extends ResponseBase { public string $status; /** @var Price[] */ public array $prices; } ``` Represents stock prices for one or more symbols. Contains an array of `Price` objects, each representing a single stock's price data. ### Properties - `status` (string): Response status (`"ok"`, `"no_data"`, or `"error"`). - `prices` (Price[]): Array of Price objects containing individual stock price data. ### Methods - `getCsv()`: Returns the raw CSV data (when using `Format::CSV`). - `getHtml()`: Returns the raw HTML data (when using `Format::HTML`). - `isJson()`: Returns `true` if the response contains JSON data. ## Price ```php class Price { public string $symbol; public float $mid; public float $change; public float $changepct; public Carbon $updated; } ``` Represents a single stock's price data. ### Properties - `symbol` (string): The ticker symbol for this price. - `mid` (float): The midpoint price as calculated by the SmartMid model. - `change` (float): Price change in currency units compared to the previous day's close. - `changepct` (float): Price change as a decimal (e.g., 0.03 = 3%). - `updated` (Carbon): Timestamp when this price was last updated. --- # Stock Quote (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/stocks/quote/index.md Get a real-time price quote for a single stock symbol. ## Making Requests Use the `quote()` method on the `stocks` resource to fetch a real-time quote: | Output Format | Return Type | Description | |---------------|-------------|-------------------------------------------------------------------| | **JSON** | `Quote` | Returns a Quote object with typed properties (default). | | **CSV** | `Quote` | Returns a Quote object with CSV data accessible via `getCsv()`. | | **HTML** | `Quote` | Returns a Quote object with HTML data accessible via `getHtml()`. | > [!WARNING] > **[HTML Not Yet Available]** > > `Format::HTML` is included for forward compatibility, but HTML responses are not currently implemented by the Market Data API. ## quote ```php public function quote( string $symbol, bool $fifty_two_week = false, bool $extended = true, ?Parameters $parameters = null ): Quote ``` Get a real-time price quote for a stock symbol, including bid/ask prices, last trade, change, and volume. ### Parameters - `symbol` (string) The stock ticker symbol (e.g., "AAPL", "MSFT"). - `fifty_two_week` (bool, optional) Enable the output of 52-week high and 52-week low data. Defaults to `false`. Uses API alias `52week`. - `extended` (bool, optional) Control the inclusion of extended hours (pre-market and after-hours) data. Defaults to `true`. - When `true`: Returns the most recent quote regardless of trading session. - When `false`: Returns only quotes from the primary trading session. During extended hours, returns the closing quote from the last regular session. - `parameters` ([Parameters](https://www.marketdata.app/docs/sdk/php/parameters), optional) Universal parameters for customizing the output format. See [Parameters](https://www.marketdata.app/docs/sdk/php/parameters) for details. ### Returns - `Quote` A Quote response object containing the current quote data. ### Example (JSON) ```php stocks->quote('AAPL'); echo "Symbol: " . $quote->symbol . "\n"; echo "Last Price: $" . number_format($quote->last, 2) . "\n"; echo "Change: $" . number_format($quote->change, 2) . " (" . number_format($quote->change_percent * 100, 2) . "%)\n"; echo "Bid: $" . $quote->bid . " x " . $quote->bid_size . "\n"; echo "Ask: $" . $quote->ask . " x " . $quote->ask_size . "\n"; echo "Volume: " . number_format($quote->volume) . "\n"; echo "Updated: " . $quote->updated->format('Y-m-d H:i:s') . "\n"; ``` **Output** ``` Symbol: AAPL Last Price: $186.19 Change: $1.05 (0.57%) Bid: $186.18 x 200 Ask: $186.20 x 300 Volume: 48,234,500 Updated: 2024-01-15 16:00:00 ``` ### Example (52-Week) ```php stocks->quote( symbol: 'AAPL', fifty_two_week: true ); echo $quote->symbol . ": $" . $quote->last . "\n"; echo "52-Week High: $" . $quote->fifty_two_week_high . "\n"; echo "52-Week Low: $" . $quote->fifty_two_week_low . "\n"; // Calculate position in 52-week range $range = $quote->fifty_two_week_high - $quote->fifty_two_week_low; $position = ($quote->last - $quote->fifty_two_week_low) / $range * 100; echo "Position in 52-Week Range: " . number_format($position, 1) . "%\n"; ``` ### Example (Regular Hours Only) ```php stocks->quote( symbol: 'AAPL', extended: false ); // During extended hours, this returns the closing quote from the regular session echo "Regular Session Quote\n"; echo "Last: $" . $quote->last . "\n"; echo "Updated: " . $quote->updated->format('Y-m-d H:i:s') . "\n"; ``` ### Example (CSV) ```php stocks->quote( symbol: 'AAPL', parameters: new Parameters(format: Format::CSV) ); echo $quote->getCsv(); ``` ### Example (Human Readable) ```php stocks->quote( symbol: 'AAPL', fifty_two_week: true, parameters: new Parameters(use_human_readable: true) ); // Access properties using capitalized names echo "Symbol: " . $quote->Symbol . "\n"; echo "Ask: $" . $quote->Ask . "\n"; echo "Bid: $" . $quote->Bid . "\n"; echo "Last: $" . $quote->Last . "\n"; echo "52-Week High: $" . $quote->{'52 Week High'} . "\n"; echo "52-Week Low: $" . $quote->{'52 Week Low'} . "\n"; ``` ## Quote ```php class Quote extends ResponseBase { public string $status; public string $symbol; public ?float $ask; public ?int $ask_size; public ?float $bid; public ?int $bid_size; public ?float $mid; public ?float $last; public ?float $change; public ?float $change_percent; public ?float $fifty_two_week_high; public ?float $fifty_two_week_low; public ?int $volume; public ?Carbon $updated; } ``` Represents a stock quote with real-time price information. ### Properties - `status` (string): Response status (`"ok"` or `"no_data"`). - `symbol` (string): The stock ticker symbol. - `ask` (float|null): The ask price. - `ask_size` (int|null): Number of shares offered at the ask price. - `bid` (float|null): The bid price. - `bid_size` (int|null): Number of shares that may be sold at the bid price. - `mid` (float|null): The midpoint price between ask and bid. - `last` (float|null): The last traded price. - `change` (float|null): Price change in dollars from previous close. - `change_percent` (float|null): Price change as a decimal (e.g., 0.30 = 30%). - `fifty_two_week_high` (float|null): 52-week high price (only when `fifty_two_week: true`). - `fifty_two_week_low` (float|null): 52-week low price (only when `fifty_two_week: true`). - `volume` (int|null): Trading volume for the current session. - `updated` (Carbon|null): Timestamp of the quote. ### Methods - `getCsv()`: Returns the raw CSV data (when using `Format::CSV`). - `getHtml()`: Returns the raw HTML data (when using `Format::HTML`). - `isJson()`: Returns `true` if the response contains JSON data. --- # Stock Quotes (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/stocks/quotes/index.md Get real-time price quotes for multiple stocks in a single API request. ## Making Requests Use the `quotes()` method on the `stocks` resource to fetch quotes for multiple symbols: | Output Format | Return Type | Description | |---------------|-------------|----------------------------------------------------------------------| | **JSON** | `Quotes` | Returns a Quotes object containing an array of Quote data (default). | | **CSV** | `Quotes` | Returns a Quotes object with CSV data accessible via `getCsv()`. | | **HTML** | `Quotes` | Returns a Quotes object with HTML data accessible via `getHtml()`. | > [!WARNING] > **[HTML Not Yet Available]** > > `Format::HTML` is included for forward compatibility, but HTML responses are not currently implemented by the Market Data API. ## quotes ```php public function quotes( array $symbols, bool $fifty_two_week = false, bool $extended = true, ?Parameters $parameters = null ): Quotes ``` Get real-time price quotes for multiple stocks in a single API request. ### Parameters - `symbols` (array) Array of stock ticker symbols (e.g., `['AAPL', 'MSFT', 'GOOGL']`). - `fifty_two_week` (bool, optional) Enable the output of 52-week high and 52-week low data for each quote. Defaults to `false`. Uses API alias `52week`. - `extended` (bool, optional) Control the inclusion of extended hours (pre-market and after-hours) data. Defaults to `true`. - When `true`: Returns the most recent quote regardless of trading session. - When `false`: Returns only quotes from the primary trading session. - `parameters` ([Parameters](https://www.marketdata.app/docs/sdk/php/parameters), optional) Universal parameters for customizing the output format. See [Parameters](https://www.marketdata.app/docs/sdk/php/parameters) for details. ### Returns - `Quotes` A Quotes response object containing quote data for all requested symbols. ### Example (JSON) ```php stocks->quotes(['AAPL', 'MSFT', 'GOOGL', 'AMZN']); // Display each quote foreach ($quotes->quotes as $quote) { $changeSign = $quote->change >= 0 ? '+' : ''; echo sprintf( "%s: $%.2f %s%.2f (%s%.2f%%)\n", $quote->symbol, $quote->last, $changeSign, $quote->change, $changeSign, $quote->change_percent * 100 ); } ``` **Output** ``` AAPL: $186.19 +1.05 (+0.57%) MSFT: $380.55 +2.30 (+0.61%) GOOGL: $143.12 -0.45 (-0.31%) AMZN: $157.45 +1.63 (+1.05%) ``` ### Example (52-Week Data) ```php stocks->quotes( symbols: ['AAPL', 'MSFT', 'GOOGL'], fifty_two_week: true ); echo "Symbol | Last | 52W High | 52W Low | % from High\n"; echo "-------|----------|----------|----------|------------\n"; foreach ($quotes->quotes as $quote) { $pctFromHigh = (($quote->last - $quote->fifty_two_week_high) / $quote->fifty_two_week_high) * 100; echo sprintf( "%-6s | $%7.2f | $%7.2f | $%7.2f | %+.1f%%\n", $quote->symbol, $quote->last, $quote->fifty_two_week_high, $quote->fifty_two_week_low, $pctFromHigh ); } ``` ### Example (Market Dashboard) ```php stocks->quotes($watchlist); // Calculate market statistics $totalVolume = 0; $gainers = 0; $losers = 0; foreach ($quotes->quotes as $quote) { $totalVolume += $quote->volume; if ($quote->change > 0) { $gainers++; } elseif ($quote->change < 0) { $losers++; } } echo "Market Dashboard\n"; echo "================\n"; echo "Stocks: " . count($quotes->quotes) . "\n"; echo "Gainers: " . $gainers . "\n"; echo "Losers: " . $losers . "\n"; echo "Total Volume: " . number_format($totalVolume) . "\n"; ``` ### Example (CSV) ```php stocks->quotes( symbols: ['AAPL', 'MSFT', 'GOOGL'], parameters: new Parameters(format: Format::CSV) ); echo $quotes->getCsv(); ``` ## Quotes ```php class Quotes extends ResponseBase { public string $status; public array $quotes; } ``` Represents a collection of stock quotes. ### Properties - `status` (string): Response status (`"ok"` or `"no_data"`). - `quotes` (array): Array of quote data for each symbol. Each element contains: - `symbol` (string): The stock ticker symbol. - `ask` (float|null): The ask price. - `ask_size` (int|null): Number of shares offered at the ask price. - `bid` (float|null): The bid price. - `bid_size` (int|null): Number of shares that may be sold at the bid price. - `mid` (float|null): The midpoint price between ask and bid. - `last` (float|null): The last traded price. - `change` (float|null): Price change in dollars from previous close. - `change_percent` (float|null): Price change as a decimal. - `fifty_two_week_high` (float|null): 52-week high (only when `fifty_two_week: true`). - `fifty_two_week_low` (float|null): 52-week low (only when `fifty_two_week: true`). - `volume` (int|null): Trading volume for the current session. - `updated` (Carbon|null): Timestamp of the quote. ### Methods - `getCsv()`: Returns the raw CSV data (when using `Format::CSV`). - `getHtml()`: Returns the raw HTML data (when using `Format::HTML`). - `isJson()`: Returns `true` if the response contains JSON data. --- # Utilities (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/utilities/index.md Access API status, debug headers, and rate limit information. ## Utilities Endpoints --- # API Status (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/utilities/api-status/index.md Check the current status of Market Data services and historical uptime. ## Making Requests Use the `api_status()` method on the `utilities` resource to check API status: ## api_status ```php public function api_status(): ApiStatus ``` Check the current status of Market Data services and historical uptime. The status is updated every 5 minutes. Historical uptime is available for the last 30 and 90 days. ### Smart Caching The SDK implements intelligent caching for API status to reduce unnecessary requests: - **Fresh cache (< 4.5 min):** Returns cached data immediately - **Refresh window (4.5-5 min):** Returns cached data and triggers async refresh - **Stale cache (> 5 min):** Blocks and fetches fresh data ### Notes - This endpoint is public and does not require authentication. - The endpoint continues to respond even if the API is offline. ### Returns - `ApiStatus` An ApiStatus response object containing service status and uptime information. ### Example (Basic) ```php utilities->api_status(); echo "Market Data API Status\n"; echo "======================\n"; foreach ($status->services as $service) { $indicator = $service['status'] === 'online' ? '✓' : '✗'; echo sprintf("%s %s\n", $indicator, $service['name']); } echo "\nUptime:\n"; echo "30-day: " . number_format($status->uptime_30d, 2) . "%\n"; echo "90-day: " . number_format($status->uptime_90d, 2) . "%\n"; ``` **Output** ``` Market Data API Status ====================== ✓ Stock Quotes ✓ Stock Candles ✓ Options Chain ✓ Options Quotes ✓ Market Status Uptime: 30-day: 99.98% 90-day: 99.95% ``` ### Example (Service Check) ```php utilities->getServiceStatus('/v1/stocks/quotes/'); switch ($stockQuotesStatus) { case ApiStatusResult::ONLINE: echo "Stock quotes service is online.\n"; break; case ApiStatusResult::OFFLINE: echo "Stock quotes service is offline!\n"; break; case ApiStatusResult::UNKNOWN: echo "Stock quotes service status is unknown.\n"; break; } ``` ### Example (Manual Refresh) ```php utilities->refreshApiStatus(blocking: true); if ($success) { echo "API status cache refreshed successfully.\n"; // Get the fresh status $status = $client->utilities->api_status(); echo "Last updated: " . $status->updated->format('Y-m-d H:i:s') . "\n"; } ``` ## ApiStatus ```php class ApiStatus extends ResponseBase { public string $status; public array $services; public float $uptime_30d; public float $uptime_90d; public Carbon $updated; } ``` Represents the API status and uptime information. ### Properties - `status` (string): Response status (`"ok"`). - `services` (array): Array of service status entries, each containing: - `name` (string): Service name - `status` (string): Service status (`"online"` or `"offline"`) - `uptime_30d` (float): 30-day uptime percentage. - `uptime_90d` (float): 90-day uptime percentage. - `updated` (Carbon): When the status was last updated. --- # Headers (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/utilities/headers/index.md Retrieve the headers your application is sending for troubleshooting authentication issues. ## Making Requests Use the `headers()` method on the `utilities` resource to see request headers: ## headers ```php public function headers(): Headers ``` Retrieve the headers sent by your application. This endpoint aids in troubleshooting authentication issues, particularly with the Authorization header. ### Notes - Sensitive header values (like Authorization) are partially redacted for security. - This is useful for debugging API authentication problems. ### Returns - `Headers` A Headers response object containing the headers sent in the request. ### Example (View Headers) ```php utilities->headers(); echo "Request Headers:\n"; echo "================\n"; foreach ($headers->headers as $name => $value) { echo "$name: $value\n"; } ``` **Output** ``` Request Headers: ================ Authorization: Bearer eyJ0...***REDACTED*** Content-Type: application/json User-Agent: MarketDataApp-PHP/1.0.0 Host: api.marketdata.app ``` ### Example (Verify Auth) ```php utilities->headers(); // Check if Authorization header is present if (isset($headers->headers['Authorization'])) { echo "✓ Authorization header is being sent.\n"; echo "Header value: " . $headers->headers['Authorization'] . "\n"; } else { echo "✗ Authorization header is missing!\n"; echo "Check your MARKETDATA_TOKEN environment variable.\n"; } ``` ## Headers ```php class Headers extends ResponseBase { public string $status; public array $headers; } ``` Represents the request headers sent to the API. ### Properties - `status` (string): Response status (`"ok"`). - `headers` (array): Associative array of header name => value pairs. --- # Rate Limits (PHP SDK) Source: https://www.marketdata.app/docs/sdk/php/utilities/user/index.md Retrieve rate limit information for the current user. ## Making Requests Use the `user()` method on the `utilities` resource to get rate limit information: ## user ```php public function user(): User ``` Retrieve rate limit information for the current user, including credit usage and limits. ### Notes - Rate limits track **credits**, not requests. Most requests consume 1 credit, but bulk requests or options requests may consume multiple credits. - Free/Starter/Trader plans have daily limits. - Prime users have per-minute limits. - The `reset` timestamp indicates when the current rate limit window resets (UTC). ### Returns - `User` A User response object containing rate limit information. ### Example (Check Limits) ```php utilities->user(); echo "Rate Limit Status\n"; echo "=================\n"; echo "Limit: " . number_format($user->limit) . " credits\n"; echo "Remaining: " . number_format($user->remaining) . " credits\n"; echo "Used: " . number_format($user->consumed) . " credits\n"; echo "Resets: " . $user->reset->format('Y-m-d H:i:s T') . "\n"; // Calculate usage percentage $usagePercent = (($user->limit - $user->remaining) / $user->limit) * 100; echo "Usage: " . number_format($usagePercent, 1) . "%\n"; ``` **Output** ``` Rate Limit Status ================= Limit: 10,000 credits Remaining: 9,850 credits Used: 1 credits Resets: 2024-01-16 00:00:00 UTC Usage: 1.5% ``` ### Example (Usage Warning) ```php utilities->user(); // Check if approaching rate limit $usagePercent = (($user->limit - $user->remaining) / $user->limit) * 100; if ($usagePercent > 90) { echo "⚠️ WARNING: You've used " . number_format($usagePercent, 1) . "% of your daily limit!\n"; echo "Only " . number_format($user->remaining) . " credits remaining.\n"; echo "Limit resets at: " . $user->reset->format('H:i:s T') . "\n"; } elseif ($usagePercent > 75) { echo "ℹ️ Notice: You've used " . number_format($usagePercent, 1) . "% of your daily limit.\n"; } else { echo "✓ Usage is normal: " . number_format($usagePercent, 1) . "% used.\n"; } ``` ### Example (Rate Limit Header) ```php stocks->quote('AAPL'); if ($client->rate_limits) { echo "Rate limits from last response:\n"; echo "Consumed: " . $client->rate_limits->consumed . " credits\n"; echo "Remaining: " . $client->rate_limits->remaining . " credits\n"; echo "Limit: " . $client->rate_limits->limit . " credits\n"; } ``` ## User ```php class User { public int $limit; public int $remaining; public int $consumed; public Carbon $reset; } ``` Represents rate limit information for the current user. ### Properties - `limit` (int): Maximum number of credits permitted per rate period. - `remaining` (int): Credits remaining in the current rate period. - `consumed` (int): Credits consumed in the current request (not cumulative). - `reset` (Carbon): When the current rate limit window resets (UTC). --- # Postman Collection Source: https://www.marketdata.app/docs/sdk/postman/index.md The Market Data API Postman Collection is designed to make it easy to integrate the Market Data API into your application or workflow. The collection includes all available endpoints, with example requests, and is designed to be easy to use and understand. [Run In Postman](https://app.getpostman.com/run-collection/27146376-b1331cd2-4749-4708-96fe-2f1708b02854?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D27146376-b1331cd2-4749-4708-96fe-2f1708b02854%26entityType%3Dcollection%26workspaceId%3Dbd65af1d-3c36-4e62-ae64-f71008c154b4) ## Getting Started Follow these steps to get started: 1. **Open**: Open the collection in Postman using the link above. 2. **Fork the Collection**: Fork the collection so you can work with your own local copy. 3. **Watch Original Collection**: When you fork the collection, ensure that you are watching the original collection to receive updates. 4. **Add Your Token**: Add your Market Data token as a variable in the Authorization tab. ## Keeping Up To Date Here's a step-by-step guide to help you keep your forked Postman collection up to date: ### Step 1: Monitor the Original Collection for Updates 1. **Stay Informed**: If you are subscribed to our newsletter, you will recieve our monthly changelog with updates and new endpoints. We send out monthly notifications to all newsletter subscribers. If you do not wish to subscribe, you can review our changelog on the web. 2. **Change Log**: Review the [Market Data Changelog](https://roadmap.marketdata.app/changelog) and release notes to identify when we've made updates or released new endpoints. ### Step 2: Pull Changes from the Original Collection 1. **Navigate to Your Forked Collection**: Open Postman and go to your workspace where the forked collection is located. 2. **Find the Pull Changes Option**: Click on the three dots (...) next to your forked collection to open the collection options. Look for an option to pull changes or merge updates from the original collection. This option is available if there have been updates to the original collection after you forked it. 3. **Review and Pull Changes**: Depending on your version of Postman, you may be allowed to review the changes before pulling them. You can then confirm to pull the changes, which will update your forked collection with the new requests or modifications from the original. #### Screenshot ![Pull Request in Postman](https://www.marketdata.app/docs/img/postman-pull-request.png) --- # Python SDK Source: https://www.marketdata.app/docs/sdk/py/index.md Welcome to the Market Data Python SDK documentation. This SDK allows you to easily integrate Market Data services into your Python applications. ## Open Source The SDK is completely open source under the MIT license and is available on GitHub. Feel free to contribute to the project, report bugs, or request new features. - [Python SDK GitHub](https://github.com/MarketDataApp/sdk-py/) - [Python SDK PyPI](https://pypi.org/project/marketdata-sdk-py/) ## 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](https://www.marketdata.app/docs/sdk/py/installation) with the appropriate dependencies for your needs. 2. Set up your [authentication token](https://www.marketdata.app/docs/sdk/py/authentication) to access the API. 3. Learn about the [client](https://www.marketdata.app/docs/sdk/py/client) and how to make your first API requests. 4. Configure [Settings](https://www.marketdata.app/docs/sdk/py/settings) to customize output format, date format, and other universal parameters. 5. Explore the available endpoints for [stocks](https://www.marketdata.app/docs/sdk/py/stocks), [options](https://www.marketdata.app/docs/sdk/py/options), [funds](https://www.marketdata.app/docs/sdk/py/funds), and [markets](https://www.marketdata.app/docs/sdk/py/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-py/issues). Please only open issues if you find a bug. Use our helpdesk for general questions or implementation assistance. --- # Authentication (Python SDK) Source: https://www.marketdata.app/docs/sdk/py/authentication/index.md 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 two ways to set your token when using the Python SDK: 1. Set it from an environment variable _(recommended)_ 2. Pass it directly when creating the [client](https://www.marketdata.app/docs/sdk/py/client) On startup, the SDK will look for the environment variable. If found, it will use the token for all requests. If the environment variable is not found, you will need to pass the token when creating the [client](https://www.marketdata.app/docs/sdk/py/client). > [!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 After setting the variable, 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. ### Mac / Linux 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 on Mac OS or Linux, follow these steps: 1. Open a terminal. 2. Type the following command and press Enter: `echo $MARKETDATA_TOKEN` 3. If the environment variable has been set correctly, this command will output the value of MARKETDATA_TOKEN to the terminal. If the variable has not been set or an error occurred, the output will be blank, indicating that the variable is not set. #### Make The Variable Persistent To make the environment variable persistent across sessions, you need to add the export command to your shell's profile script (e.g., `~/.bash_profile`, `~/.bashrc`, `~/.zshrc`, etc.) depending on the shell you are using. > [!TIP] > > To find out what shell you are using, you can open your terminal and enter the following command: `echo $SHELL` This command will output the path to the current shell being used, such as `/bin/bash`, `/bin/zsh`, or another shell. The last part of the path indicates the shell type (e.g., bash, zsh). After adding the command to the appropriate profile script, you will need to either restart your terminal or source the profile script with a command like `source ~/.bash_profile` (adjusting the file name as necessary for your shell) to apply the changes. ##### Verify Variable Persistence If you have made the environment variable persistent by adding it to your shell's profile script (e.g., ~/.bash_profile, ~/.bashrc, ~/.zshrc), you can verify that the variable persists across sessions by: 1. Closing the current terminal. 2. Opening a new terminal. 3. Running the `echo $MARKETDATA_TOKEN` command again to check if the variable is still set. If the variable is not present after restarting the terminal, ensure you have correctly added the export command to the appropriate profile script and that you have either restarted the terminal or sourced the profile script with a command like `source ~/.bash_profile` (adjusting the file name as necessary for your shell). ### Windows This command should be run in the Command Prompt on Windows. Note that after using setx, the environment variable will be set permanently but will not be available in the current Command Prompt session. You will need to start a new session to see the change. ```bash setx MARKETDATA_TOKEN "your_api_token" # Windows ``` #### Verify The Variable To verify that the MARKETDATA_TOKEN environment variable has been set properly on Windows, the follow these steps: 1. Open a new Command Prompt session (since the setx command does not affect the current session where it was run). 2. Type the following command and press Enter: `echo %MARKETDATA_TOKEN%` 3. If the environment variable has been set correctly, this command will output the value of MARKETDATA_TOKEN to the console. If the variable has not been set or an error occurred, it will likely output `%MARKETDATA_TOKEN%` indicating that the variable substitution did not happen. 4. The next step is to ensure the SDK is correctly utilizing the token. To do this, the use the provided test code to make a request to the Market Data API. If the request is successful and returns the expected data, it confirms that the SDK is correctly reading and using the MARKETDATA_TOKEN environment variable. ### Using a .env File You can also create a `.env` file in your project root: ```ini MARKETDATA_TOKEN=your_api_token ``` The SDK will automatically load this file if you have `python-dotenv` installed. ### Make A Test Request Use the following test 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. ```python from marketdata import MarketDataClient, MarketDataClientErrorResult # No need to set a token here, the SDK will read it # from the environment variable automatically. # Initialize the client (see client documentation for details) client = MarketDataClient() # Make a test request quotes = client.stocks.quotes("SPY") # Check if the result is an error if isinstance(quotes, MarketDataClientErrorResult): print(f"Error occurred: {quotes.error}") else: # Print the quotes print(quotes) ``` 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 parameter. This is not recommended for production code. ### Example Code ```python from marketdata import MarketDataClient, MarketDataClientErrorResult # Replace with your token token = "your_token_here" # Create a new client and set the token client = MarketDataClient(token=token) # Make a test request to SPY quotes = client.stocks.quotes("SPY") # Check if the result is an error if isinstance(quotes, MarketDataClientErrorResult): print(f"Error occurred: {quotes.error}") else: # Print the quotes print(quotes) ``` ## 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](https://www.marketdata.app/docs/sdk/py/client) works, so you can get familiar with interacting with the SDK. The SDK includes methods for each endpoint that allow you to retrieve data. Use any of the endpoints in the SDK to make requests to the API. You may also want to configure [Settings](https://www.marketdata.app/docs/sdk/py/settings) to customize output format, date format, and other universal parameters. --- # Client (Python SDK) Source: https://www.marketdata.app/docs/sdk/py/client/index.md The Market Data Python Client includes functionality for making API requests, handling responses, managing rate limits, and logging. The SDK supports various data types including stocks, options, funds, and market status information. ## Get Started Quickly with the MarketDataClient 1. Review the [documentation on authentication](https://www.marketdata.app/docs/sdk/py/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](#accessing-rate-limits) in the client to track credit usage and how many API credits remain. 5. Configure [Settings](https://www.marketdata.app/docs/sdk/py/settings) to customize output format, date format, and other universal parameters. ## MarketDataClient ```python class MarketDataClient: def __init__(self, token: str = None, logger: Logger = None): ... ``` [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` (str): The authentication token for API requests. See [authentication documentation](https://www.marketdata.app/docs/sdk/py/authentication) for details. - `rate_limits` ([UserRateLimits](#accessing-rate-limits)): Current rate limit information - `base_url` (str): The base URL for API requests - `api_version` (str): The API version to use - `headers` (dict): HTTP headers including Authorization and User-Agent - `default_params` ([UserUniversalAPIParams](https://www.marketdata.app/docs/sdk/py/settings)): Default universal parameters for API requests. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details on configuring these parameters. ### Resources - `stocks` ([StocksResource](https://www.marketdata.app/docs/sdk/py/stocks)): Access to stocks endpoints (prices, quotes, candles, earnings, news) - `options` ([OptionsResource](https://www.marketdata.app/docs/sdk/py/options)): Access to options endpoints (chain, expirations, quotes, lookup) - `funds` ([FundsResource](https://www.marketdata.app/docs/sdk/py/funds)): Access to funds endpoints (candles) - `markets` ([MarketsResource](https://www.marketdata.app/docs/sdk/py/markets)): Access to markets endpoints (status) ### Methods - `__init__(token=None, logger=None)` Creates a new MarketDataClient instance. ### __init__ ```python def __init__(self, token: str = None, logger: Logger = None) ``` Creates and configures a new MarketDataClient instance with default settings. This method initializes the client with the provided token (or reads it from the `MARKETDATA_TOKEN` environment variable), sets up HTTP client configuration, and initializes rate limits by making a request to the `/user/` endpoint. #### Parameters - `token` (str, optional) The authentication token for API requests. If not provided, the SDK will attempt to read it from the `MARKETDATA_TOKEN` environment variable. - `logger` (`logging.Logger`, optional) A custom logger instance from Python's `logging` module. If not provided, the SDK will use its default logger. You can create a custom logger using `marketdata.logger.get_logger()`. #### Returns - `MarketDataClient` A new MarketDataClient instance ready to make API requests. #### Notes - The client automatically fetches rate limits during initialization by making a request to `/user/` endpoint. - The client includes a User-Agent header with the format `marketdata-py-{version}` (e.g., `marketdata-py-0.0.1`). - All requests include an `Authorization: Bearer {token}` header. - The client uses `httpx.Client` for HTTP requests with automatic connection pooling. #### Example ```python from marketdata import MarketDataClient # Token will be automatically obtained from MARKETDATA_TOKEN environment variable client = MarketDataClient() # Or provide the token explicitly client = MarketDataClient(token="your_token_here") # You can also provide a custom logger from marketdata.logger import get_logger custom_logger = get_logger() client = MarketDataClient(token="your_token_here", logger=custom_logger) ``` ## Accessing Rate Limits The client automatically fetches and tracks rate limits from the API. Rate limits are initialized when the client is created by making a request to `/user/` endpoint, and are updated after each API request based on response headers. You can access current rate limits: ```python client = MarketDataClient() # Access current rate limits rate_limits = client.rate_limits # Access individual fields print(f"Limit: {rate_limits.requests_limit}") print(f"Remaining: {rate_limits.requests_remaining}") print(f"Consumed: {rate_limits.requests_consumed}") print(f"Reset at: {rate_limits.requests_reset}") # Or use the formatted string representation print(rate_limits) # Shows: "Rate used X/Y, remaining: Z credits, next reset: ISO timestamp" ``` **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 The `requests_reset` field is automatically converted to a `datetime.datetime` object for easier use. ## Configuration The SDK supports flexible configuration of universal parameters through multiple methods. You can configure settings like output format, date format, data mode, and more. See the [Settings](https://www.marketdata.app/docs/sdk/py/settings) documentation for complete details on all available configuration options and how to use them. --- # Funds (Python SDK) Source: https://www.marketdata.app/docs/sdk/py/funds/index.md The Python 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 funds data. ## Funds Endpoints --- # Fund Candles (Python SDK) Source: https://www.marketdata.app/docs/sdk/py/funds/candles/index.md Retrieve historical price candles for any supported fund symbol. ## Making Requests Use the `candles()` method on the `funds` resource to fetch fund candles. The method supports multiple output formats: | Output Format | Return Type | Description | |---------------|----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| | **DATAFRAME** | `pandas.DataFrame` or `polars.DataFrame` | Returns a DataFrame with candles data (default). | | **INTERNAL** | `list[FundsCandle]` or `list[FundsCandlesHumanReadable]` | Returns a list of FundsCandle objects. When `use_human_readable=True`, returns a list of FundsCandlesHumanReadable objects with capitalized field names. | | **JSON** | `dict` | Returns the raw JSON response as a dictionary. | | **CSV** | `str` | Writes CSV data to file and returns the filename string. | ## candles ```python def candles( symbol: str, *, resolution: str = "D", from_date: str | datetime.datetime = None, to_date: str | datetime.datetime = None, date: str | datetime.datetime = None, countback: int = None, output_format: OutputFormat = OutputFormat.DATAFRAME, date_format: DateFormat = None, columns: list[str] = None, add_headers: bool = None, use_human_readable: bool = False, mode: Mode = None, filename: str | Path = None, ) -> list[FundsCandle] | list[FundsCandlesHumanReadable] | dict | str | MarketDataClientErrorResult ``` Fetches historical candles (OHLC) data for a fund symbol. The `symbol` parameter can be passed as the first positional argument or as a keyword argument. All other parameters must be keyword-only. ### Parameters - `symbol` (str) The fund symbol for which to fetch candles data. - `resolution` (str, optional) The granularity of the candle data (e.g., "1D", "1W", "1M"). Defaults to `"D"` (daily). - `from_date` (str | datetime.datetime, optional) Start date for the date range. - `to_date` (str | datetime.datetime, optional) End date for the date range. - `date` (str | datetime.datetime, optional) Specific date for the candles data. - `countback` (int, optional) Number of candles to return, counting backwards from the `to_date`. - `output_format` ([OutputFormat](https://www.marketdata.app/docs/sdk/py/settings#output-format), optional) The format of the returned data. Defaults to `OutputFormat.DATAFRAME`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `date_format` ([DateFormat](https://www.marketdata.app/docs/sdk/py/settings#date-format), optional) The date format to use in the response. Defaults to `DateFormat.UNIX`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`columns`](https://www.marketdata.app/docs/sdk/py/settings#columns) (optional) Specify which columns to include in the response. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`add_headers`](https://www.marketdata.app/docs/sdk/py/settings#headers) (optional) Whether to include headers in the response. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`use_human_readable`](https://www.marketdata.app/docs/sdk/py/settings#human-readable) (optional) Whether to use human-readable format for values. Only applies when `output_format=OutputFormat.INTERNAL`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `mode` ([Mode](https://www.marketdata.app/docs/sdk/py/settings#data-mode), optional) The data mode to use. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `filename` (str | Path, optional) File path for CSV output (only used with `output_format=OutputFormat.CSV`). ### Returns - `list[FundsCandle]` | `list[FundsCandlesHumanReadable]` | `dict` | `str` | `MarketDataClientErrorResult` The candles data in the requested format, or a `MarketDataClientErrorResult` if an error occurred. ### Example (DataFrame) ```python from marketdata import MarketDataClient client = MarketDataClient() # Get fund candles as DataFrame (default) # symbol can be passed positionally or as keyword df = client.funds.candles("VFINX") # or df = client.funds.candles(symbol="VFINX") # Get candles for a date range df = client.funds.candles( "VFINX", resolution="1D", from_date="2023-01-01", to_date="2023-01-31" ) print(df) ``` ### Example (Internal) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get fund candles as internal objects candles = client.funds.candles( "VFINX", resolution="1D", from_date="2023-01-01", to_date="2023-01-31", output_format=OutputFormat.INTERNAL ) # Access individual candle properties for candle in candles: print(f"Time: {candle.t}") print(f"Open: {candle.o}") print(f"High: {candle.h}") print(f"Low: {candle.l}") print(f"Close: {candle.c}") ``` ### Example (JSON) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get fund candles as JSON candles = client.funds.candles( "VFINX", resolution="1D", from_date="2023-01-01", to_date="2023-01-31", output_format=OutputFormat.JSON ) print(candles) ``` ### Example (CSV) ```python from marketdata import MarketDataClient, OutputFormat from pathlib import Path client = MarketDataClient() # Get fund candles as CSV csv_file = client.funds.candles( "VFINX", resolution="1D", from_date="2023-01-01", to_date="2023-01-31", output_format=OutputFormat.CSV, filename=Path("funds_candles.csv") ) print(f"CSV file saved to: {csv_file}") ``` ### Example (Human Readable) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get fund candles in human-readable format candles = client.funds.candles( "VFINX", resolution="1D", from_date="2023-01-01", to_date="2023-01-31", output_format=OutputFormat.INTERNAL, use_human_readable=True ) # Access individual candle properties for candle in candles: print(f"Date: {candle.Date}") print(f"Open: {candle.Open}") print(f"High: {candle.High}") print(f"Low: {candle.Low}") print(f"Close: {candle.Close}") ``` ## FundsCandle ```python @dataclass class FundsCandle: t: datetime.datetime o: float h: float l: float c: float ``` FundsCandle represents a single fund candle (OHLC data), encapsulating price information for a specific time period. ### Properties - `t` (datetime.datetime): The timestamp for the candle (automatically converted from Unix timestamp). - `o` (float): The opening price. - `h` (float): The highest price. - `l` (float): The lowest price. - `c` (float): The closing price. ### Notes - The `t` field is automatically converted to a `datetime.datetime` object from a Unix timestamp. ## FundsCandlesHumanReadable ```python @dataclass class FundsCandlesHumanReadable: Date: datetime.datetime Open: float High: float Low: float Close: float ``` FundsCandlesHumanReadable represents a fund candle in human-readable format with capitalized field names and formatted values. ### Properties - `Date` (datetime.datetime): The timestamp for the candle (automatically converted from Unix timestamp). - `Open` (float): The opening price. - `High` (float): The highest price. - `Low` (float): The lowest price. - `Close` (float): The closing price. ### Notes - The `Date` field is automatically converted to a `datetime.datetime` object from a Unix timestamp. - Field names use capitalized format (e.g., `Open` instead of `o`, `Date` instead of `t`). --- # Installation (Python SDK) Source: https://www.marketdata.app/docs/sdk/py/installation/index.md This guide will help you install the Market Data Python SDK and configure it for your needs. ## Prerequisites - Python >= 3.10 ## Basic Installation Install the Market Data Python SDK from PyPI: ### pip ```bash pip install marketdata-sdk-py ``` ### uv ```bash uv pip install marketdata-sdk-py ``` ## Installation with DataFrame Support The SDK supports multiple DataFrame libraries for `OutputFormat.DATAFRAME`. To use DataFrame output format, you need to install at least one DataFrame library. ### Install with pandas (recommended) ### pip ```bash pip install "marketdata-sdk-py[pandas]" ``` ### uv ```bash uv pip install "marketdata-sdk-py[pandas]" ``` ### Install with polars ### pip ```bash pip install "marketdata-sdk-py[polars]" ``` ### uv ```bash uv pip install "marketdata-sdk-py[polars]" ``` ### Install with both pandas and polars ### pip ```bash pip install "marketdata-sdk-py[pandas,polars]" ``` ### uv ```bash uv pip install "marketdata-sdk-py[pandas,polars]" ``` ## Optional Dependencies The SDK supports multiple DataFrame libraries for `OutputFormat.DATAFRAME`. You must install at least one of the following: - **pandas** (recommended): `pandas>=2.3.3` - **polars**: `polars-lts-cpu>=1.33.1` ### DataFrame Handler Priority When using `OutputFormat.DATAFRAME`, the SDK automatically selects an available DataFrame library in the following order: 1. **pandas** (if installed) 2. **polars** (if pandas is not installed) If neither pandas nor polars is installed, a `ValueError` will be raised when attempting to use `OutputFormat.DATAFRAME`: ```python ValueError: No dataframe output handler found ``` > [!NOTE] > You can use other output formats (`OutputFormat.INTERNAL`, `OutputFormat.JSON`, `OutputFormat.CSV`) without installing pandas or polars. ## Local Development Installation For local development, install from the project directory: ```bash # Clone the repository git clone https://github.com/MarketDataApp/sdk-py.git cd sdk-py # Install the package pip install . # Or install with optional dependencies pip install ".[pandas]" ``` ## Core Dependencies The SDK includes the following core dependencies (installed automatically): - `httpx>=0.28.1`: HTTP client library for making API requests - `pydantic>=2.12.5`: Data validation and settings management - `pydantic-settings>=2.12.0`: Configuration management from environment variables - `tenacity>=9.1.2`: Retry logic library for handling transient errors ## Next Steps After installation, you'll need to: 1. Set up your [authentication token](https://www.marketdata.app/docs/sdk/py/authentication) 2. Learn about the [client](https://www.marketdata.app/docs/sdk/py/client) and how to make your first API requests 3. Configure [settings](https://www.marketdata.app/docs/sdk/py/settings) to customize output format, date format, and other universal parameters --- # Markets (Python SDK) Source: https://www.marketdata.app/docs/sdk/py/markets/index.md The Python 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 --- # Market Status (Python SDK) Source: https://www.marketdata.app/docs/sdk/py/markets/status/index.md 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. The method supports multiple output formats: | Output Format | Return Type | Description | |---------------|-----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| | **DATAFRAME** | `pandas.DataFrame` or `polars.DataFrame` | Returns a DataFrame with market status data (default). | | **INTERNAL** | `list[MarketStatus]` or `list[MarketStatusHumanReadable]` | Returns a list of MarketStatus objects. When `use_human_readable=True`, returns a list of MarketStatusHumanReadable objects with capitalized field names. | | **JSON** | `dict` | Returns the raw JSON response as a dictionary. | | **CSV** | `str` | Writes CSV data to file and returns the filename string. | ## status ```python def status( *, date: str | datetime.datetime = None, from_date: str | datetime.datetime = None, to_date: str | datetime.datetime = None, countback: int = None, country: str = None, output_format: OutputFormat = OutputFormat.DATAFRAME, date_format: DateFormat = None, columns: list[str] = None, add_headers: bool = None, use_human_readable: bool = False, mode: Mode = None, filename: str | Path = None, ) -> list[MarketStatus] | list[MarketStatusHumanReadable] | dict | str | MarketDataClientErrorResult ``` Fetches market status information for specified dates and countries. All parameters must be keyword-only. ### Parameters - `date` (str | datetime.datetime, optional) Specific date for the market status. - `from_date` (str | datetime.datetime, optional) Start date for the date range. - `to_date` (str | datetime.datetime, optional) End date for the date range. - `countback` (int, optional) Number of days to return, counting backwards from the `to_date`. - `country` (str, optional) Filter by country code (e.g., "US", "CA", "GB"). - `output_format` ([OutputFormat](https://www.marketdata.app/docs/sdk/py/settings#output-format), optional) The format of the returned data. Defaults to `OutputFormat.DATAFRAME`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `date_format` ([DateFormat](https://www.marketdata.app/docs/sdk/py/settings#date-format), optional) The date format to use in the response. Defaults to `DateFormat.UNIX`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`columns`](https://www.marketdata.app/docs/sdk/py/settings#columns) (optional) Specify which columns to include in the response. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`add_headers`](https://www.marketdata.app/docs/sdk/py/settings#headers) (optional) Whether to include headers in the response. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`use_human_readable`](https://www.marketdata.app/docs/sdk/py/settings#human-readable) (optional) Whether to use human-readable format for values. Only applies when `output_format=OutputFormat.INTERNAL`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `mode` ([Mode](https://www.marketdata.app/docs/sdk/py/settings#data-mode), optional) The data mode to use. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `filename` (str | Path, optional) File path for CSV output (only used with `output_format=OutputFormat.CSV`). ### Returns - `list[MarketStatus]` | `list[MarketStatusHumanReadable]` | `dict` | `str` | `MarketDataClientErrorResult` The market status data in the requested format, or a `MarketDataClientErrorResult` if an error occurred. ### Notes - All parameters must be keyword-only (no positional arguments). - When using `countback`, it counts backwards from `to_date` or the current date if `to_date` is not provided. ### Example (DataFrame) ```python from marketdata import MarketDataClient client = MarketDataClient() # Get market status as DataFrame (default) # All parameters must be keyword-only df = client.markets.status(countback=7) # Get market status for a specific date df = client.markets.status(date="2023-01-01") # Get market status for a date range df = client.markets.status( from_date="2023-01-01", to_date="2023-01-31", country="US" ) print(df) ``` ### Example (Internal) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get market status as internal objects status_list = client.markets.status( countback=7, output_format=OutputFormat.INTERNAL ) # Access individual status properties for status in status_list: print(f"Date: {status.date}") print(f"Status: {status.status}") ``` ### Example (JSON) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get market status as JSON status = client.markets.status( countback=7, output_format=OutputFormat.JSON ) print(status) ``` ### Example (CSV) ```python from marketdata import MarketDataClient, OutputFormat from pathlib import Path client = MarketDataClient() # Get market status as CSV csv_file = client.markets.status( countback=7, output_format=OutputFormat.CSV, filename=Path("market_status.csv") ) print(f"CSV file saved to: {csv_file}") ``` ### Example (Human Readable) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get market status in human-readable format status_list = client.markets.status( countback=7, output_format=OutputFormat.INTERNAL, use_human_readable=True ) # Access individual status properties for status in status_list: print(f"Status: {status.Status}") print(f"Date: {status.Date}") ``` ## MarketStatus ```python @dataclass class MarketStatus: date: datetime.date status: str ``` MarketStatus represents market status information for a specific date, indicating whether the market is open or closed. ### Properties - `date` (datetime.date): The date for which the market status applies (automatically converted from timestamp). - `status` (str): The market status (e.g., "open", "closed"). ### Notes - The `date` field is automatically converted to a `datetime.date` object from a Unix timestamp. ## MarketStatusHumanReadable ```python @dataclass class MarketStatusHumanReadable: Status: str Date: datetime.datetime ``` MarketStatusHumanReadable represents market status information in human-readable format with capitalized field names and formatted values. ### Properties - `Status` (str): The market status (e.g., "open", "closed"). - `Date` (datetime.datetime): The date for which the market status applies (automatically converted from timestamp). ### Notes - The `Date` field is automatically converted to a `datetime.datetime` object from a Unix timestamp. - Field names use capitalized format (e.g., `Date` instead of `date`, `Status` instead of `status`). --- # Options (Python SDK) Source: https://www.marketdata.app/docs/sdk/py/options/index.md The Python 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 --- # Option Chain (Python SDK) Source: https://www.marketdata.app/docs/sdk/py/options/chain/index.md Retrieve a complete or filtered options chain for a given underlying symbol. Both real-time and historical requests are possible. ## Making Requests Use the `chain()` method on the `options` resource to fetch options chain data. The method supports multiple output formats: | Output Format | Return Type | Description | |---------------|-----------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **DATAFRAME** | `pandas.DataFrame` or `polars.DataFrame` | Returns a DataFrame with options chain data indexed by optionSymbol (default). | | **INTERNAL** | `OptionsChain` or `OptionsChainHumanReadable` | Returns an OptionsChain object containing lists of option data. When `use_human_readable=True`, returns an OptionsChainHumanReadable object with capitalized field names. | | **JSON** | `dict` | Returns the raw JSON response as a dictionary. | | **CSV** | `str` | Writes CSV data to file and returns the filename string. | ## chain ```python def chain( symbol: str, *, expiration: str = None, date: str | datetime.datetime = None, from_date: str | datetime.datetime = None, to_date: str | datetime.datetime = None, strike: float = None, side: str = None, dte: int = None, delta: float = None, range: str = None, strike_limit: int = None, min_bid: float = None, max_bid: float = None, min_ask: float = None, max_ask: float = None, min_open_interest: int = None, min_volume: int = None, max_bid_ask_spread: float = None, max_bid_ask_spread_pct: float = None, monthly: bool = None, weekly: bool = None, quarterly: bool = None, nonstandard: bool = None, am: bool = None, pm: bool = None, month: int = None, year: int = None, output_format: OutputFormat = OutputFormat.DATAFRAME, date_format: DateFormat = None, columns: list[str] = None, add_headers: bool = None, use_human_readable: bool = False, mode: Mode = None, filename: str | Path = None, ) -> OptionsChain | OptionsChainHumanReadable | dict | str | MarketDataClientErrorResult ``` Fetches the options chain for a given symbol with extensive filtering options. The `symbol` parameter can be passed as the first positional argument or as a keyword argument. All other parameters must be keyword-only. ### Parameters - `symbol` (str) The underlying stock symbol for which to fetch the options chain. - `expiration` (str, optional) Filter by expiration date. Can be a date string or "all" to get all expirations. - `date` (str | datetime.datetime, optional) Historical date for the options chain data. - `from_date` (str | datetime.datetime, optional) Start date for the date range. - `to_date` (str | datetime.datetime, optional) End date for the date range. - `strike` (float, optional) Filter by strike price. - `side` (str, optional) Filter by option side: "call" or "put". - `dte` (int, optional) Days to expiration. Requests an expiration date based on the number of days from the present date. - `delta` (float, optional) Filter by Delta value. - `range` (str, optional) Filter by strike range: "itm" (In The Money), "otm" (Out of The Money), "atm" (At The Money). - `strike_limit` (int, optional) Maximum number of strike prices to include in the option chain. - `min_open_interest` (int, optional) Minimum open interest for options to be included. - `min_bid` (float, optional) Minimum bid price for options to be included. Uses API alias `minBid`. - `max_bid` (float, optional) Maximum bid price for options to be included. Uses API alias `maxBid`. - `min_ask` (float, optional) Minimum ask price for options to be included. Uses API alias `minAsk`. - `max_ask` (float, optional) Maximum ask price for options to be included. Uses API alias `maxAsk`. - `min_volume` (int, optional) Minimum volume for options to be included. - `max_bid_ask_spread` (float, optional) Maximum bid-ask spread for options to be included. - `max_bid_ask_spread_pct` (float, optional) Maximum bid-ask spread percentage for options to be included. - `monthly` (bool, optional) Include or exclude monthly option expirations. - `weekly` (bool, optional) Include or exclude weekly option expirations. - `quarterly` (bool, optional) Include or exclude quarterly option expirations. - `nonstandard` (bool, optional) Include or exclude non-standard option expirations. - `am` (bool, optional) Whether to include A.M. expirations. - `pm` (bool, optional) Whether to include P.M. expirations. - `month` (int, optional) Filter by specific month (1-12). - `year` (int, optional) Filter by specific year. - `output_format` ([OutputFormat](https://www.marketdata.app/docs/sdk/py/settings#output-format), optional) The format of the returned data. Defaults to `OutputFormat.DATAFRAME`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `date_format` ([DateFormat](https://www.marketdata.app/docs/sdk/py/settings#date-format), optional) The date format to use in the response. Defaults to `DateFormat.UNIX`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`columns`](https://www.marketdata.app/docs/sdk/py/settings#columns) (optional) Specify which columns to include in the response. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`add_headers`](https://www.marketdata.app/docs/sdk/py/settings#headers) (optional) Whether to include headers in the response. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`use_human_readable`](https://www.marketdata.app/docs/sdk/py/settings#human-readable) (optional) Whether to use human-readable format for values. Only applies when `output_format=OutputFormat.INTERNAL`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `mode` ([Mode](https://www.marketdata.app/docs/sdk/py/settings#data-mode), optional) The data mode to use. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `filename` (str | Path, optional) File path for CSV output (only used with `output_format=OutputFormat.CSV`). ### Returns - `OptionsChain` | `OptionsChainHumanReadable` | `dict` | `str` | `MarketDataClientErrorResult` The options chain data in the requested format, or a `MarketDataClientErrorResult` if an error occurred. ### Notes - When using `OutputFormat.DATAFRAME`, the DataFrame is indexed by the `optionSymbol` column. - When using `OutputFormat.INTERNAL`, timestamps are automatically converted to `datetime.datetime` objects. - The OptionsChain object contains lists of equal length for each field, allowing you to iterate through options by index. ### Example (DataFrame) ```python from marketdata import MarketDataClient client = MarketDataClient() # Get options chain as DataFrame (default) # symbol can be passed positionally or as keyword chain = client.options.chain("AAPL") # or chain = client.options.chain(symbol="AAPL") # Filter by side and date chain = client.options.chain( "AAPL", side="call", date="2022-01-03", dte=60, strike_limit=2, range="itm" ) print(chain) ``` **Output** ``` underlying expiration side strike ... volume openInterest inTheMoney underlyingPrice optionSymbol ... AAPL271217C00250000 AAPL 2022-03-18 call 175.0 ... 1295 15232 True 182.01 AAPL271217C00255000 AAPL 2022-03-18 call 180.0 ... 4609 18299 True 182.01 ``` ### Example (Internal) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get options chain as internal object chain = client.options.chain( "AAPL", side="call", date="2022-01-03", dte=60, strike_limit=2, range="itm", output_format=OutputFormat.INTERNAL ) # Access option data by index print(f"Number of options: {len(chain.optionSymbol)}") print(f"First option symbol: {chain.optionSymbol[0]}") print(f"First option strike: {chain.strike[0]}") print(f"First option expiration: {chain.expiration[0]}") print(f"First option bid: {chain.bid[0]}") print(f"First option ask: {chain.ask[0]}") print(f"First option mid: {chain.mid[0]}") ``` **Output** ``` Number of options: 2 First option symbol: AAPL271217C00250000 First option strike: 175.0 First option expiration: 2022-03-18 16:00:00-04:00 First option bid: 12.95 First option ask: 13.1 First option mid: 13.02 ``` ### Example (JSON) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get options chain as JSON chain = client.options.chain( "AAPL", side="call", date="2022-01-03", output_format=OutputFormat.JSON ) print(chain) ``` **Output** ```json { "s": "ok", "optionSymbol": ["AAPL271217C00250000", "AAPL271217C00255000"], "underlying": ["AAPL", "AAPL"], "expiration": [1642798800, 1642798800], "side": ["call", "call"], "strike": [175, 180], "bid": [12.95, 10.0], "ask": [13.1, 10.2], "mid": [13.02, 10.1], ... } ``` ### Example (CSV) ```python from marketdata import MarketDataClient, OutputFormat from pathlib import Path client = MarketDataClient() # Get options chain as CSV csv_file = client.options.chain( "AAPL", side="call", date="2022-01-03", output_format=OutputFormat.CSV, filename=Path("chain.csv") ) print(f"CSV file saved to: {csv_file}") ``` ### Example (Human Readable) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get options chain in human-readable format chain = client.options.chain( "AAPL", side="call", date="2022-01-03", output_format=OutputFormat.INTERNAL, use_human_readable=True ) # Access option data by index print(f"Number of options: {len(chain.Symbol)}") print(f"First option symbol: {chain.Symbol[0]}") print(f"First option strike: {chain.Strike[0]}") print(f"First option expiration: {chain.Expiration_Date[0]}") print(f"First option bid: {chain.Bid[0]}") print(f"First option ask: {chain.Ask[0]}") ``` ### Example (Filtered) ```python from marketdata import MarketDataClient client = MarketDataClient() # Get filtered options chain chain = client.options.chain( "AAPL", side="call", month=2, year=2022, range="itm", strike=150, weekly=False, monthly=True, quarterly=False, nonstandard=False ) print(chain) ``` ## OptionsChain ```python @dataclass class OptionsChain: s: str optionSymbol: list[str] underlying: list[str] expiration: list[datetime.datetime] side: list[str] strike: list[float] firstTraded: list[datetime.datetime] dte: list[int] updated: list[datetime.datetime] bid: list[float] bidSize: list[int] mid: list[float] ask: list[float] askSize: list[int] last: list[float] openInterest: list[int] volume: list[int] inTheMoney: list[bool] intrinsicValue: list[float] extrinsicValue: list[float] underlyingPrice: list[float] iv: list[float] delta: list[float] gamma: list[float] theta: list[float] vega: list[float] ``` OptionsChain represents the options chain data with lists of equal length for each field. All lists have the same length, allowing you to access option data by index. ### Properties - `s` (str): Status indicator ("ok" for successful responses). - `optionSymbol` (list[str]): List of option symbols. - `underlying` (list[str]): List of underlying symbols. - `expiration` (list[datetime.datetime]): List of expiration dates (automatically converted from timestamps). - `side` (list[str]): List of option sides ("call" or "put"). - `strike` (list[float]): List of strike prices. - `firstTraded` (list[datetime.datetime]): List of first traded dates (automatically converted from timestamps). - `dte` (list[int]): List of days to expiration. - `updated` (list[datetime.datetime]): List of last update timestamps (automatically converted from timestamps). - `bid` (list[float]): List of bid prices. - `bidSize` (list[int]): List of bid sizes. - `mid` (list[float]): List of mid prices. - `ask` (list[float]): List of ask prices. - `askSize` (list[int]): List of ask sizes. - `last` (list[float]): List of last traded prices. - `openInterest` (list[int]): List of open interest values. - `volume` (list[int]): List of trading volumes. - `inTheMoney` (list[bool]): List indicating if options are in the money. - `intrinsicValue` (list[float]): List of intrinsic values. - `extrinsicValue` (list[float]): List of extrinsic values. - `underlyingPrice` (list[float]): List of underlying prices. - `iv` (list[float]): List of implied volatilities. - `delta` (list[float]): List of Delta values. - `gamma` (list[float]): List of Gamma values. - `theta` (list[float]): List of Theta values. - `vega` (list[float]): List of Vega values. ### Notes - All lists have the same length, allowing you to access option data by index (e.g., `chain.optionSymbol[0]`, `chain.strike[0]`, etc.). - Timestamp fields (`expiration`, `firstTraded`, `updated`) are automatically converted to `datetime.datetime` objects. - The `OptionsChainHumanReadable` variant uses capitalized field names with underscores (e.g., `Expiration_Date` instead of `expiration`). ## OptionsChainHumanReadable ```python @dataclass class OptionsChainHumanReadable: Symbol: list[str] Underlying: list[str] Expiration_Date: list[datetime.datetime] Side: list[str] Strike: list[float] First_Traded: list[datetime.datetime] Days_To_Expiration: list[int] Date: list[datetime.datetime] Bid: list[float] Bid_Size: list[int] Mid: list[float] Ask: list[float] Ask_Size: list[int] Last: list[float] Open_Interest: list[int] Volume: list[int] In_The_Money: list[bool] Intrinsic_Value: list[float] Extrinsic_Value: list[float] Underlying_Price: list[float] IV: list[float] Delta: list[float] Gamma: list[float] Theta: list[float] Vega: list[float] ``` OptionsChainHumanReadable represents the options chain data in human-readable format with capitalized field names and formatted values. All lists have the same length, allowing you to access option data by index. ### Properties - `Symbol` (list[str]): List of option symbols. - `Underlying` (list[str]): List of underlying symbols. - `Expiration_Date` (list[datetime.datetime]): List of expiration dates (automatically converted from timestamps). - `Option_Side` (list[str]): List of option sides ("call" or "put"). - `Strike` (list[float | int]): List of strike prices. - `First_Traded` (list[datetime.datetime]): List of first traded dates (automatically converted from timestamps). - `Days_To_Expiration` (list[int]): List of days to expiration. - `Date` (list[datetime.datetime]): List of last update timestamps (automatically converted from timestamps). - `Bid` (list[float]): List of bid prices. - `Bid_Size` (list[int]): List of bid sizes. - `Mid` (list[float]): List of mid prices. - `Ask` (list[float]): List of ask prices. - `Ask_Size` (list[int]): List of ask sizes. - `Last` (list[float]): List of last traded prices. - `Open_Interest` (list[int]): List of open interest values. - `Volume` (list[int]): List of trading volumes. - `In_The_Money` (list[bool]): List indicating if options are in the money. - `Intrinsic_Value` (list[float]): List of intrinsic values. - `Extrinsic_Value` (list[float]): List of extrinsic values. - `Underlying_Price` (list[float]): List of underlying prices. - `IV` (list[float]): List of implied volatilities. - `Delta` (list[float]): List of Delta values. - `Gamma` (list[float]): List of Gamma values. - `Theta` (list[float]): List of Theta values. - `Vega` (list[float]): List of Vega values. ### Notes - All lists have the same length, allowing you to access option data by index (e.g., `chain.Symbol[0]`, `chain.Strike[0]`, etc.). - Timestamp fields (`Expiration_Date`, `First_Traded`, `Date`) are automatically converted to `datetime.datetime` objects. - Field names use capitalized format with underscores (e.g., `Expiration_Date` instead of `expiration`, `Option_Side` instead of `side`, `Bid_Size` instead of `bidSize`). --- # Expirations (Python SDK) Source: https://www.marketdata.app/docs/sdk/py/options/expirations/index.md Get a list of current or historical option expiration dates for an underlying symbol. ## Making Requests Use the `expirations()` method on the `options` resource to fetch expiration dates. The method supports multiple output formats: | Output Format | Return Type | Description | |---------------|-----------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------| | **DATAFRAME** | `pandas.DataFrame` or `polars.DataFrame` | Returns a DataFrame with expiration dates indexed by expirations (default). | | **INTERNAL** | `OptionsExpirations` or `OptionsExpirationsHumanReadable` | Returns an OptionsExpirations object. When `use_human_readable=True`, returns an OptionsExpirationsHumanReadable object with capitalized field names. | | **JSON** | `dict` | Returns the raw JSON response as a dictionary. | | **CSV** | `str` | Writes CSV data to file and returns the filename string. | ## expirations ```python def expirations( symbol: str, *, strike: float = None, date: str | datetime.datetime = None, output_format: OutputFormat = OutputFormat.DATAFRAME, date_format: DateFormat = None, columns: list[str] = None, add_headers: bool = None, use_human_readable: bool = False, mode: Mode = None, filename: str | Path = None, ) -> OptionsExpirations | OptionsExpirationsHumanReadable | dict | str | MarketDataClientErrorResult ``` Fetches available expiration dates for a given symbol. The `symbol` parameter can be passed as the first positional argument or as a keyword argument. All other parameters must be keyword-only. ### Parameters - `symbol` (str) The underlying stock symbol for which to fetch expiration dates. - `strike` (float, optional) Filter by strike price. - `date` (str | datetime.datetime, optional) Historical date for the expiration dates. - `output_format` ([OutputFormat](https://www.marketdata.app/docs/sdk/py/settings#output-format), optional) The format of the returned data. Defaults to `OutputFormat.DATAFRAME`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `date_format` ([DateFormat](https://www.marketdata.app/docs/sdk/py/settings#date-format), optional) The date format to use in the response. Defaults to `DateFormat.UNIX`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`columns`](https://www.marketdata.app/docs/sdk/py/settings#columns) (optional) Specify which columns to include in the response. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`add_headers`](https://www.marketdata.app/docs/sdk/py/settings#headers) (optional) Whether to include headers in the response. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`use_human_readable`](https://www.marketdata.app/docs/sdk/py/settings#human-readable) (optional) Whether to use human-readable format for values. Only applies when `output_format=OutputFormat.INTERNAL`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `mode` ([Mode](https://www.marketdata.app/docs/sdk/py/settings#data-mode), optional) The data mode to use. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `filename` (str | Path, optional) File path for CSV output (only used with `output_format=OutputFormat.CSV`). ### Returns - `OptionsExpirations` | `OptionsExpirationsHumanReadable` | `dict` | `str` | `MarketDataClientErrorResult` The expiration dates in the requested format, or a `MarketDataClientErrorResult` if an error occurred. ### Example (DataFrame) ```python from marketdata import MarketDataClient client = MarketDataClient() # Get options expirations as DataFrame (default) # symbol can be passed positionally or as keyword df = client.options.expirations("AAPL") # or df = client.options.expirations(symbol="AAPL") print(df) ``` ### Example (Internal) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get options expirations as internal object expirations = client.options.expirations("AAPL", output_format=OutputFormat.INTERNAL) # Access expiration dates print(f"Expirations: {expirations.expirations}") ``` ### Example (JSON) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get options expirations as JSON expirations = client.options.expirations("AAPL", output_format=OutputFormat.JSON) print(expirations) ``` ### Example (CSV) ```python from marketdata import MarketDataClient, OutputFormat from pathlib import Path client = MarketDataClient() # Get options expirations as CSV csv_file = client.options.expirations( "AAPL", output_format=OutputFormat.CSV, filename=Path("expirations.csv") ) print(f"CSV file saved to: {csv_file}") ``` ### Example (Human Readable) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get options expirations in human-readable format expirations = client.options.expirations( "AAPL", output_format=OutputFormat.INTERNAL, use_human_readable=True ) # Access expiration dates print(f"Expirations: {expirations.Expirations}") print(f"Date: {expirations.Date}") ``` ## OptionsExpirations ```python @dataclass class OptionsExpirations: s: str expirations: list[datetime.datetime] updated: datetime.datetime ``` OptionsExpirations represents expiration dates for options, encapsulating a list of expiration dates and an update timestamp. ### Properties - `s` (str): Status indicator ("ok" for successful responses). - `expirations` (list[datetime.datetime]): List of expiration dates (automatically converted from timestamps). - `updated` (datetime.datetime): The time when the data was last updated (automatically converted from timestamp). ### Notes - The `expirations` list contains `datetime.datetime` objects automatically converted from Unix timestamps. - The `updated` field is automatically converted to a `datetime.datetime` object from a Unix timestamp. ## OptionsExpirationsHumanReadable ```python @dataclass class OptionsExpirationsHumanReadable: Expirations: list[datetime.datetime] Date: datetime.datetime ``` OptionsExpirationsHumanReadable represents expiration dates in human-readable format with capitalized field names and formatted values. ### Properties - `Expirations` (list[datetime.datetime]): List of expiration dates (automatically converted from timestamps). - `Date` (datetime.datetime): The time when the data was last updated (automatically converted from timestamp). ### Notes - The `Expirations` list contains `datetime.datetime` objects automatically converted from Unix timestamps. - The `Date` field is automatically converted to a `datetime.datetime` object from a Unix timestamp. - Field names use capitalized format (e.g., `Expirations` instead of `expirations`, `Date` instead of `updated`). --- # Lookup (Python SDK) Source: https://www.marketdata.app/docs/sdk/py/options/lookup/index.md Lookup an option symbol from a human-readable string format. ## Making Requests Use the `lookup()` method on the `options` resource to lookup option symbols. The method supports multiple output formats: | Output Format | Return Type | Description | |---------------|-------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------| | **DATAFRAME** | `pandas.DataFrame` or `polars.DataFrame` | Returns a DataFrame with option lookup data indexed by optionSymbol (default). | | **INTERNAL** | `OptionsLookup` or `OptionsLookupHumanReadable` | Returns an OptionsLookup object. When `use_human_readable=True`, returns an OptionsLookupHumanReadable object with capitalized field names. | | **JSON** | `dict` | Returns the raw JSON response as a dictionary. | | **CSV** | `str` | Writes CSV data to file and returns the filename string. | ## lookup ```python def lookup( lookup: str, *, output_format: OutputFormat = OutputFormat.DATAFRAME, date_format: DateFormat = None, columns: list[str] = None, add_headers: bool = None, use_human_readable: bool = False, mode: Mode = None, filename: str | Path = None, ) -> OptionsLookup | OptionsLookupHumanReadable | dict | str | MarketDataClientErrorResult ``` Fetches option lookup data for a given lookup string. The lookup string should contain the underlying symbol, expiration date, strike price, and option side in the format: "SYMBOL DD-MM-YYYY STRIKE SIDE" (e.g., "AAPL 20-12-2024 150.0 call"). The `lookup` parameter can be passed as the first positional argument or as a keyword argument. All other parameters must be keyword-only. ### Parameters - `lookup` (str) The lookup string in the format "SYMBOL DD-MM-YYYY STRIKE SIDE" (e.g., "AAPL 20-12-2024 150.0 call"). - `output_format` ([OutputFormat](https://www.marketdata.app/docs/sdk/py/settings#output-format), optional) The format of the returned data. Defaults to `OutputFormat.DATAFRAME`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `date_format` ([DateFormat](https://www.marketdata.app/docs/sdk/py/settings#date-format), optional) The date format to use in the response. Defaults to `DateFormat.UNIX`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`columns`](https://www.marketdata.app/docs/sdk/py/settings#columns) (optional) Specify which columns to include in the response. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`add_headers`](https://www.marketdata.app/docs/sdk/py/settings#headers) (optional) Whether to include headers in the response. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`use_human_readable`](https://www.marketdata.app/docs/sdk/py/settings#human-readable) (optional) Whether to use human-readable format for values. Only applies when `output_format=OutputFormat.INTERNAL`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `mode` ([Mode](https://www.marketdata.app/docs/sdk/py/settings#data-mode), optional) The data mode to use. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `filename` (str | Path, optional) File path for CSV output (only used with `output_format=OutputFormat.CSV`). ### Returns - `OptionsLookup` | `OptionsLookupHumanReadable` | `dict` | `str` | `MarketDataClientErrorResult` The option lookup data in the requested format, or a `MarketDataClientErrorResult` if an error occurred. ### Notes - The lookup string format is: "SYMBOL DD-MM-YYYY STRIKE SIDE" - Example: "AAPL 20-12-2024 150.0 call" or "AAPL 20-12-2024 150.0 put" - When using `OutputFormat.DATAFRAME`, the DataFrame is indexed by the `optionSymbol` column. ### Example (DataFrame) ```python from marketdata import MarketDataClient client = MarketDataClient() # Get option lookup as DataFrame (default) # lookup can be passed positionally or as keyword df = client.options.lookup("AAPL 20-12-2024 150.0 call") # or df = client.options.lookup(lookup="AAPL 20-12-2024 150.0 call") print(df) ``` ### Example (Internal) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get option lookup as internal object lookup_result = client.options.lookup( "AAPL 20-12-2024 150.0 call", output_format=OutputFormat.INTERNAL ) # Access lookup properties print(f"Option Symbol: {lookup_result.optionSymbol}") ``` ### Example (JSON) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get option lookup as JSON lookup_result = client.options.lookup( "AAPL 20-12-2024 150.0 call", output_format=OutputFormat.JSON ) print(lookup_result) ``` ### Example (CSV) ```python from marketdata import MarketDataClient, OutputFormat from pathlib import Path client = MarketDataClient() # Get option lookup as CSV csv_file = client.options.lookup( "AAPL 20-12-2024 150.0 call", output_format=OutputFormat.CSV, filename=Path("lookup.csv") ) print(f"CSV file saved to: {csv_file}") ``` ### Example (Human Readable) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get option lookup in human-readable format lookup_result = client.options.lookup( "AAPL 20-12-2024 150.0 call", output_format=OutputFormat.INTERNAL, use_human_readable=True ) # Access lookup properties print(f"Symbol: {lookup_result.Symbol}") ``` ## OptionsLookup ```python @dataclass class OptionsLookup: s: str optionSymbol: str ``` OptionsLookup represents the result of an option symbol lookup, containing the resolved option symbol. ### Properties - `s` (str): Status indicator ("ok" for successful responses). - `optionSymbol` (str): The resolved option symbol. ## OptionsLookupHumanReadable ```python @dataclass class OptionsLookupHumanReadable: Symbol: str ``` OptionsLookupHumanReadable represents an option symbol lookup result in human-readable format with capitalized field names. ### Properties - `Symbol` (str): The resolved option symbol. ### Notes - Field names use capitalized format (e.g., `Symbol` instead of `optionSymbol`). --- # Option Quotes (Python SDK) Source: https://www.marketdata.app/docs/sdk/py/options/quotes/index.md Retrieve quotes for one or more option symbols. ## Making Requests Use the `quotes()` method on the `options` resource to fetch option quotes. The method supports multiple output formats and automatically handles multiple symbols by fetching them concurrently: | Output Format | Return Type | Description | |---------------|-------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **DATAFRAME** | `pandas.DataFrame` or `polars.DataFrame` | Returns a DataFrame with option quotes indexed by optionSymbol (default). | | **INTERNAL** | `OptionsQuotes` or `OptionsQuotesHumanReadable` | Returns an OptionsQuotes object containing lists of option quote data. When `use_human_readable=True`, returns an OptionsQuotesHumanReadable object with capitalized field names. | | **JSON** | `dict` | Returns the raw JSON response as a dictionary. | | **CSV** | `str` | Writes CSV data to file and returns the filename string. | ## quotes ```python def quotes( symbols: str | list[str], *, output_format: OutputFormat = OutputFormat.DATAFRAME, date_format: DateFormat = None, columns: list[str] = None, add_headers: bool = None, use_human_readable: bool = False, mode: Mode = None, filename: str | Path = None, ) -> OptionsQuotes | OptionsQuotesHumanReadable | dict | str | MarketDataClientErrorResult ``` Fetches option quotes for one or more option symbols. Note: `quotes()` takes option symbols (e.g., "AAPL271217C00250000"), not stock symbols. The `symbols` parameter can be passed as the first positional argument or as a keyword argument. All other parameters must be keyword-only. ### Parameters - `symbols` (str | list[str]) A single option symbol string or a list of option symbol strings for which to fetch quotes. - `output_format` ([OutputFormat](https://www.marketdata.app/docs/sdk/py/settings#output-format), optional) The format of the returned data. Defaults to `OutputFormat.DATAFRAME`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `date_format` ([DateFormat](https://www.marketdata.app/docs/sdk/py/settings#date-format), optional) The date format to use in the response. Defaults to `DateFormat.UNIX`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`columns`](https://www.marketdata.app/docs/sdk/py/settings#columns) (optional) Specify which columns to include in the response. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`add_headers`](https://www.marketdata.app/docs/sdk/py/settings#headers) (optional) Whether to include headers in the response. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`use_human_readable`](https://www.marketdata.app/docs/sdk/py/settings#human-readable) (optional) Whether to use human-readable format for values. Only applies when `output_format=OutputFormat.INTERNAL`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `mode` ([Mode](https://www.marketdata.app/docs/sdk/py/settings#data-mode), optional) The data mode to use. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `filename` (str | Path, optional) File path for CSV output (only used with `output_format=OutputFormat.CSV`). ### Returns - `OptionsQuotes` | `OptionsQuotesHumanReadable` | `dict` | `str` | `MarketDataClientErrorResult` The option quotes in the requested format, or a `MarketDataClientErrorResult` if an error occurred. ### Notes - Multiple option symbols are fetched concurrently (up to 50 concurrent requests by default). - When using `OutputFormat.DATAFRAME`, the DataFrame is indexed by the `optionSymbol` column. - The OptionsQuotes object contains lists of equal length for each field, allowing you to iterate through options by index. ### Example (DataFrame) ```python from marketdata import MarketDataClient client = MarketDataClient() # Get option quotes as DataFrame (default) # symbols can be passed positionally or as keyword # Note: quotes() takes option symbols, not stock symbols df = client.options.quotes("AAPL271217C00250000") # or df = client.options.quotes(symbols="AAPL271217C00250000") # Get quotes for multiple option symbols df = client.options.quotes(["AAPL271217C00250000", "AAPL271217P00250000"]) print(df) ``` ### Example (Internal) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get option quotes as internal object quotes = client.options.quotes( ["AAPL271217C00250000", "AAPL271217P00250000"], output_format=OutputFormat.INTERNAL ) # Access option data by index print(f"Number of options: {len(quotes.optionSymbol)}") print(f"First option symbol: {quotes.optionSymbol[0]}") print(f"First option bid: {quotes.bid[0]}") print(f"First option ask: {quotes.ask[0]}") ``` ### Example (JSON) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get option quotes as JSON quotes = client.options.quotes( ["AAPL271217C00250000", "AAPL271217P00250000"], output_format=OutputFormat.JSON ) print(quotes) ``` ### Example (CSV) ```python from marketdata import MarketDataClient, OutputFormat from pathlib import Path client = MarketDataClient() # Get option quotes as CSV csv_file = client.options.quotes( ["AAPL271217C00250000", "AAPL271217P00250000"], output_format=OutputFormat.CSV, filename=Path("options_quotes.csv") ) print(f"CSV file saved to: {csv_file}") ``` ### Example (Human Readable) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get option quotes in human-readable format quotes = client.options.quotes( ["AAPL271217C00250000", "AAPL271217P00250000"], output_format=OutputFormat.INTERNAL, use_human_readable=True ) # Access option data by index print(f"Number of options: {len(quotes.Symbol)}") print(f"First option symbol: {quotes.Symbol[0]}") print(f"First option underlying: {quotes.Underlying[0]}") print(f"First option expiration: {quotes.Expiration_Date[0]}") print(f"First option strike: {quotes.Strike[0]}") print(f"First option bid: {quotes.Bid[0]}") print(f"First option ask: {quotes.Ask[0]}") ``` ## OptionsQuotes ```python @dataclass class OptionsQuotes: s: str optionSymbol: list[str] underlying: list[str] expiration: list[datetime.datetime] side: list[str] strike: list[float] firstTraded: list[datetime.datetime] dte: list[int] updated: list[datetime.datetime] bid: list[float] bidSize: list[int] mid: list[float] ask: list[float] askSize: list[int] last: list[float] openInterest: list[int] volume: list[int] inTheMoney: list[bool] intrinsicValue: list[float] extrinsicValue: list[float] underlyingPrice: list[float] iv: list[float] delta: list[float] gamma: list[float] theta: list[float] vega: list[float] ``` OptionsQuotes represents option quotes data with lists of equal length for each field. All lists have the same length, allowing you to access option data by index. ### Properties - `s` (str): Status indicator ("ok" for successful responses). - `optionSymbol` (list[str]): List of option symbols. - `underlying` (list[str]): List of underlying symbols. - `expiration` (list[datetime.datetime]): List of expiration dates (automatically converted from timestamps). - `side` (list[str]): List of option sides ("call" or "put"). - `strike` (list[float]): List of strike prices. - `firstTraded` (list[datetime.datetime]): List of first traded dates (automatically converted from timestamps). - `dte` (list[int]): List of days to expiration. - `updated` (list[datetime.datetime]): List of last update timestamps (automatically converted from timestamps). - `bid` (list[float]): List of bid prices. - `bidSize` (list[int]): List of bid sizes. - `mid` (list[float]): List of mid prices. - `ask` (list[float]): List of ask prices. - `askSize` (list[int]): List of ask sizes. - `last` (list[float]): List of last traded prices. - `openInterest` (list[int]): List of open interest values. - `volume` (list[int]): List of trading volumes. - `inTheMoney` (list[bool]): List indicating if options are in the money. - `intrinsicValue` (list[float]): List of intrinsic values. - `extrinsicValue` (list[float]): List of extrinsic values. - `underlyingPrice` (list[float]): List of underlying prices. - `iv` (list[float]): List of implied volatilities. - `delta` (list[float]): List of Delta values. - `gamma` (list[float]): List of Gamma values. - `theta` (list[float]): List of Theta values. - `vega` (list[float]): List of Vega values. ### Notes - All lists have the same length, allowing you to access option data by index (e.g., `quotes.optionSymbol[0]`, `quotes.bid[0]`, etc.). - Timestamp fields (`expiration`, `firstTraded`, `updated`) are automatically converted to `datetime.datetime` objects. ## OptionsQuotesHumanReadable ```python @dataclass class OptionsQuotesHumanReadable: Symbol: list[str] Underlying: list[str] Expiration_Date: list[datetime.datetime] Option_Side: list[str] Strike: list[float | int] First_Traded: list[datetime.datetime] Days_To_Expiration: list[int] Date: list[datetime.datetime] Bid: list[float] Bid_Size: list[int] Mid: list[float] Ask: list[float] Ask_Size: list[int] Last: list[float] Open_Interest: list[int] Volume: list[int] In_The_Money: list[bool] Intrinsic_Value: list[float] Extrinsic_Value: list[float] Underlying_Price: list[float] IV: list[float] Delta: list[float] Gamma: list[float] Theta: list[float] Vega: list[float] ``` OptionsQuotesHumanReadable represents option quotes in human-readable format with capitalized field names and formatted values. ### Properties - `Symbol` (list[str]): List of option symbols. - `Underlying` (list[str]): List of underlying symbols. - `Expiration_Date` (list[datetime.datetime]): List of expiration dates (automatically converted from timestamps). - `Option_Side` (list[str]): List of option sides ("call" or "put"). - `Strike` (list[float | int]): List of strike prices. - `First_Traded` (list[datetime.datetime]): List of first traded dates (automatically converted from timestamps). - `Days_To_Expiration` (list[int]): List of days to expiration. - `Date` (list[datetime.datetime]): List of last update timestamps (automatically converted from timestamps). - `Bid` (list[float]): List of bid prices. - `Bid_Size` (list[int]): List of bid sizes. - `Mid` (list[float]): List of mid prices. - `Ask` (list[float]): List of ask prices. - `Ask_Size` (list[int]): List of ask sizes. - `Last` (list[float]): List of last traded prices. - `Open_Interest` (list[int]): List of open interest values. - `Volume` (list[int]): List of trading volumes. - `In_The_Money` (list[bool]): List indicating if options are in the money. - `Intrinsic_Value` (list[float]): List of intrinsic values. - `Extrinsic_Value` (list[float]): List of extrinsic values. - `Underlying_Price` (list[float]): List of underlying prices. - `IV` (list[float]): List of implied volatilities. - `Delta` (list[float]): List of Delta values. - `Gamma` (list[float]): List of Gamma values. - `Theta` (list[float]): List of Theta values. - `Vega` (list[float]): List of Vega values. ### Notes - All lists have the same length, allowing you to access option data by index (e.g., `quotes.Symbol[0]`, `quotes.Bid[0]`, etc.). - Timestamp fields (`Expiration_Date`, `First_Traded`, `Date`) are automatically converted to `datetime.datetime` objects. - Field names use capitalized format with underscores (e.g., `Expiration_Date` instead of `expiration`, `Option_Side` instead of `side`, `Open_Interest` instead of `openInterest`). --- # Settings (Python SDK) Source: https://www.marketdata.app/docs/sdk/py/settings/index.md The Market Data Python SDK provides flexible configuration options 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 multiple ways to configure universal parameters. These configuration methods follow a priority hierarchy, where higher priority settings override lower priority ones. ### Priority Order (Lowest to Highest) 1. **Environment Variables** (Lowest Priority) - Set via environment variables like `MARKETDATA_OUTPUT_FORMAT`, `MARKETDATA_DATE_FORMAT`, etc. - Applied globally to all API calls unless overridden 2. **Client Default Parameters** (`client.default_params`) - Set via `client.default_params` property - Applied to all API calls made with that client instance unless overridden 3. **Direct Method Parameters** (Highest Priority) - Passed directly as keyword arguments to resource methods - Applied only to that specific API call ### How It Works When making an API call, the SDK merges parameters in the following order: 1. **Start with environment variables** (lowest priority) - Parameters are read from environment variables like `MARKETDATA_OUTPUT_FORMAT`, `MARKETDATA_DATE_FORMAT`, etc. 2. **Override with `client.default_params`** (medium priority) - Parameters set via `client.default_params` override environment variables - Example: `client.default_params.output_format = OutputFormat.JSON` 3. **Override with direct method parameters** (highest priority) - Parameters passed directly to resource methods override both environment variables and `client.default_params` - Example: `client.stocks.prices("AAPL", output_format=OutputFormat.DATAFRAME)` ### Examples ### Environment Variables Set universal parameters via environment variables: ```python import os os.environ["MARKETDATA_OUTPUT_FORMAT"] = "json" os.environ["MARKETDATA_DATE_FORMAT"] = "timestamp" from marketdata import MarketDataClient client = MarketDataClient() # All calls will use JSON format and timestamp date format # (unless overridden by client.default_params or method parameters) prices = client.stocks.prices("AAPL") # Uses: output_format=JSON, date_format=TIMESTAMP ``` ### Client Default Params Set universal parameters via `client.default_params`: ```python from marketdata import MarketDataClient, OutputFormat, DateFormat client = MarketDataClient() # Set default parameters for this client instance client.default_params.output_format = OutputFormat.JSON client.default_params.date_format = DateFormat.TIMESTAMP # All calls will use JSON format and timestamp date format # (unless overridden by method parameters) prices = client.stocks.prices("AAPL") # Uses: output_format=JSON, date_format=TIMESTAMP quotes = client.stocks.quotes("AAPL") # Uses: output_format=JSON, date_format=TIMESTAMP ``` **Note:** `client.default_params` overrides environment variables. ### Direct Method Parameters Pass parameters directly to resource methods: ```python from marketdata import MarketDataClient, OutputFormat, DateFormat client = MarketDataClient() # Set default parameters client.default_params.output_format = OutputFormat.JSON client.default_params.date_format = DateFormat.TIMESTAMP # Direct method parameters override client.default_params prices = client.stocks.prices( "AAPL", output_format=OutputFormat.DATAFRAME, # Overrides JSON date_format=DateFormat.UNIX # Overrides TIMESTAMP ) # Uses: output_format=DATAFRAME, date_format=UNIX # This call still uses client.default_params quotes = client.stocks.quotes("AAPL") # Uses: output_format=JSON, date_format=TIMESTAMP ``` **Note:** Direct method parameters override both environment variables and `client.default_params`. ## Available Configuration Options ### Output Format Controls the format of the API response data. **Available Values:** - `OutputFormat.DATAFRAME` (default): Returns data as a pandas DataFrame - `OutputFormat.JSON`: Returns data as JSON - `OutputFormat.CSV`: Returns data as CSV (saves to file) - `OutputFormat.INTERNAL`: Returns data in internal format **Configuration Methods:** 1. **Environment Variable:** `MARKETDATA_OUTPUT_FORMAT` (values: `dataframe`, `internal`, `json`, `csv`) 2. **Client Default:** `client.default_params.output_format = OutputFormat.JSON` 3. **Direct Parameter:** `client.stocks.quotes("AAPL", output_format=OutputFormat.JSON)` **Example:** ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Method 1: Environment variable # export MARKETDATA_OUTPUT_FORMAT=json # Method 2: Client default params client.default_params.output_format = OutputFormat.JSON # Method 3: Direct parameter (highest priority) quotes = client.stocks.quotes("AAPL", output_format=OutputFormat.DATAFRAME) ``` For more details, see the [API Format documentation](https://www.marketdata.app/docs/api/universal-parameters/format). ### Date Format Specifies the format for date and time information in responses. **Available Values:** - `DateFormat.TIMESTAMP`: ISO 8601 timestamp format (e.g., `2020-12-30 16:00:00 -05:00`) - `DateFormat.UNIX`: Unix timestamp in seconds (e.g., `1609362000`) - `DateFormat.SPREADSHEET`: Excel spreadsheet format (e.g., `44195.66667`) **Configuration Methods:** 1. **Environment Variable:** `MARKETDATA_DATE_FORMAT` (values: `timestamp`, `unix`, `spreadsheet`) 2. **Client Default:** `client.default_params.date_format = DateFormat.UNIX` 3. **Direct Parameter:** `client.stocks.candles("AAPL", date_format=DateFormat.UNIX)` **Example:** ```python from marketdata import MarketDataClient, DateFormat client = MarketDataClient() # Method 1: Environment variable # export MARKETDATA_DATE_FORMAT=unix # Method 2: Client default params client.default_params.date_format = DateFormat.UNIX # Method 3: Direct parameter (highest priority) candles = client.stocks.candles("AAPL", date_format=DateFormat.TIMESTAMP) ``` For more details, see the [API Date Format documentation](https://www.marketdata.app/docs/api/universal-parameters/date-format). ### Columns Limits the response to only the columns you need, reducing data transfer and processing time. **Type:** `list[str]` or comma-separated string **Configuration Methods:** 1. **Environment Variable:** `MARKETDATA_COLUMNS` (comma-separated list, e.g., `ask,bid`) 2. **Client Default:** `client.default_params.columns = ["ask", "bid"]` 3. **Direct Parameter:** `client.stocks.quotes("AAPL", columns=["ask", "bid"])` **Example:** ```python from marketdata import MarketDataClient client = MarketDataClient() # Method 1: Environment variable # export MARKETDATA_COLUMNS=ask,bid # Method 2: Client default params client.default_params.columns = ["ask", "bid"] # Method 3: Direct parameter (highest priority) quotes = client.stocks.quotes("AAPL", columns=["ask", "bid"]) # Or as a comma-separated string quotes = client.stocks.quotes("AAPL", columns="ask,bid") ``` For more details, see the [API Columns documentation](https://www.marketdata.app/docs/api/universal-parameters/columns). ### Headers Controls whether headers are included in CSV output. **Type:** `bool` **Default:** `True` **Configuration Methods:** 1. **Environment Variable:** `MARKETDATA_ADD_HEADERS` (values: `true`, `false`) 2. **Client Default:** `client.default_params.add_headers = False` 3. **Direct Parameter:** `client.stocks.candles("AAPL", add_headers=False)` **Example:** ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Method 1: Environment variable # export MARKETDATA_ADD_HEADERS=false # Method 2: Client default params client.default_params.add_headers = False # Method 3: Direct parameter (highest priority) candles = client.stocks.candles( "AAPL", output_format=OutputFormat.CSV, add_headers=False ) ``` For more details, see the [API Headers documentation](https://www.marketdata.app/docs/api/universal-parameters/headers). ### Human Readable Uses human-readable attribute names in JSON or CSV output instead of camelCase. Only applies when `output_format=OutputFormat.INTERNAL`. **Type:** `bool` **Default:** `False` **Configuration Methods:** 1. **Environment Variable:** `MARKETDATA_USE_HUMAN_READABLE` (values: `true`, `false`) 2. **Client Default:** `client.default_params.use_human_readable = True` 3. **Direct Parameter:** `client.stocks.quotes("AAPL", use_human_readable=True)` **Example:** ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Method 1: Environment variable # export MARKETDATA_USE_HUMAN_READABLE=true # Method 2: Client default params client.default_params.use_human_readable = True # Method 3: Direct parameter (highest priority) quotes = client.stocks.quotes( "AAPL", output_format=OutputFormat.INTERNAL, use_human_readable=True ) ``` For more details, see the [API Human Readable documentation](https://www.marketdata.app/docs/api/universal-parameters/human-readable). ### Data Mode Controls how an API request is fulfilled, including data freshness guarantees and credit usage. **Available Values:** - `Mode.LIVE`: Real-time data (default for paid accounts) - `Mode.CACHED`: Recently cached data (reduces credit usage for bulk quotes) - `Mode.DELAYED`: Data delayed by at least 15 minutes (default for free/trial accounts) **Configuration Methods:** 1. **Environment Variable:** `MARKETDATA_MODE` (values: `live`, `cached`, `delayed`) 2. **Client Default:** `client.default_params.mode = Mode.CACHED` 3. **Direct Parameter:** `client.options.chain("SPY", mode=Mode.CACHED)` **Example:** ```python from marketdata import MarketDataClient, Mode client = MarketDataClient() # Method 1: Environment variable # export MARKETDATA_MODE=cached # Method 2: Client default params client.default_params.mode = Mode.CACHED # Method 3: Direct parameter (highest priority) chain = client.options.chain("SPY", mode=Mode.CACHED) quotes = client.stocks.quotes("AAPL", mode=Mode.LIVE) ``` > [!NOTE] > **[Premium Parameter]** > > The `mode` parameter is available only on paid plans. Free and trial plans cannot change the data mode and receive historical data (the last fully-closed session). See [Data Freshness](https://www.marketdata.app/docs/account/data-freshness). For more details, see the [API Mode documentation](https://www.marketdata.app/docs/api/universal-parameters/mode). ## Available Environment Variables You can set the following environment variables to configure universal parameters globally: - `MARKETDATA_OUTPUT_FORMAT`: Output format (`dataframe`, `internal`, `json`, `csv`) - `MARKETDATA_DATE_FORMAT`: Date format (`timestamp`, `unix`, `spreadsheet`) - `MARKETDATA_COLUMNS`: Comma-separated list of columns to include - `MARKETDATA_ADD_HEADERS`: Whether to include headers (`true`, `false`) - `MARKETDATA_USE_HUMAN_READABLE`: Whether to use human-readable format (`true`, `false`) - `MARKETDATA_MODE`: Data mode (`live`, `cached`, `delayed`) ## Best Practices - **Use environment variables** for global defaults that apply across your entire application - **Use `client.default_params`** when you want different defaults for different client instances - **Use direct method parameters** when you need to override defaults for specific API calls This flexible configuration system allows you to set sensible defaults while still having fine-grained control over individual API calls. --- # Stocks (Python SDK) Source: https://www.marketdata.app/docs/sdk/py/stocks/index.md The Python 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 --- # Stock Candles (Python SDK) Source: https://www.marketdata.app/docs/sdk/py/stocks/candles/index.md Retrieve historical price candles for any supported stock symbol. ## Making Requests Use the `candles()` method on the `stocks` resource to fetch stock candles. The method supports multiple output formats and automatically handles large date ranges by splitting them into year-long chunks and fetching them concurrently: | Output Format | Return Type | Description | |---------------|----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| | **DATAFRAME** | `pandas.DataFrame` or `polars.DataFrame` | Returns a DataFrame with candles data indexed by timestamp (default). | | **INTERNAL** | `list[StockCandle]` or `list[StockCandlesHumanReadable]` | Returns a list of StockCandle objects. When `use_human_readable=True`, returns a list of StockCandlesHumanReadable objects with capitalized field names. | | **JSON** | `dict` | Returns the raw JSON response as a dictionary. | | **CSV** | `str` | Writes CSV data to file and returns the filename string. | ## candles ```python def candles( symbol: str, *, resolution: str = "D", from_date: str | datetime.datetime = None, to_date: str | datetime.datetime = None, date: str | datetime.datetime = None, countback: int = None, adjust_splits: bool = None, extended: bool = None, exchange: str = None, output_format: OutputFormat = OutputFormat.DATAFRAME, date_format: DateFormat = None, columns: list[str] = None, add_headers: bool = None, use_human_readable: bool = False, mode: Mode = None, filename: str | Path = None, ) -> list[StockCandle] | list[StockCandlesHumanReadable] | dict | str | MarketDataClientErrorResult ``` Fetches historical candles (OHLCV) data for a stock symbol. Supports various timeframes (minutely, hourly, daily, weekly, monthly, yearly) and automatically handles large date ranges by splitting them into year-long chunks and fetching them concurrently. The `symbol` parameter can be passed as the first positional argument or as a keyword argument. All other parameters must be keyword-only. ### Parameters - `symbol` (str) The stock symbol for which to fetch candles data. - `resolution` (str, optional) The granularity of the candle data (e.g., "1m", "5m", "1H", "4H", "1D", "1W", "1M", "1Y"). Defaults to `"D"` (daily). - `from_date` (str | datetime.datetime, optional) Start date for the date range. - `to_date` (str | datetime.datetime, optional) End date for the date range. - `date` (str | datetime.datetime, optional) Specific date for the candles data. - `countback` (int, optional) Number of candles to return, counting backwards from the `to_date`. - `adjust_splits` (bool, optional) Whether to adjust for stock splits. Uses API alias `adjustsplits`. - `extended` (bool, optional) Whether to include extended hours data. - `exchange` (str, optional) Filter by exchange. - `output_format` ([OutputFormat](https://www.marketdata.app/docs/sdk/py/settings#output-format), optional) The format of the returned data. Defaults to `OutputFormat.DATAFRAME`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `date_format` ([DateFormat](https://www.marketdata.app/docs/sdk/py/settings#date-format), optional) The date format to use in the response. Defaults to `DateFormat.UNIX`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`columns`](https://www.marketdata.app/docs/sdk/py/settings#columns) (optional) Specify which columns to include in the response. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`add_headers`](https://www.marketdata.app/docs/sdk/py/settings#headers) (optional) Whether to include headers in the response. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`use_human_readable`](https://www.marketdata.app/docs/sdk/py/settings#human-readable) (optional) Whether to use human-readable format for values. Only applies when `output_format=OutputFormat.INTERNAL`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `mode` ([Mode](https://www.marketdata.app/docs/sdk/py/settings#data-mode), optional) The data mode to use. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `filename` (str | Path, optional) File path for CSV output (only used with `output_format=OutputFormat.CSV`). ### Returns - `list[StockCandle]` | `list[StockCandlesHumanReadable]` | `dict` | `str` | `MarketDataClientErrorResult` The candles data in the requested format, or a `MarketDataClientErrorResult` if an error occurred. ### Notes - For intraday resolutions (minutely/hourly), large date ranges are automatically split into year-long chunks and fetched concurrently (up to 50 concurrent requests by default). - When using `OutputFormat.DATAFRAME`, the DataFrame is indexed by the timestamp column (`t` or `Date`). - When using `OutputFormat.INTERNAL`, timestamps are automatically converted to `datetime.datetime` objects. ### Example (DataFrame) ```python from marketdata import MarketDataClient client = MarketDataClient() # Get stock candles as DataFrame (default) # symbol can be passed positionally or as keyword df = client.stocks.candles("AAPL", resolution="4H", from_date="2023-01-01", to_date="2023-01-04") # or df = client.stocks.candles(symbol="AAPL", resolution="4H", from_date="2023-01-01", to_date="2023-01-04") print(df) ``` **Output** ``` open high low close volume t 2023-01-03 09:30:00 130.28 130.90 124.19 124.65 64192007 2023-01-03 13:30:00 124.67 125.42 124.17 125.05 30727802 2023-01-04 09:30:00 126.89 128.66 125.08 127.26 49096197 2023-01-04 13:30:00 127.26 127.87 125.28 126.38 28870578 ``` ### Example (Internal) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get stock candles as internal objects candles = client.stocks.candles( "AAPL", resolution="4H", from_date="2023-01-01", to_date="2023-01-04", output_format=OutputFormat.INTERNAL ) # Access individual candle properties for candle in candles: print(f"Time: {candle.t}") print(f"Open: {candle.o}") print(f"High: {candle.h}") print(f"Low: {candle.l}") print(f"Close: {candle.c}") print(f"Volume: {candle.v}") ``` ### Example (JSON) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get stock candles as JSON candles = client.stocks.candles( "AAPL", resolution="4H", from_date="2023-01-01", to_date="2023-01-04", output_format=OutputFormat.JSON ) print(candles) ``` ### Example (CSV) ```python from marketdata import MarketDataClient, OutputFormat from pathlib import Path client = MarketDataClient() # Get stock candles as CSV csv_file = client.stocks.candles( "AAPL", resolution="4H", from_date="2023-01-01", to_date="2023-01-04", output_format=OutputFormat.CSV, filename=Path("candles.csv") ) print(f"CSV file saved to: {csv_file}") ``` ### Example (Human Readable) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get stock candles in human-readable format candles = client.stocks.candles( "AAPL", resolution="4H", from_date="2023-01-01", to_date="2023-01-04", output_format=OutputFormat.INTERNAL, use_human_readable=True ) # Access individual candle properties for candle in candles: print(f"Date: {candle.Date}") print(f"Open: {candle.Open}") print(f"High: {candle.High}") print(f"Low: {candle.Low}") print(f"Close: {candle.Close}") print(f"Volume: {candle.Volume}") ``` ## StockCandle ```python @dataclass class StockCandle: t: datetime.datetime o: float h: float l: float c: float v: int ``` StockCandle represents a single stock candle (OHLCV data), encapsulating price and volume information for a specific time period. ### Properties - `t` (datetime.datetime): The timestamp for the candle (automatically converted from Unix timestamp). - `o` (float): The opening price. - `h` (float): The highest price. - `l` (float): The lowest price. - `c` (float): The closing price. - `v` (int): The trading volume. ### Notes - The `t` field is automatically converted to a `datetime.datetime` object from a Unix timestamp. ## StockCandlesHumanReadable ```python @dataclass class StockCandlesHumanReadable: Date: datetime.datetime Open: float High: float Low: float Close: float Volume: int ``` StockCandlesHumanReadable represents a stock candle in human-readable format with capitalized field names and formatted values. ### Properties - `Date` (datetime.datetime): The timestamp for the candle (automatically converted from Unix timestamp). - `Open` (float): The opening price. - `High` (float): The highest price. - `Low` (float): The lowest price. - `Close` (float): The closing price. - `Volume` (int): The trading volume. ### Notes - The `Date` field is automatically converted to a `datetime.datetime` object from a Unix timestamp. - Field names use capitalized format (e.g., `Open` instead of `o`, `Date` instead of `t`). --- # Earnings (Python SDK) Source: https://www.marketdata.app/docs/sdk/py/stocks/earnings/index.md Retrieve earnings data for any supported stock symbol. ## Making Requests Use the `earnings()` method on the `stocks` resource to fetch earnings data. The method supports multiple output formats: | Output Format | Return Type | Description | |---------------|-------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------| | **DATAFRAME** | `pandas.DataFrame` or `polars.DataFrame` | Returns a DataFrame with earnings data indexed by symbol (default). | | **INTERNAL** | `StockEarnings` or `StockEarningsHumanReadable` | Returns a StockEarnings object. When `use_human_readable=True`, returns a StockEarningsHumanReadable object with capitalized field names. | | **JSON** | `dict` | Returns the raw JSON response as a dictionary. | | **CSV** | `str` | Writes CSV data to file and returns the filename string. | ## earnings ```python def earnings( symbol: str, *, report: str = None, date: str | datetime.datetime = None, from_date: str | datetime.datetime = None, to_date: str | datetime.datetime = None, countback: int = None, output_format: OutputFormat = OutputFormat.DATAFRAME, date_format: DateFormat = None, columns: list[str] = None, add_headers: bool = None, use_human_readable: bool = False, mode: Mode = None, filename: str | Path = None, ) -> StockEarnings | StockEarningsHumanReadable | dict | str | MarketDataClientErrorResult ``` Fetches earnings data for a stock symbol. The `symbol` parameter can be passed as the first positional argument or as a keyword argument. All other parameters must be keyword-only. ### Parameters - `symbol` (str) The stock symbol for which to fetch earnings data. - `report` (str, optional) Filter by report type (e.g., "annual", "quarterly"). - `date` (str | datetime.datetime, optional) Specific date for the earnings data. - `from_date` (str | datetime.datetime, optional) Start date for the date range. - `to_date` (str | datetime.datetime, optional) End date for the date range. - `countback` (int, optional) Number of earnings reports to return, counting backwards from the `to_date`. - `output_format` ([OutputFormat](https://www.marketdata.app/docs/sdk/py/settings#output-format), optional) The format of the returned data. Defaults to `OutputFormat.DATAFRAME`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `date_format` ([DateFormat](https://www.marketdata.app/docs/sdk/py/settings#date-format), optional) The date format to use in the response. Defaults to `DateFormat.UNIX`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`columns`](https://www.marketdata.app/docs/sdk/py/settings#columns) (optional) Specify which columns to include in the response. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`add_headers`](https://www.marketdata.app/docs/sdk/py/settings#headers) (optional) Whether to include headers in the response. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`use_human_readable`](https://www.marketdata.app/docs/sdk/py/settings#human-readable) (optional) Whether to use human-readable format for values. Only applies when `output_format=OutputFormat.INTERNAL`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `mode` ([Mode](https://www.marketdata.app/docs/sdk/py/settings#data-mode), optional) The data mode to use. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `filename` (str | Path, optional) File path for CSV output (only used with `output_format=OutputFormat.CSV`). ### Returns - `StockEarnings` | `StockEarningsHumanReadable` | `dict` | `str` | `MarketDataClientErrorResult` The earnings data in the requested format, or a `MarketDataClientErrorResult` if an error occurred. ### Example (DataFrame) ```python from marketdata import MarketDataClient client = MarketDataClient() # Get stock earnings as DataFrame (default) # symbol can be passed positionally or as keyword df = client.stocks.earnings("AAPL") # or df = client.stocks.earnings(symbol="AAPL") print(df) ``` ### Example (Internal) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get stock earnings as internal object earnings = client.stocks.earnings("AAPL", output_format=OutputFormat.INTERNAL) # Access earnings properties print(f"Symbol: {earnings.symbol}") print(f"Fiscal Year: {earnings.fiscalYear}") print(f"Fiscal Quarter: {earnings.fiscalQuarter}") print(f"Reported EPS: {earnings.reportedEPS}") print(f"Estimated EPS: {earnings.estimatedEPS}") ``` ### Example (JSON) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get stock earnings as JSON earnings = client.stocks.earnings("AAPL", output_format=OutputFormat.JSON) print(earnings) ``` ### Example (CSV) ```python from marketdata import MarketDataClient, OutputFormat from pathlib import Path client = MarketDataClient() # Get stock earnings as CSV csv_file = client.stocks.earnings( "AAPL", output_format=OutputFormat.CSV, filename=Path("earnings.csv") ) print(f"CSV file saved to: {csv_file}") ``` ### Example (Human Readable) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get stock earnings in human-readable format earnings = client.stocks.earnings( "AAPL", output_format=OutputFormat.INTERNAL, use_human_readable=True ) # Access earnings properties print(f"Symbol: {earnings.Symbol}") print(f"Fiscal Year: {earnings.Fiscal_Year}") print(f"Fiscal Quarter: {earnings.Fiscal_Quarter}") print(f"Reported EPS: {earnings.Reported_EPS}") print(f"Estimated EPS: {earnings.Estimated_EPS}") print(f"Surprise EPS: {earnings.Surprise_EPS}") ``` ## StockEarnings ```python @dataclass class StockEarnings: s: str symbol: list[str] fiscalYear: list[int] fiscalQuarter: list[int] date: list[datetime.datetime] reportDate: list[datetime.datetime] reportTime: list[str] currency: list[str] reportedEPS: list[float] estimatedEPS: list[float] surpriseEPS: list[float] surpriseEPSpct: list[float] updated: list[datetime.datetime] ``` StockEarnings represents earnings data for a stock, encapsulating fiscal year, quarter, and EPS information. All lists have the same length, allowing you to access earnings data by index. ### Properties - `s` (str): Status indicator ("ok" for successful responses). - `symbol` (list[str]): List of stock symbols. - `fiscalYear` (list[int]): List of fiscal years. - `fiscalQuarter` (list[int]): List of fiscal quarters. - `date` (list[datetime.datetime]): List of earnings dates (automatically converted from timestamps). - `reportDate` (list[datetime.datetime]): List of report dates (automatically converted from timestamps). - `reportTime` (list[str]): List of report times. - `currency` (list[str]): List of currencies. - `reportedEPS` (list[float]): List of reported earnings per share. - `estimatedEPS` (list[float]): List of estimated earnings per share. - `surpriseEPS` (list[float]): List of surprise earnings per share (difference between reported and estimated). - `surpriseEPSpct` (list[float]): List of surprise EPS percentages. - `updated` (list[datetime.datetime]): List of last update timestamps (automatically converted from timestamps). ## StockEarningsHumanReadable ```python @dataclass class StockEarningsHumanReadable: Symbol: list[str] Fiscal_Year: list[int] Fiscal_Quarter: list[int] Date: list[datetime.datetime] Report_Date: list[datetime.datetime] Report_Time: list[str] Currency: list[str] Reported_EPS: list[float] Estimated_EPS: list[float] Surprise_EPS: list[float] Surprise_EPS_Percent: list[float] Updated: list[datetime.datetime] ``` StockEarningsHumanReadable represents earnings data in human-readable format with capitalized field names and formatted values. All lists have the same length, allowing you to access earnings data by index. ### Properties - `Symbol` (list[str]): List of stock symbols. - `Fiscal_Year` (list[int]): List of fiscal years. - `Fiscal_Quarter` (list[int]): List of fiscal quarters. - `Date` (list[datetime.datetime]): List of earnings dates (automatically converted from timestamps). - `Report_Date` (list[datetime.datetime]): List of report dates (automatically converted from timestamps). - `Report_Time` (list[str]): List of report times. - `Currency` (list[str]): List of currencies. - `Reported_EPS` (list[float]): List of reported earnings per share. - `Estimated_EPS` (list[float]): List of estimated earnings per share. - `Surprise_EPS` (list[float]): List of surprise earnings per share (difference between reported and estimated). - `Surprise_EPS_Percent` (list[float]): List of surprise EPS percentages. - `Updated` (list[datetime.datetime]): List of last update timestamps (automatically converted from timestamps). ### Notes - Field names use capitalized format with underscores (e.g., `Fiscal_Year` instead of `fiscalYear`, `Reported_EPS` instead of `reportedEPS`). --- # News (Python SDK) Source: https://www.marketdata.app/docs/sdk/py/stocks/news/index.md Retrieve news articles for any supported stock symbol. ## Making Requests Use the `news()` method on the `stocks` resource to fetch news articles. The method supports multiple output formats: | Output Format | Return Type | Description | |---------------|-----------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------| | **DATAFRAME** | `pandas.DataFrame` or `polars.DataFrame` | Returns a DataFrame with news articles indexed by symbol (default). | | **INTERNAL** | `list[StockNews]` or `list[StockNewsHumanReadable]` | Returns a list of StockNews objects. When `use_human_readable=True`, returns a list of StockNewsHumanReadable objects with capitalized field names. | | **JSON** | `dict` | Returns the raw JSON response as a dictionary. | | **CSV** | `str` | Writes CSV data to file and returns the filename string. | ## news ```python def news( symbol: str, *, date: str | datetime.datetime = None, from_date: str | datetime.datetime = None, to_date: str | datetime.datetime = None, countback: int = None, output_format: OutputFormat = OutputFormat.DATAFRAME, date_format: DateFormat = None, columns: list[str] = None, add_headers: bool = None, use_human_readable: bool = False, mode: Mode = None, filename: str | Path = None, ) -> list[StockNews] | list[StockNewsHumanReadable] | dict | str | MarketDataClientErrorResult ``` Fetches news articles for a stock symbol. The `symbol` parameter can be passed as the first positional argument or as a keyword argument. All other parameters must be keyword-only. ### Parameters - `symbol` (str) The stock symbol for which to fetch news articles. - `date` (str | datetime.datetime, optional) Specific date for the news articles. - `from_date` (str | datetime.datetime, optional) Start date for the date range. - `to_date` (str | datetime.datetime, optional) End date for the date range. - `countback` (int, optional) Number of news articles to return, counting backwards from the `to_date`. - `output_format` ([OutputFormat](https://www.marketdata.app/docs/sdk/py/settings#output-format), optional) The format of the returned data. Defaults to `OutputFormat.DATAFRAME`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `date_format` ([DateFormat](https://www.marketdata.app/docs/sdk/py/settings#date-format), optional) The date format to use in the response. Defaults to `DateFormat.UNIX`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`columns`](https://www.marketdata.app/docs/sdk/py/settings#columns) (optional) Specify which columns to include in the response. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`add_headers`](https://www.marketdata.app/docs/sdk/py/settings#headers) (optional) Whether to include headers in the response. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`use_human_readable`](https://www.marketdata.app/docs/sdk/py/settings#human-readable) (optional) Whether to use human-readable format for values. Only applies when `output_format=OutputFormat.INTERNAL`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `mode` ([Mode](https://www.marketdata.app/docs/sdk/py/settings#data-mode), optional) The data mode to use. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `filename` (str | Path, optional) File path for CSV output (only used with `output_format=OutputFormat.CSV`). ### Returns - `list[StockNews]` | `list[StockNewsHumanReadable]` | `dict` | `str` | `MarketDataClientErrorResult` The news articles in the requested format, or a `MarketDataClientErrorResult` if an error occurred. ### Example (DataFrame) ```python from marketdata import MarketDataClient client = MarketDataClient() # Get stock news as DataFrame (default) # symbol can be passed positionally or as keyword df = client.stocks.news("AAPL") # or df = client.stocks.news(symbol="AAPL") print(df) ``` ### Example (Internal) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get stock news as internal objects news_list = client.stocks.news("AAPL", output_format=OutputFormat.INTERNAL) # Access individual news article properties for news in news_list: print(f"Symbol: {news.symbol}") print(f"Headline: {news.headline}") print(f"Source: {news.source}") print(f"Publication Date: {news.publicationDate}") print(f"Updated: {news.updated}") ``` ### Example (JSON) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get stock news as JSON news = client.stocks.news("AAPL", output_format=OutputFormat.JSON) print(news) ``` ### Example (CSV) ```python from marketdata import MarketDataClient, OutputFormat from pathlib import Path client = MarketDataClient() # Get stock news as CSV csv_file = client.stocks.news( "AAPL", output_format=OutputFormat.CSV, filename=Path("news.csv") ) print(f"CSV file saved to: {csv_file}") ``` ### Example (Human Readable) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get stock news in human-readable format news_list = client.stocks.news( "AAPL", output_format=OutputFormat.INTERNAL, use_human_readable=True ) # Access individual news article properties for news in news_list: print(f"Symbol: {news.Symbol}") print(f"Headline: {news.headline}") print(f"Source: {news.source}") print(f"Publication Date: {news.publicationDate}") print(f"Date: {news.Date}") ``` ## StockNews ```python @dataclass class StockNews: symbol: str headline: str content: str source: str publicationDate: datetime.datetime updated: datetime.datetime ``` StockNews represents a single news article for a stock, encapsulating headline, content, source, and publication information. ### Properties - `symbol` (str): The stock symbol. - `headline` (str): The news headline. - `content` (str): The news article content. - `source` (str): The news source. - `publicationDate` (datetime.datetime): The publication date (automatically converted from timestamp). - `updated` (datetime.datetime): The time when the news was last updated (automatically converted from timestamp). ### Notes - The `publicationDate` and `updated` fields are automatically converted to `datetime.datetime` objects from Unix timestamps. ## StockNewsHumanReadable ```python @dataclass class StockNewsHumanReadable: Symbol: str headline: str content: str source: str publicationDate: datetime.datetime Date: datetime.datetime ``` StockNewsHumanReadable represents a news article in human-readable format with capitalized field names and formatted values. ### Properties - `Symbol` (str): The stock symbol. - `headline` (str): The news headline. - `content` (str): The news article content. - `source` (str): The news source. - `publicationDate` (datetime.datetime): The publication date (automatically converted from timestamp). - `Date` (datetime.datetime): The time when the news was last updated (automatically converted from timestamp). ### Notes - The `publicationDate` and `Date` fields are automatically converted to `datetime.datetime` objects from Unix timestamps. - Field names use capitalized format with underscores where applicable (e.g., `Symbol` instead of `symbol`, `Date` instead of `updated`). --- # Prices (Python SDK) Source: https://www.marketdata.app/docs/sdk/py/stocks/prices/index.md Retrieve stock prices for any supported stock symbol. ## Making Requests Use the `prices()` method on the `stocks` resource to fetch stock prices. The method supports multiple output formats: | Output Format | Return Type | Description | |---------------|--------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------| | **DATAFRAME** | `pandas.DataFrame` or `polars.DataFrame` | Returns a DataFrame with prices indexed by symbol (default). | | **INTERNAL** | `list[StockPrice]` or `list[StockPricesHumanReadable]` | Returns a list of StockPrice objects. When `use_human_readable=True`, returns a list of StockPricesHumanReadable objects with capitalized field names. | | **JSON** | `dict` | Returns the raw JSON response as a dictionary. | | **CSV** | `str` | Writes CSV data to file and returns the filename string. | ## prices ```python def prices( symbols: list[str] | str, *, output_format: OutputFormat = OutputFormat.DATAFRAME, date_format: DateFormat = None, columns: list[str] = None, add_headers: bool = None, use_human_readable: bool = False, mode: Mode = None, filename: str | Path = None, ) -> list[StockPrice] | list[StockPricesHumanReadable] | dict | str | MarketDataClientErrorResult ``` Fetches stock prices for one or more symbols. The `symbols` parameter can be passed as the first positional argument or as a keyword argument. All other parameters must be keyword-only. ### Parameters - `symbols` (list[str] | str) A single symbol string or a list of symbol strings for which to fetch prices. - `output_format` ([OutputFormat](https://www.marketdata.app/docs/sdk/py/settings#output-format), optional) The format of the returned data. Defaults to `OutputFormat.DATAFRAME`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `date_format` ([DateFormat](https://www.marketdata.app/docs/sdk/py/settings#date-format), optional) The date format to use in the response. Defaults to `DateFormat.UNIX`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`columns`](https://www.marketdata.app/docs/sdk/py/settings#columns) (optional) Specify which columns to include in the response. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`add_headers`](https://www.marketdata.app/docs/sdk/py/settings#headers) (optional) Whether to include headers in the response. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`use_human_readable`](https://www.marketdata.app/docs/sdk/py/settings#human-readable) (optional) Whether to use human-readable format for values. Only applies when `output_format=OutputFormat.INTERNAL`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `mode` ([Mode](https://www.marketdata.app/docs/sdk/py/settings#data-mode), optional) The data mode to use. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `filename` (str | Path, optional) File path for CSV output (only used with `output_format=OutputFormat.CSV`). ### Returns - `list[StockPrice]` | `list[StockPricesHumanReadable]` | `dict` | `str` | `MarketDataClientErrorResult` The prices data in the requested format, or a `MarketDataClientErrorResult` if an error occurred. ### Example (DataFrame) ```python from marketdata import MarketDataClient client = MarketDataClient() # Get stock prices as DataFrame (default) # symbols can be passed positionally or as keyword df = client.stocks.prices("AAPL") # or df = client.stocks.prices(symbols="AAPL") # Get prices for multiple symbols df = client.stocks.prices(["AAPL", "MSFT"]) print(df) ``` ### Example (Internal) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get stock prices as internal objects prices = client.stocks.prices("AAPL", output_format=OutputFormat.INTERNAL) # Access individual price properties for price in prices: print(f"Symbol: {price.symbol}") print(f"Mid: {price.mid}") print(f"Change: {price.change}") print(f"Change Percent: {price.changepct}") print(f"Updated: {price.updated}") ``` ### Example (JSON) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get stock prices as JSON prices = client.stocks.prices(["AAPL", "MSFT"], output_format=OutputFormat.JSON) print(prices) ``` ### Example (CSV) ```python from marketdata import MarketDataClient, OutputFormat from pathlib import Path client = MarketDataClient() # Get stock prices as CSV csv_file = client.stocks.prices( ["AAPL", "MSFT"], output_format=OutputFormat.CSV, filename=Path("prices.csv") ) print(f"CSV file saved to: {csv_file}") ``` ### Example (Human Readable) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get stock prices in human-readable format prices = client.stocks.prices( "AAPL", output_format=OutputFormat.INTERNAL, use_human_readable=True ) for price in prices: print(f"Symbol: {price.Symbol}") print(f"Mid: {price.Mid}") print(f"Change Price: {price.Change_Price}") print(f"Change Percent: {price.Change_Percent}") print(f"Date: {price.Date}") ``` ## StockPrice ```python @dataclass class StockPrice: s: str symbol: str mid: float change: float changepct: float updated: datetime.datetime ``` StockPrice represents a single stock price, encapsulating various details such as prices and timestamps. ### Properties - `s` (str): Status indicator ("ok" for successful responses). - `symbol` (str): The stock symbol. - `mid` (float): The mid price calculated between the ask and bid prices. - `change` (float): The price change. - `changepct` (float): The percentage change in price. - `updated` (datetime.datetime): The time when the price was last updated (automatically converted from timestamp). ### Notes - The `updated` field is automatically converted to a `datetime.datetime` object from a Unix timestamp. ## StockPricesHumanReadable ```python @dataclass class StockPricesHumanReadable: Symbol: str Mid: float Change_Price: float Change_Percent: float Date: datetime.datetime ``` StockPricesHumanReadable represents a stock price in human-readable format with capitalized field names and formatted values. ### Properties - `Symbol` (str): The stock symbol. - `Mid` (float): The mid price calculated between the ask and bid prices. - `Change_Price` (float): The price change. - `Change_Percent` (float): The percentage change in price. - `Date` (datetime.datetime): The time when the price was last updated (automatically converted from timestamp). ### Notes - The `Date` field is automatically converted to a `datetime.datetime` object from a Unix timestamp. - Field names use capitalized format with underscores (e.g., `Change_Price` instead of `change`). --- # Stock Quotes (Python SDK) Source: https://www.marketdata.app/docs/sdk/py/stocks/quotes/index.md Retrieve live quotes for any supported stock symbol. ## Making Requests Use the `quotes()` method on the `stocks` resource to fetch stock quotes. The method supports multiple output formats: | Output Format | Return Type | Description | |---------------|--------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------| | **DATAFRAME** | `pandas.DataFrame` or `polars.DataFrame` | Returns a DataFrame with quotes indexed by symbol (default). | | **INTERNAL** | `list[StockQuote]` or `list[StockQuotesHumanReadable]` | Returns a list of StockQuote objects. When `use_human_readable=True`, returns a list of StockQuotesHumanReadable objects with capitalized field names. | | **JSON** | `dict` | Returns the raw JSON response as a dictionary. | | **CSV** | `str` | Writes CSV data to file and returns the filename string. | ## quotes ```python def quotes( symbols: list[str] | str, *, use_52_week: bool = None, extended: bool = None, output_format: OutputFormat = OutputFormat.DATAFRAME, date_format: DateFormat = None, columns: list[str] = None, add_headers: bool = None, use_human_readable: bool = False, mode: Mode = None, filename: str | Path = None, ) -> list[StockQuote] | list[StockQuotesHumanReadable] | dict | str | MarketDataClientErrorResult ``` Fetches stock quotes for one or more symbols. The `symbols` parameter can be passed as the first positional argument or as a keyword argument. All other parameters must be keyword-only. ### Parameters - `symbols` (list[str] | str) A single symbol string or a list of symbol strings for which to fetch quotes. - `use_52_week` (bool, optional) Whether to use the 52 week high and low. Uses API alias `52week`. - `extended` (bool, optional) Whether to use the extended quotes. - `output_format` ([OutputFormat](https://www.marketdata.app/docs/sdk/py/settings#output-format), optional) The format of the returned data. Defaults to `OutputFormat.DATAFRAME`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `date_format` ([DateFormat](https://www.marketdata.app/docs/sdk/py/settings#date-format), optional) The date format to use in the response. Defaults to `DateFormat.UNIX`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`columns`](https://www.marketdata.app/docs/sdk/py/settings#columns) (optional) Specify which columns to include in the response. If not provided, all available columns are returned. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`add_headers`](https://www.marketdata.app/docs/sdk/py/settings#headers) (optional) Whether to include headers in the response. Uses API alias `headers`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - [`use_human_readable`](https://www.marketdata.app/docs/sdk/py/settings#human-readable) (optional) Whether to use human-readable format for values. Uses API alias `human`. Only applies when `output_format=OutputFormat.INTERNAL`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `mode` ([Mode](https://www.marketdata.app/docs/sdk/py/settings#data-mode), optional) The data mode to use. Available options: `Mode.LIVE`, `Mode.CACHED`, `Mode.DELAYED`. See [Settings](https://www.marketdata.app/docs/sdk/py/settings) for details. - `filename` (str | Path, optional) File path for CSV output (only used with `output_format=OutputFormat.CSV`). Must end with `.csv`. Directory must exist. File must not already exist. If not provided, a timestamped file is created in `output/` directory. ### Returns - `list[StockQuote]` | `list[StockQuotesHumanReadable]` | `dict` | `str` | `MarketDataClientErrorResult` The quotes data in the requested format, or a `MarketDataClientErrorResult` if an error occurred. ### Notes - When using `OutputFormat.DATAFRAME`, the DataFrame is indexed by the `symbol` column. - When using `OutputFormat.INTERNAL`, timestamps are automatically converted to `datetime.datetime` objects. - When using `OutputFormat.CSV`, the method writes to a file and returns the filename string. ### Example (DataFrame) ```python from marketdata import MarketDataClient client = MarketDataClient() # Get stock quotes as DataFrame (default) # symbols can be passed positionally or as keyword df = client.stocks.quotes("AAPL") # or df = client.stocks.quotes(symbols="AAPL") # Get quotes for multiple symbols df = client.stocks.quotes(["AAPL", "MSFT"]) print(df) ``` **Output** ``` ask askSize bid bidSize mid last change changepct volume updated symbol AAPL 278.02 100 277.97 100 277.995 278.0188 -0.0112 0.0 4964676 2025-01-13 16:21:46 ``` ### Example (Internal) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get stock quotes as internal objects quotes = client.stocks.quotes("AAPL", output_format=OutputFormat.INTERNAL) # Access individual quote properties for quote in quotes: print(f"Symbol: {quote.symbol}") print(f"Ask: {quote.ask}") print(f"Bid: {quote.bid}") print(f"Mid: {quote.mid}") print(f"Last: {quote.last}") print(f"Change: {quote.change}") print(f"Change Percent: {quote.change_percent}") print(f"Volume: {quote.volume}") print(f"Updated: {quote.updated}") ``` **Output** ``` Symbol: AAPL Ask: 278.02 Bid: 277.97 Mid: 277.995 Last: 278.0188 Change: -0.0112 Change Percent: 0.0 Volume: 4964676 Updated: 2025-01-13 16:21:46 ``` ### Example (JSON) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get stock quotes as JSON quotes = client.stocks.quotes(["AAPL", "MSFT"], output_format=OutputFormat.JSON) print(quotes) ``` **Output** ```json { "s": "ok", "symbol": ["AAPL", "MSFT"], "ask": [278.02, 479.45], "askSize": [100, 40], "bid": [277.97, 479.37], "bidSize": [100, 40], "mid": [277.995, 479.41], "last": [278.0188, 479.42], "change": [-0.0112, -4.05], "changepct": [0.0, -0.0084], "volume": [4964676, 3581398], "updated": [1765552906, 1765552906] } ``` ### Example (CSV) ```python from marketdata import MarketDataClient, OutputFormat from pathlib import Path client = MarketDataClient() # Get stock quotes as CSV csv_file = client.stocks.quotes( ["AAPL", "MSFT"], output_format=OutputFormat.CSV, filename=Path("quotes.csv") ) print(f"CSV file saved to: {csv_file}") ``` **Output** ``` CSV file saved to: quotes.csv ``` ### Example (Human Readable) ```python from marketdata import MarketDataClient, OutputFormat client = MarketDataClient() # Get stock quotes in human-readable format quotes = client.stocks.quotes( "AAPL", output_format=OutputFormat.INTERNAL, use_human_readable=True ) for quote in quotes: print(f"Symbol: {quote.Symbol}") print(f"Ask: {quote.Ask}") print(f"Bid: {quote.Bid}") print(f"Change Price: {quote.Change_Price}") print(f"Change Percent: {quote.Change_Percent}") ``` **Output** ``` Symbol: AAPL Ask: 278.55 Bid: 278.54 Change Price: 0.51 Change Percent: 0.0018 ``` ## StockQuote ```python @dataclass class StockQuote: symbol: str ask: float askSize: int bid: float bidSize: int mid: float last: float change: float changepct: float volume: int updated: datetime.datetime ``` StockQuote represents a single stock quote, encapsulating various details such as prices, volumes, and timestamps. ### Properties - `symbol` (str): The stock symbol. - `ask` (float): The asking price for the stock. - `askSize` (int): The size (quantity) of the ask. - `bid` (float): The bidding price for the stock. - `bidSize` (int): The size (quantity) of the bid. - `mid` (float): The mid price calculated between the ask and bid prices. - `last` (float): The last traded price for the stock. - `change` (float): The price change. - `changepct` (float): The percentage change in price. - `change_percent` (float): Property that returns `changepct` for convenience. - `volume` (int): The trading volume for the stock. - `updated` (datetime.datetime): The time when the quote was last updated (automatically converted from timestamp). ### Notes - The `updated` field is automatically converted to a `datetime.datetime` object from a Unix timestamp. - The `change_percent` property provides convenient access to the `changepct` field. ## StockQuotesHumanReadable ```python @dataclass class StockQuotesHumanReadable: Symbol: str Ask: float Ask_Size: int Bid: float Bid_Size: int Mid: float Last: float Change_Price: float Change_Percent: float Volume: int Date: datetime.datetime ``` StockQuotesHumanReadable represents a stock quote in human-readable format with capitalized field names and formatted values. ### Properties - `Symbol` (str): The stock symbol. - `Ask` (float): The asking price for the stock. - `Ask_Size` (int): The size (quantity) of the ask. - `Bid` (float): The bidding price for the stock. - `Bid_Size` (int): The size (quantity) of the bid. - `Mid` (float): The mid price calculated between the ask and bid prices. - `Last` (float): The last traded price for the stock. - `Change_Price` (float): The price change. - `Change_Percent` (float): The percentage change in price. - `Volume` (int): The trading volume for the stock. - `Date` (datetime.datetime): The time when the quote was last updated (automatically converted from timestamp). ### Notes - The `Date` field is automatically converted to a `datetime.datetime` object from a Unix timestamp. - Field names use capitalized format with underscores (e.g., `Ask_Size` instead of `askSize`). --- # Google Sheets Add-on Documentation Source: https://www.marketdata.app/docs/sheets/index.md Welcome to Market Data’s Google Sheets Add-On documentation. --- # Authentication (Sheets) Source: https://www.marketdata.app/docs/sheets/authentication/index.md Market Data's Sheets Add-on uses **automatic Google Authentication** to authorize requests to Market Data's servers. This means that once you login to Sheets using your Google account, the Market Data Add-on will automatically link to your Market Data account _as long as you signed-up for a Market Data account using the same Google e-mail address_. > [!WARNING] > If you registered for Market Data with an e-mail address different from the one you use to log in to Google, you must update your Market Data account's e-mail to match your Google Sheets login e-mail. ## Changing Your Market Data Account E-mail Address 1. **[Login](https://dashboard.marketdata.app/marketdata/profile)** to your Market Data account. 2. This will open your account **Profile**. 3. Click on the **E-Your E-Mail Address** field. 4. Enter the e-mail address you use to login to Google Sheets. 5. Click on the **Save Profile** button. 6. Confirm the change by clicking on the confirmation link in the e-mail you receive. ## Troubleshooting Almost all issues related to authentication can be solved by changing your Market Data account's e-mail address to the same e-mail address you use to login to Google Sheets. If you continue to experience issues, please open a support ticket at our [helpdesk](https://dashboard.marketdata.app/marketdata/helpdesk). ### Multiple Google Accounts If you have multiple Google accounts, you will need to make sure that you link your Market Data account with the same Google account you use to login to Google Sheets. --- # Automatic Refreshing Source: https://www.marketdata.app/docs/sheets/automatic-refresh/index.md The WebSocket protocol is the technology used to deliver streaming quotes, such as the ones provided by your broker platform. Unfortunately, Google Workspace Add-ons do not yet offer support for WebSockets. This is why no Google Sheets Add-on has been able to deliver a product that supports streaming quotes. Please be aware that this workaround is not officially supported by Market Data. ## Refresh Button Market Data has provided a refresh button in the sidebar to allow customers to refresh whenever needed. We understand some customers would like to use streaming quotes, but that is impossible due to Google Sheets limitations. ## Workaround There is a workaround that allows your formulas to update every 2-minutes. You can embed the result of the **GOOGLEFINANCE** formula as a column attribute in any of our formulas. Please be aware that during the standard session, this will consume 195 credits for each formula you prepare in this way. > [!WARNING] > Google Sheets has a hard limit of 20,000 [urlfetch](https://www.marketdata.app/docs/sheets/troubleshooting/urlfetch) requests per day. Even if you are on a Trader plan and have 100,000 credits, Google will not allow you to make more than 20,000 requests. ### Example Formulas Place `=GOOGLEFINANCE("AAPL")` in a cell somewhere in your workbook. For this example, we'll assume it is in cell A1. ```excel-formula =OPTIONDATA("AAPL271217C00250000","all,"&A1) =STOCKDATA("AAPL","all,"&A1) ``` #### Why Is GOOGLEFINANCE Needed? The GOOGLEFINANCE formula is a special formula that updates once every two minutes. By using it as a parameter in a Market Data formula, you force the Market Data formula to refresh itself every two minutes without the need to press the refresh button manually. #### Why Can't Market Data Formulas Do This Automatically? Google Sheets places strict limits on the ability of third party developers such as Market Data. Google does not give us the ability to make your formulas refresh automatically. However, by using this workaround, we can trigger a refresh every 2-minutes as GOOGLEFINANCE updates. --- # Dates and Times (Sheets) Source: https://www.marketdata.app/docs/sheets/dates-and-times/index.md All Market Data formulas support advanced date-handling features to allow you to work with dates in a way that works best for your spreadsheet. Formulas will accept any of the following date formats: - **American Numeric Notation** Dates and times in MM/DD/YYYY format. For example, closing bell on Dec 30, 2020 for the NYSE would be: 12/30/2020 4:00 PM. - **Timestamp** An ISO 8601 timestamp in the format YYYY-MM-DD. For example, closing bell on Dec 30, 2020 for the NYSE would be: 2020-12-30 16:00:00. - **Unix** Dates and times in unix format (seconds after the unix epoch). For example, closing bell on Dec 30, 2020 for the NYSE would be: 1609362000. - **Spreadsheet** Dates and times in spreadsheet format (days after the Excel epoch). For example, closing bell on Dec 30, 2020 for the NYSE would be: 44195.66667 - **Relative Dates and Times** Keywords or key phrases that indicate specific days, relative to the current date. For example, "today" or "yesterday". - **Option Expiration Dates** Keyphrase that select specific dates that correspond with dates in the US option expiration calendar. ## Relative Dates and Times Relative dates allow Market Data formulas to continually modify the date sent to the formula based on the current date. We have a lot of relative date keywords supported already and quite a few more planned for the future, so keep an eye out on this section for continual improvements to this feature. - **Time-based Parameters** Time keyphrases let you select a specific time of day, relative to the current time. Time-based parameters are typically used to model intraday stock movements. - `now` The current time. Use this keyword to select the current open candle, for example. This is the same as the Google Sheets built-in formula `now()`. Please note that due to technical limitations by Google Sheets, `now()` cannot be embedded in or referenced by any Market Data formula. Use the `now` relative keyword as a replacement. - `-[number] minutes` Use negative minutes to specify a time in the past _n_ minutes before. When this is used alone, it is relative to the current time. When used in conjunction in `from` field (i.e. the starting date/time), it is relative to the `to` field (i.e. the ending date/time). For example, if the current time is 10:30 AM, but 10:00 AM is used in the `to` field and `-10 minutes` in the `from` field, then the starting time will be 9:50 AM. The query would return values from 9:50 AM to 10:00 AM. However, if the `to` field were to be omitted, then the same query would return data from 10:20 AM to 10:30 AM since `-10 minutes` would be relative to the current time of 10:30 AM. - `[number] minutes ago` The `minutes ago` keyword lets you select a relative time, _n_ minutes before the current time. For example, if the time is 10:00 AM then `30 minutes ago` would refer to 9:30 AM of the current day. - `-[number] hours` Use negative hours to specify a time in the past _n_ hours before. When this is used alone, it is relative to the current time. When used in conjunction in `from` field (i.e. the starting date/time), it is relative to the `to` field (i.e. the ending date/time). For example, if the current time is 10:30 AM, but 10:00 AM is used in the `to` field and `-1 hour` in the `from` field, then the starting time will be 9:00 AM. The query would return values from 9:00 AM to 10:00 AM. However, if the `to` field were to be omitted, then the same query would return data from 9:30 AM to 10:30 AM since `-1 hour` would be relative to the current time of 10:30 AM. - `[number] hours ago` The `hours ago` keyword lets you select a relative time, _n_ hours before the current time. For example, if the time is 4:00 PM then `4 hours ago` would refer to 12:00 PM of the current day. - **Daily Parameters** Daily keyphrases let you select a specific day, relative to the current day. - `today` Equivalent to today's date. This keyword can be used interchangeably with Sheets' built-in formula `today()`. - `yesterday` Yesterday's date. The same as Sheets formula `today()-1`. - `last session` Returns the date of the last closed market session. This keyphrase always gets the previous closed session, accounting for weekends, holidays, and half-days (which are counted as valid sessions). For example, on Monday during market hours, it would return Friday's date. After market close on Monday, it would return Monday's date. This allows you to always track the previous session without having to account for weekends, holidays, or other market closures. - `-[number] days` Use negative days to specify a time in the past _n_ days before. When this is used alone, it is relative to the current day. When used in conjunction in `from` field (i.e. the starting date), it is relative to the `to` field (i.e. the ending date). For example, if the current date is January 20, but January 10 is used in the `to` field and `-5 days` in the `from` field, then the starting day will be January 5. The query would return values from January 5 to January 10. However, if the `to` field were to be omitted, then the same query would return data from January 15 to January 20 since `-5 days` would be relative to the current date of January 20. - `[number] days ago` The `days ago` keyword lets you select a relative day, _n_ days before the current date. For example, if today is January 5, 2024, then using `2 days ago` would select the date January 3, 2024. - **Weekly Parameters** Weekly keyphrases let you select a day of the week in the current, previous, or following week. - `-[number] weeks` Use negative weeks to specify a date in the past _n_ weeks before. When this is used alone, it is relative to the current day. When used in conjunction in `from` field (i.e. the starting date), it is relative to the date in the `to` field (i.e. the ending date). For example, if the current date is October 15, 2023 but October 8 is used in the `to` field and `-1 week` in the `from` field, then the starting day will be October 2, 2023. The query would return values from October 2 to October 8. However, if the `to` field were to be omitted, then the same query would return data from October 9 to October 15 since `-5 days` would be relative to the current date of January 20. - `[number] weeks ago` The `weeks ago` keyword lets you select a relative week, _n_ weeks before the current date. For example, if today is January 1, 2024, then using `2 weeks ago` would select the date January 3, 2024. - **Monthly Dates** Monthly keyphrases let you select a specific day of a specific month. - `-[number] months` Use negative months to specify a date in the past _n_ months before. When this is used alone, it is relative to the current day. When used in conjunction in `from` field (i.e. the starting date), it is relative to the date in the `to` field (i.e. the ending date). For example, if the current date is October 15 but October 8 is used in the `to` field and `-1 month` in the `from` field, then the starting day will be September 8. The query would return values from September 8 to October 8. However, if the `to` field were to be omitted, then the same query would return data from September 15 to October since `-1 month` would be relative to the current date of October 15. - `[number] months ago` The months ago keyword lets you select a relative date, _n_ months before the current date. For example, if today is January 5, 2024, then using `3 months ago` would select the date October 5, 2023. - **Yearly Dates** Yearly keyphrases let you select a specific day of in the current, previous, or following year. - `-[number] years` Use negative years to specify a date in the past _n_ years before. When this is used alone, it is relative to the current day. When used in conjunction in `from` field (i.e. the starting date), it is relative to the date in the `to` field (i.e. the ending date). For example, if the current date is October 15, 2023 but October 8, 2023 is used in the `to` field and `-1 year` in the `from` field, then the starting day will be September 8, 2022. The query would return values from September 8, 2022 to October 8, 2023. However, if the `to` field were to be omitted, then the same query would return data from September 15, 2022 to October 15, 2023 since `-1 year` would be relative to the current date of October 15, 2023. - `[number] years ago` The years ago keyword lets you select a relative date, 365 days before the current date. For example, if today is January 5, 2024, then using `2 years ago` would select the date January 5, 2022. > [!CAUTION] > **[Coming Soon]** > > > The following relative date parameters are planned for the future and have not yet been implemented. - **Time-based Parameters** Time keyphrases let you select a specific time of day, relative to the current time. Time-based parameters are typically used to model intraday stock movements. - `at open`, `opening bell`, `market open` These keyphrases let you select the opening time for the market day. The phase is relative to each exchange's opening time. For example, if you were trading AAPL in the United States, using `at open` would set a time of 9:30 AM ET. - `at close`, `closing bell`, `market close` These keyphrases let you select the closing time for the market day. The phase is relative to each exchange's closing time. For example, if you were trading AAPL in the United States, using `at close` would set a time of 4:00 PM ET. - `[number] [minutes|hours] before [open|close]` These before keyword lets you select a relative time before market open or close. For example `30 minutes before close` would select the time 3:30 PM ET if you are trading a stock on a U.S. exchange. - `[number] [minutes|hours] after [open|close]` These after keyword lets you select a relative time after market open or close. For example `1 hour after open` would select the time 10:30 AM ET if you are trading a stock on a U.S. exchange. - **Weekly Parameters** Weekly keyphrases let you select a day of the week in the current, previous, or following week. - `this [day of the week]` Works the same way as specifying the day without adding _this_. The day in the _current_ week. For example, if today is Tuesday and the expression is `this Monday`, the date returned would be yesterday. If the expression were `this Wednesday` the date returned would be tomorrow. The word _this_ is optional. If it is omitted, the keyword will still return the date in the current week that corresponds with the day mentioned. - `last [day of the week]` The day in the _previous_ week. For example, if today is Tuesday and the expression used is `last Monday`, it would not refer to the Monday that occurred yesterday, but the Monday 8 days prior that occurred in the previous week. - `next [day of the week]` The day in the _following_ week. For example, if today is Monday and the expression is `next Tuesday` it would not refer to tomorrow, but the Tuesday that occurs 8 days from now. - **Monthly Dates** Monthly keyphrases let you select a specific day of a specific month. - `[ordinal number] of [the|this] month` - The nth day of the current month. For example, if today is September 10th and the phrase used is, `8th of this month` the date returned would be September 8. The keyphrase `of [the/this] month` is optional. Using a single ordinal number `8th` will also return the 8th of the current month. - `[ordinal number] of last month` - The nth day of the current month. For example, if today is December 15th and the phrase used is, `8th of last month` the date returned would be November 8. - `ordinal number] of next month` - The nth day of the following month. For example, if today is December 15th and the phrase used is, `8th of next month` the date returned would be January 8 of the following year. - `last day of [the|this|last|next] month` - Using the `last day of` keyword will always select the final day of the month. Since months can end on the 28th, 29th, 30th, or 31st, this keyword allows you to always select the final day of a month. For example: `last day of this month`, `last day of next month`. It can also be used to select the last day in February without needing to determine whether the current year is a leap year, `last day of february`. - `ordinal number] [day of the week] of [the|this|last|next] month` - Combine ordinal numbers and weekdays to specify a specific day of the week in the current, previous, or following month. For example, ``3nd Friday of last month``. - `last [day of the week] of [the|this|last|next] month` - Selects the last day of the week in a month relative to the current month. If the last Monday of the month is needed, instead of using the keyphrase `4th Monday of this month`, it is safer to use `last Monday of this month`, since months can have 4 or 5 Mondays, depending on length. - `last [day of the week] in [month` - Selects the last day of the week in a specific month. For example, Memorial Day could be selected by using the keyphrase `last Monday in May`. - **Yearly Dates** Yearly keyphrases let you select a specific day of in the current, previous, or following year. - `[month] [number]` A specific date in the current year. For example `February 18` would return February 18 of the current year. - `[month] [number] [this|last|next] year` A specific date in the current, previous, or following year. For example, if today was Dec 31, 2022, `February 18 next year` would return February 18, 2023. ## Option Expiration Dates Option expiration dates let you target the expiration dates for option contracts. Dates are based on the US option expirations calendar and are only meant for use with US markets. > [!CAUTION] > > Option date parameters are planned for the future and have not yet been implemented. > > --- > > Option-related keyphrases cannot be used to return expiration dates far in the future for options which have not yet been traded or for options in the past which have already expired. For example, if today is January 15, 2023, you couldn't use `November 2023's 1st weekly expiration` since weekly options for November would not exist yet. The formula will return a `No data` response if you try to request an expiration that does not exist, whether in the future or the past. - **Monthly Expirations** - Target a relative month or specific month's option expiration date. - `[month] [year] expiration` - The standard monthly option expiration date for [month] during [year]. This is useful for targeting the expiration date for a specific month. Although options normally expire the 3rd Friday, sometimes market holidays can modify this schedule. Using an option expiration keyphrase will ensure that you always obtain the exact date that options expire in a specific month. For example, if today was January 1, 2022, using `December expiration` or `December 2022 expiration` would both return _December 16, 2022_. - [year] is optional. If [month] is used without [year] the lookup is relative to the current date and expired options will not be returned. For example, if today is April 8, 2022, `January expiration` will return January 20, 2023 and not the options which expired in January of 2022. - `this|last|next] month's expiration` - Returns the monthly option expiration date for the current, previous, or following month relative to the current month. For example if today is October 5, 2022, and `next month's expiration` is used, the date returned would be _November 18, 2022_. > [!TIP] > > Not all underlying tickers offer weekly or quarterly options. Before building a spreadsheet that uses them, ensure that your underlying offers weekly or quarterly option contracts. - **Weekly Expirations** - Target a relative week or specific week's option expiration date. - `[this|last|next] week's expiration` - Returns the weekly option expiration date for the current, previous, or following week relative to the current week. For example if today is October 5, 2022, and `next week's expiration` is used, the date returned would be _October 14, 2022_. - `expiration in [number] weeks` - Returns closest expiration that will occur [number] weeks from today without taking into account the current week. For example, if today is August 1, 2022 the phrase `expiration in 6 weeks` would return September 16, 2022. - `[month] [year] [ordinal number] weekly expiration` - Returns the nth option expiration date for [month] during [year]. When both a month and year are combined, this can be used to lookup a weekly option date for an expired or unexpired option. For example, `March 2020's 2nd expiration` would return _March 14, 2020_. - **Quarterly Expirations** - Returns a quarterly expiration date for a relative date or specifically targeted date. - `[ordinal number] quarter's expiration` - Returns the quarterly option expiration date for the 1st, 2nd, 3rd, or 4th quarter in the current financial year. For example if today is March 1, 2022, and `4th quarter's expiration` is used, the date returned would be _December 30, 2022_. This will lookup both expired and unexpired options. - `[this|last|next] quarter's expiration` - Returns the quarterly option expiration date for the current, previous, or following quarter relative to the current date. For example if today is March 1, 2022, and `this quarter's expiration` is used, the date returned would be _March 31, 2022_. - `[expiration in [number] quarters` - Returns closest quarterly expiration that will occur [number] quarters from today without taking into account the current quarter. For example, if today is March 1, 2022 the phrase `expiration in 2 quarters` would return September 30, 2022. - `[year] [ordinal number] quarter expiration` - Returns the option expiration date for [nth] quarter during [year]. For example, `2020's 2nd quarter expiration` would return _June 30, 2020_. - **Specific Contract Expirations** - Target a specific date based on when a contract is first traded or when it expires. - `at expiration` - Returns the expiration date for the option contract. This must be used in the context of a specific option contract. For example, if you used at expiration with AAPL271217C00250000, the date returned would be December 17, 2027. - `first traded` - Returns the date when the contract was traded for the first time. This must be used in the context of a specific option contract. For example, if you used first traded with AAPL271217C00250000, the date returned would be March 13, 2025. --- # Market Formulas Source: https://www.marketdata.app/docs/sheets/markets/index.md --- # MARKETSTATUS Source: https://www.marketdata.app/docs/sheets/markets/marketstatus/index.md Get the past, present, or future status for a stock market for a single date or multiple dates. The formula will respond with "open" for trading days or "closed" for weekends or market holidays. ## Sample Usage ```excel-formula =MARKETSTATUS() =MARKETSTATUS("US","all","1/1/2022","12/31/2022") =MARKETSTATUS("US","status","yesterday") ``` ## Syntax ```excel-formula =MARKETSTATUS(country, [attributes], start date, end date) ``` - **country** _(OPTIONAL)_ The two-letter country code. Currently only the US is supported. - **[attributes]** _(OPTIONAL)_ Use one or more of the following attributes: - "date" – The date that the market was either open or closed. - "status" – The status of the market, either `open` or `closed`. - "all" – Returns all values. - **start date** _(OPTIONAL)_ The start date when fetching the market's status. If start date is specified, but end date is not, only the single day’s data is returned. - **end date** _(OPTIONAL)_ The end date when fetching the market's status, or the number of calendar days (not trading days) from the `start date` from which to return data. ## Notes - All parameters must be enclosed in quotation marks or be references to cells containing text. A possible exception is when end date is specified as a number of days. --- # Option Formulas Source: https://www.marketdata.app/docs/sheets/options/index.md --- # OPTIONCHAIN Source: https://www.marketdata.app/docs/sheets/options/optionchain/index.md Fetches a current option chain, or the end-of-day option chain of a past trading day, from Market Data. ## Video Tutorial ## Sample Usage ```excel-formula =OPTIONCHAIN("AAPL","all","1/17/2025") =OPTIONCHAIN("AAPL","side,strike,bid,ask,","1/17/2025") =OPTIONCHAIN("AAPL","all,noheaders","60 DTE","150,160) =OPTIONCHAIN("AAPL","all","60 DTE","100-200","calls") ``` ### Historical Chains ```excel-formula =OPTIONCHAIN("AAPL","strike,bid,ask","45 DTE","200-250","puts","2026-09-01") =OPTIONCHAIN("AAPL",,,,,"last session") =OPTIONCHAIN(A2,"strike,bid,ask","45 DTE","200-250","puts",B2) ``` ## Syntax ```excel-formula =OPTIONCHAIN(underlying symbol, [attributes], expiration date, [strikes], [filters], [as of]) ``` - **underlying symbol** _(REQUIRED)_ Use the stock, etf, or underlying index symbol. - **attributes** _(OPTIONAL "all" by default)_ Use one or more of the following attributes; seperate multiple attributes with commas: - "symbol" - The OCC option symbol. - "underlying" - The underlying symbol. - "expiration" - The option's expiration date. - "side" - Call or put. - "strike" - The option's strike price. - "first traded" - The date this option first began to trade. - "dte" - Days to expiration: the calendar days until the option expires, counted from today on a current quote and from the quote date on a historical one. - "date", "updated" - The date and time of the quote. - "bid" – The bid price of the option. - "bid size" – The quantity of contracts offered at the bid price. - "mid", "price", "mark" – The midpoint price of the option. - "ask" – The ask price of the option. - "ask size" – The quantity of contracts offered at the ask price. - "last" – The last price of the option. Depending on the liquidity of the symbol, the last traded price could have occurred one second ago or one day ago. - "open interest" – The number of open contracts. Open interest is published once per trading day, before the open, and does not change intraday. On a historical request it is the figure settled out of the previous session, so it lags every other field in the row by one session. - "volume" – The number of contracts negotiated during the day. - "in the money" – Will return TRUE if the option contract was in the money at the time of the quote or FALSE if the contract was not in the money at the time of the quote. - "intrinsic value" - The intrinsic value of the option, if any. - "extrinsic value" - The extrinsic or time value of the option. - "underlying price" – The price of the underlying security at the time of this option quote. - "iv", "implied volatility" - The implied volatility of the option. - "delta" - The delta of the option. - "gamma" - The gamma of the option. - "theta" - The theta of the option. - "vega" - The vega of the option. - "all" – Returns all values. - "noheaders" – Returns values without column headers. - **expiration date** - _(OPTIONAL)_ - The expiration date of the option chain. Options from this expiration will be returned. If no value is specified, the next monthly expiration will be used. - "date" - A single date in YYYY-MM-DD or MM/DD/YYYY format enclosed between quotes. - \[date cell reference] - A cell reference to a date in Sheets. - "dte _number_" - Days to expiry. Return the expiration date most closely matching the indicated number. For example, if today was Jan 1 and you used a DTE of 45, the expiration closest to Feb 15 would be selected. With an **as of** date, the days are counted from that date instead of today. - The expiration date is always the contract's expiration, never the quote date. To see a chain as it was on a past day, use **as of**. - **strikes** _(OPTIONAL)_ – Limit the strikes returned from the option chain formula to only the specified strikes or deltas. The formula will assume strikes for numbers greater than 1 and deltas for numbers less than 1 or when the word `delta` is placed before the number(s). - \[number] - A single number or a cell reference to a number. Example: `150` - \[decimal] - A decimal number will be interpreted as a delta value to be matched and the formula will return the strike that most closely matches the delta provided. Example: `.60` - \[range of numbers] - A range of strikes falling between two numbers. Example: `"100-200"` - \[range of decimals] - A range of deltas falling between two decimals. Example: `".50-.60"` - "delta \[number]" - Use a single number for delta or a cell reference to a number. Example: `"delta .60"` - \{array of numbers\} - Use a one column array containing each strike or a cell reference to the array. Example: `{1;2;3;4}` - \{array of decimals\} - Use a one column array containing each delta or a cell reference to the array. Example: `{.50;.60;.70;.80}` - "string of numbers" - Seperate multiple strikes with commas. Example: `"150,160,170"` - "string of decimals" - Seperate multiple deltas with commas. Example: `".50,.60.,70"` - "logical expression" - A logical expression. Example: `"<200 delta <.50"` - "limit \[number]" - Limit the output to \[number] strikes closest to the money. Example: `"limit 4"` - Deltas are only available for current chains. Selecting strikes by delta together with an **as of** date returns an error; use strikes instead. - **filters** _(OPTIONAL)_ – Use specific keywords to further filter the option chain. - "call", "calls" - Only return calls. - "put", "puts" - Only return puts. - "itm", "in the money" - Only return in the money options. - "otm", "out of the money" - Only return out of the money options. - "nonstandard", "non-standard" - Include non-standard contracts in the optionchain output. - **as of** _(OPTIONAL)_ – The quote date. Leave it out for the current chain, or give one past trading day to get that day's end-of-day chain. See [Working With Historical Chains](#working-with-historical-chains). - _(omitted)_ or "current" - The current chain, the same as a formula without this parameter. - "date" - A single date in YYYY-MM-DD or MM/DD/YYYY format enclosed between quotes. - \[date cell reference] - A cell reference to a date in Sheets. - "last session" - The most recent trading day whose end-of-day chain has been published. - "yesterday", "today" - The calendar day before today, or today. On a weekend or holiday these return an error, so "last session" is usually the better choice. - An empty cell or `""` returns an error, not the current chain. To switch a sheet between current and historical chains with one cell, write "current" in it. ## Usage Information ### Data Availability Without an **as of** date, the `OPTIONCHAIN` function fetches current option chain data. The type of data you receive depends on your user type and OPRA entitlement. This may include real-time data, 15-minute delayed data, or historical data, depending on the plan or access level. To get real-time options data, users need to sign the OPRA agreement. | User Type | OPRA Entitlement | Data Type | |------------------|------------------|------------------------| | Non-Professional | ✅ | Real-time | | Non-Professional | ❌ | 15-min delayed | | Professional | ✅ | 15-min delayed | | Professional | ❌ | Historical (1 day old) | | Unknown | Any | Historical (1 day old) | ### Working With Historical Chains With an **as of** date, `OPTIONCHAIN` returns the chain as it closed on that trading day. - Only end-of-day chains are available. There are no intraday snapshots, and only one date per formula. - The columns are the same as for a current chain. Implied volatility and the greeks (delta, gamma, theta, vega) are blank, and selecting strikes by delta is not available. - Open interest is the figure settled out of the previous session, as described under the "open interest" attribute. - A date in the future, a weekend or a market holiday returns an error. Today's chain returns a "not available yet" error until the end-of-day chain is published after the close. - A relative date such as "last session" is resolved when the formula calculates. It updates when the sheet is refreshed or a referenced cell changes, not on its own as days pass. - Historical data is subject to your plan's limits. [Learn more about plan limits](https://www.marketdata.app/docs/account/plan-limits). > [!NOTE] > **[What are entitlements?]** > > Entitlements are permissions granted by exchanges that allow access to their data. To get real-time options data, users need to sign the [OPRA agreement](https://www.marketdata.app/docs/account/entitlements). [Learn more about entitlements](https://www.marketdata.app/docs/account/entitlements). ## Notes - All parameters must be enclosed in quotation marks or be references to cells containing text. - Dates and times are returned in the same timezone of the exchange. --- # OPTIONDATA Source: https://www.marketdata.app/docs/sheets/options/optiondata/index.md Fetches current or historical option quotes from Market Data, for one option contract or many at once. ## Sample Usage ```excel-formula =OPTIONDATA("AAPL271217C00250000") =OPTIONDATA("AAPL271217C00250000", "bid") =OPTIONDATA("AAPL271217C00250000", "date,bid,ask", "9/1/2022", "1/31/2023") =OPTIONDATA("AAPL271217C00250000", "all", TODAY()-30, 30) ``` ### Multiple Contracts ```excel-formula =OPTIONDATA(A1:A500) =OPTIONDATA(A1:A500, "bid,ask,noheaders") =OPTIONDATA("AAPL271217C00250000,AAPL271217P00250000", "mid") =OPTIONDATA(A1:A500, "mid", "9/15/2026") =OPTIONDATA(A1:D50, "symbol,mid") ``` ## Syntax ```excel-formula =OPTIONDATA(option symbol, [attributes], start date, end date) ``` - **option symbol** _(REQUIRED)_ Use the current OCC option symbol format for the option symbol, even for historic options prior to the symbol format change in 2010. Several contracts can be fetched at once: pass a range of cells in a single row or a single column, several OCC symbols separated by commas, or a table of four columns with one contract per row. See [Working With Multiple Contracts](#working-with-multiple-contracts). - **attribute** _(OPTIONAL "price" by default)_ Use at least one of the following attributes; seperate multiple attributes with commas: - "symbol" - The OCC option symbol. - "underlying" - The underlying symbol. - "expiration" - The option's expiration date. - "side" - Call or put. - "strike" - The option's strike price. - "first traded" - The date this option first began to trade. - "dte" - Days to expiration: the calendar days until the option expires, counted from today on a current quote and from the quote date on a historical one. - "date", "updated" - The date and time of the quote. - "price", "mid", "mark" – The midpoint price of the option. - "bid" – The bid price of the option. - "ask" – The ask price of the option. - "last" – The last price of the option. Depending on the liquidity of the symbol, the last traded price could have occurred one second ago or one day ago. - "bid size" – The quantity of contracts offered at the bid price. - "ask size" – The quantity of contracts offered at the ask price. - "volume" – The number of contracts negotiated during the day. - "open interest" – The number of open contracts. Open interest is published once per trading day, before the open, and does not change intraday. On a historical request it is the figure settled out of the previous session, so it lags every other field in the row by one session. - "underlying price" – The price of the underlying security at the time of this option quote. - "in the money" – Will return TRUE if the option contract was in the money at the time of the quote or FALSE if the contract was not in the money at the time of the quote. - "intrinsic value" - The intrinsic value of the option, if any. - "extrinsic value" - The extrinsic or time value of the option. - "iv", "implied volatility" - The implied volatility of the option. - "delta" - The delta of the option. - "gamma" - The gamma of the option. - "theta" - The theta of the option. - "vega" - The vega of the option. - "all" – Returns all values. - "noheaders" – Returns values without column headers. - **start date** _(OPTIONAL)_ – The start date when fetching historical data. If start date is specified, but endDate is not, only the single day's data is returned. When fetching more than one contract, only a single date may be used. - **end date** _(OPTIONAL)_ – The end date when fetching historical data, or the number of calendar days (not trading days) from startDate for which to return data. ## Usage Information ### Data Availability The `OPTIONDATA` function can return both current option quotes and historical option quotes, depending on whether date parameters are specified. **Data availability restrictions based on user type and exchange entitlements only apply to current quotes.** Historical data is available to all users, with access limits based on your plan. > [!NOTE] > **[What are entitlements?]** > > Entitlements are permissions granted by exchanges that allow access to their data. To get real-time options data, users need to sign the [OPRA agreement](https://www.marketdata.app/docs/account/entitlements). [Learn more about entitlements](https://www.marketdata.app/docs/account/entitlements). #### Current Quotes (No Date Parameters) When you use `OPTIONDATA` without date parameters, it returns current option quote data. **The data type you receive depends on your user type and OPRA entitlement.** This may include real-time data, 15-minute delayed data, or historical data (1 day old), depending on your entitlements and user classification. | User Type | OPRA Entitlement | Data Type | |------------------|------------------|------------------------| | Non-Professional | ✅ | Real-time | | Non-Professional | ❌ | 15-min delayed | | Professional | Any | Historical (1 day old) | | Unknown | Any | Historical (1 day old) | #### Historical Quotes (With Date Parameters) When you use `OPTIONDATA` with date parameters, it returns historical option quote data. **Historical data is available to all users regardless of exchange entitlements.** However, the age of historical data you can access depends on your plan: | Plan | Historical Data Availability | |-------------------------------|------------------------------| | Free Forever | 1 year | | Trial Plans (Starter, Trader) | 1 year | | Starter | 5 years | | Trader, Quant, Prime | No limit (full access) | > [!NOTE] > **[Plan Limits]** > > If you attempt to access historical data older than your plan's limit permits, the formula will return an error. [Learn more about plan limits](https://www.marketdata.app/docs/account/plan-limits). ### Working With Multiple Contracts A single `OPTIONDATA` formula can fetch quotes for many option contracts at once. Pass a range of cells, several OCC symbols separated by commas, or a table of contracts as the option symbol. - One row comes back for each contract, in the same order as your cells, even when the range is a row. Wrap the formula in `TRANSPOSE()` to lay the results out horizontally. - A contract that cannot be served (not found, expired, or misspelled) keeps its place, so the results stay aligned with your contracts. Its row shows what you entered in the symbol column, with the other columns blank, so you can spot it. Request the `symbol` attribute to see it; without it the row is blank. - Use a single historical date (start date only). Date ranges are not supported with more than one contract. - A range must be a single row, a single column, or a table of exactly four columns. Any other table, such as three or five columns, returns an error. - A row of four cells can describe one contract in parts: underlying, expiration date, strike and side (for example, `AAPL | 12/17/2027 | 250 | call`). On its own, a row like that is read as a single contract, so it also accepts a date range. - With more than one contract the results include a header row. Add `noheaders` to the attributes to line the results up with your cells. - For best results use OCC symbols, or a table with dates in `YYYY-MM-DD` format and numeric strikes. Contracts written in words are slower to read, and a long list of them may fill in over a few refreshes. #### Tables of Contracts A table of four columns is read as one contract per row, with the underlying, expiration date, strike and side in its columns: | | A | B | C | D | |---|------|------------|-----|------| | 1 | AAPL | 2027-12-17 | 250 | call | | 2 | AAPL | 2027-12-17 | 250 | put | | 3 | MSFT | 2027-12-17 | 500 | call | `=OPTIONDATA(A1:D3, "symbol,mid")` returns a header row and one row for each of the three contracts. - The columns can be in any order. The first row sets it, and every other row is read the same way. - Because of that, the first row must be a contract, not a header. A table that starts with a header row returns an error, so start the range on the first contract (for example, `A2:D50`). - A later row that does not fit the order of the first row (columns swapped, a missing or misspelled cell) fails on its own, without affecting the other rows. ## Notes - All parameters must be enclosed in quotation marks or be references to cells containing text. A possible exception is when end date is specified as a number of days. - Results will be returned as a value within a single cell if a single attribute is requested for a single contract. Multiple attributes, multiple contracts or multi-day results will be returned with column headers. - Dates and times are returned in the same timezone of the exchange. --- # OPTIONLOOKUP Source: https://www.marketdata.app/docs/sheets/options/optionlookup/index.md Look-up the OCC option symbol from text or 4 cell references containing (1) underlying symbol, (2) expiration date, (3) strike, (4) call or put. > [!TIP] > It is not necessary to know the exact date of the option expiration. The formula will assume the monthly option expiration if both the month and the year are provided. If only the month is provided with no day or year, the formula will assume an unexpired option for the current year or the next year. ## Video Tutorial ## Sample Usage ```excel-formula =OPTIONLOOKUP("AAPL 7/21/2023 200 Call") =OPTIONLOOKUP("AAPL 7/2023 $200 Call") =OPTIONLOOKUP("AAPL July 2023 200 Call") =OPTIONLOOKUP("AAPL Jan 200 Call") =OPTIONLOOKUP(A1:D1) ``` ## Syntax ```excel-formula =OPTIONLOOKUP("text") ``` - **text** _(REQUIRED)_ Text or 4 cell references containing (1) underlying symbol, (2) expiration date, (3) strike, (4) call or put. ## Notes - All parameters must be enclosed in quotation marks or be references to 4 cells containing (1) underlying symbol, (2) expiration date, (3) strike, (4) call or put. --- # Sharing Your Sheet Source: https://www.marketdata.app/docs/sheets/sharing/index.md It is possible to share you sheet with other users by using the **Share** button in the top right corner of the sheet. You can share the sheet with anyone who has a Google account. The only two requirements are that each user who receives a copy of your sheet must have the [Market Data Add-on](https://workspace.google.com/marketplace/app/market_data/453586334945) installed from the Google Workspace Marketplace and have a Market Data account. > [!WARNING] > If you share your sheet with a user who does not have a Market Data account, they will not be able to execute the formulas in the sheet. They will receive an error message and the Market Data formulas will not execute. ## Sharing Mode You must share your sheet in **Edit** mode to allow a user to open the sidebar and execute the formulas. If you share your sheet in **View** or **Comment** mode, the Market Data formulas will not execute because the user will not have access to the sidebar. ## Sharing with Free or Trial Users You can share your sheet with free or trial users but these users will not have access to real-time data. They will only have access to delayed and historical data. If you need to share your sheet with another user who needs real-time data, each user will need to have a paid Market Data account. --- # Stock Formulas Source: https://www.marketdata.app/docs/sheets/stocks/index.md --- # EARNINGS Source: https://www.marketdata.app/docs/sheets/stocks/earnings/index.md > [!NOTE] > **[Premium Formula]** > > This formula can only be used with paid plans. Free plans or trial plans will not work. Fetches current or historical earnings data for a stock ticker. ## Sample Usage ```excel-formula =EARNINGS("AAPL") =EARNINGS("AAPL", "all,noheaders") =EARNINGS("AAPL", "year,quarter,eps,eps surprise", "1/1/2021", "12/31/2021") ``` ## Syntax ```excel-formula =EARNINGS(symbol, [attributes], start date, end date) ``` - **symbol** _(REQUIRED)_ The stock’s ticker symbol. - **[attributes]** _(OPTIONAL)_ Use one or more attributes to customize the response: - "symbol" – The ticker symbol for the stock. - "year" – The fiscal year for the earnings report. - "quarter" – The fiscal quarter for the earnings report. - "date" – The date of the earnings report. This is usually expressed as the last day of the quarter. - "report date" – The date the company reported the earnings. - "report time" - The time the company plans to report earnings. - "currency" - The currency earnings are reported in. - "reported eps" - The earnings per share reported by the company. - "estimated eps" - The estimated earnings per share by Wall Street. - "surprise eps" - The difference in eps between Wall Street and the actual earnings report. - "surprise eps percent" - The difference in percentage terms between the reported EPS and estimated EPS. - "updated" - The date and time the earnings data for this ticker was lasted refreshed. - "all" – Returns all values. - **start date** _(OPTIONAL)_ The start date when fetching data. If start date is specified, but end date is not, only the single day’s data is returned. - **end date** _(OPTIONAL)_ The end date when fetching data, or the number of calendar days (not trading days) from start date for which to return data. ## Notes Non-GAAP earnings are reported for most tickers. If the company does not report non-GAAP earnings, GAAP earnings will be used instead. > [!WARNING] > **[Estimated Dates for Future Earnings]** > > For upcoming earnings events, the report date is an **estimate** based on historical reporting patterns and is not a confirmed date. These dates frequently change as companies update their reporting schedules. Only after a company has actually reported its earnings does the report date become a confirmed, historical data point. Do not make trading decisions based on estimated future earnings dates. --- # STOCKDATA Source: https://www.marketdata.app/docs/sheets/stocks/stockdata/index.md Fetches a current stock quote or historical stock candles from Market Data. It can also fetch a single historical candle for multiple stocks. ## Sample Usage For Quotes ```excel-formula =STOCKDATA("AAPL") ``` ### Bulk Quotes ```excel-formula =STOCKDATA("AAPL,MSFT,TSLA") =STOCKDATA("AAPL,MSFT,TSLA", "all") =STOCKDATA(A1:A500, "bid,ask,last") ``` ## Sample Usage For Candles ```excel-formula =STOCKDATA("AAPL", "close", "1/1/2021", "1/31/2021", "hourly") =STOCKDATA("AAPL", "all", TODAY()-30, 30) ``` ### Bulk Candles ```excel-formula =STOCKDATA("AAPL,MSFT,TSLA", "all", "today") =STOCKDATA("AAPL,MSFT,TSLA", "all", "12/20/2023") =STOCKDATA(A1:A3, "all", "12/20/2023") ``` ## Syntax ```excel-formula =STOCKDATA(symbols, [historial attributes|quote attributes], start date, end date, resolution) ``` - **symbols** _(REQUIRED)_ One or more ticker symbols for the stocks. - **[historial attributes | quote attributes]** _(Optional)_ Use a historical or quote attribute: - **"price"** _(OPTIONAL)_ Special dual-mode quote/candle attribute. - Displays the midpoint price during the regular and sessions. - Displays the closing price of the previous session outside of market hours. - **historical attributes** _(OPTIONAL – "close" by default)_ Use one of the following attributes when requesting historical candles: - "open" – The opening price of the stock. - "high" – The high price of the stock. - "low" – The low price of the stock. - "close" – The closing price of the stock. - "volume" – The number of shares traded. - "all" – Returns all values. - "symbol" - The ticker symbol of the stock. _Only returned when using a bulk candles formula._ - "adjusted" - Returns split- and dividend-adjusted prices (CRSP methodology). On daily resolutions both split and dividend adjustments are applied; on intraday resolutions only split adjustment is applied, since dividend adjustment is available for daily resolutions only. Can be used on its own — `=STOCKDATA("AAPL", "adjusted", ...)` returns the full adjusted OHLCV row, just like "all" — or combined with a specific column, e.g. "adjusted,close". - "unadjusted" - Returns raw prices with no split or dividend adjustment. Can be used on its own — `=STOCKDATA("AAPL", "unadjusted", ...)` returns the full unadjusted OHLCV row, just like "all" — or combined with a specific column, e.g. "unadjusted,close". - **quote attributes** _(OPTIONAL – "mid" by default)_ Use one of the following attributes when requesting a quote: - "mid", "mark" – The midpoint price of the stock. - "bid" – The bid price of the stock. - "ask" – The ask price of the stock. - "last" – The last price of the stock. - "bidSize" – The quantity of shares offered at the bid price. - "askSize" – The quantity of shares offered at the ask price. - "volume" – The quantity of shares traded. - "52weekHigh" – The 52-week high for the stock. - "52weekLow" – The 52-week low for the stock. - "52week" – Adds both 52-week columns. "all" does not include them: use "all,52week" to return every value and the 52-week high and low. - "symbol" - The ticker symbol of the stock. _Only returned when using a bulk quotes formula._ - "all" – Returns all values except the 52-week high and low. - **start date** _(OPTIONAL)_ The start date when fetching historical data. If start date is specified, but endDate is not, only the single day’s data is returned. Multiple tickers may be used if only a single day's data is being returned. - **end date** _(OPTIONAL)_ The end date when fetching historical data, or the number of calendar days (not trading days) from startDate for which to return data. - **resolution** _(OPTIONAL)_ The duration of each candle when fetching historical candles. Daily resolution is default if no value is specified. Use one of the following resolutions: - Minutely Resolutions: (_minutely_, _1_, _3_, _5_, _15_, _30_, _45_, ...) - Hourly Resolutions: (_hourly_, _H_, _1H_, _2H_, ...) - Daily Resolutions: (_daily_, _D_, _1D_, _2D_, ...) - Weekly Resolutions: (_weekly_, _W_, _1W_, _2W_, ...) - Monthly Resolutions: (_monthly_, _M_, _1M_, _2M_, ...) - Yearly Resolutions: (_yearly_, _Y_, _1Y_, _2Y_, ...) ## Price vs Close vs Last vs Mid When you are looking for a single price for a stock, depending on what data you would like the `STOCKDATA` formula to return during the extended session (or when the market is closed), it may make sense to use the "close", "last", or "mid" attributes instead of STOCKDATA's default "price" attribute. | Function Call | During The Regular Session | During The Extended Session | Market Closed, Holidays, etc. | |-------------------------------|-----------------------------------------|------------------------------------------|-----------------------------------------------| | `=STOCKDATA("AAPL")` | Midpoint bid/ask of the regular session | Midpoint bid/ask of the extended session | Closing price of the previous regular session | | `=STOCKDATA("AAPL", "PRICE")` | Midpoint bid/ask of the regular session | Midpoint bid/ask of the extended session | Closing price of the previous regular session | | `=STOCKDATA("AAPL", "CLOSE")` | Last trade of the regular session | Closing price of the previous session | Closing price of the previous regular session | | `=STOCKDATA("AAPL", "LAST")` | Last trade of the regular session | Last trade of the extended session | Last trade of the previous extended session | | `=STOCKDATA("AAPL", "MID")` | Midpoint bid/ask of the regular session | Midpoint bid/ask of the extended session | No data | ## Usage Information ### Data Availability The `STOCKDATA` function can return both quotes and candles, depending on whether date parameters are specified. The data type you receive depends on your user type and exchange entitlements. > [!NOTE] > **[What are entitlements?]** > > Entitlements are permissions granted by exchanges that allow access to their data. To get quotes, users need to sign the [IEX agreement](https://www.marketdata.app/docs/account/entitlements). To get candles, users need to sign the [UTP agreement](https://www.marketdata.app/docs/account/entitlements). [Learn more about entitlements](https://www.marketdata.app/docs/account/entitlements). #### Quotes (No Date Parameters) When you use `STOCKDATA` without date parameters, it returns quote data. The type of quote you receive depends on your user type and IEX entitlement. This may include a real-time quote, a 15-minute delayed quote or historical (1 day old) quote, depending on the plan or access level. Real-time quotes are not currently available. | User Type | IEX Entitlement | Quote Type | |------------------|-----------------|------------------------| | Non-Professional | ✅ | Real-time | | Non-Professional | ❌ | 15-min delayed | | Professional | ✅ | Real-time | | Professional | ❌ | Historical (1 day old) | | Unknown | ✅ | Real-time | | Unknown | ❌ | Historical (1 day old) | #### Current Day Candles When you use `STOCKDATA` with date parameters to request candles from the current trading session (today), you receive current day candle data. **Real-time candle data is not available for candles under any plan or entitlement.** The type of candle data you receive depends on your user type and UTP entitlement. This may include 15-minute delayed candles or historical candles (1 day old), depending on the plan or access level. | User Type | UTP Entitlement | Candle Type | |------------------|-----------------|------------------------| | Non-Professional | ✅ | 15-min delayed | | Non-Professional | ❌ | 15-min delayed | | Professional | ✅ | 15-min delayed | | Unknown | Any | Historical (1 day old) | #### Historical Candles When you use `STOCKDATA` with date parameters to request candles from past dates, you receive historical candle data. Historical candles can use any resolution (intraday, daily, weekly, etc.) from past trading sessions. **Historical data is available to all users regardless of exchange entitlements.** However, the age of historical data you can access depends on your plan: | Plan | Historical Data Availability | |-------------------------------|------------------------------| | Free Forever | 1 year | | Trial Plans (Starter, Trader) | 1 year | | Starter | 5 years | | Trader, Quant, Prime | No limit (full access) | > [!NOTE] > **[Plan Limits]** > > If you attempt to access historical data older than your plan's limit permits, the formula will return an error. [Learn more about plan limits](https://www.marketdata.app/docs/account/plan-limits). ## Notes - All parameters must be enclosed in quotation marks or be references to cells containing text. A possible exception is when endDate is specified as a number of days. - Results with a single data point will be returned as a value within a single cell. Multiple data points will be returned as an expanded array with column headers. - If any date parameters are specified, the request is considered historical and only the historical attributes are allowed. - Dates and times are returned in the same timezone of the exchange. - When writing bulk candles formulas, only a single date is allowed. Date ranges are not permitted. Only daily candles are supported for bulk candles. --- # Troubleshooting (Sheets) Source: https://www.marketdata.app/docs/sheets/troubleshooting/index.md From time to time you may encounter different error messages while using the Google Sheets Add-On. These messages are almost always due to limitations that Google places on Sheets Add-ons, which have certain limitations that most users are not aware of. By learning about the limitations Google has placed on your use of Add-ons, you will be able to avoid these common errors in the future. ## Errors when Opening Your Sheet Sometimes you may get an error when you open your spreadsheet. These errors are usually related to Add-on authorization issues. - `#NAME?` - This error in your formulas indicates the Add-on is not loaded properly in the current sheet. Open the sidebar in Extensions > Market Data > Start to load the Add-on in the current sheet. See [Unknown Function Error](https://www.marketdata.app/docs/sheets/troubleshooting/unknown-function). - `Error: Unable to start a Market Data session with sidebar closed.` - This error occurs when you first open your sheet for the day. Open the sidebar in Extensions > Market Data > Start. Refresh your data using one of the refresh options provided. - `ScriptError: Authorization is required to perform that action.` - The Market Data Add-on is not installed correctly in this spreadsheet or in your Google account. This typically occurs when customers are logged into multiple Google accounts at the same time. See [Permissions Not Granted During Installation](https://www.marketdata.app/docs/sheets/troubleshooting/permissions-not-granted). ## Errors in Market Data Sidebar The Market Data sidebar can detect service outages and other problems with your Google account. If you begin to have trouble with Market Data, open the sidebar and see if an error message is displayed. - `Multiple Google accounts error.` - Your browser has multiple Google accounts open at once. Google does not permit multi-login with Sheets Add-ons, such as Market Data. You will need to log out of your other Google accounts or use Google Sheets with a single Google account in a private browsing window. See [Multiple Google Accounts Error](https://www.marketdata.app/docs/sheets/troubleshooting/multiple-google-accounts). - `Authentication error.` - The add-on could not authenticate your Google account. Make sure the Google account you use to login to Sheets matches the e-mail address on your Market Data account. See [Authentication](https://www.marketdata.app/docs/sheets/authentication). - `Market Data is offline.` - Your sheet is cannot reach Market Data's servers. Check our status page to see if there is a service outage. As soon as service is restored this error will resolve. ## Errors in Formulas Formulas will give errors in their output due to three reasons: 1. Incorrect user input 2. Problems or issues with Market Data or your Market Data account 3. Limits or throttling placed by Google on your Google account. You can see the complete list of error messages for each category below: ### Incorrect User Input The following errors are usually easily correceted by modifying the input to your formula. - `No data.` - This actually isn't an error, but it is one of the most common messages. It means Market Data does not have any data for the symbol you input. Try a different symbol or a different timeframe. - `Invalid symbol.` - The symbol you specified is not included our database of valid symbols and we cannot provide you with any data for this symbol. Check to make sure the symbol is valid. - `Invalid option symbol.` - Use valid OCC symbology for the option symbol. The option symbol you used is not valid. Learn how to form a correct OCC option symbol and try again. - `Non-string symbol.` - The 'Symbol' parameter must be text surrounded by quotes or a cell reference that contains text. You most likely forgot to add quotes around the symbol you are using or you used an incorrect cell reference. Double check the formula to make sure it is formatted correctly. - `Invalid attribute.` - You are requesting an attribute that is not valid for the data you are requesting. Usually this happens when requesting a real-time quote attribute for historic data or vice versa. - `Invalid resolution.` - We cannot generate timeseries data for the resolution you are requsting. Read the documentation to see what resolutions are supported. ### Market Data Account Issues These issues are related to problems with your specific Market Data account and are usually easily solvable. - `Authentication error.` - The add-on could not authenticate your Google account with Market Data. Make sure the e-mail address on your Market Data account matches the Google account you use to login to Sheets. See [Authentication](https://www.marketdata.app/docs/sheets/authentication). - `Unable to start a Market Data session with sidebar closed.` - This error occurs when you first open your sheet for the day. Open the sidebar in Extensions > Market Data > Start and refresh your data using one of the refresh options provided. - `Your Market Data plan has exceeded its daily limit.` - You have used all your Market Data requests for the day. Your account will be refilled with new requests at opening bell (9:30 AM Eastern Time) tomorrow. ### Market Data Service Issues These issues relate to general Market Data technical or service issues. The only solution is to check back later or submit a support ticket if the problem does not resolve itself after a few minutes. - `Internal server error, please try again later.` - This is a usually temporary failure of Market Data's server. If the error continues for more than 24 hours, open a ticket with the helpdesk. - `API Endpoint Offline.` - The Market Data API endpoint that should be responding to your formula's request is offline. If this problem continues for more than 15 minutes, report it to the helpdesk. - `API Gateway Offline.` - The gateway server is not responding or unavailable. If this problem continues for more than 15 minutes, report it to the helpdesk. - `Market Data is offline.` - Market Data servers are offline. The Add-on will not function until our servers are back online. [Check our status page](https://www.marketdata.app/status/) to see details about the service outage. - `Exceeded maximum execution time (line 0)` - It took more than 30 seconds for Market Data's servers to respond to your request. You can retry using the Refresh Errors option in the sidebar and most times this will work. If the problem is frequent, please submit a ticket to our helpdesk with your formula so we can evaluate why it is taking too long to complete the request. ### Google Account Issues These issues relate to problems with your Google account. Google places strict limits on the use of Add-ons for free (@gmail.com accounts). If you exceed those limits, an error message will be shown and your account will be throttled. - `Your Google account has exceeded the minute request limit.` - Your account was throttled by Google for making too many requests. Try refreshing your cells using Market Data's refresh options, which are calibrated to avoid throttling issues. See [Excessive Refreshing](https://www.marketdata.app/docs/sheets/troubleshooting/excessive-refreshing). - [`Your Google account has exceeded the urlfetch limit.`](https://www.marketdata.app/docs/sheets/troubleshooting/urlfetch) - You have exceeded Google's urlfetch limit. For 24 hours your account will be blocked from using all Google Workspace Add-ons, including Market Data. Please try again 24 hours from now. --- # Excessive Refreshing Source: https://www.marketdata.app/docs/sheets/troubleshooting/excessive-refreshing/index.md Due to Google's strict quota on urlfetch requests, unwanted refreshing of cells can become troublesome. Google will refresh your spreadsheet if any of the following take place: - Reopening a spreadsheet - Adding/deleting a row - Adding/deleting a column - Sorting a row/column ## Background Formula Refreshing Google Sheets automatically refreshes all formulas in the background once per hour, even when the spreadsheet is not actively being edited. This background refresh can trigger urlfetch requests for any formulas that use our functions, potentially causing you to hit the quota limit unexpectedly. **Strategy to Avoid Background Refreshing**: Close the spreadsheet tab when you're not actively working on it. This prevents Google from performing the hourly background refresh of all formulas, which can help you stay within the urlfetch quota limits. ## Add-ons, Scripts & Macros If you have other add-ons, scripts, or macros that perform any cell or row actions such as those listed above, your sheet may recaculate excessively, causing you to hit the urlfetch quota very quickly. We recommend disabling them. --- # Extensions Menu Disabled in Google Sheets Source: https://www.marketdata.app/docs/sheets/troubleshooting/extension-disabled/index.md If the **Extensions** menu in Google Sheets appears grayed out (lighter text, unclickable) when trying to use the Market Data add-on, it is almost always due to a Google Sheets behavior when viewing a fully protected sheet — it is **not** caused by the Market Data add-on itself. **What to look for:** The Extensions menu will be visible but appear lighter in color and won't respond when clicked. ## Why This Happens - If **every cell** in the current sheet is protected for your account, Google Sheets automatically disables certain menus — including **Extensions** and **Format**. - This behavior is built into Google Sheets and applies to all add-ons, not just Market Data. - If the spreadsheet you're using was provided by another person or company and contains fully locked or protected, you may encounter this when viewing those sheets. ## Quick Diagnostic To confirm this is the issue: 1. Look at the bottom-left corner of Google Sheets 2. If you see "Protected sheet" or similar text, this confirms the sheet is fully protected 3. Try clicking in any cell - if you can't edit any cells, the sheet is fully protected ## How to Fix It ### 1. Switch to a Tab With Editable Cells 1. Click on a sheet tab where you can edit at least one cell. 2. Once you are in an editable sheet, the **Extensions** menu will become active again. ### 2. Ask the Sheet Owner to Add an Editable Cell If you need to run Market Data from a protected tab: 1. Ask the sheet owner to make a cell on that sheet editable for you. 2. This keeps the menu available without removing important protections. ### 3. Work From Your Own Copy If you have permission: 1. Go to **File > Make a copy**. 2. Save it to your own Google Drive where you are the owner. 3. You will have full access to all menus (but this removes the original owner’s protections). ## Important Notes - This issue occurs because of the spreadsheet’s protection settings — not because of the add-on’s installation or your subscription. - Once you are in a sheet with at least one editable cell, the add-on will be accessible again. ## Still Having Trouble? If the Extensions menu stays disabled even when on an editable tab: ### Step 1: Refresh and Check 1. Refresh the sheet (**Ctrl+R** or **Cmd+R**) 2. Try switching to a different tab and back 3. Check that you can edit at least one cell on the current tab ### Step 2: Verify Add-on Installation 1. Go to **Extensions > Add-ons > Manage add-ons** 2. Look for "Market Data" in the list 3. Make sure it shows as "Enabled" (not "Disabled" or missing) ### Step 3: Check Permissions 1. Click **Share** in the top-right corner 2. Verify your email has "Editor" access (not just "Viewer") 3. If you only have "Viewer" access, ask the owner to change it to "Editor" ### Step 4: Contact Support If the problem persists, contact our support team and include: - The name of the spreadsheet - Which tab you were on when the menu was disabled - A screenshot showing the menu state - Your permission level (Viewer/Editor/Owner) We typically respond within a few hours during market hours. --- # Mobile Devices Source: https://www.marketdata.app/docs/sheets/troubleshooting/mobile-devices/index.md Google Workspace Add-ons are not supported on mobile devices yet. This means there is no way to launch the Market Data sidebar on your mobile device. However, if you would like to use our service on your mobile device there are several workarounds you can try. Our custom formulas like [STOCKDATA](https://www.marketdata.app/docs/sheets/stocks/stockdata) and [OPTIONDATA](https://www.marketdata.app/docs/sheets/options/optiondata) will work on your mobile device. > [!CAUTION] > The following recommendations are only for users who want to experiment with Market Data on their mobile device. We do not officially support mobile devices with our add-on. ## Enable Your Sheet For Mobile To use Market Data on a mobile device, you need to disable the sidebar check. The add-on authenticates automatically using your Google account, but since there is no way to open the sidebar on a mobile device, you must disable the sidebar requirement first from a computer. ### Disable the Sidebar Check The system currently validates that the sidebar has been opened before allowing you to execute Market Data formulas. Disable this in your sheet by using the following command (type it into any cell): =DEBUG("disable_sidebar_check") Please be aware that command will stop the sidebar check when running your custom formulas. If you leave your spreadsheet open for an extended period of time formulas will continue to execute and you may run into [urlfetch errors](https://www.marketdata.app/docs/sheets/troubleshooting/urlfetch). If you find yourself running into urlfetch errors, enable the sidebar check again using the following command (type it into any cell): =DEBUG("enable_sidebar_check") ## Using Market Data in Mobile You can continue to use all the Market Data formulas in mobile as you would on a computer. However, to refresh your sheet, since you cannot use the refresh button, there is a workaround: > [!TIP] > Insert a row above row 1 to refresh all cells in your sheet. After adding the new row, delete it again. This quick sequence of adding and deleting a new row 1 on your sheet will cause all cells in your active sheet to refresh. --- # Multiple Google Accounts Error Source: https://www.marketdata.app/docs/sheets/troubleshooting/multiple-google-accounts/index.md Google does not support multi-login with Google Workspace Add-ons. Google's official statement on the issue: > [Multi-login, or being logged into multiple Google Accounts at once, isn't supported for Apps Script, add-ons, or web apps.](https://developers.google.com/apps-script/guides/projects#fix_issues_with_multiple_google_accounts) For users who work with multiple Google accounts during the day, such as a work and home account at the same time, this can be a serious problem, since it will cause errors with Add-ons such as Market Data, which need to access your Google account to function properly. ## Error Messages This error may present itself when you first open the sheet or during your use of the sheet, depending on whether Market Data has been installed in the secondary Google account. The error messages you may see are as follows: - `ScriptError: Authorization is required to perform that action.` - `Multiple Google accounts error.` ## Workarounds Fortunately this is an easy error to fix. There are two confirmed workarounds: 1. Log out of all your Google accounts then log-in to the account that uses Market Data and use it while being signed-in to a single account. 2. Open a private browsing window (Icognito mode) and open the Google account you use with Market Data in the private window. ### Unconfirmed Workaround There is also an unconfirmed workaround that some users have reported success with and that will allow you to remain logged-in to multiple Google accounts at once: - Set the Google account you use with the Market Data Add-On as your Default Google account. - The default Google account is simply the account you sign-in first to Google with. You can check this by clicking the profile icon in the upper right corner. - If another account is showing up as the Default account, then you are not using Market Data with your default Google account. ![default google account](https://www.marketdata.app/docs/img/default-account.png) To fix this, use the following steps (not guaranteed to work, but reported successful by multiple users): 1. Sign out of all your Google accounts. 2. Sign-in to the Google account you use with Market Data. This is now your default Google account. 3. Sign-in to the rest of your Google accounts. 4. Switch back to the default account. 5. Open Google Sheets and try using Market Data. 6. If Market Data works without errors for you, you can continue to use all your Google accounts with multiple sign-in, just make sure to always sign-in first to the account that uses Market Data. --- # Permissions Not Granted During Installation Source: https://www.marketdata.app/docs/sheets/troubleshooting/permissions-not-granted/index.md ## Problem Overview If the Market Data Google Sheets add-on is installed but **functions do not work**, **menus do nothing**, or spreadsheet formulas fail to initialize, the most common cause is **missing permissions during the original installation**. Due to a recent change by Google, users can now **enable or disable individual permissions during installation**. The Market Data add-on **requires all requested permissions** to function correctly. If any permission was left unchecked during install, the add-on cannot run. ## Why This Happens Google recently introduced **granular OAuth consent** for Google Sheets add-ons. This means: - The add-on can be installed **without granting all permissions** - Google may **not re-prompt** for missing permissions afterward - The add-on cannot execute without full consent - There is currently **no way for the add-on to force the consent screen to reappear** This behavior is enforced by Google and is not controlled by Market Data. ## Required Fix: Uninstall and Reinstall the Add-on At this time, the **only reliable solution** is to remove the add-on completely and reinstall it while granting all permissions. ### Step-by-Step Instructions ![Google OAuth consent screen—select all permissions and click Continue](https://www.marketdata.app/docs/img/google-oauth-consent.gif) 1. Open Google Sheets 2. Go to **Extensions → Add-ons → Manage add-ons** 3. Locate **Market Data** 4. Click **Uninstall** 5. Close the spreadsheet 6. Reopen Google Sheets 7. Reinstall the Market Data add-on from Google Workspace Marketplace 8. During installation, when the Google permission screen appears: - Choose "Select all" - **Ensure all permission toggles are enabled** - Click **Continue**

## Important Notes - Simply reloading the spreadsheet is **not sufficient** - Re-enabling the add-on for a document will **not restore missing permissions** - The add-on cannot request additional permissions after installation if they were denied Until Google provides a way to re-trigger consent reliably, **reinstallation is required**. ## Google Workspace (Company Accounts) If you are using a Google Workspace account: - Your organization may restrict third-party add-ons - Some permissions may be blocked by policy If permissions cannot be enabled: 1. Contact your IT administrator 2. Ask them to allow the Market Data add-on 3. Confirm all requested permissions are permitted for your account ## Key Takeaway - The add-on must be installed with **all permissions enabled** - Partial permission installs will not work - Reinstallation is currently the only fix - This behavior is controlled by Google ## Still Need Help? If you have reinstalled the add-on and enabled all permissions but still experience issues, please contact support and include: - Confirmation that you reinstalled the add-on - A screenshot of the permission screen - Whether you are using a personal Google account or Google Workspace We typically respond within a few hours during market hours. --- # Price Differences Source: https://www.marketdata.app/docs/sheets/troubleshooting/price-differences/index.md You may notice that from time to time prices differ when using Market Data compared to Google Finance, TradingView, Yahoo Finance, or other financial websites, especially after hours. This is normal and it to be expected since those platforms display real-time prices using trades (i.e. the last trade or closing price), while Market Data displays real-time prices using quotes. ## Trades vs Quotes A trade is a transaction that took place between a buyer and a seller in the past. A quote is a bid/ask price from the top of the order book and represents the price a buyer or seller would pay right now if a new transaction were to take place. Trades are indiciatve of the current stock price, but they are a lagging indicator. A quote represents the most current price. ## STOCKDATA vs GOOGLEFINANCE After Hours When you run `STOCKDATA` after hours you will be shown the midpoint price between the bid and the ask. `GOOGLEFINANCE` after hours will always provide you with the last trade (i.e. the closing price). During the extended session (after hours) trading, the bid and the ask can be much further apart than during the market hours. This means the midpoint price between the bid and the ask could be much further away from the last price than usual. To get more detailed information, use the "all" parameter with STOCKDATA like this: `=STOCKDATA("TICKER","ALL")`. This will provide you with a more detailed quote with column names and help you understand where the price is coming from. ### Example From 3 AM ![price differences](https://www.marketdata.app/docs/img/price-differences.png) In the example shown, we've requested a price using both STOCKDATA and GOOGLEFINANCE at 3 AM in the early morning for a stock that was thinly traded in the extended session. Note how STOCKDATA shows a price of $128.64 (midpoint between the bid/ask) while GOOGLEFINANCE shows $129.42, the day's closing price. When STOCKDATA is run with the "ALL" parameter we can see the bid/ask and verify how the midpoint price is calculated. ## Force STOCKDATA to Use Trades If you prefer to see trades instead of quotes for real-time prices, you can modify the STOCKDATA formula slightly and the price shown will be the last trade instead of the most up to date quote. Instead of using `=STOCKDATA("TICKER")`, use instead `=STOCKDATA("TICKER","LAST")`. This will force the formula to use the last price instead of the midpoint price when outputting the result to a single cell. Please be aware that by using this method, the price shown will be a historical price instead of a price that is likely to result in an order fill. If you are using the formula for trading, the midpoint price is recommended. --- # Unknown Function Error Source: https://www.marketdata.app/docs/sheets/troubleshooting/unknown-function/index.md When you encounter a #NAME error in your spreadsheet while using the Market Data Google Sheets add-on, it typically indicates that the add-on hasn't been properly initialized or connected to your open spreadsheet. This prevents you from using any of the market data functions like =STOCKDATA(), =OPTIONDATA(), etc. If you've already activated the add-on in your Google account but still see this error, here are several solutions to resolve the issue: ## Multiple Google Account Sessions If you're using multiple Google accounts in the same browser, this can sometimes cause authentication issues with Google Sheets add-ons. This is a common scenario when switching between personal and work accounts. To resolve this: 1. Create separate Chrome profiles for different Google accounts - This keeps your work and personal accounts completely separated - Ensures proper add-on authentication for each account - Log-in to a single Google account per Chrome session 2. Alternative solution: Use an incognito window - Open Chrome Menu > File > New Incognito Window - Sign in to your Google account - Access your spreadsheet with the Market Data add-on ## Re-enable the Add-on If the above solution doesn't work, try re-enabling the add-on specifically for your current spreadsheet: 1. Go to Extensions > Add-ons > Manage add-ons 2. Find the Market Data add-on 3. Unroll the options and uncheck "Use in this document" 4. Reload your spreadsheet 5. Return to the add-ons manager 6. Check "Use in this document" again ## Perform a Clean Installation If you're still experiencing issues, try a complete reinstallation: 1. Visit https://myaccount.google.com/permissions 2. Remove the Market Data add-on from your permissions list 3. Go to Google Workspace Marketplace 4. Uninstall the Market Data add-on 5. Reinstall the add-on 6. Reload your spreadsheet ## Google Workspace Users If you're using Google Workspace (formerly G Suite) through your organization, there might be additional considerations: 1. Check Workspace Settings - Contact your IT administrator to verify that third-party add-ons are allowed - Ensure the Market Data add-on is on your organization's allowed apps list - Confirm you have the necessary permissions to install and use add-ons 2. Domain-wide Installation - If your organization uses domain-wide installation, the add-on should be installed by your IT administrator - Wait for the add-on to propagate across your organization (can take up to 24 hours) - Ensure your account is part of the organizational unit where the add-on is deployed ## Additional Troubleshooting Steps If none of the above solutions work, try these general fixes: 1. Clear your browser cache and cookies 2. Verify your subscription status is active 3. Try accessing the spreadsheet from a different browser 4. Check if the issue persists in a new spreadsheet ## Still Need Help? If you've tried all the above steps and still encounter issues: 1. Contact our support team via the helpdesk 2. Include the following information: - Screenshot of the error - Steps you've already tried - Whether you're using personal Google account or Google Workspace Our support team typically responds within a few hours during market hours. --- # Urlfetch Errors Source: https://www.marketdata.app/docs/sheets/troubleshooting/urlfetch/index.md Urlfetch is the service that Google Add-Ons use to fetch data from an external service (such as Market Data). The limit is set by Google on a per account basis. Consumer accounts (gmail) have a limit of 20,000 urlfetch calls per day, while business accounts (Google Workspace) can make up to 100,000 urlfetch calls daily. Each time Market Data recalculates the data in a cell, that will consume one call from your urlfetch limit. **Google will recalculate your spreadsheet about once per hour**, so if you use 1000 Market Data formulas in your sheet and leave your spreadsheet open for 8 hours that will consume about 8000 urlfetch calls. > [!CAUTION] > Urlfetch calls are different from Market Data requests. Even if you have our Trader plan and can make 100,000 requests per day, if you use a consumer (gmail.com) account, you'll only be able to make 20,000 urlfetch calls with your spreadsheet. Your urlfetch limit is set by Google and cannot be modified by Market Data. | Feature | Consumer Accounts (Gmail) | Google Workspace Accounts | |--------------------------|-----------------------------------------------------------|---------------------------------------------| | **Daily Urlfetch Limit** | 20,000 urlfetch calls | 100,000 urlfetch calls | | **Typical Use Case** | Personal use, small projects | Business use, large projects | | **Recalculation Impact** | Higher risk of hitting limit with frequent recalculations | Lower risk due to higher limit | | **Market Data Requests** | Limited by lower urlfetch quota | More flexibility with higher quota | | **Account Blocking** | 24-hour block after exceeding limit | 24-hour block after exceeding limit | | **Strategies Needed** | More aggressive strategies to reduce usage | Fewer strategies needed due to higher limit | ## Error Messages You May See In The Add-on Due to Urlfetch Issues Although there is no way to determine how many urlfetch calls can be made by your account, Market Data can detect when the limit has been exceeded and will display an error message. Each of the following error messages indicate that your Google account has exceeded the urlfetch limit: - `Your Google account has exceeded the urlfetch limit.` - `The Add-On cannot communicate with Market Data's servers due to your Google account exceeding the daily urlfetch limit.` - `ScriptError: Exception: Service invoked too many times for one day: urlfetch.` - `Urlfetch error.` ## Consequences Of Surpassing the Urlfetch Limit Google will block your account from making new urlfetch requests for 24 hours when you surpass the limit. When you see the urlfetch error message, you must wait a full 24 hours before making another request with the affected Google account. ## Strategies To Reduce Urlfetch Usage There are a number of strategies you can use to reduce your use of urlfetch calls. ### Copy & Paste Values For Historical Data When you query Market Data for historical data, such as the price of a stock in the past, consider copying and using the _paste values_ option once the formula finishes its output. This deletes the Market Data formula, but it will preserve the formula's output. There are a number of advantages to removing historical formulas and replacing them with the output: - It will prevent future repeat queries for the same historical data, preserving your urlfetch quota. - It will also prevent future Market Data requests, preserving your Market Data request quota. - Your spreadsheet will load more quickly on initial open. - Your spreadsheet will recalculate more quickly every hour. If you make a habit of using Copy & Paste Values for your historical outputs, your overall Market Data experience will be significantly improved. ### Use A Single Formula For All Your Real-Time Quotes Instead of using multiple [`STOCKDATA`](https://www.marketdata.app/docs/sheets/stocks/stockdata) formulas in your spreadsheet, use a single formula and send all the tickers you need at once. Not only will this use just one urlfetch call, but it will also speed up the loading process of your spreadsheet. ### Refresh Prices Less Often If you are typically updating your prices every 10 minutes, for example, consider updating every 20 or 30 minutes. You can also make use of the "Current Sheet" refresh to only refresh prices on the current sheet instead of the entire workbook. ### Check For Troublesome Add-ons, Scripts & Macros Your urlfetch quota is shared between all your Google Workspace Add-ons. If you are using other add-ons that are also making urlfetch calls, you may burn through your quota very quickly. Some add-ons may also cause your spreadsheet to recalculate cells much more frequently than Google's default of once per hour. If you have 1000 Market Data formulas and another add-on is recalculating your sheet once every 3 minutes, you'll hit the urlfetch limit in an hour. #### How To Investigate Your Google Account's Add-ons, Scripts & Macros Use the following links to investigate what other scripts and Add-ons may be running in your Google account: - [Workspace Add-ons installed in your account](https://workspace.google.com/marketplace/myapps) - [Scripts authorized to run in your account](https://script.google.com/home/all) - [Log of scripts which are executing](https://script.google.com/home/executions) #### Uninstall Unused Add-ons Unless you are 100% sure that your other add-ons don't cause excessive recalculation of your spreadsheet and don't make excessive urlfetch calls, consider uninstalling add-ons that are no longer in use. ## Strategies to Increase Your Urlfetch Limit ### Switch From Gmail To Google Workspace If you are currently using a standard Gmail account and find yourself frequently hitting the urlfetch limit, consider switching to a Google Workspace account. Google Workspace accounts often come with higher quotas for various services, including urlfetch. This upgrade can provide you with the additional capacity you need to continue using Market Data without interruptions. --- # Universal Attributes Source: https://www.marketdata.app/docs/sheets/universal-attributes/index.md Universal attributes are special modifiers that are common to all Market Data formulas. You may use them in the attributes argument in any of our formulas and they will modify the output of the formula. --- # No Headers Source: https://www.marketdata.app/docs/sheets/universal-attributes/no-headers/index.md Add the "noheaders" attribute to exclude the header row from the output of any Market Data formula. ## Sample Usage ```excel-formula =STOCKDATA("AAPL", "all,noheaders") =STOCKDATA(A1:A50, "mid,noheaders") =OPTIONDATA(A1:A50, "bid,ask,noheaders") =OPTIONDATA("AAPL271217C00250000", "bid,ask,noheaders", "1/5/2026", "2/5/2026") ``` ## Syntax ```excel-formula =FORMULA("symbol", "attribute1, attribute2, noheaders") ``` Add `noheaders` anywhere in the attribute list of any Market Data formula. Ensure that all attributes are seperated by commas. ## Notes > [!NOTE] > > The `noheaders` attribute is needed whenever the output has more than one row or column: several attributes, several symbols or contracts, or a date range. A single attribute for a single symbol and date returns one cell with no header. --- # Market Data Source: https://www.marketdata.app/docs/index.md Complete Documentation For All Market Data Products & Services [Get Started With Our API](https://www.marketdata.app/docs/api/) (image: Market Data API) ### [Market Data API](https://www.marketdata.app/docs/api/) A complete reference of all Market Data API endpoints, authentication, and universal parameters. (image: Google Sheets Add-On) ### [Google Sheets Add-On](https://www.marketdata.app/docs/sheets/) Learn how to use each Market Data formula in our Add-On with practical examples. (image: Accounts & Billing) ### [Accounts & Billing](https://www.marketdata.app/docs/account/) Frequently asked questions about your account, our billing rules, and administrative information.