The ASH pricing page displays Bitcoin and Monero prices for customers paying in cryptocurrency. The display refreshes every five minutes. For about two years, the refresh fetched data from CoinGecko’s public API.
The CoinGecko dependency was operationally fine until it was not. The API has rate limits that we hit occasionally during traffic spikes. The API also occasionally returned stale data, returned errors, or required authentication changes. Each issue cost engineering time we did not want to spend on a feature that should be transparent infrastructure.
We replaced the CoinGecko dependency with a self-hosted price aggregator that we built and open-sourced. The aggregator pulls data from multiple exchanges directly, calculates median values, caches appropriately, and serves our pricing page without external API dependency.
This post is what we built, why we built it, the trade-offs we made, and where the code lives for other operators interested in similar self-hosted approaches.
The CoinGecko dependency in context
CoinGecko’s public API is widely used. It provides cryptocurrency price data for free with reasonable rate limits. For most use cases, the service is operationally adequate.
Our use case is bounded but specific:
- Display BTC/EUR and XMR/EUR prices on the pricing page
- Refresh every 5 minutes for visitor accuracy
- Reliability matters because we serve global visitors continuously
- Latency matters because the page is part of customer conversion flow
The CoinGecko API satisfies these requirements 99% of the time. The 1% failure rate produced occasional incidents that surfaced to our visitors. The incidents were not catastrophic but were operationally annoying.
The specific issues over two years:
- Rate limit exceeded during high-traffic events (typically 2-4 incidents per year)
- API returning stale data for several minutes (5-10 incidents)
- API returning unexpected error responses (3-5 incidents)
- API specification changes requiring code updates (1-2 incidents)
- Brief API outages affecting our display (4-6 incidents)
The cumulative impact: roughly 25-30 minor incidents over two years requiring engineering response time. Each incident was small (5-30 minutes of investigation and patch). The total time investment was 8-15 hours per year on what should be transparent infrastructure.
The strategic concern beyond the immediate operational cost: the dependency on external API exposed our pricing page to risks outside our control. CoinGecko could change pricing, restrict access, modify the API in breaking ways, or have prolonged outages. Each scenario would require reactive response from our team.
The motivation to self-host
The decision to build our own aggregator came from several factors aligning.
Operational principle
Our broader business philosophy emphasizes self-hosted infrastructure. We operate self-hosted MTAs (PowerMTA, Postal), self-hosted analytics, self-hosted BTCPay Server for payment processing. Adding self-hosted price aggregation aligns with the existing architecture rather than introducing exception.
Engineering capacity
The aggregator is bounded engineering. A few hundred lines of code, basic infrastructure. The capacity existed in our team without disrupting other work.
Cost analysis
Self-hosted infrastructure cost is bounded. The hosting cost is essentially the same as our other small services. The maintenance cost is minimal once running. The total cost is lower than the engineering time spent responding to CoinGecko-related incidents.
Customer-aligned positioning
We sell privacy and self-hosting to customers. Operating critical dependencies from third-party APIs would be inconsistent with the positioning. Self-hosted aggregation aligns with what we tell customers to do for their own operations.
Open-source contribution
Building and open-sourcing the aggregator provides value to the broader self-hosting community. The contribution aligns with the values of customers attracted to our brand.
The architecture
The aggregator is a simple service with bounded scope.
Data sources
We pull from multiple cryptocurrency exchanges directly:
- Kraken (public ticker API)
- Bitstamp (public ticker API)
- Coinbase (public ticker API)
- Binance (public ticker API)
- KuCoin (public ticker API)
Each exchange exposes public APIs that do not require authentication for basic ticker data. The aggregator queries all five in parallel.
The choice of exchanges balances:
- API availability without authentication
- Geographic diversity (multiple jurisdictions)
- Volume credibility (major exchanges with real liquidity)
- API stability (well-established APIs unlikely to change abruptly)
Aggregation logic
For each currency pair (BTC/EUR, XMR/EUR, etc.), the aggregator:
- Fetches current price from each available exchange
- Filters out exchanges that returned errors or stale data
- Calculates median of remaining prices
- Outputs the median as the aggregated price
The median calculation rather than mean reduces the impact of any single exchange’s outlier data. If one exchange has wrong data or is being manipulated, the median ignores it as long as the majority of exchanges have reasonable data.
Caching
The aggregated prices are cached locally with TTL matching the display refresh rate. The pricing page reads from local cache rather than triggering aggregation calls.
Cache invalidation is time-based (5-minute TTL). Forced refresh capability exists for incident response but is not used in normal operation.
Error handling
If an exchange is unavailable, the aggregator continues with the remaining exchanges. The median calculation still works as long as at least 3 exchanges provide data.
If all exchanges are unavailable (extremely rare), the aggregator returns the last successful aggregated value with an “is_stale” flag. The pricing page can choose to display, hide, or warn based on the flag.
Storage
Historical price data is stored for trend analysis and audit. The storage is minimal (one record per currency pair per 5-minute interval = 288 records per day per pair).
The history supports:
- Comparing current to historical
- Audit when customers ask about pricing at specific times
- Detection of unusual price movements
Deployment
The aggregator runs as a small service on our infrastructure. Deployment characteristics:
Resource requirements
CPU: minimal. The aggregator runs queries every 5 minutes and does basic calculation. Even modest hardware handles it.
Memory: small. The active dataset is tiny. Total memory usage is under 100MB.
Storage: minimal. Historical data accumulates slowly. Total storage after one year of operation is approximately 200MB.
Network: low. The outbound API queries are small. The inbound traffic from our pricing page is also small.
Hosting
We run the aggregator on a small VPS in Bulgaria for jurisdictional diversification from our main infrastructure. The total monthly cost is approximately €15.
The hosting choice is independent of the rest of our infrastructure. The aggregator can move between providers easily because it has no significant state.
Monitoring
The service is monitored through our standard infrastructure monitoring. Key metrics:
- Successful queries per cycle (target 5/5 exchanges)
- Cache freshness (target under 6 minutes)
- API response times per exchange
- Aggregated price reasonableness (alerts on > 5% deviation from recent values)
The monitoring catches issues before they affect visitors. Most incidents (an exchange API returning bad data, network connectivity issues, etc.) are auto-recovered without operator intervention.
The trade-offs we made
Self-hosting versus using a service involves specific trade-offs.
Trade-off 1: Maintenance versus dependency
Self-hosting means we maintain the code. We update dependencies, respond to changes in exchange APIs, handle our own deployment. The maintenance is bounded (a few hours per year typically) but real.
Using CoinGecko meant we depended on their maintenance. They updated their code, responded to upstream changes, handled their deployment. The dependency was outside our control but their team did the work.
For us, the maintenance is worth the control. The aggregator code is simple enough that maintenance is straightforward. The benefit of independence justifies the maintenance cost.
Trade-off 2: Reliability versus complexity
CoinGecko has higher uptime than our self-hosted service ever will. Their infrastructure is more sophisticated. They have dedicated SRE staff.
Our self-hosted service has 99.5% uptime over the year since deployment. The downtime is brief and rare. For our use case (5-minute refresh on a pricing page), 99.5% is operationally adequate.
The trade-off favors self-hosted for our use case but might not for higher-stakes uses.
Trade-off 3: Speed versus accuracy
CoinGecko provides instant price updates based on their aggregation. The data is fresh.
Our aggregator updates every 5 minutes. The data can be up to 5 minutes stale.
For a pricing page where customers convert payment over many minutes, the staleness is operationally acceptable. For high-frequency trading or other time-sensitive applications, 5-minute staleness would be inadequate.
Trade-off 4: Coverage versus focus
CoinGecko covers thousands of cryptocurrency pairs. Our aggregator covers only the pairs we need (BTC/EUR, XMR/EUR, with a few others available).
The narrow coverage simplifies the aggregator. Adding new pairs is bounded engineering rather than requiring expansion of infrastructure.
Trade-off 5: Validated versus self-validated
CoinGecko’s data goes through their validation process. The data has been checked.
Our aggregator validates within its own logic. If our validation logic has gaps, we propagate the gaps.
We have tested our validation against CoinGecko output over months. The aggregator’s median prices match CoinGecko within 0.2-0.5% essentially always. The validation is working.
The open-source release
We released the aggregator under MIT license on GitHub. The repository is ash-crypto-rates.
The release includes:
- The Python source code
- Configuration examples
- Docker compose for deployment
- Documentation for operation and customization
The reasons for open-source release:
Alignment with self-hosting values
Customers attracted to our self-hosting positioning value seeing us practice what we recommend. The open-source code makes the practice visible and reproducible.
Contribution to the community
Other operators have similar needs for self-hosted crypto price data. The code can serve their use cases with bounded adaptation.
Defensive transparency
The code being public means our pricing display is auditable. Customers can verify how we calculate the prices we display.
Educational value
The code demonstrates self-hosted approaches to a common need. Operators learning about self-hosted infrastructure can see a working example.
Adoption by other operators
Since release, several other operators have adapted the code for their use cases.
Self-hosted ESP customer using BTC payments
A customer running a self-hosted ESP business uses the aggregator for their own pricing page. The adaptation was bounded (configuration changes for their currency pairs and refresh rate).
Privacy-focused service operator
A customer running a privacy-focused VPN service uses the aggregator for accepting Monero payments. Their use case required additional currency pairs that were straightforward to add.
Internal tools developer
A solo developer running tools for the cryptocurrency community uses the aggregator for various dashboard widgets. The MIT license allows their commercial use without restriction.
Custom modifications contributed back
Two operators contributed code improvements back to the repository:
- Additional exchange integrations (Bittrex, OKX)
- Improved error handling for specific edge cases
- Configuration improvements for production deployment
The contributions improve the code for all users. The open-source model is working as intended.
What we learned during development
The development process produced some operational insights.
Exchange API variability
Different exchanges return data in different formats, with different naming, different precision, different conventions. The aggregator code spent more effort normalizing exchange responses than performing aggregation logic.
The normalization layer is the largest part of the codebase. Each new exchange requires writing or adapting a normalizer for that exchange’s specific API.
Rate limit considerations
Even unauthenticated exchange APIs have rate limits. The aggregator must respect them. We use exchange-specific rate limit handling: stay well below documented limits, back off on rate limit errors, distribute requests across exchanges to reduce per-exchange load.
The rate limit handling is operationally invisible but architecturally important.
Geographic considerations
Some exchanges are not accessible from certain regions due to legal or technical restrictions. The aggregator handles this gracefully: if an exchange is unavailable from our hosting location, the aggregator continues with the remaining exchanges.
The graceful handling means we can deploy the aggregator from any jurisdiction without per-deployment configuration for exchange availability.
Currency pair edge cases
Different exchanges offer different currency pairs. BTC/EUR is available everywhere. XMR/EUR is less universally available; some exchanges offer XMR/USD only.
The aggregator handles this through configuration: which exchanges provide which currency pairs, what conversions are needed, how to handle missing pairs.
What we expect over time
Looking forward to maintenance and evolution:
Ongoing maintenance
Exchange APIs change occasionally. We expect 1-2 minor maintenance events per year. Most changes are backward-compatible; some require code updates.
The maintenance schedule is predictable. Our internal monitoring catches issues before they affect operations.
Feature additions
The current scope is intentional. Adding features expands the maintenance burden. We add features when there is clear operational need rather than speculatively.
Community contributions
We expect continued contributions from other operators using the code. The pattern of accepting external contributions while maintaining quality standards is operational.
Eventual replacement consideration
In a few years, if better self-hosted aggregation tools emerge, we may migrate to them. The current implementation serves the immediate need; long-term, the broader community may produce more sophisticated tools.
The bigger principle this illustrates
The crypto rate aggregator is one specific case of a broader principle.
Dependencies have costs
External API dependencies have costs that are not always immediately visible. Rate limits, downtime, API changes, pricing changes, organizational changes at the provider all create operational risk.
For dependencies that matter, the cost-benefit of self-hosting deserves explicit analysis. The analysis often favors external services for low-stakes uses; the analysis often favors self-hosting for higher-stakes uses or where independence is strategically valuable.
Self-hosting is bounded
The aggregator is a few hundred lines of code. The maintenance is a few hours per year. The hosting is €15/month. The cost is bounded.
Self-hosting often appears more complex than it is. The bounded nature of typical self-hosting projects means the maintenance is achievable for small teams.
Open-source contribution multiplies value
The same code serves us, our customers who adapt it, the broader community. The development cost is the same whether private or public. The benefit is much larger when public.
For code without competitive advantage (which infrastructure code rarely has), open-source release multiplies the value created by the development.
Alignment matters
Our self-hosted approach aligns with what we tell customers. Customers attracted to our positioning value seeing the consistency. The alignment is strategic positioning value beyond the operational value.
For brands selling self-hosted services, operating from third-party APIs that contradict the positioning creates trust gaps. Aligning internal practice with external positioning reinforces brand identity.
What other operators should consider
For other operators considering self-hosted alternatives to external APIs:
Identify the dependencies that matter
Not all dependencies need self-hosting. Many are operationally fine to leave external. The dependencies that matter are typically:
- High-traffic dependencies where rate limits affect operations
- Strategic dependencies that affect competitive positioning
- Dependencies on services that may change pricing or availability significantly
- Dependencies that create privacy or operational concerns
Quantify the cost of dependency
The cost of external dependency is not just the API fees. It includes engineering time for incidents, strategic risk, and trust positioning. Quantifying these reveals the actual cost.
Compare to self-hosting cost
Self-hosting cost is the development time plus ongoing maintenance plus infrastructure. For small bounded services like price aggregation, the total cost is modest.
Build only when justified
Building infrastructure that already exists externally is not always valuable. The decision should be justified by specific operational, strategic, or alignment value rather than self-hosting for its own sake.
Consider open-sourcing
If the infrastructure is built and not competitively sensitive, releasing it open-source multiplies the value created. The marginal cost of release is small; the marginal benefit can be significant.
Where this fits in our broader infrastructure
The crypto rate aggregator is one of several self-hosted services we operate. Others include:
BTCPay Server for payment processing Mautic-based marketing automation (different from our customer-facing platform) Internal monitoring infrastructure Custom analytics replacing third-party tools
The pattern across these is consistent: identify dependencies that matter, evaluate self-hosting, build when justified, document and operate ongoing.
The cumulative effect is an infrastructure stack with bounded external dependencies. The visible benefit: when external services have issues, we are typically unaffected. The invisible benefit: the operational mindset of self-sufficiency reinforces other decisions throughout our operation.
For customers reading this with interest in similar approaches: the code is available, the patterns are documented, the trade-offs are explicit. The work is bounded and achievable for small teams with operational discipline.
The crypto rate aggregator is a small example. The pattern generalizes. Operators who treat external dependencies as deliberate choices rather than default assumptions produce more resilient infrastructure over time. The work to choose deliberately is bounded; the benefit accumulates across many small decisions.