Python Requests APIs explicado simplemente (con diagramas y código real)

Python Requests APIs: lo esencial en un artículo — código real, diagramas y pasos concretos, extractos de un curso de 30 lecciones.

Python Requests APIs explicado simplemente (con diagramas y código real)

Una guía que va al grano: Python Requests APIs diseccionada con diagramas, ejemplos concretos y comandos probados. Todo proviene de un curso estructurado de 10 capítulos — aquí tienes lo mejor.

tl;dr
  • Introducción e instalación
  • Bases HTTP
  • Usar requests
  • Autenticación
  • Patrones avanzados
~$ cat ./parcours.md # Python Requests APIs — 10 capítulos
01
Introducción e instalación
→ ¿Qué es una API REST ?→ Instalar requests y httpx+ 1 más lecciones
02
Bases HTTP
→ Los métodos HTTP→ Códigos de estado HTTP+ 1 más lecciones
03
Usar requests
→ requests.get y post completos→ JSON y serialización+ 2 más lecciones
04
Autenticación
→ API Key en header o query→ Basic Auth y Bearer Token+ 1 más lecciones
05
Patrones avanzados
→ Pagination→ Rate limiting y retry inteligente+ 1 más lecciones
06
Asíncrono
→ Por qué httpx async→ asyncio + httpx en paralelo+ 1 más lecciones
07
APIs públicas útiles
→ GitHub API - repositorios e issues→ OpenWeather y NewsAPI+ 1 más lecciones
08
Webhooks y streaming
→ Recibir webhooks→ Server-Sent Events (SSE)+ 1 más lecciones
🏁
Proyecto final (+ 2 capítulos en camino)
→ Te vas con un proyecto concreto y demostrable

CLI y planificación cron

Proyecto final • argparse • cron • export

NOTEObjetivo — Finalizar el proyecto: interfaz CLI completa, export JSON/CSV y planificación automática mediante cron.

CLI con argparse

output
# app/cli.py
import argparse, asyncio, json, sqlite3, sys
from . import aggregator, config

def cmd_fetch(args):
    inserted = asyncio.run(aggregator.run_once())
    print(f"Nouveaux articles : {inserted}")

def cmd_list(args):
    conn = sqlite3.connect(config.DB_PATH)
    conn.row_factory = sqlite3.Row
    
    query = "SELECT * FROM articles WHERE 1=1"
    params = []
    if args.source:
        query += " AND source = ?"
        params.append(args.source)
    if args.search:
        query += " AND (title LIKE ? OR summary LIKE ?)"
        params.extend([f"%{args.search}%"] * 2)
    query += f" ORDER BY published_at DESC LIMIT {args.limit}"
    
    for row in conn.execute(query, params):
        print(f"[{row['source']}] {row['published_at'][:10]}")
        print(f"  {row['title']}")
        print(f"  -> {row['url']}\n")

def cmd_export(args):
    conn = sqlite3.connect(config.DB_PATH)
    conn.row_factory = sqlite3.Row
    rows = [dict(r) for r in conn.execute("SELECT * FROM articles")]
    
    if args.format == "json":
        with open(args.output, "w", encoding="utf-8") as f:
            json.dump(rows, f, ensure_ascii=False, indent=2)
    elif args.format == "csv":
        import csv
        with open(args.output, "w", newline="", encoding="utf-8") as f:
            w = csv.DictWriter(f, fieldnames=rows[0].keys())
            w.writeheader()
            w.writerows(rows)
    
    print(f"{len(rows)} articles -> {args.output}")

def main():
    p = argparse.ArgumentParser(prog="news", description="Agregateur de news")
    sub = p.add_subparsers(dest="cmd", required=True)
    
    sub.add_parser("fetch", help="Recuperer les articles").set_defaults(func=cmd_fetch)
    
    pl = sub.add_parser("list", help="Lister")
    pl.add_argument("--source", help="Filtrer par source")
    pl.add_argument("--search", help="Rechercher")
    pl.add_argument("--limit", type=int, default=20)
    pl.set_defaults(func=cmd_list)
    
    pe = sub.add_parser("export", help="Exporter")
    pe.add_argument("--format", choices=["json", "csv"], default="json")
    pe.add_argument("--output", default="export.json")
    pe.set_defaults(func=cmd_export)
    
    args = p.parse_args()
    args.func(args)

