SDK oficial de Verificar CURP para Python.
- Valida la estructura de una CURP (entidad, fecha, dígito verificador) y te dice qué está mal.
- Coteja la CURP con los datos que ya tienes de la persona.
- Verifica la CURP en el registro RENAPO; el resultado llega a tu webhook, correo o chat.
- Enlaces de captura: tu cliente escribe su CURP desde su celular y el resultado llega a tu destino.
- Verifica la firma de los webhooks que te enviamos.
Sin dependencias. Python 3.9+.
pip install verificar-curpimport os
from verificar_curp import VerificarCurpClient
client = VerificarCurpClient(api_key=os.environ["CURP_API_KEY"])
result = client.validate(
"PEGJ850101HDFRRL04",
persona={"nombres": "Julio", "fechaNacimiento": "1985-01-01", "sexo": "H"},
)
result.data["valida"] # True
result.data["entidad"] # 'Ciudad de México'
result.data["cotejo"] # 'coincide' | 'no_coincide' | 'indeterminado'
result.data["cotejoCampos"] # {'nombres': 'coincide', ...}
result.tokens_remainingUna CURP mal formada no es un error: data["valida"] es False y el motivo va en data["razon"]
(por ejemplo {"kind": "check-digit", "expected": "4", "actual": "9"}).
En persona, "segundoApellido": None significa "no tiene segundo apellido" y se compara; si omites
la clave, no se compara.
result = client.validate(
"PEGJ850101HDFRRL04",
verificar=True,
destination_id="dst_...", # panel → Destinos
idempotency_key="cliente-123", # opcional; nunca pongas la CURP aquí
)
v = result.verificacion
if v and v["aceptada"]:
print("En curso:", v["job_id"])
elif v:
print("No se pudo iniciar:", v["codigo"], "reintenta en", v["retry_after"], "s")El resultado del registro (estatus: ACTIVA, BAJA, RNE o NO_ENCONTRADA) llega a tu destino,
normalmente en segundos. También puedes consultar el estado, sin costo:
status = client.get_verification(job_id)
status.verificacion["estado"]
status.valores # dict o Nonevalores llega una sola vez y se borra en la misma operación. Si tu destino ya lo recibió, aquí
solo verás el estado; es la forma de recuperarlo cuando la entrega a tu destino falló.
Un enlace de un solo uso para que tu cliente escriba su CURP desde su celular (por ejemplo, en el
check-in). Si mandas los datos de la reservación en persona, la CURP se coteja contra ellos sin
mostrárselos; con ask_guest_persona=True el cliente también escribe su nombre y fecha de nacimiento.
link = client.create_capture_link(
"dst_...",
reference="reserva-8841", # opcional; viaja en la entrega
persona={"nombres": "Julio", "primerApellido": "Pérez", "fechaNacimiento": "1985-01-01"},
)
link.url # muéstralo como QR o envíalo; solo se devuelve aquí, no lo registres en logsCrear el enlace no cuesta. Cada respuesta del cliente cuesta 1 token (hasta 3 intentos, es decir
hasta 3 tokens) y solo una CURP válida que coincide llega a tu destino, como evento
extraction.completed con source: "capture-link" y los campos curp, valida, entidad,
cotejo_reserva, cotejo_reserva_<campo>, etc.
Verifica la firma con el cuerpo crudo, antes de interpretarlo:
from flask import Flask, request
from verificar_curp import VerificarCurpError, parse_webhook
app = Flask(__name__)
@app.post("/webhooks/curp")
def curp_webhook():
try:
event = parse_webhook(
os.environ["CURP_WEBHOOK_SECRET"], # whsec_…
request.get_data(),
request.headers.get("X-Signature"),
request.headers.get("X-Signature-Timestamp"),
)
except VerificarCurpError:
return "", 400
if event["event"] == "curp.verification.completed":
print(event["idempotencyKey"], event["data"]["estatus"])
return "", 200verify_webhook_signature() hace lo mismo y devuelve True/False. Por defecto rechaza firmas con
más de 300 s de antigüedad (tolerance_seconds). Eventos: curp.verification.completed, .failed,
.retrying, .manual_review y extraction.completed (enlaces de captura).
tokens = client.get_balance()Todo fallo lanza VerificarCurpError, con code, status, block_reason y el cuerpo crudo en
response:
from verificar_curp import VerificarCurpError
try:
client.validate(curp)
except VerificarCurpError as err:
if err.code == "INSUFFICIENT_TOKENS":
... # recarga tokens en https://verificarcurp.com| code | status | cuándo |
|---|---|---|
INVALID_REQUEST |
400 | Cuerpo inválido |
DESTINATION_NOT_FOUND |
400 | El destino no existe o no es tuyo (no se cobra) |
INVALID_PERSONA / INVALID_REFERENCE / INVALID_REQUESTER_NAME / INVALID_NAME |
400 | Campo inválido al crear un enlace |
TOO_MANY_LIVE_LINKS |
400 | Ya tienes 20 enlaces vigentes |
MISSING_API_KEY / INVALID_API_KEY |
401 | API key ausente o incorrecta |
INSUFFICIENT_TOKENS |
402 | Sin saldo |
NOT_FOUND |
404 | La verificación no es de tu cuenta |
IDEMPOTENCY_KEY_REUSED |
409 | Misma idempotency_key con otra CURP |
INTERNAL_ERROR |
500 | Error del servidor |
NETWORK_ERROR / TIMEOUT |
— | Del SDK |
INVALID_INPUT / INVALID_SIGNATURE |
— | Del SDK |
- Documentación: https://verificarcurp.com/docs
- OpenAPI: https://verificarcurp.com/api/openapi.yaml
- JavaScript / TypeScript: https://www.npmjs.com/package/verificar-curp
MIT