├── bitcoin-address.txt ├── btc_qrcode.png ├── LICENSE.md ├── scriptCoinGecko.gs ├── Readme.md └── scriptCoinPaprika.gs /bitcoin-address.txt: -------------------------------------------------------------------------------- 1 | bitcoin:bc1pgud5lk850jrk7ty3kyzazntwdnnl6xrnm2wm5trdz7myfkhccglskqmgdk -------------------------------------------------------------------------------- /btc_qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pedrojok01/Import-CryptoData-into-GoogleSheet/HEAD/btc_qrcode.png -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) <2021-2023> Pedrojok01 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /scriptCoinGecko.gs: -------------------------------------------------------------------------------- 1 | function onOpen() { 2 | SpreadsheetApp.getUi() 3 | .createMenu("CoinGecko") 4 | .addItem("Update", "CRYPTODATAJSON") 5 | .addToUi(); 6 | } 7 | 8 | function CRYPTODATA(symbol, colName) { 9 | const sheetData = SpreadsheetApp.getActiveSpreadsheet() 10 | .getSheetByName("data") 11 | .getDataRange() 12 | .getValues(); 13 | const col = sheetData[0].indexOf(colName); 14 | 15 | if (col === -1) return "datatype not found"; 16 | 17 | const row = sheetData.findIndex((row, i) => i > 0 && row[2] === symbol); 18 | return row === -1 ? "symbol not found" : sheetData[row][col]; 19 | } 20 | 21 | function CRYPTODATAJSON() { 22 | const ui = SpreadsheetApp.getUi(); 23 | const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("data"); 24 | 25 | const urls = [ 26 | "https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=250&page=1&sparkline=false", 27 | "https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=250&page=2&sparkline=false", 28 | "https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=250&page=3&sparkline=false", 29 | ]; 30 | 31 | const fetchOptions = { muteHttpExceptions: true }; 32 | const responses = UrlFetchApp.fetchAll( 33 | urls.map((url) => ({ url, ...fetchOptions })) 34 | ); 35 | 36 | if (responses.some((res) => res.getResponseCode() === 429)) { 37 | ui.alert("too many requests"); 38 | return; 39 | } 40 | 41 | if (responses.some((res) => res.getResponseCode() !== 200)) { 42 | ui.alert("server error : http " + res.getResponseCode()); 43 | return; 44 | } 45 | 46 | const dataSet = responses.flatMap((res) => JSON.parse(res.getContentText())); 47 | 48 | const header = [ 49 | "id", 50 | "name", 51 | "symbol", 52 | "market_cap_rank", 53 | "current_price", 54 | "market_cap", 55 | "circulating_supply", 56 | "total_supply", 57 | "max_supply", 58 | "total_volume", 59 | "high_24h", 60 | "low_24h", 61 | "price_change_24h", 62 | "price_change_percentage_24h", 63 | "market_cap_change_24h", 64 | "market_cap_change_percentage_24h", 65 | "usd_percent_change_1y", 66 | "ath", 67 | "ath_change_percentage", 68 | "ath_date", 69 | "atl", 70 | "atl_change_percentage", 71 | "atl_date", 72 | "last_updated", 73 | ]; 74 | 75 | const rows = [ 76 | header, 77 | ...dataSet.map((data) => header.map((col) => data[col])), 78 | ]; 79 | const dataRange = sheet.getRange(1, 1, rows.length, rows[0].length); 80 | dataRange.setValues(rows); 81 | } 82 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Import CryptoData into GoogleSheet

