Cómo integrar APIs con WordPress

Introducción

Integrar APIs con WordPress requiere distinguir la autenticación, la caché y los permisos de cada recorrido. No es lo mismo que WordPress consulte un servicio externo, que una aplicación acceda a su API REST o que un navegador intente utilizar una credencial empresarial.

Una integración puede mostrar información actualizada, intercambiar registros con otras aplicaciones o recibir avisos de procesos externos. Pero también puede ralentizar páginas, divulgar datos o generar trabajo duplicado si se añade como un fragmento aislado sin definir su funcionamiento.

Esta guía organiza esas posibilidades y desarrolla un ejemplo mediante un pequeño plugin: WordPress consulta un dato público de GitHub de forma programada y lo muestra desde almacenamiento local. El ejemplo no necesita claves de acceso, no modifica registros externos y separa la consulta de la visita a la página.

Índice

Distinguir llamadas salientes, API REST y webhooks

Antes de elegir funciones o plugins, dibuja quién inicia la comunicación, qué datos necesita y dónde se ejecuta. Esa descripción evita confundir mecanismos que resuelven problemas diferentes.

Recorridos de integración posibles
Recorrido Mecanismo habitual Pregunta de seguridad
WordPress consulta otro servicio Cliente HTTP ejecutado en PHP. ¿A qué destino puede enviar datos y credenciales?
Una aplicación consulta WordPress API REST de WordPress. ¿Qué identidad puede acceder a cada operación?
Un proveedor avisa a WordPress Endpoint receptor de un webhook. ¿Cómo se autentica el emisor y se evita repetir efectos?
El navegador consulta un servicio JavaScript y reglas de acceso entre orígenes. ¿La petición expone un secreto o datos privados?

Para llamadas salientes, WordPress dispone de su API HTTP, con funciones como wp_remote_get() y wp_remote_post(). Para recibir solicitudes utilizas su API REST y, cuando haga falta, rutas propias.

La autenticación del proveedor no es la autenticación de WordPress. Una contraseña de aplicación de WordPress sirve para acceder a WordPress bajo las condiciones previstas, no para autenticar automáticamente una consulta a cualquier servicio externo.

Si necesitas un intercambio empresarial, comienza por mapear el flujo digital. Indica qué sistema conserva la versión válida y qué acción debe ocurrir ante un fallo.

Colocar la integración en un plugin con responsabilidad propia

Una función empresarial que debe sobrevivir a un cambio de diseño no debería depender del tema visual. Un plugin propio pequeño puede mantener configuración, llamadas, rutas y tareas asociadas a la integración.

No pegues PHP en el contenido de una entrada. El editor almacena contenido, no debe convertirse en el entorno de ejecución de credenciales y conexiones. Tampoco modifiques el núcleo de WordPress para añadir una operación puntual.

Separar presentación y procesamiento

Una página puede mostrar datos ya obtenidos. El proceso que los actualiza debe tener un recorrido independiente, con un tiempo de espera y un resultado controlado. Así una visita no inicia necesariamente una consulta remota y la velocidad de la web no depende siempre del proveedor.

Para operaciones de escritura, como transmitir una solicitud aceptada, conserva primero el estado necesario y define cómo se confirma el resultado. No utilices la representación de una página como disparador informal de una acción que no debe repetirse.

Elegir entre conector y desarrollo propio

Un conector puede ser adecuado si documenta permisos, tratamiento de errores, límites, datos almacenados y mantenimiento. Un desarrollo propio permite ajustar un contrato pequeño, pero exige conservar código y pruebas. Compara lo que cada opción resuelve realmente, no solo la existencia de un botón “conectar”.

La decisión puede apoyarse en comparar aplicaciones antes de implantarlas. En ambos casos, la empresa debe saber qué datos se mueven y cómo detener la integración.

Preparar el ejemplo y comprender su alcance

El plugin consultará la información pública de la cuenta WordPress en GitHub y conservará únicamente el número de repositorios públicos y la fecha de consulta. No copiará perfiles completos ni utilizará el número como una afirmación permanente: mostrará la instantánea obtenida.

La documentación de peticiones HTTP de WordPress utiliza precisamente la consulta a esa cuenta como ejemplo de acceso externo. El código siguiente amplía el recorrido con validación, actualización programada y presentación local.

