Парсинг даних з CoinGecko / CoinMarketCap
При інтеграції DeFi-додатку із зовнішніми price feeds розробники стикаються з rate limits, лагами оновлення та неповнотою даних. Ефективний парсинг CoinGecko та інтеграція CoinMarketCap API — основа нашого price feed. Це два основні підходи для збору крипто-даних, кожен зі своїми обмеженнями та якістю покриття. Розберемо, як побудувати стійку систему збору крипто-даних, використовуючи обидва джерела з fallback, кешування в Redis та базу PostgreSQL. Ми гарантуємо стабільність системи та сертифіковані фахівці з крипто-розробки. Понад 5 років досвіду — зверніться до нас за консультацією, допоможемо обрати оптимальну архітектуру під ваш проект.
Чому варто об'єднувати CoinGecko та CoinMarketCap?
CoinGecko API кращий для DeFi-токенів та long-tail активів: у нього більш щедрий безкоштовний tier та краще покриття. CoinGecko краще CoinMarketCap у 1.5 рази для DeFi токенів, особливо на Ethereum та Polygon. CoinMarketCap дає більш точні обсяги з великих CEX. Для production price feed ми використовуємо обидва з fallback логікою — це знижує ризик при відмові одного джерела. Згідно документації CoinGecko API, базова частота оновлення даних — 1–10 секунд.
| Параметр | CoinGecko | CoinMarketCap |
|---|---|---|
| Потрібен ключ? | Опціонально (безкоштовний без ключа) | Обов'язково навіть для базових запитів |
| Безкоштовний ліміт | 10-30 req/min (без ключа) | 10 000 кредитів/міс |
| Максимальний ID за запит | 250 | 100 |
| Історичні дані | До 5 років в Pro | До року (в платному плані) |
| Затримка даних | ~1-10 сек для цін | ~1-5 сек |
Як налаштувати стабільний збір даних при обмеженнях API?
Ми використовуємо Redis для кешування з TTL 1-2 хвилини — це знижує навантаження на API в 5-10 разів у порівнянні з прямими запитами. Приклад клієнта з автоматичним retry при 429-статусі:
const COINGECKO_BASE = 'https://api.coingecko.com/api/v3'
// Pro: 'https://pro-api.coingecko.com/api/v3'
class CoinGeckoClient {
constructor(private apiKey?: string) {}
private async request<T>(path: string, params?: Record<string, string>): Promise<T> {
const url = new URL(`${COINGECKO_BASE}${path}`)
if (params) Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v))
if (this.apiKey) url.searchParams.set('x_cg_pro_api_key', this.apiKey)
const res = await fetch(url.toString())
if (res.status === 429) {
const retryAfter = res.headers.get('Retry-After')
await sleep((parseInt(retryAfter || '60') + 1) * 1000)
return this.request(path, params) // retry
}
if (!res.ok) throw new Error(`CoinGecko ${res.status}: ${await res.text()}`)
return res.json()
}
async getSimplePrice(
ids: string[],
vsCurrencies: string[] = ['usd'],
includeMarketCap = false,
include24hVol = false,
include24hChange = false
) {
return this.request<Record<string, Record<string, number>>>('/simple/price', {
ids: ids.join(','),
vs_currencies: vsCurrencies.join(','),
include_market_cap: String(includeMarketCap),
include_24hr_vol: String(include24hVol),
include_24hr_change: String(include24hChange),
})
}
async getMarkets(page = 1, perPage = 250) {
return this.request<CoinMarketData[]>('/coins/markets', {
vs_currency: 'usd',
order: 'market_cap_desc',
per_page: String(perPage),
page: String(page),
sparkline: 'false',
})
}
async getMarketChart(coinId: string, days: number | 'max') {
return this.request<MarketChart>(`/coins/${coinId}/market_chart`, {
vs_currency: 'usd',
days: String(days),
interval: days === 'max' || days > 90 ? 'daily' : 'hourly',
})
}
}
Як отримати повний список монет з контрактними адресами?
Для матчингу contract address → CoinGecko ID потрібен endpoint /coins/list?include_platform=true. Кешуємо ці дані на 24 години, оскільки вони змінюються рідко:
async function buildTokenAddressIndex(): Promise<Map<string, string>> {
const coins = await client.request<CoinWithPlatforms[]>(
'/coins/list',
{ include_platform: 'true' }
)
const index = new Map<string, string>() // 'chain:address' → coingecko_id
for (const coin of coins) {
for (const [platform, address] of Object.entries(coin.platforms || {})) {
if (address) {
index.set(`${platform}:${address.toLowerCase()}`, coin.id)
}
}
}
return index
}
CoinMarketCap API
CMC API вимагає ключ навіть для базових запитів. Безкоштовний plan — 10 000 кредитів/місяць (1 кредит ≈ 1 запит). Приклад запиту останніх котирувань:
class CoinMarketCapClient {
private headers = {
'X-CMC_PRO_API_KEY': process.env.CMC_API_KEY!,
'Accept': 'application/json',
}
async getLatestQuotes(symbols: string[]): Promise<CMCQuoteResponse> {
const res = await fetch(
`https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest?symbol=${symbols.join(',')}`,
{ headers: this.headers }
)
const data = await res.json()
if (data.status.error_code !== 0) {
throw new Error(`CMC error: ${data.status.error_message}`)
}
return data
}
}
Архітектура та стек
Для production-grade системи ми використовуємо мікросервіс на Node.js/TypeScript, який фоново збирає дані з обох API. Redis виступає як кеш першого рівня з TTL, а PostgreSQL — як довгострокове сховище. Для моніторингу та алертів піднімаємо Grafana + Prometheus, відстежуємо кількість запитів, затримки та частоту помилок.
Порівняння тарифів CoinGecko
| Рівень | Запити/хв | Ціна | Історичні дані |
|---|---|---|---|
| Безкоштовний | 10-30 | $0 | До 1 року (обмежено) |
| Pro | 500 | $129/міс | До 5 років |
| Enterprise | кастом | кастом | Повний доступ |
Кешування та зберігання
Для price feed з оновленням кожну хвилину — Redis з TTL 120 секунд і батчевим оновленням по 250 ID:
class PriceCache {
constructor(private redis: RedisClient, private client: CoinGeckoClient) {}
async getPrice(coinId: string): Promise<number> {
const cached = await this.redis.get(`price:${coinId}`)
if (cached) return parseFloat(cached)
const prices = await this.client.getSimplePrice([coinId])
const price = prices[coinId]?.usd
if (price) await this.redis.setEx(`price:${coinId}`, 60, String(price))
return price
}
async refreshPrices(coinIds: string[]): Promise<void> {
const chunks = chunk(coinIds, 250)
for (const ids of chunks) {
const prices = await this.client.getSimplePrice(ids, ['usd'], true, true, true)
const pipeline = this.redis.pipeline()
for (const [id, data] of Object.entries(prices)) {
pipeline.setEx(`price:${id}`, 120, JSON.stringify(data))
}
await pipeline.exec()
}
}
}
Історичні дані — PostgreSQL з індексом по (coin_id, timestamp). Для інтенсивних часових запитів використовуємо TimescaleDB.
Процес роботи
Процес роботи (натисніть, щоб розгорнути)
- Аналіз — оцінюємо кількість токенів, частоту оновлення, бюджет на API.
- Проектування — обираємо стек (Node.js, Redis, PostgreSQL), проектуємо схему БД та архітектуру кешу.
- Реалізація — пишемо клієнти з retry, rate limiting, кешуванням; налаштовуємо батчеві оновлення.
- Тестування — перевіряємо під навантаженням (імітуємо rate limit, обрив з'єднання).
- Деплой — розгортаємо в Docker на вашому сервері або хмарі.
- Моніторинг — налаштовуємо Grafana дашборд з метриками: latency, cache hit ratio, error rate.
- Підтримка — протягом місяця після запуску допомагаємо з інцидентами та доналаштуванням.
Типові помилки при інтеграції
Типові помилки (натисніть, щоб розгорнути)
- Ігнорування rate limit → блокування IP. Рішення: використовувати чергу з затримками.
- Відсутність fallback при відмові одного API → втрата даних. Рішення: об'єднувати обидва джерела з пріоритетом.
- Зберігання всіх даних в одній таблиці без партиціонування → повільні запити. Рішення: TimescaleDB для часових рядів.
- Кешування без TTL → застарілі ціни. Рішення: Redis TTL 60-120 секунд.
Що входить в роботу
- Архітектура під ваш обсяг даних (від 100 до 10 000 токенів)
- Реалізація API-клієнтів з retry, rate limiting, логуванням
- Redis-кеш з оптимальним TTL
- PostgreSQL/TimescaleDB для історії
- Фонові воркери для автоматичного оновлення
- Документація по експлуатації та дашборд Grafana
- Підтримка протягом місяця після запуску
- Навчання вашої команди роботі з системою
Замовте налаштування price feed — підберемо рішення під вашу задачу. Отримайте консультацію з інтеграції вже сьогодні. Наш досвід — більше 5 років у крипто-розробці, 30+ проектів.







