PHPで金価格APIを使う:cURL、Guzzle、WordPressショートコード
PHPでcURLやGuzzleを使って金価格APIを呼び出し、TransientとWP-Cronでキャッシュする[gold_price]ショートコードをWordPressに追加する方法と、ノーコードのウィジェットを紹介します。
PHPで金価格APIを使う:基本
金価格APIを使えば、PHPのコードで現在の金価格をJSONとして取得し、商品価格、テーマ内の価格表示、日次レポートなどに利用できます。このチュートリアルでは、cURLを使った素のPHP、HTTPクライアントのGuzzle、そしてWordPressを扱います。WordPressでは、WP-Cronで更新するキャッシュ付きの[gold_price]ショートコードを使って、任意の投稿や固定ページに価格を表示します。使用するのは、貴金属、産業用金属、エネルギー、通貨、暗号資産を扱うJSON REST APIのMetalAPIです。
MetalAPIのダッシュボードで取得したAPIキーが必要です。キーはmk_live_で始まります。Freeプランには月100リクエストと毎日の価格更新が含まれ、クレジットカードは不要です。キーはコードに含めず、素のPHPなら環境変数に、WordPressならwp-config.phpに保存してください。
すべてのエンドポイントは、https://metalapi.com/api/v1配下のGETリクエストです。キーはapi_keyクエリパラメーターではなく、X-API-Keyヘッダー(またはAuthorization: Bearer)で送信してください。そうすれば、キーがアクセスログに残りません。ドキュメントの例の値を使った/latestのレスポンスは次のとおりです:
{
"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は金1トロイオンスのドル建て価格です。rates.XAUはその逆数で1ドルあたりのオンス数なので、決して価格として表示しないでください。rates.USDEURは1ユーロのドル建て価格で、WordPressプラグインではこれを換算に使います。timestampは価格データの時刻をUnix秒で表したものです。
PHPとcURLで金価格を取得する方法
cURL拡張モジュールは、ほぼすべてのPHP環境に含まれています。次の関数は、キーをヘッダーで送信し、タイムアウトを設定して、ネットワーク障害とAPIエラーを例外に変換します:
<?php
// PHP 8以降とcurl拡張モジュールが必要。実行: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']));例の値では、Gold: 3997.89 USD per troy ozと出力されます。例外のコードはMetalAPIのエラーコードです。104(1分あたりの上限)はRetry-Afterの秒数だけ待ってから、500や503はバックオフを挟んで再試行します。一方、102(無効なキー)、105(月間クォータを使い切った)、106(エンドポイントがプランに含まれていない)などのコードは、原因を修正してください。エラーハンドリングガイドにすべてのコードが掲載されています。
cURLの代わりにGuzzleを使う
Composerを使うプロジェクトなら、Guzzleで同じことを少ない行数で書けます。base_uriの末尾のスラッシュは残し、latestのような相対パスをリクエストしてください。Guzzleは/latestをドメインのルートを基準に解決してしまうためです。また、http_errorsをオフにして、エラーレスポンスで例外を送出する代わりにMetalAPIのJSON本文が返されるようにします:
<?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']);タイムアウトなどのネットワーク障害では引き続きConnectExceptionが送出されます。失敗してもページが壊れてはいけない箇所では、これをキャッチしてください。
WordPressで金価格を表示する:キャッシュ付きの[gold_price]ショートコード
WordPress内では、代わりに組み込みのHTTP APIのwp_remote_get()を使います。ショートコードを高速かつ低コストに保つための設計ルールは2つです:
- ページの描画中にAPIを呼び出さない。 プラグインは価格をTransientに保存し、WP-Cronがバックグラウンドでそれを更新します。訪問者がリクエストを発生させるのは、有効化の直後などTransientが存在しない場合だけです。
- すべての通貨を1回の呼び出しで取得する。 基準通貨を
USD、currencies=XAU,EUR,GBPとすると、レスポンスには同じスナップショットから得た金と各通貨のドル建て価格が含まれます。そのため、ユーロ建ての金価格はUSDXAU ÷ USDEURで求められ、グラム単位の価格は1トロイオンス=31.1034768グラムから計算できます。1回の更新につき1リクエストで、すべての通貨と単位をカバーできます。
Transientは、永続オブジェクトキャッシュによって追い出される場合など、有効期限より前に消えることがあります。そのため、プラグインは最後に取得できた価格をオプションにも保存しておきます。更新に失敗した場合は、エラーをログに記録し、保存済みの価格を5分間返します。まず、wp-config.phpの、編集の終わりを示すコメント行より上に、キーを追加します:
define( 'METALAPI_KEY', 'mk_live_your_key_here' );次に、プラグインをwp-content/plugins/metalapi-gold-price/metalapi-gold-price.phpとして保存し、プラグイン(Plugins)画面で有効化します:
<?php
/**
* Plugin Name: MetalAPI Gold Price
* Description: [gold_price] shortcode, cached in a transient and refreshed by WP-Cron.
* Requires PHP: 8.0
*/
defined( 'ABSPATH' ) || exit;
const MGP_INTERVAL = 600; // 秒。Standard:600、Lite:3600、Free:86400
const MGP_CURRENCIES = [ 'EUR', 'GBP' ]; // USD以外に、ショートコードで使うすべての通貨
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 ); // 間隔を空け、最後に取得できた価格を維持する
}
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 ); // cronの実行が1回遅れても残る
update_option( 'mgp_prices_last', $prices, false ); // 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:間隔ごとに1回、バックグラウンドで更新する。
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' );
} );任意の投稿、固定ページ、ショートコード(Shortcode)ブロックで、[gold_price]や[gold_price currency="EUR" unit="gram"]のように使います。例のレートでは、2つ目は114.72 EUR / gramと表示されます。3997.89÷1.1204でトロイオンスあたり3,568.27ユーロとなり、それを31.1034768で割るとグラムあたりの価格になります。MGP_CURRENCIESに含まれていない通貨は何も表示されないため、ショートコードで使うすべての通貨をリストに入れてください。
WP-Cronで金価格を更新する
有効化フックは、cron_schedulesで登録したカスタム間隔で更新をスケジュールし、無効化するとそのスケジュールが削除されます。MGP_INTERVALはプランの更新間隔に合わせて設定し、クォータの都合で必要な場合はそれより長くしてください:
| プラン | MGP_INTERVAL | 31日の月のリクエスト数 | 月間クォータ |
|---|---|---|---|
| Free(毎日更新) | 86400 | 31 | 100 |
| Lite(30分ごとに更新) | 3600 | 744 | 1,000 |
| Standard(10分ごとに更新) | 600 | 4,464 | 10,000 |
WP-Cronは本物のスケジューラーではありません。予定時刻を過ぎたイベントを誰かがサイトにアクセスしたときに実行するため、アクセスの少ないサイトでは更新が遅れることがあります。その遅れは、Transientの有効期間を2倍にしていることと、保存済みのコピーで吸収されます。正確なタイミングが必要な場合は、wp-config.phpにdefine( 'DISABLE_WP_CRON', true );を追加し、システムのcronからWP-CLIで期限の来たイベントを実行してください:
*/10 * * * * cd /var/www/html && wp cron event run --due-now > /dev/null 2>&1ページキャッシュについても考慮が必要です。フルページキャッシュはショートコードの出力をページと一緒に保存するため、訪問者にはページがキャッシュされた時点の価格が表示されます。価格を表示するページのキャッシュ有効期間は、更新間隔以下にしてください。
ノーコードのウィジェットのほうが適している場合
価格をHTMLの一部にする必要がある場合や、商品価格の改定などでPHPが数値を必要とする場合には、ショートコードが適しています。価格を表示するだけなら、MetalAPIウィジェットのほうが簡単です:
- APIキーもPHPも不要。 ダッシュボードでウィジェットを作成し、その埋め込みコードをカスタムHTML(Custom HTML)ブロックに貼り付けます。後でシンボルやレイアウトを変更しても、サイト側を編集する必要はありません。
- クォータを消費しない。 ウィジェットのページビューは月間リクエストを消費しません。ウィジェットはすべてのプランに含まれており、Freeでは1個、Liteでは3個、Standardでは10個まで作成できます。
- ページキャッシュがあっても安全。 価格はiframe内で読み込まれるため、キャッシュされたページで価格が古いまま固定されることはありません。
<div id="metalapi-widget-w_abc123"></div>
<script src="https://metalapi.com/js/widget.js" data-widget="w_abc123" async></script>ホスティング環境で<script>タグが削除される場合は、iframe版の埋め込みコードを使ってください。Webサイトに金価格のライブウィジェットを追加する方法では、ビルダー、WordPressでの配置、プランの上限を説明しています。レイアウトはウィジェットページで確認できます。
次のステップ
無料アカウントを作成し、キーをwp-config.phpに追加して、MGP_INTERVALを86400に設定したプラグインをインストールしましょう。1日1回の更新なら、Freeプランの月100リクエストのうち31リクエストしか使いません。より新しい価格が必要になったら、間隔を新しいプランの更新間隔に合わせ、料金ページでプランを比較してください。Node.jsのバックエンドについては、JavaScriptで金価格APIを使うで、ExpressとNext.jsを使った同じパターンを紹介しています。
よくある質問
WordPressではAPIキーをどこに保存すべきですか?
wp-config.phpに定数として保存するか、そこで読み込む環境変数に保存してください。Gitに含まれがちなテーマファイルや、すべての訪問者が読めるJavaScriptには決して保存しないでください。PHPからのリクエストはOriginヘッダーもRefererヘッダーも送信しないため、ドメイン制限付きキーでもサーバーからなら動作します。
ショートコードが週末ずっと同じ金価格を表示するのはなぜですか?
スポット金はニューヨーク時間の金曜17:00から日曜17:00まで取引されないため、市場が再開するまでAPIは最後の価格を返します。また、Freeプランでは価格は1日1回しか変わりません。
プラグインはAPIリクエストをどれくらい使いますか?
訪問者、ページ、ショートコードの数にかかわらず、更新1回につき1リクエストです。1回の呼び出しですべての通貨が返され、単位はローカルで換算されるためです。10分間隔なら月に約4,500リクエストです。
WooCommerceの商品価格に金価格を使えますか?
はい。woocommerce_product_get_priceフィルターなどでPHPからmgp_prices()を呼び出し、グラムあたりの価格に商品の重量と純度を掛けてください。後から請求書の金額が変わらないように、使用した金価格を注文ごとに保存しておきましょう。
WordPressでcURLやGuzzleは必要ですか?
いいえ。wp_remote_get()はWordPressのHTTP APIを使います。HTTP APIは利用可能な通信手段を自動で選び、プロキシ設定にも従います。cURLやGuzzleは、素のPHPや、LaravelやSymfonyなどのフレームワークで使ってください。
関連記事
XAU/USDの意味:XAU・XAG・XPT・XPDが表すもの
XAU/USDの意味を解説します。金にISO 4217の通貨コードがある理由、XAG・XPT・XPDの意味、価格やAPIデータでの表示方向の読み方までわかります。
無料の金価格API:月100リクエストでできること
無料の金価格APIを正直に解説します。MetalAPIのFreeプランの内容、キャッシュとまとめ取得で月100リクエストを有効に使う方法、アップグレードの目安を紹介します。
Pythonで金価格APIを使う:価格の取得、キャッシュ、グラフ化
Pythonで金価格APIを使う方法を解説します。requestsでの最新価格の取得、エラー処理とリトライ、レスポンスのキャッシュ、pandasの時系列とmatplotlibによるグラフ化まで紹介します。