Lección 1.3.3 · 55 min
APIs de datos retail y prosumer
Entre las fuentes gratuitas y los proveedores institucionales hay un escalón de APIs con planes de decenas de dólares al mes. Esta lección compara siete con sus propias páginas, lee el formato de una respuesta real y escribe un cliente que pagina y respeta el límite de peticiones contra un servidor local.
- Módulo
- 1.3 · Fuentes de datos de mercado
- Duración
- 55 min
Lecciones del nivel 1
1.1 · Cómo funcionan los mercados
1.2 · Clases de activos
- 1.2.1 Acciones: splits, dividendos y eventos corporativos
- 1.2.2 ETFs: réplica, tracking error, apalancados y UCITS
- 1.2.3 Futuros: especificaciones, margen, roll y contango
- 1.2.4 Forex: pares, pips, lotes, rollover y CFD
- 1.2.5 Opciones: call, put, valor intrínseco y valor temporal
- 1.2.6 Cripto: spot, perpetuos, funding y custodia
- 1.2.7 Renta fija y tipos de interés; CFDs y productos apalancados
1.3 · Fuentes de datos de mercado
- 1.3.1 Tipos de datos de mercado: EOD, barras, ticks, libro y referencia
- 1.3.2 Fuentes de datos gratuitas
- 1.3.3 APIs de datos retail y prosumer
- 1.3.4 Proveedores de datos profesionales
- 1.3.5 Datos de cripto y on-chain
- 1.3.6 Brókeres como fuente de datos
- 1.3.7 Licencias y términos de uso de datos
- 1.3.8 Cómo elegir proveedor de datos
1.4 · Ingeniería de datos financieros
- 1.4.1 Descarga por REST y websockets: paginación, límites y reintentos
- 1.4.2 Limpieza de datos: faltantes, ticks erróneos, huecos y zonas horarias
- 1.4.3 Ajustes por splits y dividendos: precios ajustados y sin ajustar
- 1.4.4 Universos sin sesgo de supervivencia: deslistados, fusiones y cambios de símbolo
- 1.4.5 Construcción de barras: tiempo, ticks, volumen, dólar y desequilibrio
- 1.4.6 Series continuas de futuros: reglas de roll, ajuste por diferencia y ajuste por ratio
- 1.4.7 Almacenamiento: CSV, Parquet, particionado, DuckDB, TimescaleDB y ArcticDB
- 1.4.8 Pipeline reproducible: descarga, validación, actualización incremental y tests de calidad
1.5 · Datos fundamentales y macro
1.6 · Análisis exploratorio financiero
1.7 · Análisis técnico programado
El escalón entre lo gratuito y lo institucional
Las fuentes de la lección 1.3.2 son gratuitas, pero no oficiales (yfinance) o no cubren el mercado al detalle (FRED, EDGAR, bancos centrales). Los proveedores de la lección 1.3.4 cubren todo, a precio institucional. En medio hay un grupo de empresas que venden APIs de datos a particulares, a desarrolladores y a pequeñas empresas: el mercado retail y prosumer. Tienen tres cosas en común:
- Un plan gratuito, con historia corta o pocas peticiones, para probar.
- Planes de pago de decenas de dólares al mes, que suben historia, quitan límites y reducen el retraso.
- Una API REST documentada, a veces websockets para tiempo real, y un cliente oficial en Python.
Esta lección compara siete con lo que dicen sus propias páginas. Todos los precios y límites que siguen están leídos en la página de cada proveedor a fecha de 21/10/2026. Cambian a menudo: antes de pagar hay que volver a leerlos.
Massive, antes Polygon.io
Polygon.io cambió su nombre a Massive el 30 de octubre de 2025. Según el anuncio, la API antigua (api.polygon.io)
sigue funcionando «durante un periodo prolongado» en paralelo con la nueva (api.massive.com), y las páginas antiguas
redirigen a las nuevas1Nota 1 · Massive, «Polygon.io is Now Massive», 30/10/2025, y página de precios, consultadas el 21/10/2026.. El cliente oficial de Python se publica ahora como massive; el paquete antiguo,
polygon-api-client, sigue en PyPI pero su última versión es del día del cambio de nombre.
Sus planes de acciones son una escalera clara:
| Plan | Precio al mes | Historia | Retraso | Peticiones |
|---|---|---|---|---|
| Stocks Basic | 0 USD | 2 años | Fin de día | 5 por minuto |
| Stocks Starter | 29 USD | 5 años | 15 minutos | Ilimitadas |
| Stocks Developer | 79 USD | 10 años | 15 minutos | Ilimitadas; añade operaciones |
| Stocks Advanced | 199 USD | 20+ años | Tiempo real | Ilimitadas; añade cotizaciones |
La figura de la cabecera resume los cuatro planes. Precios con facturación mensual; la página ofrece un 20 % de descuento con el plan anual. El tiempo real solo está en Advanced, y la página lo marca como para usuarios no profesionales («Non-pros only»): la diferencia entre profesional y no profesional es la de las licencias de las bolsas (lección 1.3.7). Lo que suben los planes no es solo la historia: Developer añade las operaciones (tick a tick) y Advanced, las cotizaciones (bid y ask), que son los datos de las lecciones 1.1.3 y 1.3.1.
Los demás, plan por plan
| Proveedor | Plan gratuito | Plan de pago más barato con acciones de EE. UU. | Detalle |
|---|---|---|---|
| Alpaca | Basic: tiempo real solo de IEX, histórico desde 2016 sin los últimos 15 minutos, 200 llamadas por minuto | Algo Trader Plus: 99 USD al mes, todas las bolsas de EE. UU., 10.000 llamadas por minuto | El bróker da los datos con la cuenta; paginación con next_page_token |
| Tiingo | Starter: 50 peticiones por hora, 1.000 al día, 500 símbolos al mes | Power: 30 USD al mes (300 al año) para particulares; 50 USD para uso comercial interno | Fin de día con más de 30 años, con precios ajustados, dividendos y splits en la misma respuesta |
| EODHD | 20 llamadas al día, 1 año de historia | EOD Historical All-World: 19,99 USD al mes | Cobra cada llamada en unidades: 1 por fin de día, 5 por intradía, 10 por fundamentales |
| Alpha Vantage | 25 peticiones al día | 49,99 USD al mes: 75 peticiones por minuto y datos de EE. UU. con 15 minutos de retraso | El tiempo real y el diferido son de pago |
| Twelve Data | Basic: 8 créditos por minuto, 800 al día | Grow: desde 29 USD al mes (55 créditos por minuto) | Facturado al año, más barato |
| Financial Modeling Prep | Basic: 250 llamadas al día, 5 años de fin de día | Starter: 29 USD al mes (19 USD al mes con facturación anual), 300 por minuto | Fuerte en fundamentales |
Fuentes: la página de precios o de planes de cada proveedor, leída el 21/10/20262Nota 2 · Páginas de precios consultadas el 21/10/2026: Alpaca, Tiingo, EODHD, Alpha Vantage, Twelve Data, Financial Modeling Prep.. Los planes no son equivalentes: unos son de fin de día, otros intradía con retraso, otros tiempo real de una sola bolsa. Comparar solo el precio sería comparar cosas distintas.
Dos detalles que no se ven en la tabla y que importan:
- Qué bolsa da el «tiempo real». El plan gratuito de Alpaca da tiempo real solo de IEX, una de las bolsas de EE. UU.: el precio es real, pero el volumen es una fracción del total y el mejor precio puede no ser el del mercado consolidado (lección 1.1.1). Para el consolidado hace falta el plan de pago.
- Créditos, no peticiones. EODHD y Twelve Data cuentan créditos, y no todas las llamadas cuestan lo mismo. Un límite de «100.000 llamadas al día» se agota antes si cada llamada intradía cuesta cinco.
Comparar límites en la misma unidad
Los planes gratuitos dan sus límites en unidades distintas. Para compararlos hay que pasarlos a una sola, por ejemplo peticiones al día, sabiendo que es un techo teórico: nadie hace peticiones sin parar las 24 horas.
import math
import pandas as pd
limites = pd.DataFrame([
("Massive Basic", 5, "minuto"),
("Alpaca Basic", 200, "minuto"),
("Tiingo Starter", 1000, "dia"),
("Twelve Data Basic", 800, "dia"),
("FMP Basic", 250, "dia"),
("Alpha Vantage", 25, "dia"),
("EODHD gratuito", 20, "dia"),
], columns=["plan", "limite", "por"]).set_index("plan")
por_dia = {"minuto": 24 * 60, "hora": 24, "dia": 1}
limites["al_dia"] = limites["limite"] * limites["por"].map(por_dia)
limites.sort_values("al_dia", ascending=False) limite por al_dia
plan
Alpaca Basic 200 minuto 288000
Massive Basic 5 minuto 7200
Tiingo Starter 1000 dia 1000
Twelve Data Basic 800 dia 800
FMP Basic 250 dia 250
Alpha Vantage 25 dia 25
EODHD gratuito 20 dia 20Tiingo tiene dos límites, uno por hora (50) y otro por día (1.000); aquí cuenta el más restrictivo de los dos, el diario, porque 50 por hora durante 24 horas serían 1.200. Twelve Data también tiene dos (8 por minuto y 800 al día). Con este techo ya se puede estimar cuánto tarda la descarga que importa:
acciones, por_peticion = 500, 1 # una petición por acción
peticiones = acciones * por_peticion
limites["dias"] = (peticiones / limites["al_dia"]).apply(math.ceil)
limites[["al_dia", "dias"]].sort_values("dias") al_dia dias
plan
Massive Basic 7200 1
Alpaca Basic 288000 1
Tiingo Starter 1000 1
Twelve Data Basic 800 1
FMP Basic 250 2
Alpha Vantage 25 20
EODHD gratuito 20 25Con Massive, Alpaca, Tiingo o Twelve Data, el histórico diario de 500 acciones cabe en un día (en Massive, a 5 por minuto, son 100 minutos); con FMP, en dos. Con Alpha Vantage o EODHD hacen falta 20 o 25 días. Eso no hace inútiles a estos planes: obliga a diseñar la descarga para que guarde lo que lleva y continúe al día siguiente sin repetir (lección 1.4.8). Y queda la otra mitad del problema: el plan Basic de Massive solo tiene 2 años de historia, y el gratuito de EODHD, 1. El límite dice cuánto tardas; la historia, si lo que necesitas existe en ese plan.
Anatomía de una respuesta de barras
El endpoint de barras de Massive es /v2/aggs/ticker/{ticker}/range/{multiplicador}/{intervalo}/{desde}/{hasta}. La
respuesta lleva metadatos (status, request_id, resultsCount, adjusted), la lista results con una barra por
elemento y, si hay más páginas, next_url. Cada barra trae o, h, l, c (precios), v (volumen), vw (precio
medio ponderado por volumen), n (número de operaciones) y t, el inicio de la barra en milisegundos Unix; limit
admite hasta 50.000 barras por petición, con 5.000 por defecto3Nota 3 · Massive, Custom Bars (OHLC) y REST API Quickstart, consultadas el 21/10/2026..
Una muestra ilustrativa con esa estructura (los números son de NOVA, la acción ficticia del curso):
import json
texto = """{
"ticker": "NOVA", "adjusted": false, "queryCount": 2,
"resultsCount": 2, "status": "OK", "request_id": "ej-0001",
"results": [
{"t": 1748836800000, "o": 53.35, "h": 53.94, "l": 52.55,
"c": 53.12, "v": 8888714, "vw": 53.21, "n": 50946},
{"t": 1748923200000, "o": 53.05, "h": 53.4, "l": 52.6,
"c": 52.92, "v": 6151230, "vw": 52.98, "n": 41210}
],
"next_url": "https://api.massive.com/v2/aggs/...?cursor=ej"
}"""
respuesta = json.loads(texto)
barras = pd.DataFrame(respuesta["results"])
barras["t"] = pd.to_datetime(barras["t"], unit="ms", utc=True)
print(respuesta["status"], respuesta["resultsCount"],
"next_url" in respuesta)
barras.set_index("t")OK 2 True
o h l c v vw n
t
2025-06-02 04:00:00+00:00 53.35 53.94 52.55 53.12 8888714 53.21 50946
2025-06-03 04:00:00+00:00 53.05 53.40 52.60 52.92 6151230 52.98 41210Dos cosas que conviene notar. La marca de una barra diaria es la medianoche de Nueva York expresada en UTC: las
04:00 en verano y las 05:00 en invierno, como en el ejemplo de la documentación. En una barra diaria, la fecha UTC
coincide con la de Nueva York; en barras intradía no: una barra de las 21:00 de Nueva York ya es del día siguiente en
UTC. Por eso se convierte a la zona del mercado (.tz_convert("America/New_York")) antes de quedarse con la fecha. Y adjusted dice si los precios están ajustados por splits; la lección 1.3.8 explica por qué
hay que saberlo siempre.
Un cliente que pagina y respeta el límite
Un cliente REST para estas APIs tiene que hacer tres cosas bien: autenticarse sin dejar la clave en el código, seguir la paginación hasta el final y reaccionar a los errores de límite (HTTP 429) esperando lo que diga el servidor.
Fuente Mermaid
flowchart TB
U[URL inicial] --> G[GET con clave]
G --> C{¿Código?}
C -- 429 --> W[Retry-After]
W --> G
C -- 200 --> R[Guardar results]
R --> N{¿next_url?}
N -- sí --> G
N -- no --> F[Fin]Para ejecutarlo de verdad sin red ni clave, la celda siguiente levanta un servidor local en un hilo que imita la
API: sirve las 502 sesiones de NOVA de 2024 y 2025 en el formato de la sección anterior, en páginas de limit barras
con un cursor en next_url, exige la clave en apiKey y responde 429 con Retry-After: 1 si recibe más de 5
peticiones en un segundo.
import threading
import time
from collections import deque
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.parse import parse_qs, urlparse
nova = pd.read_csv("nova_diario_sin_ajustar.csv",
parse_dates=["fecha"])
nova = nova[nova["fecha"] >= "2024-01-01"]
abre_ny = (nova["fecha"].dt.tz_localize("America/New_York")
.dt.tz_convert("UTC"))
BARRAS = [
{"t": int(ts.timestamp() * 1000), "o": f.apertura,
"h": f.maximo, "l": f.minimo, "c": f.cierre, "v": f.volumen}
for ts, f in zip(abre_ny, nova.itertuples())
]
class ApiFalsa(BaseHTTPRequestHandler):
recientes = deque() # horas de las peticiones aceptadas
contador = {"200": 0, "401": 0, "429": 0}
def responder(self, codigo, cuerpo, cabeceras=()):
self.contador[str(codigo)] += 1
self.send_response(codigo)
self.send_header("Content-Type", "application/json")
for k, v in cabeceras:
self.send_header(k, v)
self.end_headers()
self.wfile.write(json.dumps(cuerpo).encode())
def do_GET(self):
q = parse_qs(urlparse(self.path).query)
if q.get("apiKey") != ["clave-de-prueba"]:
return self.responder(401, {"status": "ERROR"})
ahora = time.monotonic()
while self.recientes and ahora - self.recientes[0] > 1:
self.recientes.popleft()
if len(self.recientes) >= 5:
return self.responder(429, {"status": "ERROR"},
[("Retry-After", "1")])
self.recientes.append(ahora)
inicio = int(q.get("cursor", ["0"])[0])
limite = int(q.get("limit", ["50"])[0])
pagina = BARRAS[inicio:inicio + limite]
cuerpo = {"status": "OK", "resultsCount": len(pagina),
"results": pagina}
if inicio + limite < len(BARRAS):
base = f"http://127.0.0.1:{self.server.server_port}"
cuerpo["next_url"] = (f"{base}/v2/aggs/ticker/NOVA?"
f"cursor={inicio + limite}"
f"&limit={limite}")
self.responder(200, cuerpo)
def log_message(self, *args): # sin registro en la salida
pass
servidor = ThreadingHTTPServer(("127.0.0.1", 0), ApiFalsa)
threading.Thread(target=servidor.serve_forever, daemon=True).start()
URL = f"http://127.0.0.1:{servidor.server_port}/v2/aggs/ticker/NOVA"
print(len(BARRAS), "barras servidas")502 barras servidasEl puerto 0 pide al sistema uno libre, así que la celda no choca con nada que ya esté escuchando. Ahora el cliente,
con la biblioteca estándar (urllib): sin la clave, el servidor responde 401 y urlopen lanza una excepción.
from urllib.error import HTTPError
from urllib.request import urlopen
urlopen(f"{URL}?limit=50", timeout=5)urllib.error.HTTPError: HTTP Error 401: UnauthorizedLa clave se lee de una variable de entorno. En la celda se fija a mano para el servidor local; en un programa real
estaría en el entorno o en un .env fuera del repositorio (lección 0.2.4):
import os
from urllib.parse import urlencode
os.environ.setdefault("API_KEY", "clave-de-prueba") # solo aquí
def get_json(url, intentos=5):
sep = "&" if "?" in url else "?"
clave = urlencode({"apiKey": os.environ["API_KEY"]})
firmada = f"{url}{sep}{clave}"
for _ in range(intentos):
try:
with urlopen(firmada, timeout=5) as r:
return json.load(r)
except HTTPError as e:
if e.code != 429:
raise
time.sleep(float(e.headers.get("Retry-After", "1")))
raise RuntimeError(f"sigue en 429 tras {intentos} intentos")
def descargar_todo(url):
filas, paginas = [], 0
while url:
datos = get_json(url)
filas += datos["results"]
paginas += 1
url = datos.get("next_url")
return filas, paginas
filas, paginas = descargar_todo(f"{URL}?limit=50")
print(len(filas), "barras en", paginas, "páginas;",
ApiFalsa.contador["429"], "respuestas 429")502 barras en 11 páginas; 2 respuestas 429502 barras en 11 páginas de 50 (la última, de 2). El servidor solo acepta 5 peticiones por segundo, así que el cliente
recibe dos 429 por el camino: tras la quinta página y tras la décima. En cada uno espera el segundo que pide
Retry-After y repite la misma petición; no salta de página ni empieza de cero. Si el servidor siguiera en 429
tras varios intentos, el cliente se rinde con un error claro en lugar de quedarse en un bucle. La lección 1.4.1 añade lo
que falta para producción: espera exponencial con aleatoriedad, errores de red y 5xx, y websockets.
df = pd.DataFrame(filas)
df["t"] = pd.to_datetime(df["t"], unit="ms", utc=True)
print(df["t"].is_unique, df["t"].is_monotonic_increasing)
print(df["t"].iloc[0], "->", df["t"].iloc[-1])
servidor.shutdown()True True
2024-01-02 05:00:00+00:00 -> 2025-12-31 05:00:00+00:00La comprobación final es la de siempre: marcas únicas y crecientes, y la primera y la última donde se esperaban. Una paginación mal hecha suele dejar duplicados en las juntas de las páginas o perder la última.
El cliente oficial
Con el cliente oficial, todo lo anterior queda dentro de la librería: la paginación se recorre con un iterador y la clave va en el constructor. El ejemplo de la documentación de Massive tiene esta forma (necesita red y una clave):
import os
from massive import RESTClient # uv add massive
client = RESTClient(os.environ["MASSIVE_API_KEY"])
barras = list(client.list_aggs(
"AAPL", 1, "day", "2025-01-01", "2025-12-31",
adjusted="true", sort="asc", limit=50000,
))
print(len(barras), barras[0])Los clientes oficiales ahorran trabajo, pero conviene haber escrito uno a mano una vez: cuando una descarga falla a las tres de la madrugada, hay que saber qué es un 429, qué es un cursor y dónde se perdió la última página.
Cuánto cuesta una descarga intradía
Con el formato y los límites ya se puede presupuestar una descarga grande. Barras de un minuto de la sesión regular de EE. UU. (390 al día, 252 sesiones al año), 500 acciones y 5 años, con un máximo de 50.000 barras por petición:
barras_ano = 390 * 252
acciones, anos, max_barras = 500, 5, 50_000
por_accion = math.ceil(barras_ano * anos / max_barras)
total = acciones * por_accion
print(f"{barras_ano * anos * acciones:,} barras;"
f" {por_accion} peticiones por acción; {total:,} en total")
for plan, por_min in [("5 por minuto", 5), ("200 por minuto", 200)]:
print(f"{plan}: {total / por_min / 60:.1f} horas")245,700,000 barras; 10 peticiones por acción; 5,000 en total
5 por minuto: 16.7 horas
200 por minuto: 0.4 horasCasi 246 millones de barras caben en 5.000 peticiones, porque cada una trae 50.000 barras. A 5 peticiones por minuto serían casi 17 horas; a 200, 25 minutos. Pero el límite es solo una restricción: el plan Basic de Massive no tiene minutos de 5 años de antigüedad (su historia es de 2), así que la cuenta real empieza por mirar qué plan tiene esos datos. Y queda el almacenamiento: 246 millones de filas en CSV son decenas de gigabytes; en Parquet, bastante menos (lección 1.4.7).
Ejercicios
- Repite la estimación de días para el histórico diario de 500 acciones, pero suponiendo que solo puedes usar el 50 % del límite diario (el resto lo necesita tu sistema en producción). ¿Qué planes siguen cabiendo en un día?
Solución
limites["dias_50"] = (peticiones / (0.5 * limites["al_dia"])
).apply(math.ceil)
limites.loc[limites["dias_50"] == 1].index.tolist()['Massive Basic', 'Alpaca Basic', 'Tiingo Starter']Con la mitad del límite, Massive Basic (3.600 al día), Alpaca Basic y, justo, Tiingo (500 de 500) siguen cabiendo en un día; los demás no. Un sistema en producción consume cuota (cotizaciones, comprobaciones), y una descarga histórica que se la come entera deja al sistema sin datos. Conviene separar claves o planes para las dos cosas.
- Modifica el servidor local para que acepte solo 3 peticiones por segundo y descarga de nuevo con
limit=50. ¿Cuántos 429 recibe el cliente? ¿Y conlimit=200?
Solución
def probar(maximo, limit):
ApiFalsa.contador["429"] = 0
ApiFalsa.recientes.clear()
original = ApiFalsa.do_GET
def do_get_estricto(self):
while len(self.recientes) > maximo - 1:
if time.monotonic() - self.recientes[0] > 1:
self.recientes.popleft()
else:
return self.responder(429, {}, [("Retry-After", "1")])
original(self)
ApiFalsa.do_GET = do_get_estricto
srv = ThreadingHTTPServer(("127.0.0.1", 0), ApiFalsa)
threading.Thread(target=srv.serve_forever, daemon=True).start()
url = f"http://127.0.0.1:{srv.server_port}/v2/aggs/ticker/NOVA"
filas, paginas = descargar_todo(f"{url}?limit={limit}")
srv.shutdown()
ApiFalsa.do_GET = original
return len(filas), paginas, ApiFalsa.contador["429"]
print(probar(3, 50), probar(3, 200))(502, 11, 3) (502, 3, 0)Con 3 peticiones por segundo y páginas de 50, el cliente recibe un 429 cada tres páginas; con páginas de 200 bastan 3 peticiones y no recibe ninguno. Pedir páginas grandes (hasta el máximo que admite la API) es la forma más sencilla de gastar menos cuota y de esperar menos.
- Tu sistema opera a diario 50 acciones de EE. UU. con datos de cierre y necesita 10 años de historia ajustada. Elige entre los planes de la lección y justifica qué comprobarías antes de pagar.
Solución
Basta con fin de día, así que no hacen falta tiempo real ni intradía. Hacen falta 10 años: descarta el plan Basic de Massive (2 años), Starter (5 años), el gratuito de EODHD (1 año) y Starter de FMP (hasta 5). Quedan, entre otros, los planes de fin de día con historia larga (Tiingo, más de 30 años; EODHD All-World, más de 30) o Developer de Massive (10 años, a un precio mayor). Antes de pagar, con el plan gratuito: si da los precios brutos y los ajustados, y los dividendos y splits (lección 1.3.8); si tiene acciones deslistadas; los límites reales frente a 50 acciones; y la licencia, si el sistema va a publicar algo o a usarse con dinero de terceros (lección 1.3.7).
Footnotes
-
Massive, «Polygon.io is Now Massive», 30/10/2025, y página de precios, consultadas el 21/10/2026. ↩
-
Páginas de precios consultadas el 21/10/2026: Alpaca, Tiingo, EODHD, Alpha Vantage, Twelve Data, Financial Modeling Prep. ↩
-
Massive, Custom Bars (OHLC) y REST API Quickstart, consultadas el 21/10/2026. ↩
Respuesta plegada en cada pregunta
Autoevaluación
¿Qué plan de acciones de Massive da datos en tiempo real, según su página de precios a fecha de 21/10/2026?
- ABasic (gratuito)
- BStarter (29 USD al mes)
- CDeveloper (79 USD al mes)
- DAdvanced (199 USD al mes)
Ver respuesta
Respuesta D. Advanced (199 USD al mes)
Basic da datos de fin de día; Starter y Developer, con 15 minutos de retraso; solo Advanced da tiempo real (y, según la página, solo para usuarios no profesionales).
Una API responde con
next_urlen el JSON. ¿Qué debe hacer el cliente?- AIgnorarlo si ya tiene datos
- BPedir esa URL (añadiendo la clave) hasta que la respuesta no traiga
next_url - CAumentar
limithasta que desaparezca - DPedir la misma URL otra vez
Ver respuesta
Respuesta B. Pedir esa URL (añadiendo la clave) hasta que la respuesta no traiga
next_urlnext_urllleva un cursor con el punto donde sigue la consulta. Si no se sigue, la descarga se queda en la primera página sin ningún error.El servidor responde 429 con la cabecera
Retry-After: 1. ¿Qué es lo correcto?- AReintentar inmediatamente en bucle
- BEsperar al menos un segundo y repetir la misma petición
- CPasar a la página siguiente
- DAbortar la descarga
Ver respuesta
Respuesta B. Esperar al menos un segundo y repetir la misma petición
429 significa demasiadas peticiones.
Retry-Afterdice cuánto esperar. Reintentar en bucle solo alarga el bloqueo; saltar de página pierde datos.Con el plan gratuito de Alpha Vantage (25 peticiones al día), ¿cuántos días tardas en bajar el histórico diario de 500 acciones si cada una cabe en una petición?
- AUno
- BCinco
- CVeinte
- DCien
Ver respuesta
Respuesta C. Veinte
500 peticiones entre 25 al día son 20 días. Un límite diario cambia la arquitectura: hace falta guardar lo descargado y continuar al día siguiente (lección 1.4.8).
¿Por qué la clave de la API no debe escribirse en el código?
- APorque ocupa mucho
- BPorque el código acaba en un repositorio, una libreta compartida o un traceback, y la clave da acceso a tu cuenta y a tu cuota
- CPorque las APIs no aceptan claves en el código
- DPorque cambia cada día
Ver respuesta
Respuesta B. Porque el código acaba en un repositorio, una libreta compartida o un traceback, y la clave da acceso a tu cuenta y a tu cuota
Se lee de una variable de entorno o de un archivo fuera del control de versiones (lección 0.2.4). Una clave publicada hay que revocarla.
Ideas clave
- El escalón retail y prosumer cuesta de cero a unas decenas de dólares al mes; lo que cambia de un plan a otro es la historia, el retraso (fin de día, 15 minutos, tiempo real) y el límite de peticiones.
- Polygon.io se llama Massive desde el 30 de octubre de 2025; el dominio antiguo redirige y el cliente de Python oficial es ahora
massive. - Los límites vienen en unidades distintas (por minuto, por hora, por día): hay que pasarlos a la misma antes de comparar, y estimar con ellos cuánto tarda la descarga.
- Un cliente serio sigue la paginación (
next_url,next_page_token), respeta los 429 yRetry-After, y lee la clave de una variable de entorno. - Precios y planes cambian: cítalos con la fecha en que los leíste y vuelve a leerlos antes de pagar.