Condiciones del laboratorio

El ejemplo está planteado para una instalación individual de WordPress y PHP 8.0 o posterior. Pruébalo primero en un sitio de ensayo con posibilidad de recuperar archivos. No está diseñado como plugin multisitio ni como cola para operaciones críticas.

Crea la carpeta wp-content/plugins/em-demo-api/ y dentro un archivo em-demo-api.php con el código de la siguiente sección. Tras activarlo, añade [em_api_repos_publicos] mediante un bloque de shortcode en una página de pruebas.

La primera tarea se programa para después de la activación, pero su ejecución depende de que el mecanismo de tareas funcione. Hasta recibir datos válidos, el shortcode mostrará un mensaje de indisponibilidad. No se hará una llamada desde el propio shortcode para evitar ese mensaje.

Una decisión conservadora ante limitaciones

Si la API responde 403 o 429, el ejemplo pausa las actualizaciones y requiere revisión. No intenta deducir automáticamente si existe una restricción de acceso o una cuota agotada. Más adelante se explica cómo comprobar ese estado y cuándo reactivar.

Código del plugin: consulta programada y salida local

La URL es fija, la petición tiene un tiempo de espera, no sigue redirecciones y el resultado se valida antes de guardarlo. La cuenta del visitante nunca decide qué destino consulta el servidor.

<?php
/**
 * Plugin Name: EM Demo API Pública
 * Description: Consulta programada de un dato público y salida mediante shortcode.
 * Version: 1.0.0
 * Requires PHP: 8.0
 */

defined('ABSPATH') || exit;

function em_demo_api_status(string $state, int $http = 0): void {
    update_option('em_demo_api_status', array(
        'attempted_at' => time(), 'state' => $state, 'http_status' => $http,
    ), false);
}

function em_demo_api_activate(): void {
    delete_option('em_demo_api_paused');
    if (!wp_next_scheduled('em_demo_api_refresh')) {
        $result = wp_schedule_event(time() + 30, 'hourly',
                                    'em_demo_api_refresh', array(), true);
        if (is_wp_error($result) || false === $result) {
            em_demo_api_status('schedule_error');
        }
    }
}
register_activation_hook(__FILE__, 'em_demo_api_activate');

function em_demo_api_deactivate(): void {
    wp_clear_scheduled_hook('em_demo_api_refresh');
}
register_deactivation_hook(__FILE__, 'em_demo_api_deactivate');

function em_demo_api_refresh(): void {
    if (get_option('em_demo_api_paused', false)) {
        return;
    }
    $response = wp_safe_remote_get(
        'https://api.github.com/users/wordpress',
        array(
            'timeout' => 10,
            'redirection' => 0,
            'limit_response_size' => 65537,
            'headers' => array(
                'Accept' => 'application/vnd.github+json',
                'User-Agent' => 'EM-Demo-API/1.0',
            ),
        )
    );
    if (is_wp_error($response)) {
        em_demo_api_status('transport_error');
        return;
    }
    $http = (int) wp_remote_retrieve_response_code($response);
    if (in_array($http, array(403, 429), true)) {
        update_option('em_demo_api_paused', true, false);
        em_demo_api_status('rate_or_policy_paused', $http);
        return;
    }
    if (200 !== $http) {
        em_demo_api_status('http_error', $http);
        return;
    }
    $content_type = strtolower(trim(explode(';', (string)
        wp_remote_retrieve_header($response, 'content-type'), 2)[0]));
    $body = wp_remote_retrieve_body($response);
    if ('application/json' !== $content_type || strlen($body) > 65536) {
        em_demo_api_status('format_error', $http);
        return;
    }
    $data = json_decode($body, true);
    if (JSON_ERROR_NONE !== json_last_error() || !is_array($data)
        || !isset($data['login'], $data['public_repos'])
        || !is_string($data['login'])
        || 'wordpress' !== strtolower($data['login'])
        || !is_int($data['public_repos']) || $data['public_repos'] < 0) {
        em_demo_api_status('contract_error', $http);
        return;
    }
    $snapshot = array(
        'public_repos' => $data['public_repos'], 'fetched_at' => time(),
    );
    update_option('em_demo_api_last_good', $snapshot, false);
    set_transient('em_demo_api_cache', $snapshot, HOUR_IN_SECONDS);
    em_demo_api_status('ok', $http);
}
add_action('em_demo_api_refresh', 'em_demo_api_refresh');

