How to send API calls in Solidity using ChainLink

In this tutorial, we are going to learn how to send API calls to any API from your smart contract in Solidity using the ChainLink Oracle.

In this tutorial, we are going to learn how to send API calls to any API from your smart contract in Solidity using the ChainLink Oracle.

Calling your own API (testnet and mainnet)

Using the Chainlink oracle, you can send HTTP requests to any API you want. For that, you need an oracle node on the Chainlink network to send the API calls and send you back the response.

To get that node, you can use a provider like this one (which I recommend): https://block-farms.io/ or this one https://market.link/overview or others.

You can also ask someone in the Chainlink operator-requests discord channel to provide you with a node.

You can also set up a node yourself but it's a bit complicated. More information about it here:

Chainlink Nodes | Chainlink Documentation
Chainlink is the most widely used oracle network for powering universally connected smart contracts, enabling any blockchain to access real-world data & APIs.

So on the mainnet, you'll need to use one of the solutions above to get a job ID and an oracle address.

Depending on the type of data you get back from the API, the job ID is going to be different. There is no JSON in Solidity so you need to define what property exactly you want to get from the returned data and the path to that variable.

Depending on the type of the value that you'll access, the job ID will be different.

Also note that the oracle address and the job IDs will be different depending on the network you are interacting with. Chainlink supports the Ethereum mainnet and its testnets but also other EVM chains like Polygon or the Binance Smart Chain. On the testnet, you can find the job ID and the oracle address here.

Once you have that job ID and oracle address, we can start coding our contract.

Here is an example in which I call an example API:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol";

contract APIConsumer is ChainlinkClient {
    using Chainlink for Chainlink.Request;

    uint256 public result;
    bytes32 private jobId;
    uint256 private fee;

    event ResultReceived(bytes32 indexed requestId, uint256 result);

    constructor() {
        // pass the address of the LINK token in the network you use
        setChainlinkToken(0x514910771AF9Ca656af840dff83E8264EcF986CA);
        
        // pass the address of the oracle contract
        setChainlinkOracle(0x7AE841fCD06a794537912749C5449b3290de780f);
        
        // pass the Job ID of the oracle
        jobId = "a052732022e04e89be3e0fc06e457985";
        
        // pass in the fee for a single request (1 LINK in this case)
        fee = 1.5 * 10**18;
    }

    function requestResult() public returns (bytes32 requestId) {
        Chainlink.Request memory req = buildChainlinkRequest(
            jobId,
            address(this),
            this.fulfillRequest.selector
        );

        // Set the URL to send the GET request to
        req.add(
            "get",
            "https://your-api.com/api/endpoint/"
        );

        // Set the path to the value you want to get in the response of the API
        req.add("path", "path,to,result");

        // Send the request
        return sendChainlinkRequest(req, fee);
    }

    // Executed when the API sends a response
    function fulfillRequest(
        bytes32 _requestId,
        uint256 _result
    ) public recordChainlinkFulfillment(_requestId) {
        result = _result;
        emit ResultReceived(_requestId, _result);
    }
}

First, we need to install the Chainlink contracts using the following command:

// using NPM:
npm install @chainlink/contracts

// or using Yarn:
yarn add @chainlink/contracts

Next we import the ChainlinkClient contract from the Chainlink contracts:

import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol";

And then make our smart contract inherit from it:

contract MyContract is ChainlinkClient {
    // ...
}

After that, in the contract, we need to say that we want to use the Chainlink library and add it to the Chainlink.Request library so we have access to everything we need to send the requests.

To do that, we add this line in our contract:

using Chainlink for Chainlink.Request;

Then, in the constructor, we need to

  • call the setChainlinkToken function and pass the LINK token address in the parameters. You can find the address of the LINK token on your network here.
  • call the setChainlinkOracle function and pass the address of the oracle you are want to interact with.

Next, we create the function that will send the HTTP request. For that, we need the job ID of our oracle and its address.

As said earlier, there is 2 options to find the address of the Oracle and the job ID:

In the function that will send the API call, first we build the request using the buildChainlinkRequest function which takes in parameter:

  1. The job ID
  2. The address of your smart contract, you can just pass: address(this)
  3. The function in your contract that should be executed when a response is received. For example, if I want to pass a function called myFunction, I need to pass this: this.myFunction.selector

The value returned by that function is of type Chainlink.Request. That returned value is what we use to change the parameters of the request.

To add a parameter to the request, we use the add method of the value returned by the buildChainlinkRequest function.

That function takes 2 parameters, first the name of the parameter to add and second, the value of that parameter:

req.add("param", "value");

Refer to the documentation of your API to see what parameters you want or need to pass.

