REST API Reference

Developer API Documentation

Programmatic access to StashGamma end-of-day stock data — pick a date range, get back real OHLCV bars for any U.S.-listed symbol, going back 15 years.

Overview

The Developer API has one endpoint: end-of-day OHLCV bars for a symbol over a date range you choose — daily by default, or aggregated to weekly, monthly, quarterly or yearly candles with interval. All requests are read-only (GET) and require an API key. Base URL:

https://www.stashgamma.com/api/dataapi/v1

Authentication

Pass your API key via either header — both are accepted, use whichever your HTTP client/library makes more convenient:

X-Api-Key: sg_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

# or

Authorization: Bearer sg_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Get a key from Profile → Developer API — the Free tier is instant and self-serve, no payment required.*

* No credit card, ever — subject to the fair-use throttling limits below (300 req/hour · 800/day · 4,000/week). Keys that repeatedly exceed a window may see additional cooldown beyond the standard Retry-After.

Rate limits

PlanPricePer hourPer dayPer week
Free$03008004000

Every response (success or error) includes rate-limit headers so you can self-throttle without guessing:

X-RateLimit-Plan: free
X-RateLimit-Limit-Hour: 300
X-RateLimit-Remaining-Hour: 297
X-RateLimit-Limit-Day: 800
X-RateLimit-Remaining-Day: 790
X-RateLimit-Limit-Week: 4000
X-RateLimit-Remaining-Week: 3950

A 429 also includes a standard Retry-After header (seconds until the exceeded window resets).

GET /v1/eod/{symbol}

OHLCV bars for one symbol, optionally sliced to a date range and aggregated to weekly, monthly, quarterly or yearly candles.

Data basis (read before backtesting)

  • Prices are current-basis: split-adjusted at the source, not dividend-adjusted. Bars before a split are shown on the post-split basis. Adjustment factors are not published.
  • Each symbol's full history is regenerated and overwritten nightly. Corrections replace prior values in place.
  • There is no revision history, as-of query, or per-bar availability timestamp. The series is not point-in-time and cannot reproduce what was available on a past date.
  • Bars are regular-session US trading days, dated by trading date. Volume is in shares as delivered by the source. Delistings and symbol reuse are not flagged.
ParamTypeRequiredNotes
symbolpathyese.g. QQQ, case-insensitive
fromquerynoYYYY-MM-DD, default = 30 days before to for daily bars; 1 year for weekly, 5 years for monthly, 15 years for quarterly and yearly
toquerynoYYYY-MM-DD, default = today
intervalquerynodaily (default), weekly, monthly, quarterly or yearly. Bars above daily are aggregated from the daily bars inside your date range: open of the first day, close of the last day, running high/low, summed volume. Each aggregated bar adds start, end and days so a partial edge period is visible. Weekly bars are keyed by the Monday of the week, monthly as YYYY-MM, quarterly as YYYY-Qn, yearly as YYYY.

Example request

curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://www.stashgamma.com/api/dataapi/v1/eod/QQQ?from=2026-07-01&to=2026-08-01"

Example response — 200 OK

{
  "symbol": "QQQ",
  "interval": "daily",
  "from": "2026-07-01",
  "to": "2026-08-01",
  "count": 23,
  "bars": [
    { "date": "2026-07-01", "open": 611.2, "high": 614.8, "low": 609.5, "close": 613.1, "volume": 28451200 },
    { "date": "2026-07-02", "open": 613.4, "high": 615.0, "low": 611.9, "close": 614.2, "volume": 24310500 }
  ]
}

Aggregated example — interval=quarterly

curl -H "X-Api-Key: YOUR_API_KEY"   "https://www.stashgamma.com/api/dataapi/v1/eod/QQQ?from=2025-01-01&to=2025-12-31&interval=quarterly"

{
  "symbol": "QQQ",
  "interval": "quarterly",
  "from": "2025-01-01",
  "to": "2025-12-31",
  "count": 4,
  "bars": [
    { "date": "2025-Q1", "start": "2025-01-02", "end": "2025-03-31", "days": 60,
      "open": 514.3, "high": 540.8, "low": 467.6, "close": 468.9, "volume": 2381044100 },
    { "date": "2025-Q2", "start": "2025-04-01", "end": "2025-06-30", "days": 62, "...": "..." }
  ]
}

Code examples