if __name__ == "__main__":
    main()

Uso

output
python -m app.cli fetch
python -m app.cli list --limit 10
python -m app.cli list --source hackernews
python -m app.cli list --search "python"
python -m app.cli export --format csv --output news.csv

Punto de entrada de consola

output
# pyproject.toml
[project.scripts]
news = "app.cli:main"
output
pip install -e .
news fetch
news list --limit 5

Planificación cron (Linux/Mac)

output
crontab -e

# Cada 30 minutos
*/30 * * * * cd /home/user/news_agg && \
  /home/user/.venv/bin/news fetch >> logs/news.log 2>&1

Planificación en Windows (Task Scheduler)

output
# PowerShell
schtasks /Create /SC MINUTE /MO 30 /TN "NewsAgg" /TR "C:\path\to\python.exe -m app.cli fetch"

Stripe y SendGrid panorama

NOTEObjetivo — Descubrir Stripe (pagos) y SendGrid (emails), y elegir entre el SDK oficial y requests en bruto.

Stripe: crear un pago (API HTTP en bruto)

output
import requests, os

STRIPE_KEY = os.getenv("STRIPE_SECRET_KEY")   # sk_test_...

# Stripe utiliza Basic Auth con la clave como usuario
r = requests.post(
    "https://api.stripe.com/v1/payment_intents",
    auth=(STRIPE_KEY, ""),
    data={
        "amount": 2000,           # 20.00 EUR
        "currency": "eur",
        "payment_method_types[]": "card",
        "description": "Commande #1234"
    },
    timeout=10
)
r.raise_for_status()
intent = r.json()
print(f"client_secret : {intent['client_secret']}")
# El frontend utiliza client_secret para completar el pago

Stripe: crear un cliente

output
r = requests.post("https://api.stripe.com/v1/customers",
                   auth=(STRIPE_KEY, ""),
                   data={"email": "alice@example.com",
                         "name": "Alice Dupont"})
customer = r.json()
print(customer["id"])   # cus_xxx

SDK oficial de Stripe (recomendado)

output
pip install stripe

import stripe
stripe.api_key = STRIPE_KEY

# Más pythonico
intent = stripe.PaymentIntent.create(
    amount=2000,
    currency="eur",
    payment_method_types=["card"],
    description="Commande #1234"
)
print(intent.id)
print(intent.client_secret)
TIPSDK vs requests
  • SDK oficial: reintentos, tipado, webhooks firmados, claves de idempotencia automáticas
  • requests: útil si no hay SDK, o para comprender los conceptos
  • Recomendación: SDK oficial para Stripe/Twilio/AWS, requests para APIs personalizadas

SendGrid: enviar un email (HTTP en bruto)

output
SENDGRID_KEY = os.getenv("SENDGRID_API_KEY")

payload = {
    "personalizations": [{
        "to": [{"email": "destinataire@example.com"}],
        "subject": "Hello depuis Python"
    }],
    "from": {"email": "contact@mondomaine.com", "name": "Mon App"},
    "content": [{
        "type": "text/html",
        "value": "<h1>Bonjour !</h1><p>Envoye via API.</p>"
    }]
}

r = requests.post(
    "https://api.sendgrid.com/v3/mail/send",
    json=payload,
    headers={"Authorization": f"Bearer {SENDGRID_KEY}"},
    timeout=10
)
print(r.status_code)   # 202 Accepted

SendGrid con plantilla

output
payload = {
    "personalizations": [{
        "to": [{"email": "alice@example.com"}],
        "dynamic_template_data": {
            "name": "Alice",
            "order_id": "#1234",
            "total": "29.99 EUR"
        }
    }],
    "from": {"email": "orders@mondomaine.com"},
    "template_id": "d-xxxxx"     # creado en SendGrid
}
requests.post("https://api.sendgrid.com/v3/mail/send",
               json=payload,
               headers={"Authorization": f"Bearer {SENDGRID_KEY}"})

Otros SDKs populares

ServicioLibDominio
StripestripePagos
TwiliotwilioSMS, voz
SendGridsendgridEmails transaccionales
AWSboto3Todo AWS (S3, SQS, SES...)
OpenAIopenaiGPT, embeddings
Slackslack_sdkMensajes, webhooks

