Contents
1. Unsplash API の概要
| 項目 | 内容 |
|---|---|
| 提供形態 | 完全無料(商用・非商用問わず利用可) |
| レートリミット | 1 時間あたり 50 リクエスト(認証済み) ※ 未認証の場合は 5000 リクエスト/日 が上限です |
| 画像のライセンス | すべてロイヤリティフリー。利用規約で「作者名と Unsplash のリンクを表示」すれば商用利用も可 |
| 有料プラン | 現時点(2025 年)では公式に有料プランは提供されていません。一部エンタープライズ向けのカスタム契約があるケースがありますが、公開情報としては「無料」だけです |
1‑1. レートリミットの取得方法
Unsplash のレスポンスヘッダーに以下が含まれます(公式ドキュメントと同一表記):
| ヘッダー名 | 意味 |
|---|---|
X-Ratelimit-Limit |
1 時間あたりの上限リクエスト数 |
X-Ratelimit-Remaining |
残りのリクエスト数 |
X-Ratelimit-Reset |
リミットがリセットされる UNIX タイムスタンプ |
実装例(JavaScript)
js
fetch(url, { headers })
.then(res => {
const limit = res.headers.get('x-ratelimit-limit');
const remaining = res.headers.get('x-ratelimit-remaining');
console.log(残り ${remaining}/${limit} リクエスト);
return res.json();
});
2. 開発者登録とアクセストークン取得
2‑1. アカウント作成 & メール認証
- https://unsplash.com にアクセスし「Sign up」でメールアドレスを入力。
- 受信した認証メールのリンクをクリックして認証完了。
この時点で Developer Dashboard が利用可能になります。
2‑2. アプリケーション登録
- ダッシュボード左側メニューの Your Applications を選択。
- 「New Application」をクリックし、以下を入力します。
| フィールド | 設定例 |
|---|---|
| Application name | MyPortfolio |
| Description | ポートフォリオサイトで Unsplash 画像を表示 |
| Redirect URI | https://example.com/auth/callback(OAuth が必要な場合) |
ポイント
-Redirect URIは OAuth 認可コードフローで使用します。不要なら空欄でも構いません。
- 取得した Access Key と Secret Key は後述の環境変数へ保存してください。
2‑3. アクセストークン(OAuth)取得手順(任意)
Unsplash の公開 API キーだけで多くのエンドポイントは呼び出せますが、ユーザー固有情報や書き込み系 API を利用する場合は OAuth が必要です。
2‑3‑1. 認可コード取得
|
1 2 3 4 5 6 |
https://unsplash.com/oauth/authorize? client_id=YOUR_ACCESS_KEY& redirect_uri=YOUR_REDIRECT_URI& response_type=code& scope=public+read_user |
ユーザーが上記 URL に遷移し、認可後に code パラメータが付いたリダイレクト先が呼び出されます。
2‑3‑2. アクセストークン交換(サーバー側)
|
1 2 3 4 5 6 7 |
curl -X POST "https://unsplash.com/oauth/token" \ -d client_id=YOUR_ACCESS_KEY \ -d client_secret=YOUR_SECRET_KEY \ -d redirect_uri=YOUR_REDIRECT_URI \ -d code=AUTHORIZATION_CODE \ -d grant_type=authorization_code |
成功すると JSON で access_token が返ります。取得したトークンは 環境変数 に保存し、コードからは直接参照しないようにします。
例)
.env
dotenv
UNSPLASH_ACCESS_KEY=your_access_key
UNSPLASH_SECRET_KEY=your_secret_key
UNSPLASH_OAUTH_TOKEN=oauth_token_here # 必要な場合のみ
3. 主なエンドポイントと実装サンプル
| エンドポイント | 用途 | 主なパラメータ |
|---|---|---|
/search/photos |
キーワード検索 | query, page, per_page, orientation |
/photos/random |
ランダム画像取得 | count, collections, orientation |
/collections/:id/photos |
コレクション内の写真一覧 | page, per_page |
3‑1. キーワード検索(/search/photos)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// fetch を使った例(Node.js / ブラウザ共通) async function searchPhotos(keyword, perPage = 12) { const url = new URL('https://api.unsplash.com/search/photos'); url.searchParams.set('query', keyword); url.searchParams.set('per_page', perPage); const res = await fetch(url, { headers: { Authorization: `Client-ID ${process.env.UNSPLASH_ACCESS_KEY}` } }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const data = await res.json(); return data.results; // 配列で返却 } |
日本語検索のポイント
- 必ず
encodeURIComponent()でエンコードする。 - 空白は
%20に変換されるため、複数単語でも正しく検索できます。
|
1 2 |
searchPhotos(encodeURIComponent('東京 夜景')).then(console.log); |
3‑2. ランダム取得(/photos/random)
|
1 2 3 4 5 6 7 8 9 10 11 |
async function getRandomPhoto(options = { count: 1 }) { const url = new URL('https://api.unsplash.com/photos/random'); Object.entries(options).forEach(([k, v]) => url.searchParams.set(k, v)); const res = await fetch(url, { headers: { Authorization: `Client-ID ${process.env.UNSPLASH_ACCESS_KEY}` } }); if (!res.ok) throw new Error(`HTTP ${res.status}`); return await res.json(); // 配列 or オブジェクト } |
collectionsパラメータで特定コレクション内のランダム画像に絞り込めます。orientation=landscape|portrait|squarishで向きを制御可能です。
3‑3. コレクション取得(/collections/:id/photos)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import os, requests from dotenv import load_dotenv load_dotenv() ACCESS_KEY = os.getenv('UNSPLASH_ACCESS_KEY') def get_collection_photos(collection_id, per_page=12): url = f"https://api.unsplash.com/collections/{collection_id}/photos" headers = {"Authorization": f"Client-ID {ACCESS_KEY}"} params = {"per_page": per_page} resp = requests.get(url, headers=headers, params=params) resp.raise_for_status() return resp.json() photos = get_collection_photos(1580860) print([p['urls']['regular'] for p in photos]) |
4. 日本語検索・エンコードとフロントエンドでの CORS 対策
4‑1. URL エンコード実務例
| キーワード | encodeURIComponent 結果 |
|---|---|
桜 幕末 |
%E6%A1%9C%E3%80%80%E5%B9%95%E6%9C%AB |
東京 夜景 |
%E6%9D%B1%E4%BA%AC%20%E5%A4%9C%E6%99%AF |
Tips
- UTF‑8 が前提なので、サーバー側で文字コードを変換しないこと。
4‑2. CORS エラー回避のプロキシ実装(Node.js/Express)
Unsplash は Access-Control-Allow-Origin: * を返すエンドポイントが多いですが、一部ヘッダーや認証情報が付くとブラウザ側で CORS がブロックされることがあります。安全策として自前の軽量プロキシを置くと確実です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
// server.js (Node 18+) import 'dotenv/config'; import express from 'express'; import fetch from 'node-fetch'; const app = express(); const PORT = process.env.PORT || 3000; app.get('/api/search', async (req, res) => { const { q, page = 1, per_page = 12 } = req.query; if (!q) return res.status(400).json({ error: 'query param "q" required' }); const apiUrl = new URL('https://api.unsplash.com/search/photos'); apiUrl.searchParams.set('query', q); apiUrl.searchParams.set('page', page); apiUrl.searchParams.set('per_page', per_page); try { const apiRes = await fetch(apiUrl, { headers: { Authorization: `Client-ID ${process.env.UNSPLASH_ACCESS_KEY}` } }); // レートリミット情報をそのまま転送 res.set({ 'X-Ratelimit-Limit': apiRes.headers.get('x-ratelimit-limit') || '', 'X-Ratelimit-Remaining': apiRes.headers.get('x-ratelimit-remaining') || '' }); const data = await apiRes.json(); res.json(data); } catch (err) { console.error(err); res.status(502).json({ error: 'Unsplash API fetch failed' }); } }); app.listen(PORT, () => console.log(`Proxy running → http://localhost:${PORT}`)); |
フロントエンドは以下のように呼び出すだけで CORS を回避できます。
|
1 2 3 4 |
fetch('/api/search?q=和食') .then(r => r.json()) .then(data => console.log(data.results)); |
5. Rate Limit の監視・エラーハンドリング
5‑1. リミットに近づいたらバックオフする実装例(fetch)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
async function fetchWithRateLimit(url, opts = {}, backoffMs = 500) { const res = await fetch(url, opts); const remaining = Number(res.headers.get('x-ratelimit-remaining') ?? 0); if (remaining < 5) { console.warn(`⚠️ Rate limit が残り ${remaining} 件です。${backoffMs}ms 後に再試行します`); await new Promise(r => setTimeout(r, backoffMs)); } if (!res.ok && res.status === 429) { // 429(Too Many Requests)を受けたら指数バックオフでリトライ const wait = backoffMs * 2; console.warn(`❗️ 429 を受信。${wait}ms 後に再試行`); await new Promise(r => setTimeout(r, wait)); return fetchWithRateLimit(url, opts, wait); } return res; } |
5‑2. 再試行ロジックのベストプラクティス
| 項目 | 推奨設定 |
|---|---|
| 最大リトライ回数 | 3 回まで(それ以上は失敗としてハンドリング) |
| バックオフ方式 | 指数バックオフ (base * 2^n) + ジッターを加えると同時リクエストが集中しにくい |
| 404/401 の扱い | ユーザー入力ミスや認証失敗の可能性が高いため再試行せず即エラーハンドリング |
6. セキュリティとキー管理
6‑1. 環境変数で安全に保管
.envをプロジェクト直下に置き、必ず.gitignoreに追加。- CI/CD(GitHub Actions, GitLab CI 等)では シークレット機能 を利用し、ビルド時に環境変数として注入。
|
1 2 3 4 5 6 7 8 9 10 |
# .github/workflows/deploy.yml の例 jobs: build: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v3 - name: Set Unsplash secret run: echo "UNSPLASH_ACCESS_KEY=${{ secrets.UNSPLASH_ACCESS_KEY }}" >> $GITHUB_ENV |
6‑2. キー漏洩時の対策
- 即座にローテーション:ダッシュボードから新しい Access Key を発行し、古いキーを無効化。
- リクエストログの監視:予期せぬ大量アクセスがあれば速やかにレートリミット超過・課金リスクを把握できるようにする。
7. 商用利用時の必須クレジット表示
Unsplash のライセンスでは、以下情報を 目立つ形で 表示することが義務付けられています。
| 必要項目 | 表記例 |
|---|---|
| 作者名(ユーザー名) | Photo by **John Doe** |
| Unsplash へのリンク | on <a href="https://unsplash.com">Unsplash</a> |
| アプリケーションの UTM パラメータ(任意) | ?utm_source=your_app&utm_medium=referral |
7‑1. HTML 実装例
|
1 2 3 4 5 |
<a href="https://unsplash.com/@johndoe?utm_source=myapp&utm_medium=referral" target="_blank" rel="noopener"> Photo by John Doe on Unsplash </a> |
7‑2. Markdown 実装例(README・ブログ等)
|
1 2 |
Photo by [John Doe](https://unsplash.com/@johndoe) on [Unsplash](https://unsplash.com) |
ポイント
-target="_blank"とrel="noopener"を付与してセキュリティ向上。
- 複数画像を表示する場合は、各画像ごとに同様のクレジットリンクを生成するとベストプラクティスです。
8. 完全サンプルコード集(実務で使える形)
8‑1. JavaScript (fetch) – キーワード検索 & クレジット付与
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// utils/unsplash.js export async function searchWithCredit(keyword, limit = 8) { const url = new URL('https://api.unsplash.com/search/photos'); url.searchParams.set('query', keyword); url.searchParams.set('per_page', limit); const res = await fetch(url, { headers: { Authorization: `Client-ID ${process.env.UNSPLASH_ACCESS_KEY}` } }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const data = await res.json(); return data.results.map(item => ({ src: item.urls.small, creditHtml: `<a href="${item.user.links.html}?utm_source=myapp&utm_medium=referral" target="_blank">Photo by ${item.user.name} on Unsplash</a>` })); } |
8‑2. Node.js (axios) – ランダム画像取得
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// random-photo.js import axios from 'axios'; import dotenv from 'dotenv'; dotenv.config(); export async function getRandom(limit = 1, collectionId = null) { const params = { count: limit }; if (collectionId) params.collections = collectionId; const resp = await axios.get('https://api.unsplash.com/photos/random', { headers: { Authorization: `Client-ID ${process.env.UNSPLASH_ACCESS_KEY}` }, params }); return resp.data.map(p => ({ url: p.urls.full, credit: `Photo by ${p.user.name} on Unsplash`, link: `${p.links.html}?utm_source=myapp&utm_medium=referral` })); } |
8‑3. Python (requests) – コレクション画像一覧
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# collection.py import os, requests from dotenv import load_dotenv load_dotenv() BASE_URL = "https://api.unsplash.com" def fetch_collection(collection_id: int, per_page: int = 12): url = f"{BASE_URL}/collections/{collection_id}/photos" headers = {"Authorization": f"Client-ID {os.getenv('UNSPLASH_ACCESS_KEY')}"} params = {"per_page": per_page} resp = requests.get(url, headers=headers, params=params) resp.raise_for_status() photos = resp.json() return [ { "src": p["urls"]["regular"], "credit": f"Photo by {p['user']['name']} on Unsplash", "link": f"{p['links']['html']}?utm_source=myapp&utm_medium=referral" } for p in photos ] |
9. まとめ
- Unsplash API は無料で利用可能(1 時間あたり 50 リクエスト)。有料プランは公式には存在しません。
- 開発者登録 → アプリ作成 → Access Key 発行 が最小の手順です。OAuth が必要なケースだけ別途認可フローを実装します。
- 主要エンドポイントは
/search/photos,/photos/random,/collections/:id/photos。サンプルコードは JavaScript(fetch / axios)、Node.js、Python の3言語で提供しました。 - 日本語検索は
encodeURIComponent()が必須です。フロントエンドの CORS 問題は軽量プロキシで安全に回避できます。 - レートリミットはヘッダー
X‑Ratelimit-*で取得し、残量が少ないときは指数バックオフで再試行します。 - API キーは
.envと CI シークレットで管理し、公開リポジトリに絶対に含めません。漏洩時は即ローテーションを実施してください。 - 商用利用時は必ず作者名と Unsplash へのリンク(UTM パラメータ推奨) を画像毎に表示します。
以上の手順とベストプラクティスに沿って実装すれば、Unsplash の高品質な写真を安全かつ合法的に自分のプロジェクトへ組み込めます。ぜひ試してみてください!