function em_demo_api_shortcode(): string {
    $data = get_transient('em_demo_api_cache');
    if (false === $data) {
        $data = get_option('em_demo_api_last_good', array());
    }
    if (!is_array($data) || !isset($data['public_repos'], $data['fetched_at'])
        || !is_int($data['public_repos']) || $data['public_repos'] < 0
        || !is_int($data['fetched_at']) || $data['fetched_at'] > time()
        || time() - $data['fetched_at'] > DAY_IN_SECONDS) {
        return '<p>Dato público temporalmente no disponible.</p>';
    }
    return '<p>Repositorios públicos de WordPress: '
        . esc_html(number_format_i18n($data['public_repos']))
        . '. Última consulta: '
        . esc_html(wp_date('d/m/Y H:i', $data['fetched_at']))
        . ' (zona horaria del sitio).</p>';
}
add_shortcode('em_api_repos_publicos', 'em_demo_api_shortcode');

function em_demo_api_uninstall(): void {
    wp_clear_scheduled_hook('em_demo_api_refresh');
    delete_transient('em_demo_api_cache');
    foreach (array('em_demo_api_last_good', 'em_demo_api_status',
                   'em_demo_api_paused') as $name) {
        delete_option($name);
    }
}
register_uninstall_hook(__FILE__, 'em_demo_api_uninstall');

El código conserva un último dato válido como opción y utiliza un transient como caché. Si desaparece la caché, la página lee la instantánea local, no repite la consulta. El dato deja de mostrarse cuando supera veinticuatro horas de antigüedad. Ese plazo es una elección didáctica para este dato público, no un valor apropiado para cualquier proceso.

Al desactivar el plugin se elimina su tarea programada. Al desinstalarlo se limpian también las opciones del ejemplo. Antes de retirarlo, elimina el shortcode de la página de prueba para no dejar una referencia sin su implementación.

Entender los controles de la llamada y de la respuesta

wp_safe_remote_get() valida la URL y los destinos de redirección mediante los controles de WordPress frente a peticiones inseguras. El ejemplo añade una restricción distinta: un único destino escrito en el código y ninguna redirección.

Eso no convierte la función en un permiso universal para consultar cualquier URL que envíe un visitante. Si otro proyecto permite configurar destinos, establece una lista autorizada y revisa su acceso a redes internas. No expongas un endpoint que actúe como proxy abierto.

Comprobar transporte antes que contenido

El cliente puede devolver WP_Error antes de tener una respuesta HTTP. Por eso se comprueba primero. Después se obtiene el estado con wp_remote_retrieve_response_code() y solo se interpreta el cuerpo si se ha recibido el resultado esperado.

La validación del ejemplo exige JSON, un perfil cuya identidad coincida con la prevista y un contador entero no negativo. Una respuesta que no cumple ese contrato no sustituye el último valor correcto.

Limitar contenido y evitar exposición

El código limita la respuesta recibida y comprueba además la longitud del cuerpo antes de decodificar. No guarda HTML remoto ni devuelve al visitante los mensajes internos del proveedor. La presentación solo utiliza un dato numérico previamente validado.

Estas comprobaciones encajan con mejorar la calidad de los datos antes de automatizar procesos: el límite entre aplicaciones es un lugar adecuado para impedir que un formato inesperado se convierta en un dato válido.

Diseñar caché y último dato válido con reglas diferentes

La caché reduce trabajo repetido; la instantánea válida permite decidir qué mostrar durante una incidencia. Conviene separar ambos conceptos. Una caché que desaparece no tiene por qué significar que el último dato validado ha dejado de ser útil.

Los transients de WordPress pueden desaparecer antes de su tiempo máximo de expiración y no siempre se almacenan en la base de datos. No los utilices como única evidencia de una operación crítica ni como sustituto automático de una cola duradera.

Definir antigüedad y ámbito

Para cada dato, decide cuánto tiempo puede mostrarse y cómo se informa de su fecha. En el ejemplo, la página indica la última consulta. Un precio, una disponibilidad o un saldo pueden necesitar reglas mucho más estrictas que un contador público.

