Saltar al contenido principal
El SDK de Python trae clientes sync y async, modelos de respuesta tipados (sobre Pydantic, la librería estándar de validación de datos en Python), type hints completos, y un método tipado por cada endpoint de OrigoID.

Instalación

pip install origoid
O con uv / poetry / pdm:
uv add origoid
Paquete en PyPI: origoid. Código en github.com/origoid/sdk-python (público, para auditar). Requiere Python 3.9 o más reciente.

Inicializa el cliente

import os
from origoid import OrigoID

client = OrigoID(api_key=os.environ["ORIGOID_API_KEY"])
Ese es todo el setup — no hay nada más que configurar. Nunca hardcodees la API key. Cárgala desde os.environ, un .env via python-dotenv, o un secrets manager (HashiCorp Vault, 1Password, Doppler, etc.).

Tu primera llamada

El ejemplo usa PELJ900101HDFRRN09, un CURP sintético de los ejemplos del OpenAPI — no es CURP de persona real. Reemplázalo con el CURP que necesites validar.
from origoid import OrigoID

client = OrigoID(api_key="...")

envelope = client.renapo.validate_curp(
    curp="PELJ900101HDFRRN09",  # sintético — reemplazar con input real
)

if envelope.status == "OK" and envelope.type == "SUCCESS":
    # envelope.data trae el registro RENAPO.
    print("Titular del CURP:", envelope.data)
else:
    # Otro tipo de resultado — revisa el catálogo de respuestas del endpoint para tu lógica.
    print(envelope.type, "—", envelope.message)
Cada método devuelve una instancia Envelope: { status, type, message, data, transaction_id, processed_at, billable, errors? }. Ver Envelope de respuesta para el contrato.

Métodos por recurso

Los métodos están agrupados por dominio regulatorio. Los nombres usan snake_case Python.

client.authentication

client.authentication.issue_token(expire_after=1800)

client.renapo

client.renapo.validate_curp(curp="PELJ900101HDFRRN09")
client.renapo.lookup_curp(
    given_names="JUAN",
    first_surname="PEREZ",
    second_surname="LOPEZ",
    date_of_birth="1990-01-01",
    gender="H",
    birth_state_code="DF",
)

client.sat

extract_csf y validate_cfdi aceptan un dict request= porque los schemas subyacentes permiten shapes alternativas (identificadores directos O upload de documento). Dentro del dict usa los nombres camelCase del wire JSON:
client.sat.validate_rfc(rfc="PEZJ811011KI1")

client.sat.extract_csf(request={
    "rfc": "PEZJ811011KI1",
    "cif": "17060597619",
})

client.sat.validate_cfdi(request={
    "uuid": "7C8BD4EA-AE86-4CB5-88B8-C6E61E988A8B",
    "rfcEmisor": "PEZJ811011KI1",
    "rfcReceptor": "EMP170623KI3",
    "total": "999999.99",
})

client.imss

client.imss.lookup_nss(curp="PELJ900101HDFRRN09")
client.imss.get_employment_status(
    curp="PELJ900101HDFRRN09",
    nss="92038109713",
)

client.ine

client.ine.validate_voter_list(request={
    "cic": "123456789",
    "citizenIdentifier": "987654321",
})

client.ine.extract_voter_id_data(
    front="<jpg base64>",
    back="<jpg base64>",
)

client.ine.extract_qr_data(back="<jpg base64 del reverso>")

client.compliance

client.compliance.search_sat69(request={"rfc": "PEZJ811011KI1"})
client.compliance.search_sat69b(request={"rfc": "PEZJ811011KI1"})
client.compliance.search_ofac(name="John Doe", min_similarity_score=85)
client.compliance.search_peps(request={
    "givenNames": "JUAN",
    "firstSurname": "PEREZ",
    "secondSurname": "LOPEZ",
})

client.biometrics

client.biometrics.match_faces(
    face="<selfie base64>",
    front="<frente id base64>",
    threshold=80,
    document_type="INE",
)
client.biometrics.check_liveness(selfie="<selfie base64>")

