---
name: polygon-ingestion
description: "How to get Massive (formerly Polygon.io) data into a pandas DataFrame and Parquet: access, the request, the format, limits, quirks and terms."
---

# Load Massive (formerly Polygon.io) daily bars into pandas and Parquet

Source entry: https://signal-lab-jet.vercel.app/datasets/polygon
Provider: https://massive.com
Checked against the provider's documentation: 2026-10-29

## Access
- Sign up or log in at massive.com and copy your API key from the Dashboard (the free plan is Stocks Basic).
- Send the key as Authorization: Bearer <key> or as ?apiKey=<key>. Keep it out of code; the snippet reads MASSIVE_API_KEY.

## Request
```bash
curl -H "Authorization: Bearer $MASSIVE_API_KEY" 'https://api.massive.com/v2/aggs/ticker/AAPL/range/1/day/2026-09-29/2026-10-28?adjusted=true&sort=asc&limit=120'
```

## Format
JSON: ticker, adjusted, queryCount, resultsCount, status, results (o, h, l, c, v, vw, n, t) and next_url when there is another page. t is the Unix millisecond start of the bar.

## Load into pandas and Parquet
Needs Python 3 with requests, pandas and pyarrow.
```python
"""Massive (formerly Polygon.io) daily bars for one stock -> pandas -> Parquet. Free key required."""
import os
import requests
import pandas as pd

session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['MASSIVE_API_KEY']}"  # key from your Massive dashboard
url = "https://api.massive.com/v2/aggs/ticker/AAPL/range/1/day/2025-01-01/2026-10-28"
params = {"adjusted": "true", "sort": "asc", "limit": 50000}
rows = []
while url:
    r = session.get(url, params=params, timeout=30)
    r.raise_for_status()
    body = r.json()
    rows += body.get("results", [])
    url, params = body.get("next_url"), None   # next_url already carries the query; the header carries the key

df = pd.DataFrame(rows).rename(columns={"o": "open", "h": "high", "l": "low", "c": "close",
                                        "v": "volume", "vw": "vwap", "n": "transactions"})
df["date"] = pd.to_datetime(df.pop("t"), unit="ms", utc=True)   # t = start of the bar, Unix milliseconds
df.to_parquet("massive_aapl_daily.parquet", index=False)
print(len(df), "rows", df["date"].min(), "to", df["date"].max())
```
Not run: it needs a personal API key.

## Limits
- Free Stocks Basic: 5 API calls a minute, 2 years of history, end-of-day data.

## Quirks
- Results are split-adjusted by default; pass adjusted=false for raw prices.
- limit caps the base aggregates used to build the bars (default 5,000, maximum 50,000); follow next_url for more.
- Without a key the API answers HTTP 401 "API Key was not provided" (observed 2026-10-29).

## Terms and attribution
- Stocks Basic is a personal-license plan. Massive's Individuals terms cover personal, individual and non-business use of products labeled Individual Use; business or commercial use falls under its Businesses terms and plans.
- Terms page: https://massive.com/legal/terms

## Sources
- https://massive.com/docs/rest/quickstart: "Visit your Dashboard to find your API key."
- https://massive.com/pricing: "5 API Calls / Minute"
- https://massive.com/pricing: "2 Years Historical Data"
- https://massive.com/pricing: "End of Day Data"
- https://massive.com/legal/terms: "The Massive for Individuals Terms of Service apply to your personal, individual, and non-business use of any service or product labeled for "Individual Use"."
- https://massive.com/docs/rest/stocks/aggregates/custom-bars: "Max 50000 and Default 5000."
- https://massive.com/docs/rest/stocks/aggregates/custom-bars: "The Unix millisecond timestamp for the start of the aggregate window."

Information for educational purposes only. Free tiers, limits and licenses belong to the providers and may change; check the provider's own terms before using the data.
