Strasmore Research
Leren Matt ConnorDoor Matt Connor · Bijgewerkt 2026-10-05 · data as of October 5, 2026 · refreshed weekly

Gratis stock market data API gebruiken met Python

Leer hoe u een gratis stock market data API aanroept met Python, requests en pandas. Deze Ubuntu handleiding toont het laden van JSON naar een DataFrame voor twintigdaags volume.

Het aanroepen van een gratis stock market data API met Python vereist één HTTP-verzoek en twee bibliotheken die de meeste gebruikers al hebben: requests voor het ophalen van de JSON en pandas om deze om te zetten in een DataFrame. Er is geen API-sleutel vereist. Deze handleiding verloopt van begin tot eind in een nieuwe Ubuntu-container en eindigt met het printen van het gemiddelde volume over twintig sessies voor een enkel ticker. Wat de endpoints aanbieden, wordt behandeld in de gratis stock market data API gids; deze pagina behandelt de Python-kant van dezelfde API.

Python, requests en pandas installeren op een nieuwe Ubuntu-container

Een standaard Ubuntu-image bevat de venv-module van Python niet, en recente releases staan niet toe dat pakketten in de systeeminterpreter worden geïnstalleerd. Een virtuele omgeving omzeilt beide problemen en werkt op elke huidige Ubuntu-release op dezelfde manier. Voer deze commando's uit als root, of zet sudo voor de apt-get-regels.

export DEBIAN_FRONTEND=noninteractive
apt-get update && apt-get install -y python3 python3-venv
python3 -m venv .venv
. .venv/bin/activate
pip install "requests>=2.31,<3" "pandas>=2.0,<4"

De versiereeksen zijn bewust gekozen. Elke reeks accepteert elke onderhouden release van de library en sluit alleen een toekomstige major-versie uit waarvan het gedrag onbekend is. Hierdoor blijft dezelfde regel probleemloos werken naarmate Ubuntu de standaardversie van Python bijwerkt. Er is niets vastgezet op een build die toevallig vandaag bestaat. Alles hieronder gaat ervan uit dat de omgeving actief is.

Hoe roep ik een gratis beursdata-API aan in Python?

De demo-endpoints beantwoorden eenvoudige GET-verzoeken zonder dat een sleutel of registratie vereist is. Het eerste script vraagt om een van de samengestelde queries en print de structuur van het resultaat.

import json
import requests

BASE = "https://ai.strasmore.com/api/demo"

resp = requests.get(BASE, params={"q": "dividend_yield_leaders"}, timeout=30)
resp.raise_for_status()
payload = resp.json()

print(list(payload.keys()))
print(payload["columns"])
print(json.dumps(payload["rows"][0], indent=2))

requests.get bouwt de query-string op vanuit params, raise_for_status() zet elke 4xx- of 5xx-status om in een exception, en .json() parseert de body naar een dictionary. Teruggebracht tot de essentie ziet die dictionary er als volgt uit:

{
  "key":     "dividend_yield_leaders",
  "label":   "...",
  "nl":      "Which large-cap US stocks currently have the highest dividend yields?",
  "sql":     "SELECT ...",
  "columns": ["as_of", "ticker", "dividend_yield_pct", "price", "market_cap_bn", "price_to_earnings"],
  "rows":    [{"as_of": "<date>", "ticker": "<symbol>", "dividend_yield_pct": <number>, ...}, ...],
  "elapsed": "...",
  "source":  "Strasmore Research",
  "more":    "..."
}

Twee keys zijn van belang voor pandas. columns benoemt de velden in de juiste volgorde en rows bevat één JSON-object per record met die namen als keys. De sql key is de exacte query die de cijfers heeft gegenereerd; deze wordt bij elk antwoord meegegeven, waardoor de data controleerbaar is in plaats van een black box. Een GET naar /api/demo/catalog geeft een overzicht van alle samengestelde keys, inclusief het SQL-endpoint dat vervolgens wordt gebruikt.

Hoe laad ik de JSON in een pandas DataFrame?