Working, copy-pasteable examples for curl, Python, JavaScript, Node.js, Java, Go, Excel, and a Jupyter notebook are in the step-by-step quickstart guide. Downloadable source for all of them (plus MCP client examples):

LanguageFile
Pythoneod_quickstart.py
Node.jseod-quickstart.mjs
JavaStashGammaEodExample.java
Gomain.go
Jupyter / pandasstashgamma_eod_quickstart.ipynb
MCP (Python & Node clients)mcp/

Every example reads the key from an environment variable (STASHGAMMA_API_KEY) and demonstrates backing off on a 429 using the Retry-After header — copy that pattern into your own client.

MCP endpoint (for AI agents)

If you're wiring StashGamma data into Claude or another MCP-capable agent instead of calling the REST endpoint directly, point it at the Streamable HTTP MCP endpoint below. Same API key, same rate-limit bucket as /v1/eod/{symbol} — a tool call and a REST call against the same key draw from the same hour/day/week quota.

https://www.stashgamma.com/api/dataapi/mcp

Auth is the same X-Api-Key / Authorization: Bearer header, sent alongside the JSON-RPC POST body — most MCP client configs let you set custom headers per server.

Some hosted AI assistants only give you a box for a server URL, with no way to add a header. For those, put the key in the URL instead — either form works, and both are treated exactly like the header:

https://www.stashgamma.com/api/dataapi/mcp/YOUR_API_KEY
https://www.stashgamma.com/api/dataapi/mcp?key=YOUR_API_KEY

A URL with a key in it is a secret — it can end up in that assistant's saved settings and logs. If one leaks, rotate the key from Profile → Developer API and the old URL stops working immediately. When a header is also sent, the header wins.

Three read-only tools are exposed:

ToolArgumentsReturns
get_eod_datasymbol (required), from/to (optional, YYYY-MM-DD), interval (optional: daily, weekly, monthly, quarterly, yearly)Same OHLCV bar shape as the REST endpoint, including aggregated candles
get_latest_quotesymbol (required)Most recent daily bar plus change vs the previous close
get_api_usagenonePlan, used/remaining requests per hour/day/week — does not count against quota
run_stock_scanquery, universe?, sort?, limit?Run a stock scan. See the AI Stock Scanner MCP docs, which also cover validate_scan_query, get_scan_syntax and get_example_scans
get_portfolio_positionsportfolio (optional)Your account's private position list (symbol, side, quantity, average cost, cost basis)
add_portfolio_positionsymbol, quantity, avg_cost?, side?, notes?, mode? (replace | add)The saved position (merged with a weighted average cost when mode is add)
import_portfolio_csvcsv text, mode? (merge | replace)Counts of positions added / updated / removed plus per-row errors
remove_portfolio_position / clear_portfoliosymbol + side? · confirm: trueThe removed position / number removed. See the portfolio tracker guide.

Stateless — every call is a single JSON response (no SSE stream, no session to keep alive). Rate-limit/auth errors come back as a tool result with isError: true rather than an HTTP error, so your agent can see and react to the reason (e.g. "retry in Ns") directly.

Step-by-step setup, ready-to-drop config files, and code samples for each client:

  • Claude Code / Claude Desktop — drop a url + headers entry into your MCP server config.
  • OpenAI — the Responses API takes this same endpoint as a built-in type: "mcp" tool.
  • Hosted assistants with a URL-only connector form — paste the key-in-URL form above and choose "no authentication" if asked.
  • ChatGPT Custom GPTs — connect via an OpenAPI schema (Actions) instead of MCP; a ready-made schema is provided.
  • Your own agent/script — call get_eod_data directly via the official Python or Node.js MCP SDK.

Full walkthrough for each: Connect via MCP.

Error codes

StatusWhenExample body
400Missing symbol in path, or `from`/`to` malformed/inverted{"error":"\"from\" must be on or before \"to\"","success":false}
401Missing API key (no X-Api-Key / Authorization header){"error":"Missing API key. Pass it via the X-Api-Key header or Authorization: Bearer <key>.","success":false}
401Malformed, unknown, expired, suspended, or revoked key{"error":"This API key has expired — resubscribe to get a new one","success":false}
404No EOD data for that symbol{"error":"No EOD data for symbol ZZZZ","success":false}
429Hour/day/week rate limit exceeded for your plan{"error":"Rate limit exceeded for the hour window (101/100). Try again in 1800s.","success":false}

Status & support

Live uptime and per-endpoint health (tested daily, automatically) are on the API status page. Questions or issues: contact support.