Si la respuesta depende del usuario o de la empresa, la clave de caché debe incluir ese ámbito. Compartir un resultado privado bajo una clave global puede mostrar información de una cuenta a otra. El ejemplo evita esa dimensión porque solo utiliza un dato público fijo.

No confundir validación con escape

Validar comprueba que un dato cumple el contrato. Escapar adapta su representación al contexto de salida. La guía de escape de WordPress explica funciones como esc_html(), esc_attr() y esc_url(). El ejemplo escapa el texto al construir el HTML, aunque el contador ya haya sido validado.

Revisa asimismo la caché de página o del proxy. Una página servida desde una caché externa puede conservar el HTML anterior después de que el dato local haya cambiado. Configura su duración o invalidación de acuerdo con la antigüedad que prometes mostrar.

Comprender qué garantiza WP-Cron y cuándo usar otro ejecutor

El mecanismo de WP-Cron comprueba tareas programadas en relación con las solicitudes al sitio; no equivale a una garantía de ejecución exacta a una hora. Si faltan visitas, hay bloqueos o falla el disparador, la actualización puede retrasarse.

wp_schedule_event() registra la recurrencia y wp_next_scheduled() ayuda a evitar programaciones duplicadas. La existencia del evento no demuestra que la última ejecución haya terminado correctamente.

Separar fecha prevista y fecha efectiva

Supervisa cuándo debía ejecutarse el trabajo y cuándo se obtuvo el último dato válido. Si tu proceso necesita mayor regularidad, configura un programador del sistema o un ejecutor adecuado a la instalación, con un procedimiento documentado para disparar las tareas y comprobar su resultado.

No desactives el disparo ordinario de WP-Cron antes de tener funcionando y verificado el sustituto. Tampoco ejecutes varias copias sin revisar concurrencia y exclusión: una integración de escritura podría procesar dos veces el mismo trabajo.

Límites del ejemplo

Este plugin no incorpora bloqueo distribuido, reanudación de lotes ni entrega garantizada de trabajos. Su consulta es de lectura y se programa con una frecuencia baja. Una sincronización de pedidos necesita persistencia por operación, control de duplicados y una estrategia de recuperación adicional.

La elección del ejecutor forma parte de gestionar la disponibilidad de los servicios digitales, no solo de escoger un intervalo en una pantalla.

Añadir una ruta REST propia con permisos explícitos

Una aplicación externa puede necesitar conocer el estado de la integración. Para enseñar el recorrido inverso, el siguiente fragmento añade una ruta de solo lectura que informa del último intento guardado. No realiza una llamada al proveedor ni publica el cuerpo de sus respuestas.

Puedes añadirlo al final del mismo archivo PHP, sin otra etiqueta de apertura. Es una ampliación opcional del ejemplo anterior.

add_action('rest_api_init', function (): void {
    register_rest_route('em-integraciones/v1', '/estado', array(
        'methods' => WP_REST_Server::READABLE,
        'permission_callback' => function (): bool {
            return current_user_can('manage_options');
        },
        'callback' => function (): WP_REST_Response {
            $status = get_option('em_demo_api_status', array());
            $states = array('ok', 'schedule_error', 'transport_error',
                'rate_or_policy_paused', 'http_error', 'format_error',
                'contract_error');
            $state = is_array($status) ? ($status['state'] ?? '') : '';
            return new WP_REST_Response(array(
                'state' => in_array($state, $states, true) ? $state : 'unknown',
                'last_attempt' => is_array($status)
                    ? absint($status['attempted_at'] ?? 0) : 0,
                'paused' => (bool) get_option('em_demo_api_paused', false),
            ), 200, array('Cache-Control' => 'private, no-store'));
        },
    ));
});

La ruta será relativa a la URL REST de esa instalación: /wp-json/em-integraciones/v1/estado en una configuración habitual de enlaces. La documentación sobre endpoints propios describe el registro de rutas y su permission_callback.

El ejemplo exige manage_options y está pensado para una comprobación administrativa. Para un servicio externo real, define una capacidad específica y una identidad con los permisos mínimos; no concedas administración completa solo para leer ese estado.

