CoinGecko and Airtable: fetching many records at once

Hi

I’m using Airtable to store cryptocurrency price info.

Here is the Airtable: Airtable - Grid view

I need this workflow to to two things:

  • Update each currency with it’s latest price
  • Do so using only one CoinGecko call (because having lots of currencies exceeds CoinGeckos rate limits)

My issues are:

  1. I can’t figure out how to do the workflow without using functions, so it feels messy. It would be nice to use only the Set and Merge nodes. But I can’t figure out how to.
  2. I’m struggling to create the correct expected output to be able to update the table.

I suspect that if issue 1 can be solved, issue 2 won’t exist. But in case issue 1 isn’t possible to solve without javascript, can anyone help me with creating the correct format to input to the airtable update?

Workflow
{
  "nodes": [
    {
      "parameters": {},
      "name": "Start",
      "type": "n8n-nodes-base.start",
      "typeVersion": 1,
      "position": [
        250,
        300
      ]
    },
    {
      "parameters": {
        "operation": "list",
        "application": "appAlrtmJ6kF7vxwG",
        "table": "Currencies",
        "additionalOptions": {
          "fields": [
            "CoinGecko Id"
          ]
        }
      },
      "name": "Airtable",
      "type": "n8n-nodes-base.airtable",
      "typeVersion": 1,
      "position": [
        500,
        300
      ],
      "credentials": {
        "airtableApi": "Airtable - Neil Kirk"
      }
    },
    {
      "parameters": {
        "functionCode": "let cgids = [];\nfor (item of items) {\n  cgids.push(item.json.fields[\"CoinGecko Id\"]);\n}\nlet ids = cgids.join(',');\n\nreturn [{json:{\"ids\":ids}}];"
      },
      "name": "Function",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        490,
        590
      ]
    },
    {
      "parameters": {
        "mode": "wait"
      },
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 1,
      "position": [
        1070,
        330
      ]
    },
    {
      "parameters": {
        "keepOnlySet": true,
        "values": {
          "string": [
            {
              "name": "id",
              "value": "={{$json[\"id\"]}}"
            }
          ],
          "number": [
            {
              "name": "price",
              "value": "={{$json[\"current_price\"]}}"
            }
          ]
        },
        "options": {}
      },
      "name": "Set1",
      "type": "n8n-nodes-base.set",
      "typeVersion": 1,
      "position": [
        870,
        590
      ]
    },
    {
      "parameters": {
        "functionCode": "let coins = $items(\"Airtable\");\nlet cgprices = $items(\"Set1\");\n\nlet prices = {};\nfor (cgprice of cgprices) {\n  prices[cgprice.json[\"id\"]] = cgprice.json[\"price\"];\n}\n\nfor (coin of coins) {\n  coin.json.fields[\"Latest Price\"] = prices[coin.json.fields[\"CoinGecko Id\"]];\n  delete coin.json[\"createdTime\"];\n}\nconsole.log (coins);\n\nreturn [{json:coins}];"
      },
      "name": "Function1",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        1270,
        330
      ]
    },
    {
      "parameters": {
        "operation": "update",
        "application": "={{$node[\"Airtable\"].parameter[\"application\"]}}",
        "table": "={{$node[\"Airtable\"].parameter[\"table\"]}}",
        "id": "={{$json[\"0\"][\"json\"][\"id\"]}}",
        "options": {}
      },
      "name": "Airtable1",
      "type": "n8n-nodes-base.airtable",
      "typeVersion": 1,
      "position": [
        1470,
        330
      ],
      "credentials": {
        "airtableApi": "Airtable - Neil Kirk"
      }
    },
    {
      "parameters": {
        "operation": "market",
        "baseCurrency": "gbp",
        "returnAll": true,
        "options": {
          "ids": "={{$json[\"ids\"]}}"
        }
      },
      "name": "CoinGecko",
      "type": "n8n-nodes-base.coinGecko",
      "typeVersion": 1,
      "position": [
        690,
        590
      ]
    }
  ],
  "connections": {
    "Start": {
      "main": [
        [
          {
            "node": "Airtable",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Airtable": {
      "main": [
        [
          {
            "node": "Function",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Function": {
      "main": [
        [
          {
            "node": "CoinGecko",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge": {
      "main": [
        [
          {
            "node": "Function1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set1": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Function1": {
      "main": [
        [
          {
            "node": "Airtable1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "CoinGecko": {
      "main": [
        [
          {
            "node": "Set1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

I modified your workflow slightly. The function node for creating the CSV would still be necessary (but I’m going to save this as a case when we look to expand our no-code transform nodes, since I can see how this could be generally useful).
When updating the airtable row, we only need to send in “Price”:“XXX” for each item (also I believe I used a text input for price, so you may have to swap that).

It should be sufficient to change the Airtable creds + table ID, tried to keep row names etc. the same. I hope taking a look at my example helps show how you can implement similar WFs in future :slight_smile:

{
  "nodes": [
    {
      "parameters": {
        "operation": "list",
        "application": "appxhaX88hWXJT1GW",
        "table": "Currencies",
        "additionalOptions": {
          "fields": [
            "CoinGecko Id"
          ]
        }
      },
      "name": "Airtable",
      "type": "n8n-nodes-base.airtable",
      "typeVersion": 1,
      "position": [
        410,
        710
      ],
      "credentials": {
        "airtableApi": "cloud demo 2"
      }
    },
    {
      "parameters": {
        "functionCode": "let cgids = [];\nfor (item of items) {\n  cgids.push(item.json.fields[\"CoinGecko Id\"]);\n}\nlet ids = cgids.join(',');\n\nreturn [{json:{\"ids\":ids}}];"
      },
      "name": "Function",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        890,
        990
      ]
    },
    {
      "parameters": {
        "mode": "mergeByIndex"
      },
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 1,
      "position": [
        1474,
        730
      ]
    },
    {
      "parameters": {
        "keepOnlySet": true,
        "values": {
          "string": [
            {
              "name": "id",
              "value": "={{$json[\"id\"]}}"
            }
          ],
          "number": [
            {
              "name": "price",
              "value": "={{$json[\"current_price\"]}}"
            }
          ]
        },
        "options": {}
      },
      "name": "Set1",
      "type": "n8n-nodes-base.set",
      "typeVersion": 1,
      "position": [
        1274,
        990
      ]
    },
    {
      "parameters": {
        "operation": "update",
        "application": "appxhaX88hWXJT1GW",
        "table": "Currencies",
        "id": "={{$node[\"Airtable\"].json[\"id\"]}}",
        "options": {}
      },
      "name": "Airtable1",
      "type": "n8n-nodes-base.airtable",
      "typeVersion": 1,
      "position": [
        1870,
        730
      ],
      "credentials": {
        "airtableApi": "cloud demo 2"
      }
    },
    {
      "parameters": {
        "operation": "market",
        "baseCurrency": "gbp",
        "returnAll": true,
        "options": {
          "ids": "={{$json[\"ids\"]}}"
        }
      },
      "name": "CoinGecko",
      "type": "n8n-nodes-base.coinGecko",
      "typeVersion": 1,
      "position": [
        1090,
        990
      ]
    },
    {
      "parameters": {
        "keepOnlySet": true,
        "values": {
          "string": [
            {
              "name": "Price",
              "value": "={{$json[\"price\"].toString()}}"
            }
          ]
        },
        "options": {}
      },
      "name": "Set",
      "type": "n8n-nodes-base.set",
      "typeVersion": 1,
      "position": [
        1674,
        730
      ]
    }
  ],
  "connections": {
    "Airtable": {
      "main": [
        [
          {
            "node": "Function",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Function": {
      "main": [
        [
          {
            "node": "CoinGecko",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge": {
      "main": [
        [
          {
            "node": "Set",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set1": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "CoinGecko": {
      "main": [
        [
          {
            "node": "Set1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set": {
      "main": [
        [
          {
            "node": "Airtable1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
3 Likes

Thank you Max; that’s very helpful. I like how the ‘no code’ does run things “one at a time” but it would be helpful to be able to ‘group’ them somehow.

I thought I would mention though, that Merging by Index doesn’t necessarily match the prices to coins correctly. The CoinGecko API doesn’t return the results in the same order. It seemed to work OK with three, but I noticed lots of mismatches with more coins.

The answer is to merge by key.

Thank you again!

My pleasure! And thanks for letting me know, in this case “Merge by Key” is the more airtight approach.
As for grouping nodes, that’s a feature we envision for n8n in future (just got a few “bigger” things on the immediate roadmap first). Meanwhile though, you could look to the “Execute workflow” node, which runs another workflow and returns the output data to the parent flow. So useful for things you’re doing often across multiple workflows and would like a single source of truth.