Introduction
The PublicOptions API is a REST interface to historical price data for US equity options. All responses are JSON. All endpoints are versioned under /v1.
Servers
All traffic goes through a single production base URL.
| Environment | Base URL | Use for |
|---|---|---|
| Production | https://api.czar28.com/v1 | All live traffic. |
Quickstart
Create an API key in your dashboard, then pull a year of daily quotes for AAPL Jan-2026 $180 calls.
curl https://api.czar28.com/v1/options/quote/eod \
-H "Authorization: Bearer sopk_live_..." \
-G --data-urlencode "root=AAPL" \
--data-urlencode "exp=20250117" \
--data-urlencode "strike=180" \
--data-urlencode "right=C" \
--data-urlencode "start_date=20240101" \
--data-urlencode "end_date=20240601"Authentication
Every request must include an Authorization header with your API key. Keys are scoped to your account and can be rotated or revoked instantly from the dashboard.
Authorization: Bearer sopk_live_xxxxxxxxxxxxxxxxKey types
sopk_live_…— production keys, count against your monthly quota.sopk_test_…— test keys, separate quota, safe to embed in CI.
Rotation
Rotating a key issues a new secret and keeps the old one alive for a 24-hour grace window, so you can deploy without downtime. Revoking a key takes effect immediately.
Security
- Treat the key like a password. Never commit it or expose it in browser code.
- API keys are hashed at rest; the full value is shown exactly once at creation.
- All traffic must be TLS — plain HTTP is rejected.
Errors
Failed requests return a JSON body with stable error and message fields. Validation errors include a details object listing the offending parameters.
{
"error": "invalid_parameters",
"message": "exp must be YYYYMMDD",
"details": { "fieldErrors": { "exp": ["exp must be YYYYMMDD"] } }
}| Status | Error code | Meaning |
|---|---|---|
| 400 | invalid_parameters | Query parameters missing, malformed, or out of range. |
| 401 | unauthorized | Missing, malformed, revoked, or expired API key. |
| 402 | subscription_inactive | Account has no active subscription. Visit billing to resume. |
| 404 | not_found | Contract or date range produced no data. |
| 429 | quota_exceeded | Monthly request quota exhausted. Resets at the date in X-RateLimit-Reset. |
| 502 | upstream_unavailable | Upstream data feed timed out or returned 5xx. Safe to retry with backoff. |
| 503 | circuit_open | Upstream is failing repeatedly; circuit breaker is open. Retry in 30–60s. |
Retry guidance
502/503— retry with exponential backoff (250 ms × 2ⁿ, max 5 attempts).429— stop, upgrade your plan, or wait untilX-RateLimit-Reset.400/401/402— never retry; fix the request or account first.
Rate limits & quotas
Every successful response carries the following headers so clients can self-throttle without polling:
| Header | Value |
|---|---|
| X-RateLimit-Limit | Your plan's monthly request quota. |
| X-RateLimit-Remaining | Requests remaining in the current period. |
| X-RateLimit-Reset | Unix timestamp (seconds) when the quota resets. |
| Retry-After | On 429 / 503 only — seconds to wait before retrying. |
Quotas are tracked per account, not per key, and reset at 00:00 UTC on the first of each month. Per-minute rate limits also apply (Free 60/min burst 20, Pro 600/min burst 100, Business 3000/min burst 500). Responses include X-RateLimit-Limit-Minute, X-RateLimit-Burst, and Retry-After headers. See pricing for plan limits.
Idempotency
Every endpoint accepts an optional Idempotency-Key request header. When two requests arrive within 24 hours with the same key and the same body, the second request returns the original cached response — without re-hitting the upstream provider and without consuming additional monthly quota. This is the recommended pattern for safely retrying after a timeout or network error.
Rules
- Key is an opaque client-generated string, ≤ 255 characters. UUIDs work well.
- Scope is per API key. Two different keys may reuse the same value without collision.
- Replays return the original status code and body, plus
Idempotent-Replayed: true. - Reusing a key with a different request body returns
409 idempotency_conflict. 5xxresponses are not cached, so transient upstream failures are safe to retry with the same key.- Entries expire after 24 hours.
curl "https://api.czar28.com/v1/options/quote/eod?root=AAPL&exp=20260116&strike=180&right=C&start_date=20250601&end_date=20250701" \
-H "Authorization: Bearer sopk_live_..." \
-H "Idempotency-Key: 8e3b5c0a-2f4a-4b9d-9a8e-1c2d3f4a5b6c"CORS
Every /v1 endpoint allows cross-origin GET from any origin and exposes the rate-limit headers, so you can call the API directly from a browser app running on your own domain.
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, Idempotency-Key
Access-Control-Expose-Headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-RateLimit-Burst, Retry-After, API-Version, Idempotent-ReplayedHeads-up: anything you ship to the browser is publicly visible. Don't embed a high-volume key in a public site — proxy through your own backend instead.
Response envelope
All data endpoints return the same shape: a header with metadata and a response array of rows. The header.format array names each column, in order, for the rows in response.
{
"header": {
"latency_ms": 47,
"format": ["ms_of_day", "bid", "ask", "bid_size", "ask_size", "date"]
},
"response": [
[34200000, 6.85, 7.05, 12, 9, 20240102],
[34260000, 6.80, 7.00, 14, 11, 20240102]
]
}Loading into pandas:
import pandas as pd
df = pd.DataFrame(body["response"], columns=body["header"]["format"])API reference
Every endpoint below is generated directly from our OpenAPI 3.1 spec — the same file our SDKs and Postman collection consume. If the spec changes, this page changes with it.
End-of-day quotes for a single contract
Query parameters
| Name | Type | Description |
|---|---|---|
| rootrequired | string | Underlying ticker (uppercase). |
| exprequired | YYYYMMDD | Expiration date YYYYMMDD. |
| strikerequired | number | Strike price in dollars. |
| rightrequired | "C" | "P" | Option right (C=call, P=put). |
| start_daterequired | YYYYMMDD | Start date YYYYMMDD. |
| end_daterequired | YYYYMMDD | End date YYYYMMDD. |
Response fields
Columns appear in header.format in this order; rows are arrays in response.
| Field | Type | Description |
|---|---|---|
| date | int (YYYYMMDD) | Trading date. |
| open | number | Opening trade price. |
| high | number | Session high. |
| low | number | Session low. |
| close | number | Closing trade price. |
| bid | number | Closing best bid. |
| ask | number | Closing best ask. |
| volume | int | Contracts traded. |
Example
curl https://czar28.com/v1/options/quote/eod
-H "Authorization: Bearer sopk_live_..." \
-G --data-urlencode "root=AAPL" \
--data-urlencode "exp=20260619" \
--data-urlencode "strike=200" \
--data-urlencode "right=C" \
--data-urlencode "start_date=20260101" \
--data-urlencode "end_date=20260131"Intraday quotes for a single contract
Query parameters
| Name | Type | Description |
|---|---|---|
| rootrequired | string | Underlying ticker (uppercase). |
| exprequired | YYYYMMDD | Expiration date YYYYMMDD. |
| strikerequired | number | Strike price in dollars. |
| rightrequired | "C" | "P" | Option right (C=call, P=put). |
| start_daterequired | YYYYMMDD | Start date YYYYMMDD. |
| end_daterequired | YYYYMMDD | End date YYYYMMDD. |
| ivl | integer | Bar interval in milliseconds (1000 – 3600000). Default 60000. |
| rth | "true" | "false" | Regular trading hours only. |
Response fields
Columns appear in header.format in this order; rows are arrays in response.
| Field | Type | Description |
|---|---|---|
| ms_of_day | int | Milliseconds since midnight (US/Eastern). |
| bid | number | Best bid at end of interval. |
| ask | number | Best ask at end of interval. |
| bid_size | int | Contracts at the best bid. |
| ask_size | int | Contracts at the best ask. |
| date | int (YYYYMMDD) | Trading date. |
Example
curl https://czar28.com/v1/options/quote/intraday
-H "Authorization: Bearer sopk_live_..." \
-G --data-urlencode "root=AAPL" \
--data-urlencode "exp=20260619" \
--data-urlencode "strike=200" \
--data-urlencode "right=C" \
--data-urlencode "start_date=20260101" \
--data-urlencode "end_date=20260131" \
--data-urlencode "ivl=60000" \
--data-urlencode "rth=true"Historical trades for a single contract
Query parameters
| Name | Type | Description |
|---|---|---|
| rootrequired | string | Underlying ticker (uppercase). |
| exprequired | YYYYMMDD | Expiration date YYYYMMDD. |
| strikerequired | number | Strike price in dollars. |
| rightrequired | "C" | "P" | Option right (C=call, P=put). |
| start_daterequired | YYYYMMDD | Start date YYYYMMDD. |
| end_daterequired | YYYYMMDD | End date YYYYMMDD. |
Response fields
Columns appear in header.format in this order; rows are arrays in response.
| Field | Type | Description |
|---|---|---|
| ms_of_day | int | Milliseconds since midnight (US/Eastern). |
| sequence | int | Exchange sequence number. |
| size | int | Trade size in contracts. |
| condition | int | OPRA condition code. |
| price | number | Trade price. |
| exchange | int | OPRA exchange code. |
| date | int (YYYYMMDD) | Trading date. |
Example
curl https://czar28.com/v1/options/trades
-H "Authorization: Bearer sopk_live_..." \
-G --data-urlencode "root=AAPL" \
--data-urlencode "exp=20260619" \
--data-urlencode "strike=200" \
--data-urlencode "right=C" \
--data-urlencode "start_date=20260101" \
--data-urlencode "end_date=20260131"List option contracts for a root + expiration
Query parameters
| Name | Type | Description |
|---|---|---|
| rootrequired | string | Underlying ticker (uppercase). |
| exprequired | YYYYMMDD | Expiration date YYYYMMDD. |
Response fields
Columns appear in header.format in this order; rows are arrays in response.
| Field | Type | Description |
|---|---|---|
| root | string | Underlying ticker. |
| expiration | int (YYYYMMDD) | Expiration date. |
| strike | int (1/10¢) | Strike in tenths of a cent. Divide by 1000 for dollars. |
| right | C | P | Call or put. |
Example
curl https://czar28.com/v1/options/chain
-H "Authorization: Bearer sopk_live_..." \
-G --data-urlencode "root=AAPL" \
--data-urlencode "exp=20260619"Health check
Response fields
| Field | Type | Description |
|---|---|---|
| status | string | "ok" when healthy, "degraded" otherwise. |
| upstream.mdds_status | string | "CONNECTED" | "DISCONNECTED" | "UNKNOWN". |
| upstream.upstream_ms | int | Round-trip latency to upstream in ms. |
Example
curl https://czar28.com/v1/options/healthVersioning & deprecation policy
The API is versioned in the URL path. The current stable version is /v1. We will never make a backwards-incompatible change to a released version — breaking changes ship as a new major version (/v2) alongside the old one.
What counts as a breaking change
- Removing or renaming an endpoint, field, parameter, or error code
- Changing the type or semantics of an existing field
- Making a previously optional parameter required
- Tightening validation in a way that rejects previously valid input
What is not breaking (safe to add anytime)
- New endpoints, optional parameters, or response fields
- New optional response headers
- New enum values returned by the server (clients must tolerate unknown values)
- Bug fixes that align behavior with the documented contract
Deprecation timeline
- Day 0 — deprecation announced via email and on this page. Successor version is generally available.
- Day 0 onward — deprecated endpoints return a
Deprecation: trueheader (RFC 9745) and aSunsetheader (RFC 8594) with the removal date. ALinkheader points to the successor. - 12 months minimum — deprecated version continues to work unchanged. Paid plans get a written 12-month commitment from announcement to sunset.
- Sunset date — deprecated version returns
410 Gonewith{ "error": "version_sunset" }.
Currently active versions
| Version | Status | Sunset |
|---|---|---|
| v1 | Stable | — |
Changelog
Notable changes to the public API. Additive changes (new endpoints, optional parameters, new response fields) ship without notice; anything breaking follows the deprecation policy above.
| Date | Version | Change |
|---|---|---|
| 2026-05-21 | v1.1 | Added Idempotency-Key header support on every endpoint; added API-Version response header; documented Deprecation/Sunset/Link headers. |
| 2026-05-20 | v1.0 | Per-minute token-bucket rate limiting (Free 60/min burst 20, Pro 600/min burst 100, Business 3000/min burst 500). |
| 2026-05-19 | v1.0 | Per-API-key monthly quotas and usage analytics in the dashboard. |
| 2026-05-18 | v1.0 | Initial public release: EOD, intraday, trades, chain, and health endpoints. |
OpenAPI & Postman
Machine-readable spec and a ready-to-import Postman collection. Set the apiKey variable to your sopk_… key after importing.