AlchemyCode API docs

Trabajos asíncronos

Seis de los ocho servicios descargan documentos de portales oficiales y tardan entre 15 y 90 segundos. En lugar de mantener la conexión abierta, la API crea un job: recibes 202 con su identificador y el resultado se consulta después o se entrega por webhook.

El modelo en una imagen

  1. POST al endpoint del servicio con company_id → 202 Accepted y job en queued.
  2. El worker toma el job y lo pasa a running. Puede reintentarse hasta 3 veces si el fallo es del portal.
  3. Termina en succeeded (con result y files) o failed (con error).
  4. Tú haces polling con GET /v1/jobs/{id} o esperas el webhook job.succeeded / job.failed.
Los servicios síncronos (artículo 69-B y CAPTCHA) no crean job: responden 200 en la misma petición. Ver Artículo 69-B y Resolución de CAPTCHA.

Estados del job

EstadoSignificadoQué hacer
queuedEn cola, aún no lo toma un worker.Esperar; se puede cancelar.
runningEjecutándose contra el portal.Esperar; ya no se puede cancelar.
succeededTerminó con resultado.Leer result y descargar files.
failedTerminó con error no reintentable o agotó intentos.Leer error.code; corregir y volver a solicitar.
canceledSe canceló antes de ejecutarse.Solicitar de nuevo si hace falta.
expiredEl resultado superó la retención de 72 h.Solicitar de nuevo la consulta.

La respuesta 202

JSON
{
  "object": "job",
  "id": "4d1f8c0a-6b2e-4c93-8f51-2a7d9e3b6c08",
  "service": "sat.constancia",
  "status": "queued",
  "company_id": "9b2f5c1e-7a3d-4f8b-9c21-6d4e8a0b5f17",
  "created_at": "2026-10-31T16:42:07.318920+00:00",
  "started_at": null,
  "finished_at": null,
  "units": 0,
  "result": null,
  "files": [],
  "error": null,
  "warnings": [],
  "sandbox": false,
  "links": {
    "self": "/v1/jobs/4d1f8c0a-6b2e-4c93-8f51-2a7d9e3b6c08"
  }
}

El job incluye units (unidades que consumirá al terminar), sandbox y links.self con la ruta para consultarlo. Guarda siempre el id: es un UUID, no un folio corto.

Polling

Consulta cada 2 segundos con un límite de 2 minutos para el primer intento. Si el job sigue en curso, aumenta el intervalo (2 s, 3 s, 5 s) y continúa hasta el límite de retención. No hagas polling más rápido de 1 petición por segundo por job: consume tu rate limit sin acelerar el resultado.

Shell
# 1) crea el job
curl -sS -X POST "https://api.434alchemycode.com/v1/sat/constancias" \
  -H "Authorization: Bearer $ALCHEMY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"company_id": "9b2f5c1e-7a3d-4f8b-9c21-6d4e8a0b5f17"}'

# 2) consulta el estado (repite hasta succeeded/failed)
curl -sS "https://api.434alchemycode.com/v1/jobs/4d1f8c0a-6b2e-4c93-8f51-2a7d9e3b6c08" \
  -H "Authorization: Bearer $ALCHEMY_API_KEY"

# 3) lista trabajos recientes si perdiste el id
curl -sS "https://api.434alchemycode.com/v1/jobs?status=succeeded&service=sat.constancia&per_page=10" \
  -H "Authorization: Bearer $ALCHEMY_API_KEY"

El listado acepta page, per_page (máx. 100), status, service y company_id. Los jobs se ordenan del más reciente al más antiguo.

wait: esperar sin polling

Si necesitas el resultado en la misma petición, envía "wait": true. La API espera hasta 25 s:

  • Termina antes de 25 s → 200 OK con status: "succeeded".
  • No termina → 202 Accepted con el job en curso; sigue con polling.
Shell
curl -sS -X POST "https://api.434alchemycode.com/v1/sat/opiniones" \
  -H "Authorization: Bearer $ALCHEMY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"company_id": "9b2f5c1e-7a3d-4f8b-9c21-6d4e8a0b5f17", "wait": true}'
No uses wait=true en procesos masivos: bloquea un hilo de tu servidor y muchos gateways cortan a los 30 s. Para backend, el patrón correcto es 202 + webhook.

Cancelación

Sólo se puede cancelar un job en queued. Si ya está running, la API responde 409 JOB_NOT_READY con el estado actual.

Shell
curl -sS -X POST "https://api.434alchemycode.com/v1/jobs/4d1f8c0a-6b2e-4c93-8f51-2a7d9e3b6c08/cancel" \
  -H "Authorization: Bearer $ALCHEMY_API_KEY"

La cancelación no consume unidades. El job queda en canceled, sin result ni archivos; cancelar un job que ya terminó responde 409 JOB_NOT_READY.

Expiración y archivos

RecursoVigenciaQué pasa al expirar
Job y result72 horas410 JOB_EXPIRED; vuelve a solicitar la consulta.
URL de files[]15 minutosLa descarga responde 404 NOT_FOUND; consulta el job para regenerarla.
Idempotency-Key24 horasLa misma clave con el mismo cuerpo crea un job nuevo.
Invitación7 días410 JOB_EXPIRED al aceptarla; pide una nueva.

Los archivos se guardan fuera de la base de datos. La URL firmada incluye file_id, p, exp y sig; no requiere encabezado de autenticación, pero caduca. No la publiques ni la compartas: cualquiera con la URL puede descargar el archivo mientras esté vigente.

Webhooks de resultado

Cuando termina un job, si tienes un webhook activo con el evento job.succeeded o job.failed, AlchemyCode envía un POST firmado con el resumen del job. Es la forma recomendada de enterarte sin polling.

JSON
{
  "id": "evt_5b1e9c3a7d2f4b68a0c1",
  "type": "job.succeeded",
  "created_at": "2026-10-31T16:44:13.104221+00:00",
  "data": {
    "job_id": "4d1f8c0a-6b2e-4c93-8f51-2a7d9e3b6c08",
    "service": "sat.constancia",
    "status": "succeeded",
    "company_id": "9b2f5c1e-7a3d-4f8b-9c21-6d4e8a0b5f17",
    "result": {
      "rfc": "OMX2503255F9",
      "razon_social": "OMNICONMX, S.A. de C.V.",
      "estatus_padron": "Activo"
    },
    "error": null
  }
}

La firma y los reintentos se explican en Webhooks. El payload del evento es un resumen: para archivos y datos completos consulta el job por API.

Buenas prácticas

  • Persiste el job_id antes de responder a tu usuario; si tu proceso muere, el job sigue vivo.
  • Trata queued y running igual: ambos significan "vuelve después".
  • Usa Idempotency-Key al crear jobs desde reintentos automáticos.
  • No descargues archivos dentro del bucle de polling: hazlo una vez al recibir succeeded.
  • Mide tu latencia extremo a extremo desde el POST hasta finished_at, no sólo la del POST.
  • Si un job falla por el portal (PORTAL_UNAVAILABLE), ya se reintentó solo; espera y solicita otro.

Continúa