4 | 5 | [![Contributors](https://img.shields.io/github/contributors/Pedrojok01/Import-CryptoData-into-GoogleSheet)](https://github.com/Pedrojok01/Import-CryptoData-into-GoogleSheet/graphs/contributors) 6 | [![Forks](https://img.shields.io/github/forks/Pedrojok01/Import-CryptoData-into-GoogleSheet)](https://github.com/Pedrojok01/Import-CryptoData-into-GoogleSheet/network/members) 7 | [![Stargazers](https://img.shields.io/github/stars/Pedrojok01/Import-CryptoData-into-GoogleSheet)](https://github.com/Pedrojok01/Import-CryptoData-into-GoogleSheet/stargazers) 8 | [![Issues](https://img.shields.io/github/issues/Pedrojok01/Import-CryptoData-into-GoogleSheet)](https://github.com/Pedrojok01/Import-CryptoData-into-GoogleSheet/issues) 9 | [![MIT License](https://img.shields.io/github/license/Pedrojok01/Import-CryptoData-into-GoogleSheet)](https://github.com/Pedrojok01/Import-CryptoData-into-GoogleSheet/blob/main/LICENSE.md) 10 | [![LinkedIn](https://img.shields.io/badge/-LinkedIn-blue)](https://www.linkedin.com/in/pierre-e/) 11 | 12 |
13 | 14 |

15 | 16 | ## Looking for an easy way to import crypto data into your google sheet? 17 | 18 |

Fetch cryptodata direclty into your Google sheets from either Coingecko or Coinpaprika APIs for free. Enjoy!

19 | 20 |
21 | 22 | ## Table of contents 23 | 24 | - [General info](#general-info) 25 | - [Which one do I need?](#Which-one-do-I-need?) 26 | - [Setup](#setup) 27 | - [Additional explanations](#Additional-explanations) 28 | - [Update data](#Update-data) 29 | 30 |
31 | 32 | ## Update March 2023: 33 | 34 | - Both script have been cleaned, refactored and optimized. The performance should be better now. 35 | - I highly recommend using the coinpaprika script, as the free CoinGecko API is too crowded and is getting hard to refresh, unless you only need some of the big coins. 36 | 37 |
38 | 39 | # General info? 40 | 41 | In order to go soft on those 2 great and free API, the script fetch all data at once in a dedicated tab in your spreadsheet.
42 | You can then simply import any data you need into your coinfolio/project by using this function: `CRYPTODATA("symbol"; "data")`. 43 | 44 |
45 | 46 | # Which one do I need? 47 | 48 | The CoinGecko API import the first 750 coins by marketcap. However, prices are only quoted in USD (all coins within the first 750, but less data).
49 | The CoinPaprika APi import all coins listed on coinpaprika.com, and prices are quoted in USD, BTC, or ETH. However, I found out that many coins were missing from their great API (more data, but some coins missing). 50 | 51 | > Notes:
52 | > 1/ As of now, you can only use one script at a time. So depending on what you need, you can either pick the [Coingecko.gs](https://github.com/Pedrojok01/Import-CryptoData-into-GoogleSheet/blob/main/scriptCoinGecko.gs) or [Coinpaprika.gs](https://github.com/Pedrojok01/Import-CryptoData-into-GoogleSheet/blob/main/scriptCoinPaprika.gs).
53 | > 2/ The performance isn't great and it takes a little time to refresh, but it's free to use and it goes soft on both API (which hopefully will stay free a bit longer). 54 | 55 |
56 | 57 | # Setup 58 | 59 | - Open your crypto spreadsheet, and create an additional empty tab named `data`; 60 | - Go to the menu `Add-ons` > `Script Apps`; 61 | - Paste the content of the file `scriptCoinGecko.gs` or `scriptCoinPaprika.gs` into the editor, then save the file; 62 | - Go back to your spreadsheet and refresh the page; 63 | - A new menu item called `CoinGecko` or `CoinPaprika` depending on the one you chose should appear. Click on it, then click on `Update`; 64 | - All the data will be imported into your `data` tab. From now on, you can import any data with this simple: `=CRYPTODATA("coin_sympol", "data_needed")` 65 | 66 | ❗ Do not use the data sheet, it's used by the script ❗ 67 | 68 |
69 | 70 | # Additional explanations 71 | 72 | ### For the coingecko script: 73 | 74 | `=CRYPTODATA("btc", "current_price")` => Will get btc price quoted in usd;
75 | Feel free to explore the data tab to find the data you need. Just copy the column title and paste it in the formula above. 76 | 77 | ### For the coinpaprika script: 78 | 79 | #### Get data for a specific coin : 80 | 81 | `=CRYPTODATA("ETH"; "btc_price")` => Will get eth price quoted in btc; 82 | 83 | #### Get historical data for a specific coin : 84 | 85 | `=CRYPTODATAHISTORY(""; ""; ""; "")` with params being : 86 | 87 | - coin : a single coin ticket like "ETH" or "BTC" or "XMR" 88 | - date : something like "2018-02-20" 89 | - type of data : can be "price", "volume_24h" or "market_cap" 90 | - quote : optional, defaults to usd, but can be set to "usd" or "btc" 91 | 92 | #### Get global data : 93 | 94 | `=CRYPTODATAGLOBAL("bitcoin_dominance_percentage")` 95 | 96 |
97 | 98 | # Update data 99 | 100 | - Click on the menu item, then on Update to refresh the data;
101 | - Then, you need to refresh your spreadsheet. One simple way is to add a new line at the top of your sheet, and to delete it right away. This small change will refresh all data automatically. 102 | 103 | Any issues, improvements, forks are welcomed. It is certainly not optimized, but it's working :) 104 | 105 |
106 | 107 |
108 |

If you like it, a donation is always welcome!

109 | 110 | [![btc_qrcode](./btc_qrcode.png)](https://raw.githubusercontent.com/Pedrojok01/Import-CryptoData-into-GoogleSheet/main/bitcoin-address.txt) 111 | 112 | ``` 113 | BTC: bc1pgud5lk850jrk7ty3kyzazntwdnnl6xrnm2wm5trdz7myfkhccglskqmgdk 114 | ``` 115 | 116 |

117 | -------------------------------------------------------------------------------- /scriptCoinPaprika.gs: -------------------------------------------------------------------------------- 1 | function onOpen() { 2 | SpreadsheetApp.getUi() 3 | .createMenu("CoinPaprika") 4 | .addItem("Update", "CRYPTODATAJSON") 5 | .addToUi(); 6 | } 7 | 8 | function CRYPTODATA(symbol, colName) { 9 | var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("data"); 10 | var data = sheet.getDataRange().getValues(); 11 | var colIndex = data[0].indexOf(colName); 12 | 13 | if (colIndex === -1) { 14 | return "datatype not found"; 15 | } 16 | 17 | for (var i = 1; i < data.length; i++) { 18 | if (data[i][2] === symbol) { 19 | return data[i][colIndex]; 20 | } 21 | } 22 | 23 | return "symbol not found"; 24 | } 25 | 26 | function CRYPTODATAHISTORY(symbol, date, type, quote) { 27 | quote = quote || "usd"; 28 | 29 | const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("data"); 30 | const data = sheet.getDataRange().getValues(); 31 | const colIndex = data[0].indexOf("id"); 32 | 33 | if (colIndex === -1) { 34 | return "datatype not found"; 35 | } 36 | 37 | for (let i = 1; i < data.length; i++) { 38 | if (data[i][2] === symbol) { 39 | const coinId = data[i][colIndex]; 40 | const formattedDate = date instanceof Date ? date.toISOString() : date; 41 | const url = `https://api.coinpaprika.com/v1/tickers/${coinId}/historical?start=${formattedDate}&interval=5m&limit=1"e=${quote}`; 42 | 43 | const responseData = fetchUrl(url); 44 | return responseData[0].hasOwnProperty(type) 45 | ? responseData[0][type] 46 | : "price not found"; 47 | } 48 | } 49 | 50 | return "symbol not found"; 51 | } 52 | 53 | function CRYPTODATACOINDETAILS(coin_id, colName) { 54 | const param = encodeURI(coin_id); 55 | 56 | if (colName.indexOf("/") !== -1) { 57 | const [quote, property] = colName.split("/"); 58 | const url = 59 | "https://api.coinpaprika.com/v1/tickers/" + param + "?quotes=" + quote; 60 | const responseData = fetchUrl(url); 61 | 62 | if ( 63 | responseData.hasOwnProperty("error") && 64 | responseData["error"] === "id not found" 65 | ) { 66 | return "coin_id not found"; 67 | } 68 | 69 | if (responseData["quotes"][quote].hasOwnProperty(property)) { 70 | return responseData["quotes"][quote][property]; 71 | } else { 72 | return "property not found"; 73 | } 74 | } else { 75 | const url = "https://api.coinpaprika.com/v1/coins/" + param; 76 | const responseData = fetchUrl(url); 77 | 78 | if ( 79 | responseData.hasOwnProperty("error") && 80 | responseData["error"] === "id not found" 81 | ) { 82 | return "coin_id not found"; 83 | } 84 | 85 | if (responseData.hasOwnProperty(colName)) { 86 | return responseData[colName]; 87 | } else { 88 | return "property not found"; 89 | } 90 | } 91 | } 92 | 93 | function CRYPTODATAJSON() { 94 | const ui = SpreadsheetApp.getUi(); 95 | const ss = SpreadsheetApp.getActiveSpreadsheet(); 96 | const sheet = ss.getSheetByName("data"); 97 | 98 | const url = "https://api.coinpaprika.com/v1/tickers?quotes=USD,BTC"; 99 | const dataAll = fetchUrl(url); 100 | const dataSet = dataAll.sort((a, b) => (a.rank > b.rank ? 1 : -1)); 101 | 102 | const rows = [headers]; 103 | 104 | dataSet.forEach((data) => { 105 | const rowData = headers.map((header) => { 106 | if (header.startsWith("btc_")) { 107 | const key = header.slice(4); 108 | return data.quotes["BTC"][key] || ""; 109 | } 110 | if (header.startsWith("usd_")) { 111 | const key = header.slice(4); 112 | return data.quotes["USD"][key] || ""; 113 | } 114 | return data[header] || ""; 115 | }); 116 | rows.push(rowData); 117 | }); 118 | 119 | const dataRange = sheet.getRange(1, 1, rows.length, rows[0].length); 120 | dataRange.setValues(rows); 121 | } 122 | 123 | /* UTILS: 124 | ***********/ 125 | 126 | function fetchUrl(url) { 127 | const response = UrlFetchApp.fetch(url, { muteHttpExceptions: true }); 128 | RESPONSECODE(response); 129 | return JSON.parse(response.getContentText()); 130 | } 131 | 132 | function RESPONSECODE(v) { 133 | var responseCode = v.getResponseCode(); 134 | 135 | if (responseCode === 429) { 136 | throw new Error("Too many requests"); 137 | } else if (responseCode !== 200) { 138 | throw new Error("Server error"); 139 | } 140 | } 141 | 142 | /* CRYPTODATAJSON() - DATA HEADERS 143 | **********************************/ 144 | 145 | const headers = [ 146 | "id", 147 | "name", 148 | "symbol", 149 | "rank", 150 | "circulating_supply", 151 | "total_supply", 152 | "max_supply", 153 | "beta_value", 154 | "first_data_at", 155 | "last_updated", 156 | "btc_price", 157 | "btc_volume_24h", 158 | "btc_volume_24h_change_24h", 159 | "btc_market_cap", 160 | "btc_market_cap_change_24h", 161 | "btc_percent_change_15m", 162 | "btc_percent_change_30m", 163 | "btc_percent_change_1h", 164 | "btc_percent_change_6h", 165 | "btc_percent_change_12h", 166 | "btc_percent_change_24h", 167 | "btc_percent_change_7d", 168 | "btc_percent_change_30d", 169 | "btc_percent_change_1y", 170 | "btc_ath_price", 171 | "btc_ath_date", 172 | "btc_percent_from_price_ath", 173 | "usd_price", 174 | "usd_volume_24h", 175 | "usd_volume_24h_change_24h", 176 | "usd_market_cap", 177 | "usd_market_cap_change_24h", 178 | "usd_percent_change_15m", 179 | "usd_percent_change_30m", 180 | "usd_percent_change_1h", 181 | "usd_percent_change_6h", 182 | "usd_percent_change_12h", 183 | "usd_percent_change_24h", 184 | "usd_percent_change_7d", 185 | "usd_percent_change_30d", 186 | "usd_percent_change_1y", 187 | "usd_ath_price", 188 | "usd_ath_date", 189 | "usd_percent_from_price_ath", 190 | ]; 191 | --------------------------------------------------------------------------------