MetalAPI
XAU3997,89−1,54 %XAG53,936−1,51 %XPT1554,47−0,83 %XPD1047,03−0,44 %XCU6,5080+2,29 %WTI103,27+3,82 %BRENT111,70−1,30 %EUR1,120400−0,01 %GBP1,337500−0,01 %JPY0,0063761+0,14 %CHF1,213445−0,25 %TRY0,020125−0,12 %BTC80.681,12−0,12 %ETH2352,17−0,16 %
Todos los artículos
OroPHPWordPressTutorial

API de precios del oro en PHP: cURL, Guzzle y WordPress

Usa una API de precios del oro en PHP con cURL o Guzzle y añade a WordPress un shortcode [gold_price] con caché, transients y WP-Cron, o un widget sin código.

Por MetalAPI Team··7 min de lectura

API de precios del oro en PHP: lo básico

Una API de precios del oro proporciona a tu código PHP el precio actual del oro en JSON, para precios de productos, una línea de precio en un tema o un informe diario. Este tutorial trata PHP sin frameworks con cURL, el cliente HTTP Guzzle y WordPress, donde un shortcode [gold_price] con caché, actualizado por WP-Cron, inserta el precio en cualquier entrada o página. Usa MetalAPI, una API REST en JSON para metales preciosos, metales industriales, energía, divisas y criptomonedas.

Necesitas una clave API del panel de MetalAPI. Las claves empiezan por mk_live_, y el plan Free incluye 100 solicitudes al mes con actualización diaria de precios, sin tarjeta de crédito. Mantén la clave fuera de tu código: en una variable de entorno para PHP sin frameworks, o en wp-config.php para WordPress.

Cada endpoint es una solicitud GET bajo https://metalapi.com/api/v1. Envía la clave en la cabecera X-API-Key (o como Authorization: Bearer) en lugar del parámetro de consulta api_key, para que no aparezca en los registros de acceso. Una respuesta de /latest con los valores de ejemplo de la documentación:

GET /api/v1/latest?base=USD&currencies=XAU,EUR
{
  "success": true,
  "timestamp": 1792834892,
  "date": "2026-10-24",
  "base": "USD",
  "rates": {
    "XAU": 0.0002501319,
    "USDXAU": 3997.89,
    "EUR": 0.8925,
    "USDEUR": 1.1204
  },
  "rate_limit": { "...": "..." }
}

rates.USDXAU es el precio de una onza troy de oro en dólares; rates.XAU es su inverso, onzas por dólar, así que nunca lo muestres como precio. rates.USDEUR es el precio de un euro en dólares, que el plugin de WordPress usa para convertir. timestamp es la hora de los datos de precios en segundos Unix.

Cómo obtener el precio del oro en PHP con cURL

La extensión cURL viene incluida en casi todas las instalaciones de PHP. Esta función envía la clave en una cabecera, establece tiempos de espera y convierte los fallos de red y los errores de la API en excepciones:

gold.php
<?php
// PHP 8+ con la extensión curl. Ejecuta: METALAPI_KEY=mk_live_... php gold.php
function metalapi(string $path, array $params = []): array
{
    $ch = curl_init('https://metalapi.com/api/v1/' . $path . '?' . http_build_query($params));
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CONNECTTIMEOUT => 5,
        CURLOPT_TIMEOUT        => 10,
        CURLOPT_HTTPHEADER     => ['X-API-Key: ' . getenv('METALAPI_KEY')],
    ]);
    $body = curl_exec($ch);
    if ($body === false) {
        throw new RuntimeException('Network error: ' . curl_error($ch));
    }
    $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    $data = json_decode($body, true);
    if (empty($data['success'])) {
        $code = $data['error']['code'] ?? $status;
        $info = $data['error']['info'] ?? "HTTP $status";
        throw new RuntimeException("MetalAPI $code: $info", $code);
    }
    return $data;
}

$data = metalapi('latest', ['base' => 'USD', 'currencies' => 'XAU']);
printf("Gold: %.2f USD per troy oz\n", $data['rates']['USDXAU']);
printf("Time: %s UTC\n", gmdate('Y-m-d H:i', $data['timestamp']));

