ccdata_api/lib.rs
1//! # CoinDesk API Wrapper
2//!
3//! `ccdata-api` is a wrapper for CoinDesk REST API endpoints (Formerly CCData). This crate supports non-exhausitve list of CoinDesk endpoints,
4//! you can check what endpoints are supported by checking the variants of the enum `APIEndpoint` - it contains all
5//! supported endpoint URLs.
6//!
7//! For documentation on CoinDesk REST API endpoints visit CoinDesk online documentation:
8//! - API Documentation: <https://developers.coindesk.com/documentation/data-api/introduction>
9//! - Legacy API Documentation: <https://developers.coindesk.com/documentation/legacy/Price/SingleSymbolPriceEndpoint>
10//!
11//! **Disclaimer:** This crate is an unofficial CoinDesk REST API wrapper, the maintainers of the crate are independent developers.
12//! The developers of the crate do not accept any responsibility or liability for the accuracy, security, or completeness of the code,
13//! or the information provided within the crate.
14//!
15//! # Errors
16//!
17//! The REST API functions in the crate will error if the data received does not fit into the pre-defined schemas provided
18//! in the crate. If you encounter any errors, please open an issue on GitHub with the parameters that you have used (e.g., asset symbol,
19//! timestamp, limit, etc.). **Do not provide your API key or any personal data!**
20//!
21//! # Deprecated API Endpoints
22//!
23//! Some API endpoints have been deprecated by CoinDesk, who strongly recommend migrating to newer alternatives suggested in their API documentation.
24//!
25//! # Features
26//! - `debug`: If this feature is enabled, you can set `CCDATA_API_DEBUG` environment variable to `true`, which will print the response body
27//! for every request to the command line.
28//!
29//! # Examples
30//!
31//! To start making the REST API requests, define a data collection backend. This can be done by either directly passing an
32//! API key to the data collection backend, or by defining a `.env` file with an API key as an environment variable (Preferred
33//! method).
34//!
35//! ## Build Backend Explicitly Stating API Key (May expose API key)
36//!
37//! ```rust
38//! use ccdata_api::CoinDesk;
39//!
40//! let mut backend: CoinDesk = CoinDesk::new();
41//!
42//! let api_key: String = String::from("xxxxxxx");
43//! backend.update_api_key(api_key);
44//!
45//! assert_eq!(backend.api_key().unwrap(), "xxxxxxx");
46//! ```
47//!
48//! ## Build Backend Using .env File (Preferred method)
49//!
50//! ```rust
51//! use ccdata_api::CoinDesk;
52//!
53//! let mut backend: CoinDesk = CoinDesk::new();
54//! // Provide API key as the environment variable called API_KEY
55//! backend.build(&"API_KEY").unwrap();
56//!
57//! println!("{}", backend.api_key().unwrap());
58//! ```
59//!
60//! ## Making First API Call
61//!
62//! After the backend has been build, you can make API requests using the methods provided in the backend. For example, to
63//! get a daily spot OHLCV data for Bitcoin you can do the following (note that the calls use Rust's `async` functionality):
64//!
65//! ```rust
66//!
67//! use ccdata_api::{CoinDesk, Unit, SpotMarket};
68//!
69//! #[tokio::main]
70//! async fn main() -> () {
71//!
72//! let mut backend: CoinDesk = CoinDesk::new();
73//! // Provide API key as the environment variable called API_KEY
74//! backend.build(&"API_KEY").unwrap();
75//!
76//! // Define the API parameters
77//! let market: SpotMarket = SpotMarket::KRAKEN;
78//! let limit: usize = 2000;
79//! let to_timestamp: Option<i64> = Some(1728860400);
80//!
81//! // Make the API call
82//! let ohlcv = backend.get_spot_ohlcv("BTC-USD", to_timestamp, Some(limit), market, Unit::Day).await.unwrap();
83//! assert_eq!(ohlcv.data.unwrap().len(), limit);
84//!
85//! }
86//! ```
87//!
88//! # General information
89//! If you would like to add a commit or an issue, please do so using the GitHub link to the project:
90//! - <https://github.com/rsadykhov/ccdata-api>
91
92
93// Re-Exports
94pub use self::backend::CoinDesk;
95pub use self::utils::{Group, AssetLookupPriority, Param, call_api_endpoint};
96// Min-API Re-Exports
97pub use self::schemas::{CCMinResponse, CCMinWrapper, CCRateLimit, CCMaxCalls, CCCallsMade};
98pub use self::schemas::min_api::{BalanceDistribution, SupplyBand};
99// Data-API Re-Exports
100pub use self::schemas::{CoinDeskResponse, CCError, CCErrorOtherInfo};
101pub use self::schemas::data_api::indices_and_reference_rates::{IndicesMarket, IndicesOHLCV};
102pub use self::schemas::data_api::spot::{SpotMarket, SpotInstrumentStatus, SpotOHLCV, SpotInstrumentMetdata, SpotMarkets, SpotMarketsInstruments};
103pub use self::schemas::data_api::futures::{FuturesMarket, FuturesOHLCV, FuturesInstrumentMetadata, FuturesMarkets};
104pub use self::schemas::data_api::options::{OptionsMarket, OptionsOHLCV, OptionsInstrumentMetadata, OptionsMarkets};
105pub use self::schemas::data_api::derivatives_indices::{DerIndicesMarket, DerIndicesOHLCV, DerIndicesMarkets};
106pub use self::schemas::data_api::on_chain_dex::{OCDEXMarket, OCDEXOHLCV, OCDEXMarkets};
107pub use self::schemas::data_api::on_chain_core::{OCCoreETHBlock, OCCoreAssetByChain, OCCoreAssetByAddress, OCCoreSupply};
108pub use self::schemas::data_api::asset::{AssetMetadata, AssetEvent, AssetCodeRepoMetrics, AssetDiscord, AssetReddit, AssetTelegram, AssetTwitter};
109pub use self::schemas::data_api::news::{NewsStatus, NewsLang, NewsSourceID, NewsLatestArticle, NewsSourceType, NewsSource, NewsCategory};
110pub use self::schemas::data_api::overview::OverviewMktCapOHLCV;
111
112
113pub mod error;
114pub mod schemas;
115pub mod utils;
116pub mod backend;
117
118
119use std::fmt::Display;
120use serde::{Serialize, Deserialize};
121
122
123#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
124/// Unit of the interval between successive data points.
125pub enum Unit {
126 #[default]
127 /// Daily data interval
128 Day,
129 /// Hourly data interval
130 Hour,
131 /// Minutely data interval
132 Minute,
133 /// Used for API endpoints that do not have a specified unit for data or where unit is unapplicable
134 NA,
135}
136
137impl Display for Unit {
138 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139 match self {
140 Self::Day | Unit::NA => write!(f, "/days"),
141 Self::Hour => write!(f, "/hours"),
142 Self::Minute => write!(f, "/minutes"),
143 }
144 }
145}
146
147
148/// Trait that defines all required methods for API endpoint URL construction (prior to adding non-default parameters).
149/// This is useful for defining custom API endpoints that are not included with the crate.
150pub trait APIEndpointTrait {
151 /// Returns a vector of default groups to apply to match the pre-defined schemas.
152 ///
153 /// Note: If there are no default groups defined the `None` variant is returned.
154 fn default_groups(&self) -> Option<Vec<Group>>;
155
156 /// Returns a vector of default API endpoint parameters to apply to match the pre-defined schemas.
157 ///
158 /// Note: If there are no default parameters defined the `None` variant is returned.
159 fn default_params(&self) -> Option<Vec<Param<'_>>>;
160
161 /// Produces URL for a given API endpoint.
162 ///
163 /// # Input
164 /// - `unit`: Unit of the interval between successive data points
165 fn url(&self, unit: &Unit) -> String;
166}
167
168
169#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
170/// All supported API endpoints.
171pub enum APIEndpoint {
172 #[deprecated(since="1.0.6", note="Deprecated by CoinDesk")]
173 // Legacy API Endpoint
174 /// Description: Retrieves the balance distribution for a specified asset over a specified time range at a daily interval
175 ///
176 /// Note: This is a legacy API endpoint and is not actively supported. Only data for BTC (Bitcoin) is available.
177 ///
178 /// URL: https://min-api.cryptocompare.com/data/blockchain/balancedistribution/histo/day
179 BalanceDistribution,
180 // Indices & Reference Rates
181 ///
182 /// Description: Provides historical candlestick data for various indices
183 ///
184 /// URL: https://data-api.coindesk.com/index/cc/v1/historical
185 IndicesOHLCV,
186 // Spot
187 /// Description: Provides candlestick data for specific cryptocurrency instruments across selected exchanges
188 ///
189 /// URL: https://data-api.coindesk.com/spot/v1/historical
190 SpotOHLCV,
191 /// Description: Delivers vital metadata about financial instruments traded on specified exchanges, focusing solely on non-price related information
192 ///
193 /// URL: https://data-api.coindesk.com/spot/v1/latest/instrument/metadata
194 SpotInstrumentMetadata,
195 /// Description: Provides comprehensive information about various cryptocurrency spot markets,
196 /// featuring extensive exchange metadata and operational details.
197 ///
198 /// URL: https://data-api.coindesk.com/spot/v2/markets
199 SpotMarketsV2,
200 /// Description: Retrieves a comprehensive dictionary of mapped instruments across one or more spot markets, filtered by a specified state or status
201 ///
202 /// URL: https://data-api.coindesk.com/spot/v1/markets/instruments
203 SpotMarketsInstruments,
204 // Futures
205 /// Description: Provides aggregated candlestick data for specific futures instruments on designated exchanges
206 ///
207 /// URL: https://data-api.coindesk.com/futures/v1/historical
208 FuturesOHLCV,
209 /// Description: Provides essential metadata about futures instruments traded on various exchanges.
210 ///
211 /// URL: https://data-api.coindesk.com/futures/v1/latest/instrument/metadata
212 FuturesInstrumentMetadata,
213 /// Description: Provides comprehensive information about various cryptocurrency futures markets,
214 /// featuring extensive exchange metadata and derivatives-specific operational details.
215 ///
216 /// URL: https://data-api.coindesk.com/futures/v2/markets
217 FuturesMarketsV2,
218 // Options
219 /// Description: Provides historical OHLCV (open, high, low, close, volume) data for specified options instruments on a chosen exchange
220 ///
221 /// URL: https://data-api.coindesk.com/options/v1/historical
222 OptionsOHLCV,
223 /// Description: Provides detailed metadata about options instruments across various exchanges.
224 ///
225 /// URL: https://data-api.coindesk.com/options/v1/latest/instrument/metadata
226 OptionsInstrumentMetadata,
227 /// Description: Provides comprehensive information about various cryptocurrency options markets,
228 /// featuring extensive exchange metadata and derivatives-specific operational details.
229 ///
230 /// URL: https://data-api.coindesk.com/options/v2/markets
231 OptionsMarketsV2,
232 // Derivatives Indices
233 /// Description: Provides historical OHLC (open, high, low, close) data for specified index instruments on a selected market
234 ///
235 /// URL: https://data-api.coindesk.com/index/v1/historical
236 DerIndicesOHLCV,
237 /// Description: Provides comprehensive information about various derivatives index markets,
238 /// featuring extensive exchange metadata and index-specific operational details.
239 ///
240 /// URL: https://data-api.coindesk.com/index/v2/markets
241 DerIndicesMarketsV2,
242 // On-Chain DEX
243 /// Description: Retrieves aggregated candlestick data for AMM swap transactions
244 ///
245 /// URL: https://data-api.coindesk.com/onchain/v1/amm/historical/swap
246 OCDEXOHLCV,
247 /// Description: Provides comprehensive information about various on-chain decentralized exchange markets,
248 /// featuring extensive exchange metadata and DEX-specific operational details.
249 ///
250 /// URL: https://data-api.coindesk.com/onchain/v2/amm/markets
251 OCDEXMarketsV2,
252 // On-Chain Core
253 /// Description: Delivers exhaustive details on a specific Ethereum block in a meticulously processed format, complete with detailed explanations for each field
254 ///
255 /// URL: https://data-api.coindesk.com/onchain/v1/block/2
256 OCCoreETHBlocks,
257 /// Description: Retrieves a comprehensive summary of chain asset information for a specified blockchain, identified by its chain symbol
258 ///
259 /// URL: https://data-api.coindesk.com/onchain/v3/summary/by/chain
260 OCCoreAssetsByChain,
261 /// Description: Retrieves comprehensive asset information for a specific asset identified by its smart contract address and associated blockchain asset
262 ///
263 /// URL: https://data-api.coindesk.com/onchain/v2/data/by/address
264 OCCoreAssetByAddress,
265 /// Description: Retrieves comprehensive historical supply data for various digital assets identified by either their CoinDesk asset ID or unique asset symbol
266 ///
267 /// URL: https://data-api.coindesk.com/onchain/v2/historical/supply/days
268 OCCoreSupply,
269 /// Descriptions: Returns an object that provides detailed and comprehensive information about multiple cryptocurrency assets in response to a request
270 ///
271 /// URL: https://data-api.coindesk.com/asset/v2/metadata
272 AssetMetadataV2,
273 /// Description: Retrieves an array of significant events related to digital assets, such as security incidents, rebrandings, blockchain forks, and other impactful developments
274 ///
275 /// URL: https://data-api.coindesk.com/asset/v1/events
276 AssetEvents,
277 /// Description: Provides an in-depth, daily snapshot of a digital asset's code repositories
278 ///
279 /// URL: https://data-api.coindesk.com/asset/v1/historical/code-repository/days
280 AssetCodeRepo,
281 /// Description: Aggregates detailed daily metrics from all Discord servers related to a specific digital asset, offering a multifaceted view into community engagement and the asset's standing within Discord communities
282 ///
283 /// URL: https://data-api.coindesk.com/asset/v1/historical/discord/days
284 AssetDiscord,
285 /// Description: Aggregates key performance indicators from all the subreddits related to a specific digital asset, providing a comprehensive understanding of the asset's footprint on Reddit
286 ///
287 /// URL: https://data-api.coindesk.com/asset/v1/historical/reddit/days
288 AssetReddit,
289 /// Description: Collates essential data points across all Telegram groups affiliated with a particular cryptocurrency asset
290 ///
291 /// URL: https://data-api.coindesk.com/asset/v1/historical/telegram/days
292 AssetTelegram,
293 /// Description: Aggregates essential metrics from all X (Twitter) accounts associated with a specific cryptocurrency asset
294 ///
295 /// URL: https://data-api.coindesk.com/asset/v1/historical/twitter/days
296 AssetTwitter,
297 // News
298 /// Description: Serves as the pulse of the crypto news landscape, providing users with instant access to the most recent articles across the industry
299 ///
300 /// URL: https://data-api.coindesk.com/news/v1/article/list
301 NewsLatestArticles,
302 /// Description: Offers a comprehensive listing of all news sources available through CoinDesk API
303 ///
304 /// URL: https://data-api.coindesk.com/news/v1/source/list
305 NewsSources,
306 /// Description: Provide a straightforward listing of all news categories available through CoinDesk API
307 ///
308 /// URL: https://data-api.coindesk.com/news/v1/category/list
309 NewsCategories,
310 // Overview
311 /// Description: Presents a thorough historical daily overview of market capitalisation for digital assets that meet the volume and listing criteria
312 ///
313 /// URL: https://data-api.coindesk.com/overview/v1/historical/marketcap/all/assets/days
314 OverviewMktCapOHLCV,
315}
316
317impl APIEndpoint {
318 fn resolve_url(&self) -> String {
319 match self {
320 // Legacy API
321 Self::BalanceDistribution => String::from("https://min-api.cryptocompare.com/data/blockchain/balancedistribution/histo/day"),
322 // Indices & Reference Rates
323 Self::IndicesOHLCV => String::from("https://data-api.coindesk.com/index/cc/v1/historical"),
324 // Spot
325 Self::SpotOHLCV => String::from("https://data-api.coindesk.com/spot/v1/historical"),
326 Self::SpotInstrumentMetadata => String::from("https://data-api.coindesk.com/spot/v1/latest/instrument/metadata"),
327 Self::SpotMarketsV2 => String::from("https://data-api.coindesk.com/spot/v2/markets"),
328 Self::SpotMarketsInstruments => String::from("https://data-api.coindesk.com/spot/v1/markets/instruments"),
329 // Futures
330 Self::FuturesOHLCV => String::from("https://data-api.coindesk.com/futures/v1/historical"),
331 Self::FuturesInstrumentMetadata => String::from("https://data-api.coindesk.com/futures/v1/latest/instrument/metadata"),
332 Self::FuturesMarketsV2 => String::from("https://data-api.coindesk.com/futures/v2/markets"),
333 // Options
334 Self::OptionsOHLCV => String::from("https://data-api.coindesk.com/options/v1/historical"),
335 Self::OptionsInstrumentMetadata => String::from("https://data-api.coindesk.com/options/v1/latest/instrument/metadata"),
336 Self::OptionsMarketsV2 => String::from("https://data-api.coindesk.com/options/v2/markets"),
337 // Derivatives Indices
338 Self::DerIndicesOHLCV => String::from("https://data-api.coindesk.com/index/v1/historical"),
339 Self::DerIndicesMarketsV2 => String::from("https://data-api.coindesk.com/index/v2/markets"),
340 // On-Chain DEX
341 Self::OCDEXOHLCV => String::from("https://data-api.coindesk.com/onchain/v1/amm/historical/swap"),
342 Self::OCDEXMarketsV2 => String::from("https://data-api.coindesk.com/onchain/v2/amm/markets"),
343 // On-Chain Core
344 Self::OCCoreETHBlocks => String::from("https://data-api.coindesk.com/onchain/v1/block/2"),
345 Self::OCCoreAssetsByChain => String::from("https://data-api.coindesk.com/onchain/v3/summary/by/chain"),
346 Self::OCCoreAssetByAddress => String::from("https://data-api.coindesk.com/onchain/v2/data/by/address"),
347 Self::OCCoreSupply => String::from("https://data-api.coindesk.com/onchain/v2/historical/supply/days"),
348 // Asset
349 Self::AssetMetadataV2 => String::from("https://data-api.coindesk.com/asset/v2/metadata"),
350 Self::AssetEvents => String::from("https://data-api.coindesk.com/asset/v1/events"),
351 Self::AssetCodeRepo => String::from("https://data-api.coindesk.com/asset/v1/historical/code-repository/days"),
352 Self::AssetDiscord => String::from("https://data-api.coindesk.com/asset/v1/historical/discord/days"),
353 Self::AssetReddit => String::from("https://data-api.coindesk.com/asset/v1/historical/reddit/days"),
354 Self::AssetTelegram => String::from("https://data-api.coindesk.com/asset/v1/historical/telegram/days"),
355 Self::AssetTwitter => String::from("https://data-api.coindesk.com/asset/v1/historical/twitter/days"),
356 // News
357 Self::NewsLatestArticles => String::from("https://data-api.coindesk.com/news/v1/article/list"),
358 Self::NewsSources => String::from("https://data-api.coindesk.com/news/v1/source/list"),
359 Self::NewsCategories => String::from("https://data-api.coindesk.com/news/v1/category/list"),
360 // Overview
361 Self::OverviewMktCapOHLCV => String::from("https://data-api.coindesk.com/overview/v1/historical/marketcap/all/assets/days"),
362 }
363 }
364
365 fn add_unit_to_url(&self, url: &mut String, unit: &Unit) -> () {
366 match self {
367 Self::IndicesOHLCV | Self::SpotOHLCV | Self::FuturesOHLCV |
368 Self::OptionsOHLCV | Self::DerIndicesOHLCV | Self::OCDEXOHLCV => {
369 url.push_str(&unit.to_string());
370 },
371 _ => (),
372 }
373 }
374}
375
376impl APIEndpointTrait for APIEndpoint {
377 fn default_groups(&self) -> Option<Vec<Group>> {
378 match self {
379 Self::FuturesOHLCV => Some(vec![ Group::Id, Group::Mapping, Group::OHLC, Group::Trade, Group::Volume, Group::MappingAdvanced, Group::OHLCTrade]),
380 Self::FuturesInstrumentMetadata => Some(vec![Group::Status, Group::General]),
381 Self::OptionsOHLCV => Some(vec![Group::Id, Group::Mapping, Group::OHLC, Group::Trade, Group::Volume, Group::MappingAdvanced, Group::OHLCTrade]),
382 Self::OptionsInstrumentMetadata => Some(vec![Group::Status, Group::General]),
383 Self::DerIndicesOHLCV => Some(vec![Group::Id, Group::OHLC, Group::OHLCMessage, Group::Message, Group::MappingAdvanced]),
384 Self::OCDEXOHLCV => Some(vec![Group::Id, Group::Mapping, Group::MappingAdvanced, Group::OHLC, Group::OHLCSwap, Group::Swap, Group::Volume]),
385 Self::OCCoreETHBlocks => Some(vec![Group::Id, Group::Metadata, Group::Transactions, Group::OrphanTraces, Group::Uncles, Group::Withdrawals]),
386 Self::OCCoreAssetByAddress => Some(vec![
387 Group::Id, Group::Basic, Group::SupportedPlatforms, Group::SecurityMetrics, Group::Supply, Group::SupplyAddresses,
388 Group::AssetTypeSpecificMetrics, Group::ResourceLinks, Group::Classification, Group::Price, Group::MktCap, Group::Volume,
389 Group::Change, Group::ToplistRank, Group::Description, Group::DescriptionSummary, Group::Contact, Group::SEO,
390 ]),
391 Self::AssetMetadataV2 => Some(vec![Group::Id, Group::Basic, Group::Supply, Group::SupplyAddresses, Group::Classification]),
392 Self::AssetCodeRepo => Some(vec![Group::Id, Group::General, Group::Activity, Group::Source]),
393 Self::AssetDiscord => Some(vec![Group::Id, Group::General, Group::Activity, Group::Source]),
394 Self::AssetReddit => Some(vec![Group::Id, Group::General, Group::Activity, Group::Source]),
395 Self::AssetTelegram => Some(vec![Group::Id, Group::General, Group::Source]),
396 Self::AssetTwitter => Some(vec![Group::Id, Group::General, Group::Activity, Group::Source]),
397 Self::OverviewMktCapOHLCV => Some(vec![Group::Id, Group::OHLC, Group::Volume]),
398 _ => None,
399 }
400 }
401
402 fn default_params(&self) -> Option<Vec<Param<'_>>> {
403 match self {
404 Self::FuturesInstrumentMetadata => Some(vec![Param::ApplyMapping { v: true, }]),
405 Self::OptionsInstrumentMetadata => Some(vec![Param::ApplyMapping { v: true, }]),
406 Self::OCCoreAssetsByChain => Some(vec![Param::AssetLookupPriority { v: AssetLookupPriority::Symbol, }]),
407 Self::OCCoreAssetByAddress => Some(vec![Param::AssetLookupPriority { v: AssetLookupPriority::Symbol, }]),
408 Self::AssetMetadataV2 => Some(vec![Param::AssetLookupPriority { v: AssetLookupPriority::Symbol, }, Param::QuoteAsset { v: "USD", }]),
409 _ => None,
410 }
411 }
412
413 fn url(&self, unit: &Unit) -> String {
414 let mut url: String = self.resolve_url();
415 self.add_unit_to_url(&mut url, unit);
416 url
417 }
418}
419
420
421#[cfg(test)]
422mod tests {
423
424 #[test]
425 fn unit_test_add_unit_to_url() -> () {
426 use crate::{Unit, APIEndpointTrait, APIEndpoint};
427 let unit: Unit = Unit::Hour;
428 let api_endpoint: APIEndpoint = APIEndpoint::IndicesOHLCV;
429 assert_eq!(api_endpoint.url(&unit), String::from("https://data-api.coindesk.com/index/cc/v1/historical/hours"));
430 }
431
432 #[test]
433 fn unit_test_custom_api_endpoint() -> () {
434 use crate::{Unit, APIEndpointTrait, call_api_endpoint};
435
436 enum CustomEndpoint { CustomSpotOHLCV, }
437
438 impl APIEndpointTrait for CustomEndpoint {
439 fn default_groups(&self) -> Option<Vec<crate::Group>> { None }
440
441 fn default_params(&self) -> Option<Vec<crate::Param<'_>>> { None }
442
443 fn url(&self, unit: &crate::Unit) -> String {
444 format!("https://data-api.coindesk.com/spot/v1/historical{}", unit.to_string())
445 }
446 }
447
448 assert_eq!(String::from("https://data-api.coindesk.com/spot/v1/historical/days"), CustomEndpoint::CustomSpotOHLCV.url(&Unit::Day))
449 }
450}