Webhook'larla Altın ve Gümüş Fiyat Alarmlarını Otomatikleştirin
İmzalı webhook'larla altın ve gümüş fiyat alarmları: koşullar, bekleme süreleri, payload, Node ve Python'da HMAC-SHA256 doğrulama ve yeniden denemeler.
Sorgulama yerine alarmlar
Birçok entegrasyon fiyatlarla yalnızca belirli anlarda ilgilenir: altın bir müşterinin sorduğu bir seviyeyi geçtiğinde, gümüş sert düştüğünde ya da bir para birimi bir kataloğu yeniden fiyatlandırmayı gerektirecek kadar hareket ettiğinde. Bu anları yakalamak için /latest endpoint'ini dakikada bir sorgulamak ayda yaklaşık 45.000 istek harcar ve eşiğin geçildiğini tespit eden mantığı yine de sizin yazmanız gerekir.
MetalAPI fiyat alarmları bu işi sunucuya taşır. Alarm motoru, her aktif alarmı gelen canlı fiyatlarla karşılaştırır ve bir koşul gerçekleştiğinde sizi bilgilendirir. Alarm kontrolleri API kotanızı kullanmaz ve kodunuz yalnızca bir şey olduğunda çalışır.
| Plan | Alarmlar | Gönderim |
|---|---|---|
| Free | Yok | — |
| Lite | 3 | E-posta |
| Standard | 10 | E-posta |
| Advanced | 25 | E-posta veya webhook |
| Pro | 50 | E-posta veya webhook |
| Ultra | 100 | E-posta veya webhook |
| Business | 200 | E-posta veya webhook |
| Enterprise | 500 | E-posta veya webhook |
Alarmları paneldeki Alerts bölümünden yönetirsiniz. Webhook rehberi kısa bir başvuru kaynağıdır; bu yazı ise canlı ortamda çalışan bir endpoint için gereken ayrıntıları ele alıyor. Eşik olayları yerine her fiyat güncellemesine ihtiyacınız varsa gerçek zamanlı akış yazısı diğer yaklaşımı anlatıyor.
Alarm koşulları ve ne zaman tetiklendikleri
Her alarm, tek bir para birimi cinsinden fiyatlanan tek bir sembolü izler; örneğin USD cinsinden XAU veya EUR cinsinden XAG. Platin, paladyum, endüstriyel metaller, enerji, döviz ve kripto dahil tüm aktif semboller kullanılabilir. Dört koşul vardır:
| Koşul | Ne zaman tetiklenir | Eşik |
|---|---|---|
above | fiyat ≥ eşik | Alarmın para birimi cinsinden bir fiyat |
below | fiyat ≤ eşik | Alarmın para birimi cinsinden bir fiyat |
change_pct_up | önceki kapanıştan bu yana değişim ≥ eşik | 100'ün altında bir yüzde |
change_pct_down | önceki kapanıştan bu yana değişim ≤ −eşik | Pozitif sayı olarak girilen, 100'ün altında bir yüzde |
Metal eşikleri, standart birim olan troy ons başınadır. Bu, başka yerlerde genellikle libre (pound) başına fiyatlanan bakır gibi endüstriyel metaller için de geçerlidir. Enerji kendi kontrat birimini kullanır (ham petrol için varil) ve webhook payload'ı birimi her zaman belirtir.
Fiyatlar ve yüzde değişimler alarmın para birimi cinsinden hesaplanır. EUR cinsinden XAG için, gümüş ABD doları cinsinden hiç değişmese bile yalnızca euronun güçlenmesi gümüşü eşiğinizin altına itebilir veya change_pct_down koşulunu tetikleyebilir.
Alarmlar, koşulun geçerli olduğu her fiyat güncellemesinde değil, eşiğin geçildiği anda tetiklenir. Altın 4.000 $ seviyesini yukarı yönde geçtiğinde tek bir bildirim alırsınız. Koşul yeniden geçersiz hale geldiğinde alarm tekrar kurulur; böylece fiyatın daha sonra seviyenin altına inip ardından yeniden yukarı geçmesi sizi tekrar bilgilendirir.
Bekleme süresi (cooldown), aynı alarmdan gelen iki bildirim arasındaki minimum süredir: 5 dakika ile 7 gün (10.080 dakika) arasında ayarlanabilir, varsayılan değer 60 dakikadır. Bir eşiğin çevresinde dalgalanan bir fiyat, bir saat içinde eşiği defalarca geçebilir; bekleme süresi bunu tek bir mesaja dönüştürür. Yalnızca bir kez önemli olan bir seviye için uzun bir bekleme süresi kullanın ve alarm tetiklendikten sonra onu duraklatın.
Bir davranış test için kullanışlıdır. Yeni bir alarmın önceki bir durumu yoktur; bu nedenle koşulu zaten geçerliyse bir sonraki fiyat güncellemesinde, piyasa açıkken genellikle bir dakika içinde tetiklenir. 5 dakikalık bekleme süresine sahip, XAU 1 USD'nin üzerindeyken tetiklenen bir alarm, test edebileceğiniz gerçek bir gönderim sağlar. Sonrasında alarmı silin.
E-posta veya webhook ile gönderim
E-posta alarmları Lite planından itibaren kullanılabilir. İnsanlar için tasarlanmıştır: “MetalAPI alert: Gold (XAU) rose above 4000 USD” gibi bir konu satırı, güncel fiyat ve alarma eklediğiniz not.
Advanced planından itibaren kullanılabilen webhook'lar ise sistemler için tasarlanmıştır: seçtiğiniz bir URL'ye JSON gövdeli bir HTTPS POST isteği; MetalAPI'den geldiğini kanıtlayabilmeniz için imzalanmıştır. Bir webhook URL'si:
https://kullanmalı,- herkese açık bir IP adresine çözümlenmeli (özel, loopback ve link-local aralıklar alarmı kaydettiğinizde reddedilir ve gönderim sırasında yeniden kontrol edilir),
- kullanıcı adı veya parola içermemelidir.
Yönlendirmeler takip edilmez, bu nedenle nihai URL'yi girin. Bir hesap webhook içermeyen bir plana geçerse webhook alarmları düşürülmek yerine e-posta ile gönderilir.
Bir hesabın tüm webhook'ları, whsec_ ile başlayan tek bir gizli anahtarla imzalanır. Bu anahtarı Alerts sayfasındaki Webhook signing secret panelinde oluşturabilir, görüntüleyebilir, kopyalayabilir ve yenileyebilirsiniz. Henüz oluşturmadıysanız ilk webhook gönderimiyle birlikte üretilir ve ardından panelde gösterilir. Yenileme hemen geçerli olur; bu da aşağıdaki doğrulama kodu açısından önemlidir.
Webhook isteği
Her gönderim şöyle görünür:
POST /webhooks/metalapi HTTP/1.1
Content-Type: application/json
User-Agent: MetalAPI-Webhooks/1.0
X-MetalAPI-Timestamp: 1792765800
X-MetalAPI-Signature: t=1792765800,v1=5f0c1e…9a2b
{
"event": "alert.triggered",
"id": 18342,
"created": 1792765800,
"alert": {
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"symbol": "XAU",
"base": "USD",
"condition": "above",
"threshold": 4000,
"note": "Gold above 4k"
},
"price": 4001.25,
"unit": "troy_oz"
}| Alan | Anlamı |
|---|---|
event | Şu anda her zaman alert.triggered. Yeni olay türlerinin işleyicinizi hiçbir zaman bozmaması için bilinmeyen değerleri yok sayın. |
id | Sayısal olay kimliği; tetiklenen her alarm için benzersizdir ve yeniden denemelerde aynı kalır |
created | Alarmın tetiklendiği Unix zamanı |
alert | Yapılandırıldığı haliyle alarm: id (bir UUID), symbol, base, condition, threshold ve note |
price | Alarmı tetikleyen fiyat; unit başına base cinsinden |
unit | Metaller için troy_oz, enerji için bbl, mmbtu veya gal, döviz için currency, kripto için coin |
En fazla 200 karakter alan note alanı, panelde belirlediğiniz yönlendirme bilgileri için kullanışlı bir yerdir; örneğin bir ekip adı, bir kanal veya bir ürün SKU'su.
Node.js'te imzayı doğrulama
X-MetalAPI-Signature header'ı t=<timestamp>,v1=<signature> biçimindedir. İmza, <timestamp>.<raw body> dizesinin whsec_ gizli anahtarınızla hesaplanan, küçük harfli hex HMAC-SHA256 değeridir. Zaman damgası ayrıca tek başına X-MetalAPI-Timestamp içinde de gönderilir. Bir kontrolün canlı ortamda çalışıp çalışmayacağını üç ayrıntı belirler:
- Ham baytların hash'ini alın. JSON'u ayrıştırıp yeniden serileştirmek boşlukları veya sayı biçimlendirmesini değiştirip imzayı bozabilir. Gövdeyi bir buffer olarak okuyun.
- Header'daki zaman damgasını kontrol edin. Gövdedeki
createddeğerini değil, header'dakitdeğerini kullanın. Her yeniden deneme, gönderim anında yeni birtile yeniden imzalanır;createdise ilk zamanı korur.createddeğerine dayalı bir tekrar oynatma (replay) penceresi geçerli yeniden denemeleri reddeder. - Yenileme sırasında iki gizli anahtarı kabul edin. Panelde yenileme yapmak imzalamayı hemen yeni anahtara geçirir. Yeni anahtarı eskisinin yanına ekleyip dağıtın; bu arada başarısız olan gönderimler birkaç dakika boyunca yeniden denenir. Yeni anahtar çalıştığında eskisini kaldırın.
Express ile express.raw(), gövdeyi bir Buffer olarak sağlar:
// webhook-server.mjs — Node.js 18+, npm install express
import crypto from "node:crypto";
import express from "express";
// Virgülle ayrılmış: geçerli gizli anahtar ve yenileme sırasında bir öncekisi.
const SECRETS = (process.env.METALAPI_WEBHOOK_SECRETS ?? "").split(",").filter(Boolean);
const TOLERANCE_S = 300; // 5 dakikadan eski imzaları reddet
export function verifySignature(rawBody, header) {
const parts = {};
for (const item of (header ?? "").split(",")) {
const i = item.indexOf("=");
if (i > 0) parts[item.slice(0, i).trim()] = item.slice(i + 1).trim();
}
const ts = parts.t ?? "";
if (!/^\d+$/.test(ts) || Math.abs(Date.now() / 1000 - Number(ts)) > TOLERANCE_S) return false;
if (!/^[0-9a-f]{64}$/.test(parts.v1 ?? "")) return false;
const given = Buffer.from(parts.v1, "hex");
return SECRETS.some((secret) => {
const expected = crypto.createHmac("sha256", secret).update(`${ts}.`).update(rawBody).digest();
return crypto.timingSafeEqual(expected, given);
});
}
async function handleAlert(event) {
const { alert, price, unit } = event;
console.log(`${alert.symbol}/${alert.base} ${alert.condition} ${alert.threshold}: ${price} per ${unit}`);
}
const app = express();
app.post("/webhooks/metalapi", express.raw({ type: "application/json", limit: "64kb" }), (req, res) => {
if (!Buffer.isBuffer(req.body) || !verifySignature(req.body, req.get("X-MetalAPI-Signature"))) {
return res.status(401).send("invalid signature");
}
const event = JSON.parse(req.body.toString("utf8"));
res.sendStatus(204); // 8 saniye içinde yanıt ver, işi sonra yap
if (event.event === "alert.triggered") {
handleAlert(event).catch((err) => console.error("alert handler failed:", err));
}
});
app.listen(3000);İşleyici, herhangi bir iş yapmadan önce yanıt verir. İş önemliyse olayı önce kalıcı olarak saklayın (sonraki bölümler nasıl yapılacağını gösteriyor); böylece yanıttan sonra yaşanan bir çökme olayın kaybolmasına yol açmaz.
Python'da imzayı doğrulama
Python'daki aynı kontrol yalnızca standart kütüphaneyi kullanır. Sabit zamanlı (constant-time) karşılaştırmayı hmac.compare_digest sağlar:
# webhook_app.py — pip install flask, ardından: flask --app webhook_app run --port 3000
import hashlib
import hmac
import json
import os
import time
from flask import Flask, abort, request
# Virgülle ayrılmış: geçerli gizli anahtar ve yenileme sırasında bir öncekisi.
SECRETS = [s for s in os.environ.get("METALAPI_WEBHOOK_SECRETS", "").split(",") if s]
TOLERANCE_S = 300 # 5 dakikadan eski imzaları reddet
app = Flask(__name__)
def verify_signature(raw: bytes, header: str) -> bool:
parts = dict(p.strip().split("=", 1) for p in header.split(",") if "=" in p)
ts, given = parts.get("t", ""), parts.get("v1", "")
if not ts.isdigit() or abs(time.time() - int(ts)) > TOLERANCE_S:
return False
signed = ts.encode() + b"." + raw
return any(
hmac.compare_digest(hmac.new(s.encode(), signed, hashlib.sha256).hexdigest(), given)
for s in SECRETS
)
@app.post("/webhooks/metalapi")
def metalapi_webhook():
raw = request.get_data() # ham baytlar, herhangi bir JSON ayrıştırmasından önce
if not verify_signature(raw, request.headers.get("X-MetalAPI-Signature", "")):
abort(401)
event = json.loads(raw)
if event.get("event") == "alert.triggered":
alert = event["alert"]
# Olayı burada bir iş kuyruğuna verin ve hızlıca dönün.
app.logger.warning("%s/%s %s %s: %s", alert["symbol"], alert["base"],
alert["condition"], alert["threshold"], event["price"])
return "", 204Diğer framework'ler yalnızca ham gövdeyi nasıl okuduğunuz konusunda farklılık gösterir: FastAPI'de await request.body(), Django'da request.body. Framework ne olursa olsun, işleyiciniz çalışmadan önce hiçbir middleware'in gövdeyi ayrıştırıp tüketmediğinden emin olun.
Yeniden denemeler, yinelenen gönderimler ve yanıt süreleri
Endpoint'iniz 8 saniye içinde herhangi bir 2xx durum koduyla yanıt verdiğinde gönderim başarılı sayılır. Zaman aşımları, bağlantı hataları, yönlendirmeler ve diğer tüm durum kodları başarısızlık sayılır ve yeniden denenir:
| Deneme | Ne zaman |
|---|---|
| 1 | Alarm tetiklenir tetiklenmez |
| 2 | Alarm tetiklendikten yaklaşık 1 dakika sonra |
| 3 | Alarm tetiklendikten yaklaşık 4 dakika sonra |
Üç denemeden sonra olay başarısız olarak işaretlenir. Alerts sayfasındaki Recent deliveries tablosu, son denemenin HTTP durum kodu veya hata mesajı dahil her olayı sonucuyla birlikte listeler. Bir endpoint beklendiği gibi çalışmadığında ilk bakılacak yer burasıdır.
Yeniden denemeler aynı olay id değerini kullanır. Sunucunuzun işlediği ancak 8 saniyeden uzun sürede yanıt verdiği bir gönderim MetalAPI tarafında başarısız sayılır ve yeniden gelir; bu nedenle işleyicinin idempotent olması gerekir. En basit yol, olay kimliğiyle anahtarlanmış bir tablodur:
create table metalapi_events (
event_id bigint primary key, -- payload'daki "id", yeniden denemelerde aynıdır
alert_id uuid not null,
payload jsonb not null,
received_at timestamptz not null default now(),
processed_at timestamptz
);
-- Webhook işleyicisinde, imzayı doğruladıktan sonra ve yanıt vermeden önce:
insert into metalapi_events (event_id, alert_id, payload)
values ($1, $2, $3)
on conflict (event_id) do nothing;Olayı ekleyin, 204 ile yanıt verin ve processed_at değeri null olan satırları bir worker'ın işlemesine bırakın. Yinelenen bir gönderim birincil anahtara takılır ve hiçbir şeyi değiştirmez. Ekleme işlemi milisaniyeler sürer; bu da devamındaki iş yavaş olsa bile sizi 8 saniyelik pencerenin rahatça içinde tutar.
Alarmları sohbet uygulamalarına ve diğer sistemlere iletmek
Yaygın kullanımlardan biri, alarmları bir ekip sohbetine göndermektir. Slack ve Discord'un ikisi de gelen webhook'ları (incoming webhooks) kabul eder, ancak bir MetalAPI alarmını doğrudan onlara yönlendirmeyin: farklı bir payload beklerler ve imza doğrulamasını kaybedersiniz. Doğrulamayı kendi endpoint'inizde yapın, ardından mesajı biçimlendirip iletin. handleAlert fonksiyonunun bu sürümü, yukarıdaki Node.js sunucusundaki basit loglama fonksiyonunun yerini alır:
const VERBS = { above: "rose above", below: "fell below", change_pct_up: "is up", change_pct_down: "is down" };
async function handleAlert({ alert, price, unit }) {
const level = alert.condition.startsWith("change_pct")
? `${alert.threshold}% since the previous close`
: `${alert.threshold} ${alert.base}`;
const per = unit && unit !== "currency" ? ` per ${unit}` : "";
const text = `${alert.symbol} ${VERBS[alert.condition]} ${level}: now ${price} ${alert.base}${per}` +
(alert.note ? ` (${alert.note})` : "");
// Slack gelen webhook'ları {"text": ...}, Discord webhook'ları {"content": ...} bekler.
const body = process.env.CHAT_KIND === "discord" ? { content: text } : { text };
const res = await fetch(process.env.CHAT_WEBHOOK_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
signal: AbortSignal.timeout(10_000),
});
if (!res.ok) throw new Error(`chat webhook returned ${res.status}`);
}Örnek payload için mesaj şöyle olur: “XAU rose above 4000 USD: now 4001.25 USD per troy_oz (Gold above 4k)”. Aynı düzen HTTP API'si olan her şey için işe yarar: bir destek kaydı açın, altın bir günde %2'den fazla hareket ettiğinde mağazanızdaki önbelleğe alınmış ürün fiyatlarını yenileyin ya da bir fiyat akışının sorumlusunu bilgilendirin. Yavaş bir sohbet servisinin bir gönderimin zaman aşımına uğramasına asla yol açmaması için iletme işlemini istek işleyicisinde değil, bir worker'da çalıştırın.
Endpoint'inizi piyasayı beklemeden test etmek için örnek bir payload'ı kendi gizli anahtarınızla kendiniz imzalayın:
SECRET="whsec_your_secret"
BODY='{"event":"alert.triggered","id":1,"created":1792765800,"alert":{"id":"00000000-0000-0000-0000-000000000000","symbol":"XAU","base":"USD","condition":"above","threshold":4000,"note":"test"},"price":4001.25,"unit":"troy_oz"}'
TS=$(date +%s)
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //')
curl -i -X POST http://localhost:3000/webhooks/metalapi \
-H "Content-Type: application/json" \
-H "X-MetalAPI-Timestamp: $TS" \
-H "X-MetalAPI-Signature: t=$TS,v1=$SIG" \
--data-raw "$BODY"204 yanıtı, imza kontrolünün geçtiği anlamına gelir. Uçtan uca bir test için yukarıda anlatıldığı gibi koşulu zaten geçerli olan bir alarm oluşturun ve olayın Recent deliveries tablosunda görünmesini izleyin. Webhook ile gönderilen alarmlar Advanced planından itibaren sunulur; planlar için fiyatlandırma sayfasına bakın ya da önce Lite planda e-posta alarmlarını denemek için bir hesap oluşturun.
Kendi verilerinizle deneyin
Ücretsiz bir hesap oluşturun ve ilk isteğinizi iki dakikadan kısa sürede gönderin. Ayda 100 istek, kredi kartı gerekmez.
Okumaya devam edin
Altın Fiyatı API'si: Geliştiriciler İçin Eksiksiz Rehber
Altın fiyatı API'si nasıl kullanılır: anahtar alın, curl, JavaScript ve Python ile çağırın, kurları doğru okuyun; geçmiş veri, hata, kota ve önbelleği yönetin.
Web Sitenize Canlı Altın Fiyatı Widget'ı Nasıl Eklenir
WordPress, Shopify veya HTML sitenize canlı altın fiyatı widget'ı ekleyin: MetalAPI panelinde oluşturun, sembol, düzen ve temayı seçin, iki satırı yapıştırın.
Altın Ayar Fiyatları: 24, 22 ve 18 Ayar Nedir, Nasıl Hesaplanır?
24, 22 ve 18 ayar ne demek, 916 ve 750 gibi milyem damgaları ne anlama gelir, ayar altının gram fiyatı nasıl hesaplanır ve altın fiyatı API'sinden nasıl alınır?