GetAff
Artículo

Automatizar la captación de afiliados con la API REST de GetAff

La API REST de GetAff expone las mismas operaciones que las pantallas de captación de socios: criterios de búsqueda, una sola búsqueda en todas las fuentes, la preselección, los prospectos y sus correos, y la inscripción en una secuencia de prospección. Así, una tarea nocturna puede actualizar una búsqueda, quedarse con los mejores creadores nuevos y preparar su contacto sin que nadie abra la aplicación.

Este artículo sigue esa tarea de la primera petición a la última, con las tres reglas que hacen segura una automatización: las llamadas de pago se confirman con una cifra, las escrituras llevan una clave de idempotencia y el cliente lee las cabeceras de límite de uso en lugar de adivinar.

Actualizado el

Cree una clave con los scopes adecuados

Inicie sesión, abra la página Claves de API y cree una clave por integración: es lo que le permite revocar una sin detener las demás. El valor se muestra una sola vez: GetAff solo guarda un resumen criptográfico.

  • read siempre está incluido: sitios, criterios, resultados, prospectos, secuencias.
  • search para las búsquedas que no gastan créditos (índice, perfiles parecidos, competidores).
  • write para cambiar criterios, la preselección, los prospectos e inscribir en una secuencia.
  • spend para las búsquedas en vivo en los proveedores y las búsquedas de correos de pago, solo para una integración en la que confíe para respetar un presupuesto.

Mantenga la clave en el servidor, en un gestor de secretos o una variable de entorno. La API rechaza por diseño cualquier origen de navegador: una clave en el JavaScript de una página es una clave publicada. Más detalles en Autenticación.

Primera llamada: sus sitios

La URL base es https://www.getaff.org/api/v1. Cada búsqueda de socios pertenece a un sitio, así que empiece por listarlos:

curl -s https://www.getaff.org/api/v1/sites \
  -H "Authorization: Bearer $GETAFF_API_KEY"

Cada sitio viene con el programa que gestiona. GET /v1/sites/{id}/criteria devuelve los criterios que usará la próxima búsqueda y los que GetAff deduce del sitio; PATCH sobre la misma ruta reemplaza los campos que envíe y conserva los demás.

POST /v1/sites/{id}/partner-search ejecuta la misma búsqueda que la aplicación: el índice de creadores de GetAff clasificado para su programa, perfiles parecidos a su preselección, quienes promocionan a sus competidores y, con includeLive, los proveedores de datos de creadores en vivo. Los resultados se fusionan, se deduplican y se clasifican, cada uno con una puntuación de afinidad y sus motivos; statuses indica qué hizo cada fuente.

Solo fuentes gratuitas
SITE_ID=Wq3b9ZK1
curl -s -X POST https://www.getaff.org/api/v1/sites/$SITE_ID/partner-search \
  -H "Authorization: Bearer $GETAFF_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: nightly-$SITE_ID-$(date +%F)" \
  -d '{"limit": 50}'

La ejecución reemplaza la última búsqueda (conservada 30 días) y se añade a las búsquedas guardadas, de modo que GET /v1/sites/{id}/partner-search/latest y los endpoints del historial pueden volver a leerla más tarde sin ejecutar nada.

Llamadas de pago: el intercambio 402

Dos operaciones gastan créditos de proveedores: la búsqueda de socios con includeLive: true y POST /v1/prospects/find-emails. Ambas exigen el scope spend Y un confirmCost al menos igual a la estimación. Sin él, no se gasta nada y la API responde 402 cost_confirmation_required, con la estimación exacta en error.details.estimate.

  1. Envíe la petición sin confirmCost.
  2. Lea error.details.estimate.credits en la respuesta 402.
  3. Compárelo con su presupuesto, o muéstreselo a una persona.
  4. Reenvíe la misma petición con confirmCost igual a esa cifra o a un tope superior.
