Search
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 Tasks and External adapters are and how Oracle Jobs use them. You will also learn how to find the Oracle Jobs and Tasks for your contract and how to request data from an Oracle Job.
Topics
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.
Chainlink nodes require Jobs to do anything useful. In the case of a Request and Receive job, the Direct Request job monitors the blockchain for a request from a smart contract. Once it catches a request, it runs the tasks (both core and external adapters) 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 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 Tasks:
method
must be set to GET.The job specs example can be found here.
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
...,
}
}
}
}
JSON Parse 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
ETH ABI Encode formats the input into an integer and then converts it into Solidity's uint256
format. Example: 0xc618a1e4
ETH Tx 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 tasks accept parameters to be passed to them to inform them how to run. Example: JSON Parse accepts a path
parameter which informs the task 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';
import '@chainlink/contracts/src/v0.8/ConfirmedOwner.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.
* THIS EXAMPLE USES UN-AUDITED CODE.
* DO NOT USE THIS CODE IN PRODUCTION.
*/
contract APIConsumer is ChainlinkClient, ConfirmedOwner {
using Chainlink for Chainlink.Request;
uint256 public volume;
bytes32 private jobId;
uint256 private fee;
event RequestVolume(bytes32 indexed requestId, uint256 volume);
/**
* @notice Initialize the link token and target oracle
*
* Goerli Testnet details:
* Link Token: 0x326C977E6efc84E512bB9C30f76E30c160eD06FB
* Oracle: 0xCC79157eb46F5624204f47AB42b3906cAA40eaB7 (Chainlink DevRel)
* jobId: ca98366cc7314957b8c012c72f05aeeb
*
*/
constructor() ConfirmedOwner(msg.sender) {
setChainlinkToken(0x326C977E6efc84E512bB9C30f76E30c160eD06FB);
setChainlinkOracle(0xCC79157eb46F5624204f47AB42b3906cAA40eaB7);
jobId = 'ca98366cc7314957b8c012c72f05aeeb';
fee = (1 * LINK_DIVISIBILITY) / 10; // 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 req = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
// Set the URL to perform the GET request on
req.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"); // Chainlink nodes prior to 1.0.0 support this format
req.add('path', 'RAW,ETH,USD,VOLUME24HOUR'); // Chainlink nodes 1.0.0 and later support this format
// Multiply the result by 1000000000000000000 to remove decimals
int256 timesAmount = 10**18;
req.addInt('times', timesAmount);
// Sends the request
return sendChainlinkRequest(req, fee);
}
/**
* Receive the response in the form of uint256
*/
function fulfill(bytes32 _requestId, uint256 _volume) public recordChainlinkFulfillment(_requestId) {
emit RequestVolume(_requestId, _volume);
volume = _volume;
}
/**
* Allow withdraw of Link tokens from the contract
*/
function withdrawLink() public onlyOwner {
LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress());
require(link.transfer(msg.sender, link.balanceOf(address(this))), 'Unable to transfer');
}
}
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 Tasks in the job to perform correctly. get
is used by HTTP, path
is used by JSON Parse 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 fee, which by default is 0.1 LINK. You can use this tutorial to learn how 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 node. You can follow the APIConsumer tutorial here.
Here are some examples nodes with external adapters:
If all the parameters are defined within the Oracle job, the only things a smart contract needs to define to consume are:
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 req = 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 sendChainlinkRequest(req, fee);
}
You can follow a full Existing Job Tutorial here.
More on External Adapters can be found here.
Chainlink has facilitated the launch of several new oracle data services that allow dApps to access rich data from external data sources. For instance, you can create a smart contract that checks Google's DNS service to determine if a given domain is owned by a given blockchain address using oracle job without having to specify the URL inside the contract.
Join the operator-requests discord channel to directly communicate with community node operators.
A full example on Goerli testnet can be found here.
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.