The first parameter you need to add is the URL that you want to send a request to. To send a GET request, the parameter to add is "get":

req.add("get", "https://your-url.com/");

If the API doesn't return a single value, we need to define the path to get the value that we want in the data that the API sends back.

To do that, we also use the add method and the name of the parameter is "path" and the value is the name of the properties we need to access in the data returned by the API. The name of the properties should be separated by commas.

For example, if the API returns this:

{
    "data": {
        "user": {
          "email": "example@example.com",
          "name": "0xDev"
        }
    }
}

And you want to get the email property, then you need to do:

req.add("path", "data,user,email");

For some APIs, you can set operations to make on the value you want to get. Again, if you need to do it to receive the data in the right format, it will be explained in the documentation.

Here is an example:

Single Word Response | Chainlink Documentation
Chainlink is the most widely used oracle network for powering universally connected smart contracts, enabling any blockchain to access real-world data & APIs.

Finally we can send the request using the sendChainlinkRequest function. It takes in parameter the request you build and the fee.

The fee is the amount of LINK tokens that it costs to send the request. That amount is fixed no matter what transaction you send but it depends on the oracle you use and the network.

On the testnets, you can find the fee you need to pass in the documentation here:

Testnet Oracles | Chainlink Documentation
Chainlink is the most widely used oracle network for powering universally connected smart contracts, enabling any blockchain to access real-world data & APIs.

And on the mainnet, it depends on the node operator you use. It will be shown along with the job ID and oracle address. For example, if you use Block-farms, the fee is 1.5 LINK per request (at the time of writing this article).

Lastly, we define a function that will be executed when the contract receives a response. That's the function we pass to the buildChainlinkRequest function.

That function takes in parameter the id of the request that was sent (a unique identifier for your request) and the result of the API call.

Now, you need to slightly change your code if you want to read multiple values from the data returned, if you want to read arrays or receive large data.

Calling the existing APIs on the mainnet

Chainlink has a set of pre-configured APIs that you can send requests to. You can find that list of APIs here:

Data Provider Nodes | Chainlink Documentation
Chainlink is the most widely used oracle network for powering universally connected smart contracts, enabling any blockchain to access real-world data & APIs.

It works the same way as I explained above but with minor differences.

Here is an example in which I use the Google Weather API to fetch the average temperature of the day in New York, USA:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol";

contract NewYorkWeather is ChainlinkClient {
    using Chainlink for Chainlink.Request;

    bytes32 public jobId = "0606a7c2811e4dbab659be400ecd41f9";
    uint256 public temperature;
    uint256 public fee;

    event FetchTemperatureResult(bytes32 indexed _requestId, uint256 _temperature);

    constructor() {
        // pass in the LINK token address
        setChainlinkToken(0x514910771AF9Ca656af840dff83E8264EcF986CA);
        
        // pass in the Chainlink Oracle address
        setChainlinkOracle(0x92c08A635C7525505123F0F8e327C6Fa66E09a18);
        
        // pass in the fee for a single request (1 LINK in this case)
        fee = 1 * 10**18;
    }

    function requestTemperature(string memory _dateFrom, string memory _dateTo) external {
        // build the request object
        Chainlink.Request memory req = buildChainlinkRequest(
            jobId,
            address(this),
            this.fulfillAverageTemperature.selector
        );
        
        // add the parameters of the request
        // first, add a parameter to containing the location to get the weather for
        req.add("geoJson", '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[40.758896,-73.98513]}}]}');
        req.add("dateFrom", _dateFrom);
        req.add("dateTo", _dateTo);
        req.add("method", "AVG");
        req.add("column", "temp");
        req.add("units", "metric");
        
        // send the request
        sendChainlinkRequest(req, fee);
    }

    // executed when the request receives a response
    function fulfillAverageTemperature(
        bytes32 _requestId,
        uint256 _result
    ) external recordChainlinkFulfillment(_requestId) {
        temperature = _result;
        emit FetchTemperatureResult(_requestId, _result);
    }
}

The first difference is that the Job ID, the Oracle address and the fee are provided in the Chainlink documentation for that API that you can find in the link above.

The second difference is that you don't need to pass the URL that you want to send the request to. It will be derived from the job ID.

In most cases, like in the example above, you also don't need to specify the path to the returned value you want to get because these APIs return a single value.

The reason why they return a single value is because they have pre-defined tasks that each have a job ID. Depending on the value you want, you pass in a different job ID.

However, you can still pass parameters to the API to request data differently like we've done in the example above. These parameters are defined in the documentation of the API in the Chainlink documentation.

Again the fee depends on the API you use. Check out the documentation to see what that fee is. In our case, for the Google Weather API, it costs 1 LINK per request.

And that's it 🎉

Thank you for reading this article