Node — confirmar dentro de un presupuesto
async function confirmed(url, body) {
  const call = (b) =>
    fetch(url, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.GETAFF_API_KEY}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': crypto.randomUUID(),
      },
      body: JSON.stringify(b),
    });
  let res = await call(body);
  if (res.status === 402) {
    const { error } = await res.json();
    const credits = error.details.estimate.credits;
    if (credits > MAX_CREDITS_PER_RUN) throw new Error(`Over budget: ${credits}`);
    res = await call({ ...body, confirmCost: credits });
  }
  return res.json();
}

Cada gasto confirmado queda anotado en un registro de auditoría (clave, acción, créditos; nunca datos de un creador). Las asignaciones diarias de su plan se aplican además de cualquier presupuesto que usted fije.

De los resultados a los prospectos

Cada resultado lleva una key como instagram:1784140. Quédese con los que seleccionen sus reglas —una puntuación de afinidad mínima, un país, una audiencia mínima— y encadene tres llamadas:

  1. POST /v1/sites/{id}/shortlist con {"keys": [...]} los añade a la preselección (admite 100).
  2. POST /v1/sites/{id}/shortlist/to-prospects los convierte en prospectos privados. Gratis; cuenta en la cuota de descubrimiento de su plan.
  3. POST /v1/prospects/find-emails con los ids de los prospectos busca las direcciones que faltan (Pro, de pago, confirmado como arriba). Nunca reemplaza una dirección existente, y se aplica la lista de exclusión.

GET /v1/prospects?siteId=…&hasEmail=false lista lo que aún falta; la colección se pagina por cursor: devuelva page.nextCursor tal cual como cursor.

Inscriba en una secuencia de prospección

POST /v1/outreach/sequences programa la secuencia de tres pasos (mensaje inicial y dos seguimientos) para un prospecto, desde un buzón conectado, con todos los controles de la aplicación: ajustes del remitente completos, un programa publicado, una dirección válida y fuera de la lista de exclusión, y el régimen de cumplimiento del país del destinatario.

{
  "prospectId": "kJ2p8wQxYb_ig_1784140",
  "attestations": ["b2b_role_relevant", "legitimate_interest_assessed"]
}

Sin draft, se usa el borrador guardado para ese prospecto en GetAff. Cada rechazo tiene su propio código —mailbox_required, draft_required, recipient_suppressed, compliance_blocked (con details.missing), etc.— para que su tarea pueda registrar exactamente por qué se descartó un prospecto. No hay ningún endpoint que envíe un correo directamente: el envío se produce después, desde su buzón, en los horarios programados.

Reintente sin pagar dos veces

Las escrituras aceptan una cabecera Idempotency-Key. Un reintento con la misma clave y el mismo cuerpo en un plazo de 24 horas reproduce el primer éxito (Idempotent-Replayed: true) en lugar de ejecutarse de nuevo, algo importante en una búsqueda que ha gastado créditos. Los errores no se guardan: corrija la petición y reinténtela con la misma clave.

  • Derive la clave del significado de la tarea: nightly-<siteId>-<date>, no un valor aleatorio por intento.
  • La misma clave con otro cuerpo responde idempotency_conflict; una petición que sigue en curso responde idempotency_in_progress.

Límites de uso y errores

El caudal se cuenta por clave en un minuto: 60 peticiones en Gratuito, 600 en Pro, con un tope por cuenta compartido por todas las claves y el servidor MCP. Cada respuesta lleva X-RateLimit-Limit, X-RateLimit-Remaining y las cabeceras IETF RateLimit-Policy / RateLimit; un 429 añade Retry-After. También se aplican los topes propios de la aplicación, como seis búsquedas de socios por minuto.

Los errores son documentos de problema RFC 9457 con un error.code estable: bifurque su lógica según el código, nunca según el mensaje. Reintente los 429 y 503 (respetando Retry-After); los 400, 401, 403 y 404 no cambiarán por reintentar. Las listas completas están en Límites de uso y Errores.

Próximos pasos

La página de introducción recoge la primera llamada y las reglas de versionado, y la referencia documenta cada endpoint, generada a partir de los esquemas que aplican las rutas. Para reaccionar cuando un prospecto responde en lugar de consultar periódicamente, añada un webhook firmado.

Documentación

El contrato exacto, generado a partir de los esquemas que aplican las rutas:

Para leer después