Practical examples for common use cases.
Display a random quote that changes daily:
async function getDailyQuote() {
// Use date as seed for consistent daily quote
const today = new Date().toISOString().split('T')[0];
const cacheKey = `daily-quote-${today}`;
// Check localStorage cache
const cached = localStorage.getItem(cacheKey);
if (cached) return JSON.parse(cached);
const response = await fetch(
'https://quotegallery.nl/api/quotes?limit=1&random=true&language=en'
);
const { data } = await response.json();
const quote = data[0];
// Cache for the day
localStorage.setItem(cacheKey, JSON.stringify(quote));
return quote;
}
// Usage
const quote = await getDailyQuote();
console.log(`"${quote.translations[0].text}" - ${quote.author.name}`);
import requests
from datetime import date
import json
from pathlib import Path
def get_daily_quote():
cache_file = Path(f"quote-{date.today()}.json")
# Check cache
if cache_file.exists():
return json.loads(cache_file.read_text())
response = requests.get('https://quotegallery.nl/api/quotes', params={
'limit': 1,
'random': 'true',
'language': 'en'
})
quote = response.json()['data'][0]
# Cache
cache_file.write_text(json.dumps(quote))
return quote
quote = get_daily_quote()
print(f'"{quote["translations"][0]["text"]}" - {quote["author"]["name"]}')
Load quotes progressively without duplicates:
class QuoteLoader {
constructor() {
this.seenIds = [];
this.hasMore = true;
this.page = 1;
}
async loadNext(count = 10) {
if (!this.hasMore) return [];
const params = new URLSearchParams({
limit: count.toString(),
page: this.page.toString(),
exclude_ids: this.seenIds.join(',')
});
const response = await fetch(
`https://quotegallery.nl/api/v1/quotes?${params}`
);
const { data, pagination, meta } = await response.json();
this.seenIds.push(...data.map(q => q.id));
this.hasMore = pagination.hasMore;
this.page++;
// Reset if running low on unseen quotes
if (meta.reset_exclusion) {
this.reset();
}
return data;
}
reset() {
this.seenIds = [];
this.page = 1;
this.hasMore = true;
}
}
// Usage
const loader = new QuoteLoader();
// Initial load
const quotes = await loader.loadNext(10);
// Load more (e.g., on scroll)
const moreQuotes = await loader.loadNext(10);
Browse quotes by category:
const CATEGORIES = [
'philosophical', 'scientific', 'literary',
'historical', 'contemporary', 'business'
];
async function getQuotesByCategory(category, page = 1) {
const params = new URLSearchParams({
category,
page: page.toString(),
limit: '10',
sort: 'most_liked'
});
const response = await fetch(
`https://quotegallery.nl/api/v1/quotes?${params}`
);
return response.json();
}
// Usage
const philosophicalQuotes = await getQuotesByCategory('philosophical');
console.log(`Found ${philosophicalQuotes.pagination.total} philosophical quotes`);
Implement search with debouncing:
function debounce(func, wait) {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(() => func(...args), wait);
};
}
async function searchQuotes(query) {
if (query.length < 2) return { results: [] };
const params = new URLSearchParams({
q: query,
limit: '10',
language: 'en'
});
const response = await fetch(
`https://quotegallery.nl/api/v1/search?${params}`
);
return response.json();
}
// Usage with debouncing
const debouncedSearch = debounce(async (query) => {
const results = await searchQuotes(query);
console.log(results);
}, 300);
// In your input handler
input.addEventListener('input', (e) => {
debouncedSearch(e.target.value);
});
Custom React hook for quotes:
import { useState, useEffect } from 'react';
function useQuotes(options = {}) {
const [quotes, setQuotes] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchQuotes() {
try {
setLoading(true);
const params = new URLSearchParams({
limit: options.limit?.toString() || '10',
...options.category && { category: options.category },
...options.language && { language: options.language },
...options.random && { random: 'true' }
});
const response = await fetch(
`https://quotegallery.nl/api/quotes?${params}`
);
if (!response.ok) throw new Error('Failed to fetch quotes');
const { data } = await response.json();
setQuotes(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
fetchQuotes();
}, [options.limit, options.category, options.language, options.random]);
return { quotes, loading, error };
}
// Usage
function QuoteList() {
const { quotes, loading, error } = useQuotes({
limit: 5,
category: 'philosophical',
language: 'en'
});
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return (
<ul>
{quotes.map(quote => (
<li key={quote.id}>
"{quote.translations[0].text}" - {quote.author.name}
</li>
))}
</ul>
);
}
Composable for Vue 3:
import { ref, onMounted } from 'vue';
export function useQuotes(options = {}) {
const quotes = ref([]);
const loading = ref(true);
const error = ref(null);
async function fetchQuotes() {
try {
loading.value = true;
const params = new URLSearchParams({
limit: options.limit?.toString() || '10',
...options.category && { category: options.category },
...options.language && { language: options.language }
});
const response = await fetch(
`https://quotegallery.nl/api/quotes?${params}`
);
if (!response.ok) throw new Error('Failed to fetch');
const { data } = await response.json();
quotes.value = data;
} catch (err) {
error.value = err.message;
} finally {
loading.value = false;
}
}
onMounted(fetchQuotes);
return { quotes, loading, error, refetch: fetchQuotes };
}
<script setup>
const { quotes, loading, error } = useQuotes({
limit: 5,
category: 'philosophical'
});
</script>
<template>
<div v-if="loading">Loading...</div>
<div v-else-if="error">{{ error }}</div>
<ul v-else>
<li v-for="quote in quotes" :key="quote.id">
"{{ quote.translations[0].text }}" - {{ quote.author.name }}
</li>
</ul>
</template>