Cómo Funciona
Esta guía explica en detalle cómo funciona el procesamiento de leads en MasLeads API.
Visión General
MasLeads API es asíncrona. Esto significa que:
- Envías los leads a procesar
- Recibes un
job_idinmediatamente - El procesamiento ocurre en segundo plano
- Consultas o recibes los resultados cuando estén listos
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ TU APP │ │ MASLEADS │ │ TU APP │
│ │ │ API │ │ │
│ Crear Job │──────▶│ Procesa... │ │ │
│ │ │ │ │ │
│ Consultar │──────▶│ "pending" │ │ │
│ estado │◀──────│ │ │ │
│ │ │ │ │ │
│ Consultar │──────▶│ "completed" │ │ │
│ estado │◀──────│ │ │ │
│ │ │ │ │ │
│ Obtener │──────▶│ │──────▶│ Resultados │
│ resultados │ │ │ │ │
└──────────────┘ └──────────────┘ └──────────────┘
Paso 1: Crear un Job
Endpoint
POST /api/v1/enrich/leads
Request Body
{
"leads": [
{
"first_name": "Carlos",
"last_name": "García",
"company": "Telefonica",
"linkedin_url": "https://linkedin.com/in/carlosgarcia",
"title": "Director Comercial"
}
],
"enrichment_type": ["email", "phone"],
"webhook_url": "https://tu-servidor.com/webhook"
}
Campos de Lead
| Campo | Requerido | Descripción |
|---|---|---|
first_name |
✅ Sí | Nombre del contacto |
last_name |
✅ Sí | Apellido del contacto |
company |
✅ Sí | Nombre de la empresa |
linkedin_url |
❌ No | URL de perfil LinkedIn (mejora precisión) |
title |
❌ No | Cargo en la empresa |
domain |
❌ No | Dominio de la empresa (alternativa a company) |
Tipos de Datos Solicitados
| Tipo | Créditos | Descripción |
|---|---|---|
email |
1 | Email corporativo verificado |
phone |
1 | Teléfono directo |
["email", "phone"] |
2 | Ambos datos |
Límites por Defecto
| Restricción | Valor por defecto | Configuración |
|---|---|---|
| Máximo leads por job | 100 | Según plan |
| Mínimo leads por job | 1 | Fijo |
| Jobs concurrentes máximo | 10 | Según plan |
Nota: Los límites pueden variar según tu plan contratado.
Response
{
"job_id": "job_abc123xyz",
"status": "pending",
"total_leads": 1,
"credits_reserved": 2,
"created_at": "2025-01-08T10:30:00Z"
}
Paso 2: Monitorear el Estado
Opción A: Polling
Consulta periódicamente el estado del job:
GET /api/v1/jobs/{job_id}
Estados del Job
| Estado | Descripción | Acción |
|---|---|---|
pending |
En cola | Esperar y reintentar |
processing |
Procesando | Esperar y reintentar |
completed |
Finalizado | Obtener resultados |
failed |
Error | Ver mensaje de error |
Estrategia de Polling Recomendada
import time
def poll_job(job_id, max_attempts=60, interval=2):
"""
Polling con backoff exponencial suave.
Timeout total: ~2 minutos
"""
for attempt in range(max_attempts):
response = get_job_status(job_id)
status = response["status"]
if status == "completed":
return response
elif status == "failed":
raise Exception(f"Job failed: {response.get('error')}")
# Aumentar intervalo gradualmente
wait_time = min(interval * (1.2 ** (attempt // 10)), 10)
time.sleep(wait_time)
raise TimeoutError("Job did not complete in time")
Opción B: Webhooks (Recomendado)
En lugar de hacer polling, configura un webhook para recibir notificaciones automáticas.
Ver Guía de Webhooks para configuración completa.
Paso 3: Obtener Resultados
Cuando status sea completed:
GET /api/v1/jobs/{job_id}/results
Response
{
"job_id": "job_abc123xyz",
"results": [
{
"input": {
"first_name": "Carlos",
"last_name": "García",
"company": "Telefonica"
},
"status": "found",
"data": {
"email": "carlos.garcia@telefonica.com",
"email_confidence": 0.95,
"phone": "+34612345678",
"phone_confidence": 0.87
}
}
],
"summary": {
"total": 1,
"found": 1,
"not_found": 0,
"error": 0
}
}
Estados de Resultado por Lead
| Status | Descripción | Créditos |
|---|---|---|
found |
Datos encontrados | Consumidos |
not_found |
No se encontró información | Reembolsados |
error |
Error de procesamiento | Reembolsados |
Niveles de Confianza
| Rango | Interpretación |
|---|---|
| 0.90 - 1.00 | Alta confianza, verificado |
| 0.70 - 0.89 | Confianza media, probable |
| 0.50 - 0.69 | Baja confianza, requiere verificación |
| < 0.50 | No incluido en resultados |
Listar Jobs
Para ver el historial de jobs:
GET /api/v1/jobs?status=completed&limit=10
Query Parameters
| Parámetro | Tipo | Descripción |
|---|---|---|
status |
string | Filtrar por estado |
limit |
int | Máximo resultados (default: 20, max: 100) |
offset |
int | Para paginación |
created_after |
ISO date | Jobs creados después de fecha |
created_before |
ISO date | Jobs creados antes de fecha |
Response
{
"jobs": [
{
"job_id": "job_abc123xyz",
"status": "completed",
"total_leads": 50,
"leads_found": 42,
"created_at": "2025-01-08T10:30:00Z",
"completed_at": "2025-01-08T10:32:15Z"
}
],
"pagination": {
"total": 156,
"limit": 10,
"offset": 0,
"has_more": true
}
}
Errores Comunes
400 Bad Request - Leads inválidos
{
"error": "validation_error",
"message": "Invalid leads data",
"details": [
{"index": 2, "field": "first_name", "error": "required"}
]
}
402 Payment Required - Sin créditos
{
"error": "insufficient_credits",
"message": "Not enough credits. Required: 200, Available: 50"
}
429 Too Many Requests - Límite de jobs
{
"error": "max_concurrent_jobs_exceeded",
"message": "Maximum concurrent jobs reached. Wait for existing jobs to complete."
}
Mejores Prácticas
Optimizar la tasa de éxito
- Incluye LinkedIn URL cuando esté disponible (aumenta precisión ~30%)
- Usa el nombre de empresa exacto (no abreviaciones)
- Incluye el cargo para desambiguar contactos
Batch eficiente
# ✅ Bueno: Un job con 100 leads
create_job(leads=leads_batch)
# ❌ Malo: 100 jobs de 1 lead cada uno
for lead in leads:
create_job(leads=[lead])
Manejo de errores
try:
job = create_job(leads)
except InsufficientCreditsError:
notify_admin("Need to purchase more credits")
except ConcurrentJobsLimitError:
queue_for_later(leads)
except RateLimitError as e:
time.sleep(e.retry_after)
retry()
Anterior: ← Autenticación | Siguiente: Webhooks →