Con los valores de ejemplo, imprime Gold: 3997.89 USD per troy oz. El código de la excepción es el código de error de MetalAPI: reintenta 104 (el límite por minuto) después de los segundos de Retry-After y 500 o 503 con espera exponencial, pero corrige la causa de códigos como 102 (clave no válida), 105 (cuota mensual agotada) o 106 (endpoint no incluido en tu plan). La guía de manejo de errores enumera todos los códigos.

Usar Guzzle en lugar de cURL

En un proyecto con Composer, Guzzle hace lo mismo con menos líneas. Mantén la barra final en base_uri y solicita rutas relativas como latest, porque Guzzle resuelve /latest a partir de la raíz del dominio. Y desactiva http_errors, para que las respuestas de error devuelvan el cuerpo JSON de MetalAPI en lugar de lanzar una excepción:

gold-guzzle.php
<?php
// composer require guzzlehttp/guzzle
require __DIR__ . '/vendor/autoload.php';

$client = new GuzzleHttp\Client([
    'base_uri'    => 'https://metalapi.com/api/v1/',
    'timeout'     => 10,
    'http_errors' => false,
    'headers'     => ['X-API-Key' => (string) getenv('METALAPI_KEY')],
]);

$res  = $client->get('latest', ['query' => ['base' => 'EUR', 'currencies' => 'XAU', 'unit' => 'gram']]);
$data = json_decode((string) $res->getBody(), true);

if (empty($data['success'])) {
    $code = $data['error']['code'] ?? $res->getStatusCode();
    throw new RuntimeException("MetalAPI $code: " . ($data['error']['info'] ?? $res->getReasonPhrase()), $code);
}
printf("Gold: %.2f EUR per gram\n", $data['rates']['EURXAU']);

Los tiempos de espera agotados y otros fallos de red siguen lanzando una ConnectException, así que captúrala allí donde un fallo no deba romper la página.

Precio del oro en WordPress: un shortcode [gold_price] con caché

Dentro de WordPress, usa en su lugar wp_remote_get() de la API HTTP integrada. Dos reglas de diseño mantienen el shortcode rápido y barato:

  • No llames a la API mientras se genera una página. El plugin guarda los precios en un transient que WP-Cron actualiza en segundo plano. Los visitantes solo provocan una solicitud cuando falta el transient, por ejemplo justo después de la activación.
  • Pide todas las divisas en una sola llamada. Con la base USD y currencies=XAU,EUR,GBP, la respuesta contiene el precio en dólares del oro y de cada divisa a partir de la misma instantánea, así que el oro en euros es USDXAU ÷ USDEUR, y los gramos se obtienen a partir de los 31,1034768 gramos de una onza troy. Una solicitud por actualización cubre todas las divisas y unidades.

Los transients pueden desaparecer antes de caducar, por ejemplo cuando una caché de objetos persistente los expulsa, así que el plugin también guarda los últimos precios válidos en una opción. Si una actualización falla, registra el error y sirve esa copia durante cinco minutos. Primero añade la clave a wp-config.php, encima de la línea que indica que dejes de editar:

wp-config.php
define( 'METALAPI_KEY', 'mk_live_your_key_here' );

Después guarda el plugin como wp-content/plugins/metalapi-gold-price/metalapi-gold-price.php y actívalo en Plugins:

metalapi-gold-price.php
<?php
/**
 * Plugin Name:  MetalAPI Gold Price
 * Description:  Shortcode [gold_price], guardado en caché en un transient y actualizado por WP-Cron.
 * Requires PHP: 8.0
 */
defined( 'ABSPATH' ) || exit;

const MGP_INTERVAL   = 600;              // segundos. Standard: 600, Lite: 3600, Free: 86400
const MGP_CURRENCIES = [ 'EUR', 'GBP' ]; // además de USD, todas las divisas que usan tus shortcodes
const MGP_GRAMS      = [ 'troy_oz' => 31.1034768, 'gram' => 1, 'kilogram' => 1000 ];