Si creas una ruta pública, hazlo porque su contenido está expresamente autorizado para cualquier visitante. No utilices una función que siempre devuelve verdadero como solución a un error de permisos de una operación privada.

Elegir autenticación según quién accede a WordPress

Una persona que ya ha iniciado sesión en WordPress puede utilizar la autenticación basada en cookies desde el contexto previsto. Las peticiones REST correspondientes necesitan el tratamiento de nonce descrito por WordPress para proteger ese recorrido.

Para aplicaciones externas, WordPress admite contraseñas de aplicación mediante HTTPS. Son credenciales asociadas a un usuario y no eliminan la necesidad de comprobar sus capacidades. Utiliza identidades separadas y revoca accesos que dejen de ser necesarios.

El nonce no sustituye a la autorización

Comprobar un nonce no responde por sí solo si la persona puede modificar un determinado registro. La ruta debe aplicar los permisos adecuados al recurso y a la operación. Tampoco debes tratar un nonce de WordPress como un secreto permanente para autenticar un proveedor externo.

Proteger secretos de llamadas salientes

El ejemplo público no necesita credenciales de GitHub. Cuando otra API sí las requiera, mantenlas en el servidor, fuera del contenido de páginas y del código enviado al navegador. El plugin debe acceder únicamente al secreto que necesita y no incluirlo en errores, URLs o registros.

Separa pruebas y producción. Una copia del sitio no debería empezar a enviar operaciones reales utilizando credenciales copiadas accidentalmente. Revisa los disparadores y la configuración antes de activar el entorno clonado.

Recibir webhooks sin ejecutar efectos duplicados

Un webhook es otro recorrido entrante: el proveedor comunica un evento a una ruta de WordPress. No basta con que la URL sea difícil de adivinar. Verifica el mecanismo de autenticación o firma documentado por el emisor antes de aceptar el evento.

Autenticar el mensaje correcto

Cuando el contrato utiliza una firma sobre el cuerpo original, la comprobación debe hacerse sobre esos bytes según sus reglas, no sobre un JSON reconstruido. Revisa también los mecanismos de fecha o protección frente a repetición que contemple el proveedor.

El diseño debe limitar tamaño, validar estructura y conservar un identificador de evento. Un mensaje autenticado puede seguir conteniendo datos que tu proceso no sabe interpretar.

Acusar recepción y procesar con control

Para trabajo que no cabe en una respuesta rápida, guarda primero una tarea de forma duradera y después devuelve el acuse apropiado. No respondas que has aceptado el trabajo si solo lo has dejado en memoria y puedes perderlo al terminar la petición.

Conserva qué eventos ya se trataron y qué resultado produjeron. Un reenvío puede ser legítimo y no debería crear un segundo efecto. Si la escritura externa pierde la respuesta, el identificador de evento local no basta para demostrar qué ocurrió en el otro sistema: necesitarás su garantía o una conciliación.

El ejemplo de esta guía no implementa un receptor de webhooks. Antes de construirlo, define el contrato concreto y revisa cómo funcionan los webhooks, diferenciando entrega del mensaje y ejecución del proceso.

Probar la integración antes de incorporarla a la web pública

Prepara una matriz que incluya el recorrido normal y las situaciones que más pueden afectar a la página o al proceso. En el plugin del ejemplo puedes simular respuestas del cliente HTTP en un entorno de pruebas y comprobar las opciones guardadas.

Comprobaciones propuestas para el ejemplo
Situación Resultado esperado
No existen datos guardados El shortcode muestra indisponibilidad sin consultar la API.
Respuesta JSON válida Se guarda el contador y la fecha; la página los muestra escapados.
JSON incorrecto o contador inválido No se sustituye la última instantánea correcta.
Fallo temporal de transporte Se conserva el último dato y queda un estado de error.
Respuesta 403 o 429 Se pausa la actualización hasta revisar la causa.
Caché ausente Se consulta la instantánea local, no el proveedor.
Dato demasiado antiguo No se presenta como disponible.
Llamada REST sin permiso No se entrega el estado administrativo.

Comprueba además que desactivar cancela la programación y que desinstalar limpia solo los datos del ejemplo. Evita pruebas destructivas sobre una instalación pública.