De samengestelde queries beantwoorden vaste vragen. Dagkoersen voor een ticker naar keuze zijn afkomstig van het SQL-eindpunt zonder registratie op /api/demo/sql, dat een read-only query in een sql-parameter accepteert en hetzelfde columns- en rows-paar retourneert. De limieten, zoals die gelden per september tweeduizend zesentwintig, worden in elk antwoord vermeld: vijfhonderd rijen, twintig seconden, één jaar historie, geen sleutel vereist. De gratis SQL API met een sleutel verhoogt het plafond naar honderd queries per dag over een langere historie; de aanvraagcode is identiek.

Twee beveiligingen in de SQL houden het gemiddelde betrouwbaar. Een tabel die tijdens de sessie wordt bijgewerkt, kan meer dan één rij voor de huidige dag bevatten, en het volume van die dag is gedeeltelijk zolang de markt open is. De query stopt bij gisteren met date < today() en voegt eventuele dubbele rijen voor een datum samen met GROUP BY date en max(volume). Als u niet heeft gekeken hoe een dagkoers wordt samengesteld uit de tape, legt hoe OHLCV-bars worden opgebouwd uit waar die rijen vandaan komen.

import time
import requests
import pandas as pd

SQL_URL = "https://ai.strasmore.com/api/demo/sql"
TICKER = "AAPL"

SQL = f"""
SELECT date, max(volume) AS volume
FROM stocks_daily_aggs
WHERE ticker = '{TICKER}'
  AND date < today()
GROUP BY date
ORDER BY date DESC
LIMIT 20
"""


def fetch(sql, attempts=4):
    """GET the SQL endpoint. Back off on 429 and 5xx; stop on any other 4xx."""
    delay = 2.0
    for attempt in range(1, attempts + 1):
        resp = requests.get(SQL_URL, params={"sql": " ".join(sql.split())}, timeout=30)
        if resp.status_code == 200:
            return resp.json()
        if resp.status_code == 429 or resp.status_code >= 500:
            try:
                wait = max(1.0, float(resp.headers.get("Retry-After")))
            except (TypeError, ValueError):
                wait = delay
            print(f"HTTP {resp.status_code}; waiting {wait:.0f}s (attempt {attempt} of {attempts})")
            time.sleep(wait)
            delay *= 2
            continue
        raise SystemExit(f"HTTP {resp.status_code}: {resp.text[:400]}")
    raise SystemExit("gave up after repeated 429 or 5xx responses")


payload = fetch(SQL)
df = pd.DataFrame(payload["rows"], columns=payload["columns"])
df["date"] = pd.to_datetime(df["date"])
df["volume"] = pd.to_numeric(df["volume"])
df = df.sort_values("date").reset_index(drop=True)

print(df.to_string(index=False))

avg_20 = df["volume"].mean()
first, last = df["date"].iloc[0], df["date"].iloc[-1]
print(f"{TICKER} 20-session average volume: {avg_20 / 1e6:.1f}M shares "
      f"({first:%Y-%m-%d} to {last:%Y-%m-%d}, {len(df)} sessions)")

pd.DataFrame(rows, columns=columns) bouwt de tabel direct vanuit de lijst met objecten, en het doorgeven van columns behoudt de veldvolgorde van de API. Twee conversies volgen: datums komen binnen als strings en worden echte timestamps, en volume wordt naar een getal omgezet voor het geval een antwoord dit ooit als tekst serialiseert. Sorteren van oud naar nieuw plaatst het frame in de volgorde die een grafiek verwacht. df["volume"].mean() over exact twintig rijen is het gemiddelde over twintig sessies, en de laatste regel drukt dit af in miljoenen, naast het datumbereik dat het beslaat.

Wat meet het twintigdaags gemiddelde volume nu eigenlijk?

Het gemiddelde volume vlakt een volatiele dagelijkse reeks af. Het aantal verhandelde aandelen in een enkele sessie schommelt door indexherwegingen, optie-expiraties, publicaties van kwartaalcijfers en nieuwsberichten; twintig sessies beslaan ongeveer één kalendermaand, lang genoeg om die uitschieters te dempen en kort genoeg om een verandering in de handelsactiviteit van een aandeel te volgen. Het onderstaande paneel berekent dezelfde statistiek als het script, op basis van dezelfde dagelijkse tabel, over de afgelopen maanden, waarbij de ruwe sessies en het voortschrijdend gemiddelde naast elkaar worden weergegeven.