client.email

client.email.validate_email(email="user@example.com")

client.proof_of_address

client.proof_of_address.extract_proof_of_address(file="<pdf o jpg base64>")

Cliente async

Para aplicaciones sobre FastAPI, aiohttp, Starlette, u otros frameworks async, usa AsyncOrigoID. Mismos nombres de método, mismos argumentos, mismos tipos de retorno — sólo agregas await para que la llamada no bloquee el event loop mientras el HTTP request está en vuelo.
import asyncio
from origoid import AsyncOrigoID

async def main():
    client = AsyncOrigoID(api_key="...")
    env = await client.renapo.validate_curp(curp="PELJ900101HDFRRN09")
    if env.status == "OK":
        print(env.type)

asyncio.run(main())
Para lanzar varias llamadas en paralelo (ej. validar varios CURPs al mismo tiempo), usa asyncio.gather — el mismo patrón que Promise.all en JavaScript:
results = await asyncio.gather(
    client.renapo.validate_curp(curp="PELJ900101HDFRRN09"),
    client.sat.validate_rfc(rfc="PEZJ811011KI1"),
    client.compliance.search_ofac(name="John Doe"),
)
Si tu aplicación no está construida sobre un stack async, usa OrigoID — async sólo aporta cuando necesitas manejar muchas operaciones concurrentes sin spawnear threads.

Manejo de errores

El SDK distingue entre errores de negocio (vienen dentro del envelope) y errores de transporte (se lanzan como excepciones).

Errores de negocio — inspecciona el envelope

Para cualquier respuesta HTTP 200, incluyendo INVALID_REQUEST, el SDK devuelve un Envelope. Revisa status y type antes de usar data:
env = client.renapo.validate_curp(curp="BAD")

if env.status == "ERROR":
    # INVALID_REQUEST, SERVICE_UNAVAILABLE, …
    print(env.type, env.message)
    for err in (env.errors or []):
        print(f"  {err.field}: {err.code}{err.message}")
else:
    # status == "OK" — puede ser un NOT_FOUND de negocio
    match env.type:
        case "SUCCESS":
            print("Titular del CURP:", env.data)
        case "CURP_NOT_FOUND":
            print("Sin match")
Ver Errores y Catálogos para el catálogo completo de códigos type.

Errores de transporte — try/except

Para 401, 429 y fallas irrecuperables el SDK lanza excepciones tipadas:
from origoid import OrigoID
from origoid.core.api_error import ApiError

client = OrigoID(api_key="...")

try:
    env = client.renapo.validate_curp(curp="PELJ900101HDFRRN09")
except ApiError as e:
    # e.status_code — 401, 429, etc.
    # e.body — envelope que devolvió el servidor
    print(e.status_code, e.body)

Configuración por llamada (avanzado)

Pasa request_options para override de timeout, retries o headers por llamada:
client.ine.validate_voter_list(
    request={"cic": "123456789", "citizenIdentifier": "987654321"},
    request_options={
        "timeout_in_seconds": 120,   # default 60
        "max_retries": 3,            # default 2
        "additional_headers": {"x-trace-id": "..."},
    },
)
Lee esto antes de tunear timeouts o retries. El SDK sólo reintenta errores 5xx y fallas de red, nunca respuestas de negocio exitosas — entonces los retries no crean llamadas duplicadas facturables cuando el API respondió correctamente. crean llamadas extra cuando el request realmente falló: un request que hace timeout tres veces puede consumir tres créditos si la llamada eventualmente tuvo éxito en un intento posterior.
  • Los defaults (60 s, 2 reintentos) son correctos para casi cualquier workload. Cambia sólo con razón específica.
  • Combinar timeout largo con max_retries alto (ej. 120 s × 5) significa que un único request fallando puede ocupar un thread hasta 10 minutos — malo para tu throughput y tu infraestructura.
  • Sobrescribe per-call sólo en endpoints con cold starts lentos conocidos.