function mgp_refresh(): ?array {
    if ( ! defined( 'METALAPI_KEY' ) ) {
        return null;
    }
    $url  = add_query_arg(
        [ 'base' => 'USD', 'currencies' => implode( ',', [ 'XAU', ...MGP_CURRENCIES ] ) ],
        'https://metalapi.com/api/v1/latest'
    );
    $res  = wp_remote_get( $url, [ 'timeout' => 8, 'headers' => [ 'X-API-Key' => METALAPI_KEY ] ] );
    $data = is_wp_error( $res ) ? null : json_decode( wp_remote_retrieve_body( $res ), true );

    if ( empty( $data['success'] ) ) {
        error_log( 'MetalAPI refresh failed: ' . ( is_wp_error( $res ) ? $res->get_error_message() : wp_remote_retrieve_body( $res ) ) );
        $last = get_option( 'mgp_prices_last' );
        if ( $last ) {
            set_transient( 'mgp_prices', $last, 5 * MINUTE_IN_SECONDS ); // espera y conserva los últimos precios válidos
        }
        return $last ?: null;
    }

    $per_oz = [ 'USD' => $data['rates']['USDXAU'] ];
    foreach ( MGP_CURRENCIES as $cur ) {
        if ( ! empty( $data['rates'][ "USD$cur" ] ) ) {
            $per_oz[ $cur ] = $data['rates']['USDXAU'] / $data['rates'][ "USD$cur" ];
        }
    }
    $prices = [ 'per_oz' => $per_oz, 'time' => $data['timestamp'] ];
    set_transient( 'mgp_prices', $prices, 2 * MGP_INTERVAL ); // sobrevive a una ejecución tardía de cron
    update_option( 'mgp_prices_last', $prices, false );       // respaldo si se expulsa el transient
    return $prices;
}

function mgp_prices(): ?array {
    return get_transient( 'mgp_prices' ) ?: mgp_refresh();
}

add_shortcode( 'gold_price', function ( $atts ): string {
    $a    = shortcode_atts( [ 'currency' => 'USD', 'unit' => 'troy_oz', 'decimals' => 2 ], $atts, 'gold_price' );
    $cur  = strtoupper( $a['currency'] );
    $unit = strtolower( $a['unit'] );
    $p    = mgp_prices();
    if ( ! $p || ! isset( $p['per_oz'][ $cur ] ) || ! array_key_exists( $unit, MGP_GRAMS ) ) {
        return '';
    }
    $price = $p['per_oz'][ $cur ] / MGP_GRAMS['troy_oz'] * MGP_GRAMS[ $unit ];
    return sprintf(
        '<span class="gold-price">%s %s / %s</span> <small>%s</small>',
        esc_html( number_format_i18n( $price, absint( $a['decimals'] ) ) ),
        esc_html( $cur ),
        esc_html( str_replace( '_', ' ', $unit ) ),
        esc_html( wp_date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $p['time'] ) )
    );
} );

// WP-Cron: actualiza en segundo plano una vez por intervalo.
add_filter( 'cron_schedules', function ( array $schedules ): array {
    $schedules['mgp_interval'] = [ 'interval' => MGP_INTERVAL, 'display' => 'MetalAPI refresh' ];
    return $schedules;
} );
add_action( 'mgp_refresh_event', 'mgp_refresh' );

register_activation_hook( __FILE__, function (): void {
    if ( ! wp_next_scheduled( 'mgp_refresh_event' ) ) {
        wp_schedule_event( time(), 'mgp_interval', 'mgp_refresh_event' );
    }
} );
register_deactivation_hook( __FILE__, function (): void {
    wp_clear_scheduled_hook( 'mgp_refresh_event' );
} );

Úsalo en cualquier entrada, página o bloque Código corto (Shortcode) como [gold_price] o [gold_price currency="EUR" unit="gram"]. Con las cotizaciones de ejemplo, el segundo muestra 114,72 EUR / gram: 3997,89 ÷ 1,1204 da 3568,27 euros por onza troy, y al dividir entre 31,1034768 se obtiene el precio por gramo. Una divisa que no esté en MGP_CURRENCIES no muestra nada, así que incluye todas las divisas que usan tus shortcodes.

Actualizar los precios del oro con WP-Cron

El hook de activación programa la actualización con el intervalo personalizado registrado mediante cron_schedules, y la desactivación la elimina. Ajusta MGP_INTERVAL al intervalo de actualización de tu plan, o a uno más largo si la cuota lo exige:

PlanMGP_INTERVALSolicitudes en un mes de 31 díasCuota mensual
Free (actualizaciones diarias)8640031100
Lite (actualizaciones cada 30 minutos)36007441000
Standard (actualizaciones cada 10 minutos)600446410.000