Buena práctica: clave de idempotencia

output
import uuid

r = requests.post(
    "https://api.stripe.com/v1/charges",
    auth=(STRIPE_KEY, ""),
    headers={"Idempotency-Key": str(uuid.uuid4())},
    data={...}
)
# En caso de reintento, misma clave = sin doble facturación

Basic Auth y Bearer Token

NOTEObjetivo — Comprender HTTP Basic Auth, los Bearer Tokens y JWT.

HTTP Basic Authentication

Identificador + contraseña codificados en base64 en la cabecera Authorization.

output
import requests

# requests lo hace automáticamente con auth=
r = requests.get(
    "https://api.example.com/me",
    auth=("alice", "mot_de_passe")
)

# cabecera equivalente en bruto:
# Authorization: Basic YWxpY2U6bW90X2RlX3Bhc3Nl
output
from requests.auth import HTTPBasicAuth

r = requests.get(url, auth=HTTPBasicAuth("user", "pwd"))
WARNINGLímites
  • La contraseña se envía en claro (base64 = no es cifrado)
  • HTTPS OBLIGATORIO
  • Sin revocación sencilla
  • Cada vez menos utilizado en favor de Bearer

Bearer Token

El token (token) se envía en la cabecera Authorization: Bearer .... Es el estándar moderno (OAuth, JWT...).

output
TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

r = requests.get(
    "https://api.github.com/user",
    headers={"Authorization": f"Bearer {TOKEN}"}
)

print(r.json())

JWT (JSON Web Token)

Formato de Bearer más extendido. Tres partes separadas por .: header.payload.signature.

output
import jwt   # pip install PyJWT

token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMifQ.signature"

# Decodificar sin verificar (para depuración)
payload = jwt.decode(token, options={"verify_signature": False})
print(payload)
# {'sub': '123', 'exp': 1700000000}

# Decodificar verificando
payload = jwt.decode(token, key="mon_secret", algorithms=["HS256"])

Auth personalizada (subclase de AuthBase)

output
from requests.auth import AuthBase

class BearerAuth(AuthBase):
    def __init__(self, token):
        self.token = token
    def __call__(self, req):
        req.headers["Authorization"] = f"Bearer {self.token}"
        return req

# Uso
r = requests.get(url, auth=BearerAuth(TOKEN))

Refresco automático del token

output
import time

class TokenManager:
    def __init__(self, client_id, client_secret):
        self.client_id = client_id
        self.client_secret = client_secret
        self._token = None
        self._expires_at = 0
    
    def get_token(self) -> str:
        if not self._token or time.time() > self._expires_at - 60:
            self._refresh()
        return self._token
    
    def _refresh(self):
        r = requests.post("https://auth.example.com/token", data={
            "grant_type": "client_credentials",
            "client_id": self.client_id,
            "client_secret": self.client_secret
        })
        r.raise_for_status()
        d = r.json()
        self._token = d["access_token"]
        self._expires_at = time.time() + d["expires_in"]

mgr = TokenManager(...)
r = requests.get(url, headers={"Authorization": f"Bearer {mgr.get_token()}"})
va-plus-loin

Este artículo cubre los extractos más útiles — el curso completo Python Requests APIs (10 capítulos, 30 lecciones, ejercicios corregidos y proyecto final) te lleva hasta el final.

./acceder-au-cours-complet curso gratuito: Vibe Coding

FAQ

¿Cuánto tiempo se necesita para aprender Python Requests APIs?
Con una progresión estructurada (10 capítulos, 30 lecciones cortas y prácticas), se alcanza un nivel operativo en unas semanas dedicando 30 a 60 minutos al día. Lo importante es practicar cada concepto de inmediato.
¿Se necesitan requisitos previos?
Basta con nociones básicas de informática. Si sabes usar un terminal y leer código sencillo, estás listo.
¿Por dónde empezar de forma concreta?
Reproduce los comandos de este artículo y sigue el curso completo Python Requests APIs: encadena las 30 lecciones en orden, con ejercicios y proyecto final.

📬 ¿Quieres recibir este tipo de guía cada semana? Suscríbete gratis — código real, cero palabrería.