Reactivar una pausa de forma consciente

La opción em_demo_api_paused refleja la pausa del ejemplo. Revisa la documentación y el estado de la cuenta o de la dirección de salida, y espera el plazo que indique el proveedor. Solo después de resolverlo, desactiva y activa el plugin para limpiar la pausa y volver a programar su consulta. No utilices reactivaciones repetidas para insistir ante una limitación.

Antes del despliegue, conserva copia del código anterior y comprueba cómo retirar la integración sin perder el contenido de la web. Ese procedimiento encaja con despliegues web controlados.

Mantener el vínculo entre la web y los sistemas externos

Una vez desplegada, asigna un responsable y conserva la descripción de la operación, la versión del plugin, el destino autorizado y las pruebas. Registra la última consulta válida y los fallos sin guardar secretos ni respuestas completas innecesarias.

Revisa cambios del proveedor y de WordPress, pero también cambios locales: un plugin de seguridad, una caché, una modificación de certificados o una nueva configuración de tareas puede afectar al recorrido. Prueba la integración después de cambios que modifiquen esas dependencias.

No convertir WordPress en un puente indiscriminado

Una ruta que acepta cualquier URL y devuelve cualquier respuesta aumenta mucho el alcance del riesgo. Un endpoint que permite escribir campos sin validar puede exponer el sistema de destino. Mantén operaciones concretas y permisos relacionados con su función.

Cuando el volumen o la criticidad crezcan, puede convenir que WordPress conserve el punto de interacción y otro componente ejecute el procesamiento pesado. Esa decisión debe basarse en tiempos, acumulación y capacidad de recuperación observados, no en la idea de que todo debe salir de WordPress.

El resultado buscado es una web que utilice información externa sin depender de una llamada remota en cada visita y sin confundir acceso público con operación empresarial autorizada.

Preguntas frecuentes

¿Tengo que usar la API REST de WordPress para consultar una API externa?

No. Para llamadas salientes desde PHP puedes utilizar la API HTTP de WordPress. La API REST sirve para exponer o consumir operaciones de WordPress desde otros clientes, aunque ambos recorridos pueden formar parte del mismo proyecto.

¿Puedo pegar la clave de una API en un bloque HTML?

No cuando deba mantenerse confidencial. El contenido que llega al navegador puede inspeccionarse. Conserva el secreto en el servidor y expón solo las operaciones y datos permitidos.

¿Por qué el ejemplo no consulta GitHub desde el shortcode?

Para que la visita no dependa directamente del servicio externo ni multiplique llamadas. El shortcode lee datos locales y muestra indisponibilidad cuando todavía no hay una instantánea válida.

¿Un transient sirve para guardar un trabajo que no puede perderse?

No como única persistencia. Su desaparición anticipada forma parte de su comportamiento posible. Un trabajo crítico necesita un almacenamiento y unos estados apropiados para recuperación y confirmación.

¿WP-Cron garantiza una actualización exactamente cada hora?

No. El ejemplo programa una recurrencia horaria, pero la ejecución puede retrasarse. Para requisitos más estrictos necesitas un disparador y una supervisión acordes con la instalación.

¿El plugin es un conector completo con cualquier API?

No. Es un ejemplo de lectura pública con contrato concreto. Una API privada, un webhook o una sincronización de escritura requieren autenticación, validaciones y garantías adicionales específicas.

Conclusión

Integrar APIs con WordPress resulta más controlable cuando separas dirección de comunicación, permisos, procesamiento y presentación. La web no tiene por qué consultar al proveedor en cada visita ni exponer una credencial para mostrar información útil.

El ejemplo ilustra una base concreta: destino fijo, consulta programada, respuesta validada y salida local. A partir de ella, amplía el diseño según el contrato real y prueba los fallos antes de incorporar escrituras o información privada.

Profundizar en desarrollo e integración de WordPress

Trabajar con APIs en WordPress permite conectar PHP, protocolos HTTP, seguridad, tareas programadas y diseño de aplicaciones. Para estudiar estas áreas de forma estructurada, consulta los programas de formación de ESTUDIO METADATOS, basados en cursos y másteres online, y revisa su relación con las competencias que quieres adquirir.

Ver programas de formación relacionados

Written by