QueryAAPL dagelijks volume en het voortschrijdend gemiddelde over 20 sessies, in miljoenen aandelen
34 rows (showing 20)
sessievolume (miljoenen)gem. 20d (miljoenen)
2026-08-1738.251.9
2026-08-1853.452.5
2026-08-1950.553
2026-08-204153.1
2026-08-2146.953
2026-08-2434.752.3
2026-08-2525.951
2026-08-263449.9
2026-08-2732.447.8
2026-08-2838.643.1
2026-08-3141.241.4
2026-09-0153.240.6
2026-09-0233.839.8
2026-09-0337.239.4
2026-09-0439.639.7
2026-09-0835.539.2
2026-09-0965.640.6
2026-09-107042
2026-09-1150.742.5
2026-09-1439.343.1
De exacte SQL achter elk getal
SELECT
    session,
    volume_millions,
    avg_20d_millions
FROM
(
    SELECT
        toString(d)                                                                            AS session,
        round(vol / 1e6, 1)                                                                    AS volume_millions,
        round(avg(vol) OVER (ORDER BY d ROWS BETWEEN 19 PRECEDING AND CURRENT ROW) / 1e6, 1)  AS avg_20d_millions,
        count() OVER (ORDER BY d ROWS BETWEEN 19 PRECEDING AND CURRENT ROW)                    AS sessions_in_window
    FROM
    (
        SELECT
            date                    AS d,
            toFloat64(max(volume))  AS vol
        FROM global_markets.stocks_daily_aggs
        WHERE ticker = 'AAPL'
          AND date >= today() - 75
          AND date <  today()
        GROUP BY date
    )
)
WHERE sessions_in_window = 20
ORDER BY session
Voer het zelf uit

De dagelijkse reeks bestaat uit één punt per sessie; de afgevlakte reeks is het gemiddelde van die sessie en de negentien voorgaande. Het paneel loopt van 2026-08-17 tot 2026-10-02, 34 sessies die elk een volledig venster van twintig sessies achter zich hebben. Op de laatste van die sessies verhandelde AAPL 33.3 miljoen aandelen tegenover een twintigdaags gemiddelde van 42.2 miljoen. Dat gemiddelde is het getal dat het script afdrukte op de dag dat deze pagina werd gegenereerd. Voer het vandaag opnieuw uit en de twintig sessies zijn opgeschoven, en daarmee ook het cijfer.

De ticker wijzigen

Wijzig TICKER en het script werkt voor elk in de VS genoteerd symbool in de tabel. De onderstaande vergelijking voert dezelfde berekening over twintig sessies uit voor vier bekende namen; dit geeft een snelle indicatie van de schaal van het "gemiddelde volume" voor een index-ETF en drie mega-caps.

QueryVoortschrijdend gemiddelde volume over 20 sessies, vier bekende tickers, in miljoenen aandelen
tickergem. 20d (miljoenen)eerste sessielaatste sessie
NVDA1102026-09-042026-10-02
SPY462026-09-042026-10-02
AAPL42.22026-09-042026-10-02
MSFT21.32026-09-042026-10-02
De exacte SQL achter elk getal
SELECT
    ticker,
    round(avg(vol) / 1e6, 1)   AS avg_20d_millions,
    toString(min(d))           AS first_session,
    toString(max(d))           AS last_session
FROM
(
    SELECT
        ticker,
        d,
        vol,
        row_number() OVER (PARTITION BY ticker ORDER BY d DESC) AS rn
    FROM
    (
        SELECT
            ticker,
            date                    AS d,
            toFloat64(max(volume))  AS vol
        FROM global_markets.stocks_daily_aggs
        WHERE ticker IN ('SPY', 'NVDA', 'AAPL', 'MSFT')
          AND date >= today() - 45
          AND date <  today()
        GROUP BY ticker, date
    )
)
WHERE rn <= 20
GROUP BY ticker
HAVING count() = 20
ORDER BY avg_20d_millions DESC
Voer het zelf uit

NVDA heeft het hoogste gemiddelde over twintig sessies van de vier, met 110 miljoen aandelen per sessie, en MSFT het laagste met 21.3 miljoen, alles gemeten over de sessies van 2026-09-04 tot 2026-10-02. Het cijfer voor AAPL is hier hetzelfde getal als waar de bovenstaande trace eindigt: één definitie, eenmalig berekend, ongeacht waar deze verschijnt.

