Requirements
This guide requires basic knowledge about smart contracts. If you are new to smart contract development, read the Consuming Data Feeds and Random Numbers guides before you begin.
In this guide, you will learn how to request data from a public API in a smart contract. This includes understanding what Core adapters and External adapters are and how Oracle Jobs use them. You will also learn how to find the Oracle Jobs and Adapters for your contract and how to request data from an Oracle Job.
Table of Contents
The request and receive cycle describes how a smart contract requests data from an oracle and receives the response in a separate transaction. If you need a refresher, check out the Basic Request Model.
For contracts that use Chainlink VRF, you request randomness from a VRF oracle and then await the response. The fulfillment function is already given to us from the VRFConsumerBase contract, so oracles already know where to send the response to. However, with API calls, the contract itself defines which function it wants to receive the response to.
Before creating any code, you should understand how Oracle jobs can get data on-chain.
Initiators are what start, or initiate, a job inside an Oracle. In the case of a Request and Receive job, the RunLog initiator monitors the blockchain for a request from a smart contract. Once it catches a request, it initiates the job. This runs the adapters (both core and external) that the job is configured to run and eventually returns the response to the requesting contract.
Each oracle job has a configured set of tasks it must complete when it is run. These tasks are defined by the Adapters they support. Adapters are split into two subcategories:
If a job needs to make a GET request to an API, find a specific unsigned integer field in a JSON response, then submit that back to the requesting contract, it would need a job containing the following Core Adapters:
Let's walk through a real example, where you will retrieve 24 volumes of the ETH/USD pair from the cryptocompare API.
{"RAW":
{"ETH":
{"USD":
{
...,
"VOLUMEDAYTO":953806939.7194247,
"VOLUME24HOUR":703946.0675653099,
"VOLUME24HOURTO":1265826345.488568
...,
}
}
}
}
JsonParse walks a specified path ("RAW.ETH.USD.VOLUME24HOUR") and returns the value found at that result. Example: 703946.0675653099
Multiply parses the input into a float and multiplies it by the 10^18. Example: 703946067565309900000000
EthUint256 formats the input into an integer and then converts it into Solidity's uint256 format. Example: 0xc618a1e4
EthTx takes the given input, places it into the data field of the transaction, signs a transaction, and broadcasts it to the network. Example: transaction result
Note: Some core adapters accept parameters to be passed to them to inform them how to run. Example: JsonParse accepts a path parameter which informs the adapter where to find the data in the JSON object.
Let's see what this looks like in a contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol";
/**
* Request testnet LINK and ETH here: https://faucets.chain.link/
* Find information on LINK Token Contracts and get the latest ETH and LINK faucets here: https://docs.chain.link/docs/link-token-contracts/
*/
/**
* THIS IS AN EXAMPLE CONTRACT WHICH USES HARDCODED VALUES FOR CLARITY.
* PLEASE DO NOT USE THIS CODE IN PRODUCTION.
*/
contract APIConsumer is ChainlinkClient {
using Chainlink for Chainlink.Request;
uint256 public volume;
address private oracle;
bytes32 private jobId;
uint256 private fee;
/**
* Network: Kovan
* Oracle: 0xc57B33452b4F7BB189bB5AfaE9cc4aBa1f7a4FD8 (Chainlink Devrel
* Node)
* Job ID: d5270d1c311941d0b08bead21fea7747
* Fee: 0.1 LINK
*/
constructor() {
setPublicChainlinkToken();
oracle = 0xc57B33452b4F7BB189bB5AfaE9cc4aBa1f7a4FD8;
jobId = "d5270d1c311941d0b08bead21fea7747";
fee = 0.1 * 10 ** 18; // (Varies by network and job)
}
/**
* Create a Chainlink request to retrieve API response, find the target
* data, then multiply by 1000000000000000000 (to remove decimal places from data).
*/
function requestVolumeData() public returns (bytes32 requestId)
{
Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
// Set the URL to perform the GET request on
request.add("get", "https://min-api.cryptocompare.com/data/pricemultifull?fsyms=ETH&tsyms=USD");
// Set the path to find the desired data in the API response, where the response format is:
// {"RAW":
// {"ETH":
// {"USD":
// {
// "VOLUME24HOUR": xxx.xxx,
// }
// }
// }
// }
request.add("path", "RAW.ETH.USD.VOLUME24HOUR");
// Multiply the result by 1000000000000000000 to remove decimals
int timesAmount = 10**18;
request.addInt("times", timesAmount);
// Sends the request
return sendChainlinkRequestTo(oracle, request, fee);
}
/**
* Receive the response in the form of uint256
*/
function fulfill(bytes32 _requestId, uint256 _volume) public recordChainlinkFulfillment(_requestId)
{
volume = _volume;
}
// function withdrawLink() external {} - Implement a withdraw function to avoid locking your LINK in the contract
}
Here is a breakdown of each component of this contract:
requestVolumeData functions: This builds and sends a request - which includes the fulfillment functions selector - to the oracle. Notice how it adds the get, path and times parameters. These are read by the Adapters in the job to perform the tasks correctly. get is used by HttpGet, path is used by JsonParse and times is used by Multiply.fulfill function: This is where the result is sent upon the Oracle Job's completion.Note: The calling contract should own enough LINK to pay the specified fee (by default 0.1 LINK). You can use this tutorial to fund your contract.
This is an example of a basic HTTP GET request. However, it requires defining the API URL directly in the smart contract. This can, in fact, be extracted and configured on the Job level inside the Oracle.
Here are some examples of external adapters:
These external adapters, along with many others, can be found on Chainlink Market.
If all the parameters are defined within the Oracle job, the only thing a smart contract needs to define to consume it is:
This will make your smart contract much more succinct. The requestVolumeData function from the code example above would look more like this:
function requestVolumeData() public returns (bytes32 requestId) {
Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
// Extra parameters don't need to be defined here because they are already defined in the job
return sendChainlinkRequestTo(oracle, request, fee);
}
Create a smart contract that can get sports data using the SportsDataIO oracle job found on Chainlink Market, without having to specify the URL inside the contract.
To consume an API response, your contract should inherit from ChainlinkClient. This contract exposes a struct called Chainlink.Request that your contract should use to build the API request.
pragma solidity ^0.8.7;
import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol";
contract sportContract is ChainlinkClient {
using Chainlink for Chainlink.Request;
}
The request should include the oracle address, the job id, the fee, adapter parameters, and the callback function signature. Create variables for these items using the correct data types.
pragma solidity ^0.8.7;
import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol";
contract sportContract is ChainlinkClient {
using Chainlink for Chainlink.Request;
address private oracle;
bytes32 private jobId;
uint256 private fee;
}
In the constructor, set up the contract with the Oracle address, Job ID, and LINK fee that the oracle charges for the job.
pragma solidity ^0.8.7;
import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol";
contract sportContract is ChainlinkClient {
using Chainlink for Chainlink.Request;
// ...
// { previously created variables }
// ...
constructor() {
setPublicChainlinkToken();
oracle = 0xfF07C97631Ff3bAb5e5e5660Cdf47AdEd8D4d4Fd;
jobId = "9abb342e5a1d41c6b72941a3064cf55f";
fee = 0.1 * 10 ** 18; // (Varies by network and job)
}
}
requestData FunctionThe SportsDataIO job page specifies the request parameters to be date and teamName. To account for this, your requestData function should have both of these items as parameters. Please refer to the job page to understand the specific input format for these items.
pragma solidity ^0.8.7;
import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol";
contract sportContract is ChainlinkClient {
using Chainlink for Chainlink.Request;
// ...
// { previously created variables }
// ...
// ...
// { constructor }
// ...
// Initial Request
function requestData(string memory _date, string memory _team) public returns (bytes32 requestId) {
Chainlink.Request memory req = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
req.add("date", _date);
req.add("teamName", _team);
return sendChainlinkRequestTo(oracle, req, fee);
}
}
The last component of your contract should be the fulfill function. This is where the sports data is sent upon the Oracle Job's completion. The SportsDataIO job page specifies that the request returns a bytes32 packed string. You should add a data variable to your contract to store this result.
Note: Currently, any return value must fit within 32 bytes. If the value is bigger than that, you must make multiple requests.
pragma solidity ^0.8.7;
import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol";
contract sportContract is ChainlinkClient {
using Chainlink for Chainlink.Request;
bytes32 public data;
// ...
// { previously created variables }
// ...
// ...
// { constructor }
// ...
// ...
// { requestData function }
// ...
// Callback Function
function fulfill(bytes32 _requestId, bytes32 _data) public recordChainlinkFulfillment(_requestId) {
data = _data;
}
}
Your contract is complete and ready to be compiled and deployed. You can see a complete version of the contract in Remix:
If you don't know how to deploy a contract to the Kovan testnet using Remix, follow getting started guide for Deploying Your First Smart Contract. To make a job request, you must have enough LINK to pay for it. Learn how to acquire testnet LINK and fund your contract. Once these steps are completed, you should be able to get sports data.
To learn more about connecting smart contracts to external APIs, read our blog posts:
To explore more applications of external API requests, check out our other tutorials.