Manual de integración
API de Búsqueda de Antecedentes
Consulte antecedentes de personas y vehículos desde su propio sistema. Las búsquedas se ejecutan contra los portales oficiales — SUNARP, SISGEN, INFONOT, SUTRAN, SAT, entre otros — y devuelven el resultado junto con la evidencia en PDF.
| Entorno | URL base |
|---|---|
| Producción | https://api.notaryos.com/api/v1 |
| Pruebas | https://api-dev.notaryos.com/api/v1 |
Todos los ejemplos usan el entorno de pruebas. Recomendamos integrar ahí y recién después apuntar a producción: solo cambian la URL base y la API key.
01Cómo funciona
El modelo es asíncrono. Una búsqueda consulta varios portales y cada consulta abre un navegador contra el sitio de la entidad, así que puede tardar entre 30 segundos y varios minutos.
- 1Usted crea una búsquedaRespondemos de inmediato con 202 y un identificador.
- 2Consultamos los portalesEn segundo plano, una fuente a la vez.
- 3Usted recibe el resultadoPor webhook, o consultando el identificador.
No conviene esperar la respuesta de forma sincrónica. Hay dos maneras de enterarse de que terminó: webhook (recomendado), donde le avisamos a una URL suya, o consulta periódica, donde usted pregunta por el identificador.
02Antes de empezar
Necesita una API key, que le entrega el administrador de la notaría desde la consola. Al crearla se define qué fuentes puede consultar y, opcionalmente, su URL de webhook.
Al momento de crearla el sistema muestra una sola vez dos valores:
| Valor | Para qué sirve |
|---|---|
API key nk_… | Autenticar cada llamada |
| Secreto del webhook | Verificar que los avisos vienen de nosotros |
Permisos de la key
Son los nombres que aparecen en los mensajes de error cuando la key pide algo que no tiene habilitado.
| Permiso | Habilita |
|---|---|
CONSULTAS_READ | Listar y consultar búsquedas hechas con esa misma key |
CONSULTAS_WRITE | Crear búsquedas y reintentar fuentes fallidas |
EVIDENCIA_READ | Descargar el PDF consolidado |
FUENTE_SISGEN | Consultar SISGEN |
FUENTE_INFONOT | Consultar INFONOT |
FUENTE_GOOGLE_ANTECEDENTES | Consultar Google Antecedentes |
FUENTE_REGIMEN_REFORZADO | Consultar las 5 fuentes del Régimen Reforzado |
FUENTE_VEHICULAR | Consultar las 9 fuentes vehiculares, por placa |
FUENTE_VER_DETALLE | Con costo: Ver Detalle SUNARP, S/ 6.60 por placa |
FUENTE_INDICE_PERSONAL | Con costo: Índice Personal, S/ 6.60 por persona |
Los permisos de capacidad y los de fuente son independientes: para buscar en SISGEN hacen falta CONSULTAS_WRITE y FUENTE_SISGEN.
03Autenticación
Toda llamada lleva la key en una cabecera:
X-API-Key: nk_7e3e2036a0d548efb3a45cd9b9f1b173Si la key falta, es inválida, fue revocada o venció, la respuesta es 401:
{ "code": "UNAUTHORIZED", "message": "API key inválida, revocada o vencida" }Su key solo ve lo suyo. El historial y el detalle devuelven únicamente las búsquedas hechas con esa misma key. No verá las de otras keys ni las que el personal de la notaría haga desde la consola.
04Guía rápida
Crear una búsqueda
curl -X POST https://api-dev.notaryos.com/api/v1/antecedentes/busquedas \
-H "X-API-Key: $NOTARYOS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"kardex": "EXP-2026-0042",
"etiqueta": "Due diligence proveedor",
"documentos": [
{ "tipo": "DNI", "numero": "41943873" }
],
"fuentes": ["SISGEN", "INFONOT"]
}'{
"id": "a2dd8053-3730-4a35-aaa9-90c308184cd0",
"kardex": "EXP-2026-0042",
"etiqueta": "Due diligence proveedor",
"estado": "EN_PROCESO",
"creadaEn": "2026-08-08T15:01:22",
"resultados": [
{
"id": "7c1f…",
"documento": "41943873",
"fuente": "SISGEN",
"estado": "PENDIENTE",
"cached": false,
"generadoEn": "2026-08-08T15:01:22",
"tieneEvidencia": false,
"datos": {},
"error": null
}
],
"saldoSunarp": null
}Consultar el resultado
curl https://api-dev.notaryos.com/api/v1/antecedentes/busquedas/{id} \
-H "X-API-Key: $NOTARYOS_API_KEY"Cuando estado deja de ser EN_PROCESO, la búsqueda terminó.
Descargar la evidencia
curl -o evidencia.pdf \
https://api-dev.notaryos.com/api/v1/antecedentes/busquedas/{id}/documentos/41943873/evidencia \
-H "X-API-Key: $NOTARYOS_API_KEY"05Fuentes disponibles
Cada fuente consulta un portal distinto. Su key tiene habilitadas solo algunas: si pide una que no tiene, la respuesta es 422 y no se ejecuta nada.
Sobre personas
| Fuente | Qué consulta | Requiere |
|---|---|---|
SISGEN | Consulta previa SISGEN | Persona en el padrón RENIEC |
INFONOT | Alertas INFONOT | Documento o nombre |
GOOGLE_ANTECEDENTES | Búsqueda pública en Google, con resumen | Nombre, o un DNI que esté en el padrón RENIEC |
REGIMEN_REFORZADO | OSCE Proveedor, OSCE Inhabilitados, SBS Casas de Cambio, SBS Préstamos y REINFO | Documento |
INDICE_PERSONAL | Índice Nacional de Registro Personal (SUNARP) | Persona en el padrón RENIEC |
REGIMEN_REFORZADO cubre las cinco fuentes que exige la Resolución SBS 01754-2024 y se pide como una sola. En la respuesta aparecen los cinco resultados por separado.
INDICE_PERSONAL cuesta S/ 6.60 por persona consultada, descontados del monedero SUNARP de la notaría. Se habilita solo si la notaría lo autoriza expresamente al crear la key.Sobre vehículos
| Fuente | Qué consulta | Requiere |
|---|---|---|
VEHICULAR | SUTRAN, ATU, SOAT, SOAT Callao, SAT (captura, papeletas e impuesto vehicular), SIGM y GNV | Placa |
VER_DETALLE_SUNARP | Propietario, características y gravámenes del vehículo (SPRL SUNARP) | Placa |
VEHICULAR se pide como una sola fuente y devuelve nueve resultados, uno por portal.
VER_DETALLE_SUNARP cuesta S/ 6.60 por placa, del mismo monedero SUNARP que el Índice Personal. Va aparte del grupo VEHICULAR justamente para que pedir la consulta vehicular no gaste sin querer. Es la única fuente que devuelve propietario, características y gravámenes: las otras nueve informan infracciones, SOAT, deudas y garantías.Personas y vehículos juntos
Ambos tipos van en la misma lista documentos, diferenciados por tipo. Cada fuente se ejecuta solo sobre lo que sabe consultar: SUTRAN no corre contra un DNI ni SISGEN contra una placa.
{
"kardex": "EXP-2026-0042",
"documentos": [
{ "tipo": "DNI", "numero": "41943873", "nombre": "JUAN PEREZ GOMEZ" },
{ "tipo": "PLACA", "numero": "BXF282" }
],
"fuentes": ["INFONOT", "VEHICULAR"]
}Esto cubre en un solo pedido al comprador y al vehículo de una transferencia: devuelve 10 resultados — 1 de INFONOT sobre el DNI y 9 vehiculares sobre la placa. Si pide fuentes de un tipo sin incluir ningún sujeto de ese tipo, la respuesta es 422.
Los datos extraídos
Cada resultado trae un objeto datos con lo que devolvió la entidad, listo para guardar en su base. Los campos dependen de la fuente:
{
"fuente": "VER_DETALLE_SUNARP",
"estado": "COMPLETADO",
"datos": {
"resumen": "Ver Detalle SUNARP de la placa BXF282",
"placa": "BXF282",
"propietarios": ["JUAN PEREZ GOMEZ"],
"marca": "TOYOTA",
"modelo": "YARIS",
"anioFabricacion": "2019"
}
}Las fuentes que solo confirman si hay o no hallazgos devuelven el resumen:
{
"fuente": "SISGEN",
"estado": "COMPLETADO",
"datos": { "resumen": "Sin coincidencias en SISGEN" }
}datos viene vacío mientras la consulta está en curso y en las fuentes que no devuelven nada estructurado. No incluye rutas internas ni identificadores nuestros: para la evidencia se usa el endpoint del PDF.
El saldo del monedero
Cuando la búsqueda usa una fuente con costo (INDICE_PERSONAL o VER_DETALLE_SUNARP), la respuesta trae el saldo del monedero SUNARP de la notaría tras esa consulta:
{
"estado": "COMPLETADA",
"saldoSunarp": 984.40,
"resultados": [ ... ]
}Es null cuando no se usó ninguna fuente con costo. Sirve para saber si queda saldo antes de lanzar un lote grande: sin saldo, esas fuentes fallan con ERROR y no se cobra nada — el resto de la búsqueda sigue normalmente.
Va a nivel de la búsqueda y no dentro de datos a propósito: es el estado de la cuenta de la notaría, no un dato del documento o la placa consultada.
06Referencia de endpoints
Crea una búsqueda. Responde 202 de inmediato.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
kardex | string (≤50) | No | Su referencia interna. No la validamos |
etiqueta | string (≤255) | No | Texto libre para reconocer la búsqueda |
documentos | array | Sí | Al menos uno. Máximo 10 en total |
documentos[].tipo | string | Sí | DNI, CE, RUC, PASAPORTE o PLACA |
documentos[].numero | string (≤20) | Sí | El número, o la placa sin guion |
documentos[].nombre | string (≤255) | No | Solo personas. Si se omite, lo resolvemos contra el padrón RENIEC |
fuentes | array | Sí | Al menos una. Ver sección 5 |
Estado y resultados de una búsqueda.
Historial, más reciente primero. page arranca en 0 y size admite hasta 100. La respuesta trae content, totalElements, totalPages y number.
Vuelve a ejecutar una fuente que quedó en ERROR. Útil cuando el portal de la entidad estaba caído o dio timeout. Responde 202.
Devuelve un PDF (application/pdf) con todas las capturas de esa persona o vehículo en esa búsqueda, consolidadas en un solo documento con carátula. El {numero} es el documento o la placa. Requiere que al menos una fuente haya terminado con evidencia.
07Estados
Estado de la búsqueda
| Estado | Significado |
|---|---|
EN_PROCESO | Al menos una fuente sigue ejecutándose |
COMPLETADA | Todas terminaron correctamente |
COMPLETADA_CON_ERRORES | Terminaron todas, alguna falló |
COMPLETADA_CON_ERRORES no invalida la búsqueda: el resto de los resultados es válido y utilizable. Suele deberse a que un portal estuvo caído o lento. Puede reintentar solo esa fuente.
Estado de cada resultado
| Estado | Significado |
|---|---|
PENDIENTE | Encolado |
EN_PROCESO | Consultando el portal |
COMPLETADO | Consulta exitosa |
DEUDA_PENDIENTE | Consulta exitosa: el vehículo registra deuda. No es un error |
ERROR | No se pudo completar. El motivo viene en error |
Errores frecuentes en una fuente
| Mensaje | Causa |
|---|---|
| Documento no encontrado en el padrón RENIEC | SISGEN e Índice Personal necesitan nombres y apellidos por separado, que resolvemos contra el padrón |
| Timeout … exceeded | El portal de la entidad no respondió a tiempo. Reintente |
| Sin nombre para buscar | La fuente busca por nombre y el documento no está en el padrón |
08Caché de 24 horas
Si el mismo documento o placa ya se consultó contra la misma fuente dentro de las últimas 24 horas, devolvemos ese resultado en lugar de volver a consultar el portal.
{
"fuente": "SISGEN",
"estado": "COMPLETADO",
"cached": true,
"generadoEn": "2026-08-07T19:04:11"
}cached es true, generadoEn es la fecha real de captura de la evidencia, anterior a su pedido. Si su caso de uso exige evidencia del día, revise ese campo.La caché es transparente y le conviene: la respuesta es inmediata y no consume cuota de búsquedas con costo. Repetir un documento no penaliza. Para forzar una captura nueva hay que esperar a que venza la ventana.
Si todas las fuentes del pedido salen de caché, el 202 ya viene con estado: "COMPLETADA" y los resultados completos: no hay nada que esperar ni que consultar después. Vale la pena contemplarlo en su código, porque es el caso en que la búsqueda termina antes de que usted empiece a preguntar por ella.
09Webhooks
Si su key tiene una URL configurada, le avisamos cuando la búsqueda termina.
POST https://su-sistema.pe/hooks/antecedentes
Content-Type: application/json
X-Webhook-Event: ANTECEDENTES_BUSQUEDA_COMPLETADA
X-Webhook-Signature: 3f7a9c2b…
{
"evento": "ANTECEDENTES_BUSQUEDA_COMPLETADA",
"consultaId": "a2dd8053-3730-4a35-aaa9-90c308184cd0",
"kardex": "EXP-2026-0042",
"etiqueta": "Due diligence proveedor",
"creadaEn": "2026-08-08T15:01:22",
"resultados": [
{
"resultadoId": "7c1f…",
"documento": "41943873",
"fuente": "SISGEN",
"estado": "COMPLETADO",
"cached": false,
"generadoEn": "2026-08-08T15:02:44",
"error": null
}
]
}Verificar la firma
X-Webhook-Signature es el HMAC-SHA256 del cuerpo exacto, en hexadecimal, calculado con el secreto que recibió al crear la key.
import hmac, hashlib
def firma_valida(cuerpo: bytes, firma: str, secreto: str) -> bool:
esperada = hmac.new(secreto.encode(), cuerpo, hashlib.sha256).hexdigest()
return hmac.compare_digest(esperada, firma)Requisitos de su endpoint
| Requisito | Detalle |
|---|---|
| Protocolo | HTTPS obligatorio |
| Respuesta | Cualquier 2xx. Otro código cuenta como fallo |
| Redirecciones | No las seguimos. Configure la URL final |
| Reintentos | Hasta 5. Agotados, el resultado sigue disponible por GET |
| Demora | Hasta ~30 segundos desde que termina la última fuente |
Responda rápido — idealmente bajo 10 segundos — y procese de forma asíncrona. Si tarda, cuenta como fallo. Puede recibir el mismo aviso más de una vez ante fallos de red: trate el consultaId de forma idempotente.
10Límites
| Límite | Valor |
|---|---|
| Documentos y placas por búsqueda | 10 en total |
| Búsquedas por hora | 30 por defecto, configurable por key |
| Consultas de lectura por hora | 300 por defecto, configurable por key |
| API keys activas por notaría | 20 |
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 297
X-RateLimit-Reset: 1786204907Al excederlos, la respuesta es 429 con Retry-After en segundos. Los valores por defecto son conservadores a propósito: cada consulta abre un navegador real contra el portal de la entidad, y una integración nueva conviene arrancarla con margen. La cuota se ajusta por key: si su volumen la supera, la notaría la sube desde la consola sin que haya que cambiar nada de su lado.
Si el servicio está saturado
Además de la cuota horaria hay un límite de trabajo en curso. Tenga presente que una búsqueda no es una tarea sino varias: 10 placas con la fuente VEHICULAR son 90 consultas a portales distintos.
Cuando se supera, la búsqueda se crea igual y las fuentes que no entraron quedan en ERROR con el motivo, en lugar de esperar indefinidamente. El webhook llega normalmente y esas fuentes se recuperan con el endpoint de reintento. Si le pasa seguido, conviene espaciar los envíos en vez de mandar los lotes de golpe.
11Códigos de error
| Código | Significado | Qué hacer |
|---|---|---|
400 | El cuerpo no es válido | Revise details, indica el campo exacto |
401 | Key ausente, inválida, revocada o vencida | Verifique la cabecera; si persiste, pida una nueva |
403 | La key no tiene el permiso necesario | Pida que le habiliten el permiso |
404 | La búsqueda no existe o no es suya | Verifique el identificador |
422 | Regla de negocio | El mensaje explica el motivo |
429 | Excedió el límite | Espere lo indicado en Retry-After |
5xx | Error de nuestro lado | Reintente con espera creciente |
{
"code": "BUSINESS_RULE_VIOLATION",
"message": "La API key no tiene permiso para estas fuentes. Scopes faltantes: [FUENTE_INDICE_PERSONAL]",
"timestamp": "2026-08-08T15:03:53"
}Una búsqueda de otra key devuelve 404 y no 403: confirmar que existe ya sería revelar información.
12Ejemplos completos
Python
import os, time, requests
API = "https://api-dev.notaryos.com/api/v1"
KEY = os.environ["NOTARYOS_API_KEY"]
H = {"X-API-Key": KEY, "Content-Type": "application/json"}
def crear_busqueda(documentos, fuentes, kardex=None):
r = requests.post(f"{API}/antecedentes/busquedas", headers=H, timeout=30, json={
"kardex": kardex,
"documentos": documentos,
"fuentes": fuentes,
})
r.raise_for_status()
return r.json()
def esperar(busqueda_id, timeout=600, intervalo=15):
"""Solo si no usa webhooks. Con webhook, esto no hace falta."""
limite = time.time() + timeout
while time.time() < limite:
r = requests.get(f"{API}/antecedentes/busquedas/{busqueda_id}", headers=H, timeout=30)
r.raise_for_status()
datos = r.json()
if datos["estado"] != "EN_PROCESO":
return datos
time.sleep(intervalo)
raise TimeoutError(f"La búsqueda {busqueda_id} sigue en proceso")
def descargar_evidencia(busqueda_id, numero, destino):
r = requests.get(
f"{API}/antecedentes/busquedas/{busqueda_id}/documentos/{numero}/evidencia",
headers={"X-API-Key": KEY}, timeout=120)
r.raise_for_status()
with open(destino, "wb") as f:
f.write(r.content)
busqueda = crear_busqueda(
documentos=[
{"tipo": "DNI", "numero": "41943873"},
{"tipo": "PLACA", "numero": "BXF282"},
],
fuentes=["INFONOT", "VEHICULAR"],
kardex="EXP-2026-0042",
)
resultado = esperar(busqueda["id"])
print("Estado:", resultado["estado"])
for r in resultado["resultados"]:
marca = " (de caché)" if r["cached"] else ""
print(f" {r['fuente']:20} {r['documento']:10} {r['estado']}{marca}")
if r["error"]:
print(f" motivo: {r['error']}")
descargar_evidencia(busqueda["id"], "BXF282", "evidencia_BXF282.pdf")import hmac, hashlib, os
from flask import Flask, request, abort
app = Flask(__name__)
SECRETO = os.environ["NOTARYOS_WEBHOOK_SECRET"]
@app.post("/hooks/antecedentes")
def recibir():
cuerpo = request.get_data() # crudo, sin parsear
firma = request.headers.get("X-Webhook-Signature", "")
esperada = hmac.new(SECRETO.encode(), cuerpo, hashlib.sha256).hexdigest()
if not hmac.compare_digest(esperada, firma):
abort(401)
evento = request.get_json()
# Responda rápido y procese aparte; el mismo aviso puede llegar más de una vez.
encolar_procesamiento(evento["consultaId"])
return "", 200PHP
<?php
$api = 'https://api-dev.notaryos.com/api/v1';
$key = getenv('NOTARYOS_API_KEY');
$payload = json_encode([
'kardex' => 'EXP-2026-0042',
'documentos' => [['tipo' => 'DNI', 'numero' => '41943873']],
'fuentes' => ['SISGEN', 'INFONOT'],
]);
$ch = curl_init("$api/antecedentes/busquedas");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["X-API-Key: $key", 'Content-Type: application/json'],
]);
$respuesta = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($codigo !== 202) {
throw new RuntimeException("Error $codigo: $respuesta");
}
$busqueda = json_decode($respuesta, true);
echo "Búsqueda creada: {$busqueda['id']}\n";$cuerpo = file_get_contents('php://input');
$firma = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$esperada = hash_hmac('sha256', $cuerpo, getenv('NOTARYOS_WEBHOOK_SECRET'));
if (!hash_equals($esperada, $firma)) {
http_response_code(401);
exit;
}Node.js
const API = "https://api-dev.notaryos.com/api/v1";
const KEY = process.env.NOTARYOS_API_KEY;
async function crearBusqueda(documentos, fuentes, kardex) {
const r = await fetch(`${API}/antecedentes/busquedas`, {
method: "POST",
headers: { "X-API-Key": KEY, "Content-Type": "application/json" },
body: JSON.stringify({ kardex, documentos, fuentes }),
});
if (!r.ok) throw new Error(`${r.status}: ${await r.text()}`);
return r.json();
}
const busqueda = await crearBusqueda(
[{ tipo: "PLACA", numero: "BXF282" }],
["VEHICULAR"],
"EXP-2026-0042"
);
console.log("Búsqueda creada:", busqueda.id);import crypto from "node:crypto";
import express from "express";
const app = express();
app.post("/hooks/antecedentes",
express.raw({ type: "application/json" }), // sin parsear
(req, res) => {
const esperada = crypto
.createHmac("sha256", process.env.NOTARYOS_WEBHOOK_SECRET)
.update(req.body)
.digest("hex");
const firma = req.get("X-Webhook-Signature") ?? "";
if (!crypto.timingSafeEqual(Buffer.from(esperada), Buffer.from(firma))) {
return res.sendStatus(401);
}
const evento = JSON.parse(req.body);
encolar(evento.consultaId);
res.sendStatus(200);
});13Buenas prácticas
- Use webhooks en lugar de consultar en bucle. Si igual necesita consultar, hágalo cada 15 segundos o más: consultar cada segundo agota su cuota de lectura sin acelerar nada.
- Guarde el identificador de cada búsqueda. Es la forma de recuperar resultados y evidencia después.
- Trate los webhooks como idempotentes. El mismo
consultaIdpuede llegar más de una vez. - Verifique siempre la firma. Sin eso, cualquiera que conozca su URL puede inyectar avisos falsos.
- No trate
COMPLETADA_CON_ERROREScomo fallo total. Los resultados que sí salieron son válidos. - Revise
cachedygeneradoEnsi su proceso requiere evidencia capturada el mismo día. - Reintente con espera creciente ante
429y5xx. Ante4xx(salvo 429), reintentar no sirve: corrija el pedido. - Rote la key si sospecha que se filtró. El administrador la revoca al instante desde la consola y emite una nueva.
14Lista de verificación
Antes de pasar a producción:
- La API key y el secreto del webhook están en variables de entorno o un gestor de secretos, no en el código
- Su endpoint de webhook usa HTTPS y responde 2xx en menos de 10 segundos
- Verifica la firma sobre el cuerpo crudo
- Procesa los avisos de forma idempotente
- Maneja 429 con Retry-After y reintento con espera creciente
- Distingue ERROR en una fuente de un fallo de toda la búsqueda
- Guarda el identificador de cada búsqueda
- Probó el flujo completo en el entorno de pruebas
- Cambió la URL base y la API key por las de producción
15Soporte
Ante cualquier problema, tenga a mano:
- El identificador de la búsqueda, o el prefijo de su key (los primeros 11 caracteres)
- La fecha y hora aproximada del pedido
- El código de error y el
messageque recibió