Hoe ziet een 429 eruit en hoe voer ik een beleefde retry uit?

Een rate limit wordt niet als data, maar als HTTP-statuscode verzonden. De respons bevat status 429 Too Many Requests en vaak een Retry-After-header met het aantal seconden dat u moet wachten; de body bevat niet de opgevraagde data, wat de reden is dat de fetch-helper status_code controleert voordat er iets wordt geparseerd. De regels hiervoor zijn, in volgorde:

  1. 200: parseer en retourneer de JSON.
  2. 429 of elke andere 5xx: wacht Retry-After indien aanwezig, anders hanteert u een back-off (twee, vier, acht, dan zestien seconden) en probeert u het opnieuw, tot in totaal vier pogingen.
  3. Elke andere 4xx: stop. De body geeft precies aan waarom de query werd afgewezen, zoals een tabel die niet bestaat of een statement dat door de read-only gate werd geweigerd; opnieuw proberen verandert niets aan het resultaat. Een onbekende tabel resulteert bijvoorbeeld in een 400.

Twee gewoontes voorkomen dat u de limiet overschrijdt. Samengestelde resultaten verversen maximaal elke tien minuten; een loop die sneller polt, haalt identieke bytes op, terwijl het lokaal cachen van de payload niets kost. Vraag bovendien alleen om wat u nodig heeft: LIMIT 20 voor een gemiddelde over twintig sessies, in plaats van een jaar aan rijen die u vervolgens weggooit. Netwerkfouten (een verbroken verbinding, een DNS-storing) verschijnen als requests.RequestException, wat de helper niet opvangt; verpak de call in try/except als het script onbeheerd draait.

Veelgestelde vragen

Is er een gratis API voor aandelenmarktdata voor Python?

Ja. De demo-endpoints in deze handleiding beantwoorden niet-geauthenticeerde GET-verzoeken van requests met JSON die direct in pandas wordt geladen: samengestelde queries op /api/demo?q=<key> en handgeschreven read-only SQL op /api/demo/sql, met een limiet van vijfhonderd rijen en één jaar aan historie. Geen key, geen registratie vereist.

Heb ik een API key nodig om aandelenkoersen op te halen in Python?

Niet voor de demo-laag die hier wordt gebruikt. Een gratis key verhoogt de limiet naar honderd queries per dag met een langere historie, waarbij de Python-code ongewijzigd blijft. Ontwikkelaars die dezelfde endpoints als tools voor een LLM inzetten, kunnen beginnen bij marktdata-vaardigheden voor AI-agents.

Hoe converteer ik een JSON API-respons naar een pandas DataFrame?

Parse de body met resp.json() en geef de lijst met rij-objecten door aan pd.DataFrame(rows, columns=columns). Converteer datums die als string zijn opgemaakt met pd.to_datetime en numerieke strings met pd.to_numeric voordat u berekeningen uitvoert; JSON bevat geen datumtype en sommige API's serialiseren decimalen als tekst.

Wat betekent een 429-foutmelding bij het aanroepen van een aandelen-API?

HTTP 429 staat voor Too Many Requests: de server past rate-limiting toe op de aanroeper. Wacht het aantal seconden dat wordt aangegeven in de Retry-After-header indien deze wordt meegestuurd, hanteer anders een exponentiële back-off en cache de antwoorden in plaats van te pollen naar een resultaat dat slechts elke tien minuten ververst.

Hoe bereken je het gemiddelde volume in pandas?

Laad één rij per sessie met een volume-kolom, behoud de laatste twintig volledige sessies en roep df["volume"].mean() aan. Gebruik voor een voortschrijdende versie over een langere historie df["volume"].rolling(20).mean(); dit is de methode die het bovenstaande trace-paneel tekent.


Elk paneel hierboven wordt geleverd met de exacte SQL eronder, en het script is gebaseerd op hetzelfde principe met een requests-aanroep aan het begin. Gratis API key. Honderd queries per dag, tweeëntwintig jaar aan historie, SQL over de ruwe tape. Geen creditcard nodig. Vraag een API key aan

#python#stock market api#free market data#requests#pandas#tutorial