WP-Cron no es un programador real: ejecuta los eventos pendientes cuando alguien visita el sitio, así que en un sitio con poco tráfico una actualización puede retrasarse; la duración doble del transient y la copia guardada lo compensan. Para una sincronización exacta, añade define( 'DISABLE_WP_CRON', true ); a wp-config.php y ejecuta los eventos pendientes desde el cron del sistema con WP-CLI:

crontab
*/10 * * * * cd /var/www/html && wp cron event run --due-now > /dev/null 2>&1

Ten en cuenta también la caché de páginas: una caché de página completa guarda la salida del shortcode junto con la página, así que los visitantes ven el precio del momento en que se guardó la página. Mantén la duración de la caché de las páginas con precios igual o inferior a tu intervalo de actualización.

Cuándo es mejor un widget sin código

El shortcode es la herramienta adecuada cuando el precio debe formar parte de tu HTML o cuando PHP necesita la cifra, por ejemplo para recalcular el precio de los productos. Si solo quieres mostrar precios, un widget de MetalAPI es más sencillo:

  • Sin clave API y sin PHP. Creas el widget en el panel y pegas su código de inserción en un bloque HTML personalizado (Custom HTML). Los cambios posteriores de símbolos o diseño no requieren ninguna edición en tu sitio.
  • Sin cuota. Las visitas a páginas con widgets no consumen tus solicitudes mensuales, y todos los planes incluyen widgets: 1 en Free, 3 en Lite y 10 en Standard.
  • Compatible con la caché de páginas. Los precios se cargan dentro de un iframe, así que una página en caché nunca los congela.
Código de inserción del widget
<div id="metalapi-widget-w_abc123"></div>
<script src="https://metalapi.com/js/widget.js" data-widget="w_abc123" async></script>

Si tu proveedor de alojamiento elimina las etiquetas <script>, usa la versión iframe del código de inserción. Cómo añadir un widget con el precio del oro en tiempo real explica el editor, la colocación en WordPress y los límites de cada plan; la página de widgets muestra los diseños.

Próximos pasos

Crea una cuenta gratuita, añade tu clave a wp-config.php e instala el plugin con MGP_INTERVAL establecido en 86400: una actualización diaria consume 31 de las 100 solicitudes mensuales del plan Free. Cuando necesites precios más recientes, ajusta el intervalo al de actualización de tu nuevo plan y compara los planes en la página de precios. Para backends de Node.js, API de precios del oro en JavaScript muestra los mismos patrones con Express y Next.js.

Preguntas frecuentes

¿Dónde debo guardar la clave API en WordPress?

En wp-config.php como constante, o en una variable de entorno que se lea allí. Nunca en los archivos del tema, que a menudo acaban en Git, ni en JavaScript, que cualquier visitante puede leer. Las solicitudes desde PHP no envían cabecera Origin ni Referer, así que incluso una clave restringida por dominio funciona desde el servidor.

¿Por qué el shortcode muestra el mismo precio del oro todo el fin de semana?

El oro al contado no cotiza desde el viernes a las 17:00 hasta el domingo a las 17:00, hora de Nueva York, así que la API devuelve el último precio hasta que los mercados vuelven a abrir. En el plan Free, además, los precios solo cambian una vez al día.

¿Cuántas solicitudes a la API consumirá el plugin?

Una por actualización, tengas los visitantes, páginas o shortcodes que tengas, porque una sola llamada devuelve todas las divisas y las unidades se convierten localmente: unas 4500 al mes con un intervalo de 10 minutos.

¿Puedo usar el precio del oro en los precios de productos de WooCommerce?

Sí. Llama a mgp_prices() desde PHP, por ejemplo en un filtro woocommerce_product_get_price, y multiplica el precio por gramo por el peso y la pureza del producto. Guarda el precio del oro usado con cada pedido, para que las facturas no cambien más adelante.

¿Necesito cURL o Guzzle en WordPress?

No. wp_remote_get() usa la API HTTP de WordPress, que elige un transporte disponible y respeta la configuración de proxy. Reserva cURL o Guzzle para PHP sin frameworks y para frameworks como Laravel o Symfony.

Pruébalo con tus propios datos

Crea una cuenta gratuita y haz tu primera solicitud en menos de dos minutos. 100 solicitudes al mes, sin tarjeta de crédito.

Sigue leyendo