汎用APIを実際に使うHTML+JSデモ
See the Pen Free API Demos (Weather, News, Translation, Currency, Images, Maps) by MONO365 -Color your days- (@monoqlo365) on CodePen.
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>汎用APIデモ — Weather / News / Translate / FX / Images / Holidays</title>
<style>
body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial;s}
header{display:flex;gap:12px;align-items:center;padding:12px;background:#0f172a;color:#fff}
.container{max-width:980px;margin:18px auto;padding:12px}
nav button{margin-right:8px}
section{display:none;padding:12px;border:1px solid #e5e7eb;border-radius:8px;background:#fff}
section.active{display:block}
.row{display:flex;gap:12px;align-items:flex-start}
.card{flex:1;padding:12px;border:1px dashed #e5e7eb;border-radius:8px}
pre{white-space:pre-wrap;background:#f8fafc;padding:8px;border-radius:6px;overflow:auto}
input,select,textarea{width:100%;padding:8px;margin:6px 0;border:1px solid #cbd5e1;border-radius:6px}
button.primary{background:#06b6d4;border:none;color:#fff;padding:8px 12px;border-radius:6px;cursor:pointer}
.thumb{width:120px;height:80px;object-fit:cover;border-radius:6px}
.small{font-size:13px;color:#374151}
</style>
</head>
<body>
<header>
<h1 style="margin:0;font-size:18px">汎用API 実演デモ</h1>
<div class="small">OpenWeatherMap / NewsAPI / LibreTranslate / ExchangeRate.host / Pixabay / Nager.Date をデモ</div>
</header>
<div class="container">
<nav>
<button data-target="weather">天気</button>
<button data-target="news">ニュース</button>
<button data-target="translate">翻訳</button>
<button data-target="fx">為替</button>
<button data-target="images">画像検索</button>
<button data-target="holidays">祝日</button>
</nav>
<!-- 天気 -->
<section id="weather">
<h2>天気デモ</h2>
<div class="row">
<div class="card">
<label>都市名(例: Tokyo)</label>
<input id="weather-city" placeholder="Tokyo" />
<label>OpenWeatherMap APIキー(任意)</label>
<input id="owm-key" placeholder="APIキーを貼り付け" />
<div style="margin-top:6px">
<button class="primary" id="btn-weather">取得</button>
</div>
<p class="small">※ OpenWeatherMap APIキーを入れない場合、デモ用の簡易データ(ダミー)を表示します。</p>
</div>
<div class="card" id="weather-result">
<h3>結果</h3>
<div id="weather-output">ここに結果が出ます</div>
<pre id="weather-json"></pre>
</div>
</div>
</section>
<!-- ニュース -->
<section id="news">
<h2>ニュースデモ</h2>
<div class="row">
<div class="card">
<label>国コード(例: jp)</label>
<input id="news-country" placeholder="jp" />
<label>NewsAPI.org APIキー(必要)</label>
<input id="news-key" placeholder="APIキーを貼り付け" />
<button class="primary" id="btn-news">取得</button>
<p class="small">※ NewsAPI はブラウザから直接呼ぶとCORSでブロックされる場合があります。問題が出たらプロキシ経由で試してください。</p>
</div>
<div class="card" id="news-result">
<h3>見出し</h3>
<ul id="news-list"></ul>
<pre id="news-json"></pre>
</div>
</div>
</section>
<!-- 翻訳 -->
<section id="translate">
<h2>翻訳デモ(LibreTranslate)</h2>
<div class="row">
<div class="card">
<label>翻訳したいテキスト</label>
<textarea id="txt-src" rows="4">Hello, how are you?</textarea>
<label>ソース言語(auto可)</label>
<input id="src-lang" placeholder="auto" value="auto" />
<label>ターゲット言語(例: ja)</label>
<input id="tgt-lang" placeholder="ja" value="ja" />
<button class="primary" id="btn-translate">翻訳</button>
</div>
<div class="card">
<h3>翻訳結果</h3>
<div id="translate-output"></div>
<pre id="translate-json"></pre>
</div>
</div>
</section>
<!-- 為替 -->
<section id="fx">
<h2>為替デモ(ExchangeRate.host)</h2>
<div class="row">
<div class="card">
<label>基準通貨(例: USD)</label>
<input id="fx-base" value="USD" />
<label>変換先(カンマ区切りで複数。例: JPY,EUR)</label>
<input id="fx-symbols" value="JPY" />
<button class="primary" id="btn-fx">取得</button>
</div>
<div class="card">
<h3>為替レート</h3>
<div id="fx-output"></div>
<pre id="fx-json"></pre>
</div>
</div>
</section>
<!-- 画像検索 -->
<section id="images">
<h2>画像検索デモ(Pixabay)</h2>
<div class="row">
<div class="card">
<label>検索ワード</label>
<input id="img-query" value="cat" />
<label>Pixabay APIキー(必要)</label>
<input id="pixabay-key" placeholder="APIキーを貼り付け" />
<button class="primary" id="btn-images">検索</button>
<p class="small">※ APIキーが無い場合はダミー画像を表示します。</p>
</div>
<div class="card">
<h3>検索結果</h3>
<div id="images-output" style="display:flex;flex-wrap:wrap;gap:8px"></div>
<pre id="images-json"></pre>
</div>
</div>
</section>
<!-- 祝日 -->
<section id="holidays">
<h2>祝日デモ(Nager.Date)</h2>
<div class="row">
<div class="card">
<label>年(例: 2025)</label>
<input id="year" value="2025" />
<label>国コード(例: JP)</label>
<input id="country" value="JP" />
<button class="primary" id="btn-holidays">取得</button>
</div>
<div class="card">
<h3>祝日一覧</h3>
<ul id="holidays-list"></ul>
<pre id="holidays-json"></pre>
</div>
</div>
</section>
</div>
<script>
// シンプルなタブ
document.querySelectorAll('nav button').forEach(b=>{
b.addEventListener('click',()=>{
document.querySelectorAll('section').forEach(s=>s.classList.remove('active'));
document.getElementById(b.dataset.target).classList.add('active');
});
});
// 初期タブ
document.querySelector('nav button').click();
// --- 天気 ---
document.getElementById('btn-weather').addEventListener('click',async ()=>{
const city = document.getElementById('weather-city').value || 'Tokyo';
const key = document.getElementById('owm-key').value.trim();
const out = document.getElementById('weather-output');
const js = document.getElementById('weather-json');
out.innerHTML='取得中...'; js.textContent='';
try{
if(key){
const url = `https://api.openweathermap.org/data/2.5/weather?q=${encodeURIComponent(city)}&appid=${encodeURIComponent(key)}&lang=ja&units=metric`;
const r = await fetch(url);
if(!r.ok) throw new Error('OpenWeatherMapからエラー: '+r.status);
const j = await r.json();
out.innerHTML = `<strong>${j.name}</strong> — ${j.weather?.[0]?.description || ''}<br>気温: ${j.main?.temp} ℃`;
js.textContent = JSON.stringify(j,null,2);
} else {
// ダミー表示(キー無しデモ)
const sample = {name: city, weather:[{description:'晴れ'}], main:{temp:23.4}};
out.innerHTML = `<strong>${sample.name}</strong> — ${sample.weather[0].description}<br>気温: ${sample.main.temp} ℃ (ダミー)`;
js.textContent = JSON.stringify(sample,null,2);
}
}catch(e){ out.innerHTML = 'エラー: '+e.message; js.textContent=''; }
});
// --- ニュース ---
document.getElementById('btn-news').addEventListener('click',async ()=>{
const country = document.getElementById('news-country').value || 'jp';
const key = document.getElementById('news-key').value.trim();
const list = document.getElementById('news-list');
const js = document.getElementById('news-json');
list.innerHTML='取得中...'; js.textContent='';
try{
if(!key){ list.innerHTML='<li>APIキーが必要です(NewsAPI.org)。</li>'; return; }
const url = `https://newsapi.org/v2/top-headlines?country=${encodeURIComponent(country)}&apiKey=${encodeURIComponent(key)}`;
const r = await fetch(url);
if(!r.ok) throw new Error('NewsAPIエラー: '+r.status+' (CORS制限の可能性あり)');
const j = await r.json();
js.textContent = JSON.stringify(j,null,2);
list.innerHTML = '';
(j.articles||[]).slice(0,10).forEach(a=>{
const li = document.createElement('li');
li.innerHTML = `<a href="${a.url}" target="_blank">${a.title}</a> <span class="small">${a.source?.name||''}</span>`;
list.appendChild(li);
});
if((j.articles||[]).length===0) list.innerHTML='<li>見つかりませんでした</li>';
}catch(e){ list.innerHTML = '<li>エラー: '+e.message+'</li>'; js.textContent=''; }
});
// --- 翻訳 (LibreTranslate) ---
document.getElementById('btn-translate').addEventListener('click',async ()=>{
const q = document.getElementById('txt-src').value;
const src = document.getElementById('src-lang').value || 'auto';
const tgt = document.getElementById('tgt-lang').value || 'ja';
const out = document.getElementById('translate-output');
const js = document.getElementById('translate-json');
out.innerHTML='翻訳中...'; js.textContent='';
try{
const url = 'https://libretranslate.com/translate';
const r = await fetch(url,{
method:'POST',
headers:{'Content-Type':'application/json'},
body: JSON.stringify({q,source:src,target:tgt,format:'text'})
});
if(!r.ok) throw new Error('LibreTranslateエラー: '+r.status);
const j = await r.json();
out.textContent = j.translatedText;
js.textContent = JSON.stringify(j,null,2);
}catch(e){ out.innerHTML='エラー: '+e.message; js.textContent=''; }
});
// --- 為替 ---
document.getElementById('btn-fx').addEventListener('click',async ()=>{
const base = document.getElementById('fx-base').value || 'USD';
const symbols = document.getElementById('fx-symbols').value || 'JPY';
const out = document.getElementById('fx-output');
const js = document.getElementById('fx-json');
out.innerHTML='取得中...'; js.textContent='';
try{
const url = `https://api.exchangerate.host/latest?base=${encodeURIComponent(base)}&symbols=${encodeURIComponent(symbols)}`;
const r = await fetch(url);
if(!r.ok) throw new Error('ExchangeRate.hostエラー: '+r.status);
const j = await r.json();
js.textContent = JSON.stringify(j,null,2);
out.innerHTML = Object.entries(j.rates||{}).map(([k,v])=>`<div><strong>${k}</strong>: ${v}</div>`).join('');
}catch(e){ out.innerHTML='エラー: '+e.message; js.textContent=''; }
});
// --- 画像検索 (Pixabay) ---
document.getElementById('btn-images').addEventListener('click',async ()=>{
const q = document.getElementById('img-query').value || 'cat';
const key = document.getElementById('pixabay-key').value.trim();
const out = document.getElementById('images-output');
const js = document.getElementById('images-json');
out.innerHTML='取得中...'; js.textContent='';
try{
if(!key){
// ダミー画像(外部ホスト)
out.innerHTML = Array.from({length:6}).map((_,i)=>`<div><img class="thumb" src="https://placekitten.com/200/${150+i}" alt="kitten"></div>`).join('');
js.textContent = '{"note":"APIキーが無いためダミー画像を表示しています"}';
return;
}
const url = `https://pixabay.com/api/?key=${encodeURIComponent(key)}&q=${encodeURIComponent(q)}&per_page=12&image_type=photo&lang=ja`;
const r = await fetch(url);
if(!r.ok) throw new Error('Pixabayエラー: '+r.status);
const j = await r.json();
js.textContent = JSON.stringify(j,null,2);
out.innerHTML = (j.hits||[]).map(h=>`<div><a href="${h.pageURL}" target="_blank"><img class="thumb" src="${h.previewURL}" alt="${h.tags}"></a></div>`).join('');
if((j.hits||[]).length===0) out.innerHTML='<div>見つかりませんでした</div>';
}catch(e){ out.innerHTML='エラー: '+e.message; js.textContent=''; }
});
// --- 祝日 ---
document.getElementById('btn-holidays').addEventListener('click',async ()=>{
const year = document.getElementById('year').value || '2025';
const country = document.getElementById('country').value || 'JP';
const list = document.getElementById('holidays-list');
const js = document.getElementById('holidays-json');
list.innerHTML='取得中...'; js.textContent='';
try{
const url = `https://date.nager.at/api/v3/PublicHolidays/${encodeURIComponent(year)}/${encodeURIComponent(country)}`;
const r = await fetch(url);
if(!r.ok) throw new Error('Nager.Dateエラー: '+r.status);
const j = await r.json();
js.textContent = JSON.stringify(j,null,2);
list.innerHTML = (j||[]).map(h=>`<li>${h.date} — ${h.localName} (${h.name})</li>`).join('');
}catch(e){ list.innerHTML='エラー: '+e.message; js.textContent=''; }
});
</script>
</body>
</html>
HTMLポイント:
- OpenWeatherMap, NewsAPI, Pixabay は APIキー入力欄 を用意(無ければダミー表示や注意文を出します)。
- LibreTranslate / ExchangeRate.host / Nager.Date / REST系は APIキー不要 で即試せます。
- NewsAPI はブラウザから直接呼ぶと CORS 制限が出る場合がある旨の注記あり。
- 初心者向けに最小限の UI と取得結果の JSON 表示を用意しました。
See the Pen General-Purpose API + Map Demo by MONO365 -Color your days- (@monoqlo365) on CodePen.


