Introduction
Chainalysis Know Your Transaction (KYT) is an automated cryptocurrency transaction monitoring and compliance solution. At the core of KYT is a REST-based API that provides you with transaction risk monitoring, alerts on risky transfers, and comprehensive user risk profiles, among other features. The API allows your internal business systems and Chainalysis's technology to communicate with each other.
KYT API endpoint versions do not affect compatibility. You can use the v1 and v2 endpoints together.
About the KYT API
KYT’s API is a REST-based web service that:
- Has uniform resource endpoints (
alerts
,/transfers/sent
). - Accepts a request body (your data).
- Uses standard HTTP response codes (200, 404, 503).
- Returns responses in JSON.
After integrating your data into KYT, the API provides you a more comprehensive picture of risky activity. It returns additional information on transfers, alerts, and user risk that the KYT UI doesn't provide alone.
Sorting and filtering are available on many endpoints to help facilitate workflows. For example, you can query for all alerts created between certain dates, see exposure details on a specific user, or pre-screen for counterparty risk within your withdrawal workflow.
All times are in UTC.
What's new
See below for KYT API releases, updates, and changes.
May 2022
Tron (TRX) and Tether on Tron (USDT) are now mature assets
We've recently graduated Tron’s native token (TRX) and Tether on Tron (TRC-20 USDT) to the mature tier. For Reactor, this graduation will allow you to conduct investigations of TRX and USDT on the Tron network. For KYT, you will have deeper coverage of services on Tron through attribution of lower risk entities and advanced clustering, as well as receive indirect exposure alerts for transfers of TRX and USDT on Tron.
Note that all other TRC-20 metatokens are supported in our emerging tier.
To learn more about the functionality we provide at each asset tier, see Asset coverage. For a list of the networks we support, see Supported networks and assets.
February 15, 2022
On March 10, 2022, we will be increasing our asset coverage extensively with the Emerging asset tier. To complement this change, we've added a new request body property (network
) to identify the blockchain network a transfer occurs on. For all assets and networks that Chainalysis supported before February 15, 2022, you can effectively ignore the network
property, and KYT is backward compatible for those assets. The network
property will be required for all new assets and networks that Chainalysis begins to support going forward.
To help customers acclimate to this transition, we've published a new article, Navigating asset tiers, and updated our lists of Mature and emerging networks and Pre-growth networks. We will update the rest of our KYT API reference documentation to account for the network
property as we near the March 10th release date.
To learn more about KYT's functionality for each asset tier, see our knowledge base article, Asset coverage.
Additionally, on March 10th, 2022, we will be deprecating the KYT API /assets
endpoint. This endpoint will not be updated with a current asset list after March 10th, 2022 and will be fully deprecated and unavailable on June 15th, 2022.
January 28, 2022
We've added support to our GET /v1/alerts
endpoint for behavioral alerts. You can now programmatically retrieve behaiovral alerts with the query parameter alertType
. Additionally, we've added the following JSON response properties for behavioral alerts:
transferCountWindow
ruleAsset
period
windowSize
transferCount
alertType
To learn more about the new query parameter or response properties, check out the definitions and descriptions in the schema.
August 18, 2021
We've released a new endpoint to assign alerts. You can now make a POST request to programmatically assign alerts to users in your organization. To unassign an alert, use null
in the request body.
Additionally, we've added the following response body fields to the the GET /v1/alerts
endpoint:
assignedTo
- the email address of the user assigned to the alert.assignedAt
- the UTC timestamp when the alert was assigned, in IS0 8601 format with timezone information.assignedBy
- the email address of the user who assigned the alert.
You can also sort and filter with these fields.
August 11, 2021
We've changed the requirement of certain request body fields for the POST /v2/users/{userId}/transfers
endpoint. Now, transferTimestamp
, assetAmount
, outputAddress
, inputAddress
, assetPrice
, and assetDenomination
are optional for both CDN and fully-supported assets.
July 26, 2021
We've recently released a new endpoint that retrieves an alert's status and comment history.
We've also added two new parameters to the get alerts endpoint:
alertStatusCreatedAt_gte
alertStatusCreatedAt_lte
Use these parameters to retrieve alerts with statuses and comments that were recently updated.
You can also use the sort
parameter with alertStatusCreatedAt
to sort your alerts by the time their status was created.
July 9, 2021
We've updated our KYT API docs to restructure and reframe our endpoints. We believe the new structure brings more clairity to our API. It is important to note that both v1 and v2 endpoints are comptaible. We've included a new API implementation that details how you might integrate with our API. We've renamed our old integration guide to Legacy implementation.
May 27, 2021
We've recently released a new endpoint, POST: alert status and comment.
You can now make a POST request to change an alert's status and include a comment, giving you the ability to action historic alerts programmatically and ensure your case management syncs with KYT. View the endpoint here.
March 31, 2021
We've recently added Algorand as a supported asset.
For your transfers, KYT includes staking rewards where present. Algorand block explorers display staking rewards separately from the main payment transfer(s). Therefore, block explorers may introduce a discrepancy in the output index, e.g. a block explorer presents a counterparty at position 0, yet our representation shows the same counterparty at position 2. For this reason, you should include the output address, rather than output index, in transfer references for your KYT API requests.
View all the assets that Chainalysis supports here.
October 28, 2020
We’ve recently added support for another 16 ERC-20 tokens. These include DeFi tokens, one of the most exciting and talked about areas of crypto, and a tranche of stable coins. Chainalysis now supports over 100 cryptocurrency assets. See the complete list of assets that KYT suppports here.
June 8, 2020
Chainalysis has launched support for two notable cryptocurrencies: Dash and Zcash. As two of the most popular so-called “privacy coins” — cryptocurrencies with privacy enhancing features encoded into their protocols — they account for over $1.5 billion of reported daily trading volume.
Our latest blog post explains how these two privacy coins allow investigators and compliance professionals to investigate and monitor illicit activity using our products.
See all cryptocurrencies that Chainalysis supports here.
May 26, 2020
KYT has introduced a new user risk score calculation that's easier to understand, customize, and control.
There will be a new property in the API called
riskScore
, found inGET /users
andGET /users/{userId}
. It returns the risk score based on the new model asSEVERE
,HIGH
,MEDIUM
, andLOW
.The
score
property in the API, based on the legacy model, will remain. SEVERE and HIGH will be returned asred
, MEDIUM asamber
, and LOW asgreen
. However, we recommend using theriskScore
field going forward.Note that this is an additive change; no existing fields will be removed.
March 12, 2020
Chainalysis has added 43 new tokens, more than doubling the number of ERC-20 tokens we support. KYT can now help you understand what’s happening with 97% of the total value in ERC-20s and over $1 billion of ERC-20 transfers every day. See a complete list of assets that KYT suppports here.
February 11, 2020
- XRP is now available in Chainalysis! XRP is the cryptocurrency best known as a payment protocol for remittances, payment settlement, and the exchange of assets. It is the native currency of Ripple, a company that works with financial institutions to enable fast and low cost global payments. See the Assets section for more information.
December 10, 2019
- Ethereum Classic (ETC) is now supported in Chainalysis! Ethereum Classic is an alternate version of Ether.
- Register ETH smart contract transfers in KYT using a new transfer reference: "transaction hash:output address".
December 5, 2019
- You can now sort and filter alerts via the API. Sort GET
/alerts
bytimestamp
,createdAt
,level
, andalertAmountUsd
, and filter bylevel
. Read more about alert sorting and filtering capabilities here.
October 16, 2019
- KYT has released support for many new ERC-20s including LINK, CRPT, MCO, CRO, GNO, HT, MLN, LEO, WETH, ZIL, TGBP, REP, HOLD, AUDX, CADX, CHFX, CNYX, EURX, GBPX, GLDX, NZDX, RUBX, SLVX, USDEX, JPYX, HUSD, PAXG, and BUSD. See a full list of all coins that KYT supports here.
September 25, 2019
- Send data to the KYT API for hundreds of assets we don’t yet support. Implement the KYT API once and no changes are required when we support a new asset.
- Filter the
/alerts
KYT API endpoint bytransferReference
andasset
.
August 6, 2019
- Alert information is now available via the KYT API. Users can use this API to integrate KYT Alerts into your own internal tools. See here for more information.
July 23, 2019
- ZRX, BAT, OMG, and MKR are now supported in the API.
Getting started
Registering transfers is required to get the total value of KYT: transaction screening, generating alerts, and building user and transaction risk profiles, among other features.
You can submit transfers via the API and KYT will provide insights on the transfer and counterparty involved. KYT will automatically monitor each transaction posted for changes in exposure and counterparty risk. All alerts, user risk scores, and transfer profile updates will be reflected in both the KYT Dashboard and API responses.
See the KYT implementation workflows below for information on implementing the API, including various use cases and example compliance workflows.
Authentication
The KYT API uses a unique API token to authorize calls. You pass this unique token in the token header. Additionally, API calls must include Accept: application/json
in the request header.
Example request header:
curl -X GET "https://api.chainalysis.com/api/kyt/{ENDPOINT}" \
--header "Token: {YOUR_API_KEY}" \
--header "Accept: application/json"
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
response = requests.get('https://api.chainalysis.com/api/kyt/{ENDPOINT}', headers=headers)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/{ENDPOINT}', {
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json'
}
});
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/{ENDPOINT}");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
Note: Make sure to replace
{YOUR_API_KEY}
with your API key.
Environments
We recommend using a sandbox KYT instance to test initial API integration and validation. Once you have finished testing and completed the integration, you can move to a second, "clean" KYT instance with a new API key. This second instance will be your primary, ongoing KYT instance.
Please contact your CSM to help you create your KYT instances and obtain their credentials.
Environment URLs:
- Chainalysis KYT UI https://kyt.chainalysis.com
- Chainalysis KYT API Endpoint https://api.chainalysis.com/api/kyt
- Chainalysis Reactor https://reactor.chainalysis.com
Security: All KYT API access is secured using HTTPS with TLS (v1.2) being supported.
Creating API keys
You can create API keys from the Setting option in KYT's Account menu.
To create an API key:
- Log into the KYT instance (either sandbox or primary) for which you want to create an API key.
- From the Account drop-down menu, click Developers > API Keys.
- Click the Generate API Key button. Your API key appears below.
You can also obtain an API key from the Settings menu in Reactor, under the API Keys tab.
Moving from a sandbox instance to a primary instance
Access the sandbox KYT instance environment
Log into KYT with your sandbox credentials (obtained via email) and generate your API key for your sandbox KYT instance.Make successful requests on the sandbox KYT instance
Ensure requests are successful (register a transfer, register a withdrawal attempt, get alerts)Set up compliance workflows and verify that requests are aligned in the sandbox instance
Move from the sandbox instance environment to the primary instance
Log into KYT with your primary credentials (obtained via email) and generate your API key for your primary KYT instance environment (URLs provided above).
You will need to regularly post new transfers to the KYT API. Each transfer needs to be posted to the API only once, and KYT will continuously monitor counterparties and transaction flows. If transfers are accidentally posted more than once, this will not have an effect.
Response pagination
In the following example, the query parameters of
"limit": 25
and"offset": 3
would display items 3-28 in the result set of 100total
items.Note that the
"data"
array would typically display the data queried for these objects but has been truncated for this example.
The KYT API supports pagination for endpoints that return long lists of objects. Pagination allows you to retrieve a limited set (or subset) of results and to offset those results. Responses are displayed using total
, limit
, and offset
. To see a different page, append the parameters limit
and/or offset
to the GET
request.
{
"total": 100,
"limit": 25,
"offset": 3,
"data": [
...
]
}
Optional pagination parameters
Parameter Name | Type | Description |
---|---|---|
limit |
Int |
A limit on the number of objects returned. |
offset |
Int |
The position of the first object returned in the response. The default is 0, which starts the page at the first result. |
total
represents the total number of objects that answer the query, including the ones that are not displayed.data
is an array of the data you requested.
If there is no data to be returned, the data array will be empty.
Tips
- You must have a valid API key and be using live mainnet data, or else you will receive 400 or 403 responses.
- All API access is over HTTPS.
- Data is sent and received in JSON format.
- Arguments are sent in the request body with the exception of the API key (Token), which is sent as a header.
- Blank fields are included as null instead of being omitted.
- TLS (v1.2) is supported.
Implementation
Workflows
The API implementation guide details three common use cases:
- Crediting a user's funds upon deposit
- Processing a user's withdrawal attempt
- Retrieving alerts for transaction monitoring
This diagram shows a general overview of common KYT workflows. For information on which endpoints to use, and in what order, see the procedures below.
Crediting a user’s funds
Typically, services do not have control over received transfers. This guide details how to use KYT data to take programmatic action when receiving a user deposit. Outlined below is an example describing how to use the KYT API to navigate received deposits.
When you receive a user deposit:
- Call the POST
/v2/users/{userId}/transfers
endpoint. Be sure to indicate the"direction"
as"RECEIVED"
in the request body. If successful, you will:- Receive a 202 response.
- Receive an external identifier (
externalId
). Store this external identifier in your system for later use.
Call the GET
/v2/transfers/{externalId}
summary endpoint using theexternalId
received in step 1. You will need to poll this endpoint untilupdatedAt
is no longernull
. Once populated, KYT generates alerts according to your organization’s alert rules.Note: How quickly the
updatedAt
field populates depends on how many confirmations Chainalysis requires before processing transactions for a given asset. Some require fewer confirmations or are quicker than others. Learn more about polling the summary endpoints here.Once the
updatedAt
field populates, determine whether the asset is part of a mature or emerging network or pre-growth network and follow the corresponding procedure below.
User deposits for mature and emerging assets
With mature and emerging assets, you can retrieve the following additional information about the transfer, if available:
- Direct exposure information
- Alerts specific to the transfer
To retrieve additional information about the received transfer:
- Call the GET
/v2/transfers/{externalId}/exposures
endpoint to retrieve any available direct exposure information. - Call the GET
/v2/transfers/{externalId}/alerts
endpoint to retrieve any generated alerts specific to this transfer.
Learn more about retrieving alert data for ongoing transaction monitoring here.
User deposits for pre-growth assets
For pre-growth assets, you can retrieve the following additional information about the transfer, if available:
- Alerts specific to the transfer
- Network Identifications - learn more about Network Identifications here.
To retrieve additional information about the received transfer:
- Call the GET
/v2/transfers/{externalId}/alerts
endpoint to retrieve any generated alerts specific to this transfer. - Call the GET
/v2/transfers/{externalId}/network-identifications
endpoint to retrieve the counterparty name for any asset and transaction hash matches.
Learn more about retrieving alert data for ongoing transaction monitoring here.
Note: Depending on the transfer, direct exposure information may be available. You can check by calling the GET /v2/transfers/{externalId}/exposures
endpoint.
Processing withdrawals
Sometimes services do not have information about the counterparty where their users attempt to make a withdrawal. This guide details how to use KYT data to take programmatic action when users attempt withdrawals. Outlined below is an example describing how to use the KYT API to navigate withdrawal attempts.
When a user attempts a withdrawal:
- Call the POST
/v2/users/{userId}/withdrawal-attempts
endpoint. If successful, you will:- Receive a 202 response.
- Receive an external identifier (
externalId
). Store the external identifier in your system for later use.
- Call the GET
/v2/withdrawal-attempts/{externalId}
summary endpoint, using theexternalId
received in step 1. You will need to poll this endpoint untilupdatedAt
is no longernull
. Once populated, KYT generates alerts according to your organization’s alert rules. - Once the
updatedAt
field populates, determine whether the asset is part of a mature or emerging network or pre-growth network and follow the corresponding procedure below.
Withdrawal attempts for mature and emerging assets
With mature and emerging assets, you can retrieve the following additional information, if available:
- Direct exposure information
- Alerts specific to the withdrawal attempt
- Chainalysis Identifications
To retrieve additional information about the withdrawal attempt:
- Call the GET
/v2/withdrawal-attempts/{externalId}/exposures
endpoint to retrieve any counterparty exposure information. - Call the GET
/v2/withdrawal-attempts/{externalId}/alerts
endpoint to retrieve any available alerts specific to this counterparty. - Call the GET
/v2/withdrawal-attempts/{externalId}/high-risk-addresses
endpoint to check if the counterparty has any Chainalysis Identifications. - After successfully processing a user’s withdrawal, call the POST
/v2/users/{userId}/transfers
endpoint and indicate the"direction"
as"SENT"
to register the transfer for ongoing monitoring.
Withdrawal attempts for pre-growth assets
With pre-growth assets, you can retrieve the following additional information, if available:
- Alerts specific to the withdrawal attempt
- Chainalysis Identifications
- Network Identifications - learn more about Network Identifications here.
To retrieve additional information about the withdrawal attempt:
- Call the GET
/v2/withdrawal-attempts/{externalId}/alerts
endpoint to retrieve any alerts specific to the counterparty. - Call the GET
/v2/withdrawal-attempts/{externalId}/high-risk-addresses
endpoint to check if the counterparty has any Chainalysis Identifications. - Call the GET
/v2/withdrawal-attempts/{externalId}/network-identifications
endpoint to retrieve the counterparty name of any asset and transaction hash matches. - After successfully processing a user’s withdrawal, call the POST
/v2/users/{userId}/transfers
endpoint and indicate the"direction"
as"SENT"
to register the transfer for ongoing monitoring.
Polling the summary endpoints
After registering your transfer or withdrawal attempt, you should poll the summary endpoints (i.e., summary for transfers or summary for withdrawal attempts) to ensure KYT has processed your request before you attempt to retrieve alerts, exposure, or identifications. KYT will indicate that it has processed your request by returning a non-null
value for the updatedAt
response body property. How long to pull the summary endpoints depends on the blockchain and its number and speed of confirmations.
Transfers example
For the /transfers
endpoints, most transfers process in less than a minute (assuming you have made the initial POST request after a block has been mined). However, we still suggest polling for at least a few minutes if the updatedAt
field has not yet populated. In cases when the updatedAt
field still returns null
after a number of minutes, we suggest setting a policy about how long to wait before crediting a user's account.
Many services require a few confirmations before crediting a user's funds, which usually takes several minutes. It is during this time that you can begin polling the GET /transfers/{externalId}
endpoint.
Withdrawal attempts example
The /withdrawal-attempts
endpoints do not require blockchain confirmations to register successfully in KYT. Often, updatedAt
returns a non-null
value in the response of the POST request. Nonetheless, we still recommend setting a policy for how long to wait when updatedAt
returns null
for an extended amount of time.
Processing times for each network
KYT begins processing block data after a transaction achieves a predetermined number of confirmations. This confirmation threshold differs for each blockchain network. Additionally, the speed at which networks acquire confirmations varies from network to network (e.g., Bitcoin’s block time is ~10 minutes while Ethereum’s block time is ~12-14 seconds).
See the table below for the confirmation threshold and approximate time required for block data to be available to KYT.
Note that these are estimates, and sometimes latency can fluctuate depending on various network factors. We suggest building a buffer in your internal system to account for any potential irregularities.
Network | Number of confirmations required for KYT to process block data | Once a transaction meets the confirmation threshold, the approximate time for data to be available to KYT |
---|---|---|
BTC | 1 confirmation | ~30 seconds |
BCH | 1 confirmation | ~10 seconds |
BSV | 1 confirmation | ~ 1 minute |
ETH and ERC-20s | 1 confirmation | ~5 seconds |
ETC | 1 confirmations | ~5 minutes |
LTC | 1 confirmation | ~10 seconds |
EOS | 360 confirmations | ~3 minutes |
XRP | 1 confirmations | ~5 seconds |
DOGE | 1 confirmation | ~5 seconds |
ZEC | 1 confirmation | ~5 seconds |
DASH | 1 confirmation | ~5 seconds |
Registering transfers before they are confirmed
If you register a transfer before it has any confirmations, processing times increase as KYT first polls the blockchain to validate the transaction exists. While KYT polls the blockchain, updatedAt
will remain null
for an extended time until the transaction meets our above confirmation threshold. Once the above threshold is met, the approximate time starts.
Retrieving alerts for transaction monitoring
After you've decided whether to credit a user's funds or process a user's withdrawal attempt, KYT automatically monitors the transaction and generates alerts according to your Alert Rules. You can retrieve those alerts with the GET /v1/alerts
endpoint.
If you call the endpoint without any query parameters, you will retrieve all of the alerts within your organization. To retrieve specific alerts, you can filter or sort with various query parameters.
Use the following query parameters to filter the alerts you wish to retrieve:
asset
- the asset used in the transaction.userId
- the user's unique identifier as defined in the transfer registration.level
- the severity of the alert, e.g.,SEVERE
,HIGH
,MEDIUM
, etc.createdAt_lte
- the timestamp less than or equal to when the alert generated.createdAt_gte
- the timestamp greater than or equal to when the alert generated.alertStatusCreatedAt_lte
- the timestamp less than or equal to when the most recent alert status was created.alertStatusCreatedAt_gte
- the timestamp greater than or equal to when the most recent alert status was created.
Use the sort
parameter with one of the following items to sort the order in which you retrieve alerts:
timestamp
- the blockchain date of the transfer that caused the alert.createdAt
- the date the alert generated.alertStatusCreatedAt
- the date the alert status was last updated.level
- The severity of the alert, e.g.,SEVERE
,HIGH
,MEDIUM
, etc.alertAmountUsd
- The amount of the transfer that triggered the alert.
After choosing the item you wish to sort by, you must add a URL encoded space character and indicate the order as either ascending (asc
) or descending (desc
). For example, sort=createdAt%20asc
.
Retrieving high severity alerts for a specified user
You can specify a combination of these query parameters to retrieve a specific result. As an example, to retrieve only HIGH
severity alerts of a specific userId
generated after a particular timestamp (createdAt_gte
), call GET /v1/alerts?level=HIGH&userId=user001&createdAt_gte=2020-01-06
This will return all high severity alerts for user001
after January 6th, 2020. Notice the timestamp includes only the date. You can filter even further with the inclusion of time to retrieve alerts down to the microsecond: &createdAt_gte=2020-01-06T12:42:14.124Z
If you want to sort the alerts by their creation date, add sort=createdAt%20asc
as a query parameter.
Retrieving severe alerts for a specified date range
You can use both createdAt_gte
andcreatedAt_lte
to retrieve alerts for a determined time period. As an example, to retrieve only SEVERE
alerts within your organization between the hours of 1:00AM and 2:00AM, call GET /v1/alerts?level=SEVERE&createdAt_gte=2021-05-01T00:00:00.00Z&createdAt_lte=2021-05-07T00:00:00.00Z
You can adjust these parameters to then retrieve all SEVERE
alerts between the hours of 2:00AM and 3:00AM, so on and so forth. Alternatively, you can discard the time information (T00:00:00Z
) from the query parameters altogether to retrieve alerts for certain days, weeks, or even months. The level of frequency you choose to call these endpoints depends on your organizational needs.
Legacy implementation
Suggested implementations of the Chainalysis KYT API are detailed below, increasing in complexity and the amount of functionality you will receive. You can choose to work entirely in the KYT user interface (UI), or build a response within your own internal systems based on feedback and analysis from Chainalysis.
Building the analysis and data from the KYT API into your internal system provides robust functionality and gives you a more comprehensive picture of your risk. We suggest reviewing example compliance workflows to help assess which implementation best meets your needs.
At minimum, the two major endpoints required for integration are: /transfers/sent
and /transfers/received
. For basic functionality, you must execute at least those two requests.
Overview
The following graphic serves as a preview and comparison for the various KYT implementations. The implementations are described in detail in the sections below, including benefits and drawbacks as well as required endpoints for each.
To help you visualize a compliance workflow using data from the KYT API, here is a basic KYT implementation.
Prepare for launch 1
KYT UI only
This is the minimum implementation and keeps all data and functionality entirely within Chainalysis’s environment. It does not pull data into your internal systems.
You will receive the full functionality of the KYT UI, but you will not have the ability to automate actions on transfers within your internal systems based on the information from KYT.
This implementation requires registering /transfers/sent
and /transfers/received
. /depositaddresses
is optional.
- Register a received transfer Registers a received transfer to a user and deposit address at your organization. The API response will contain a risk rating (high/low/unknown) of the counterparty that sent the transfer. If the cluster has been identified, the entity name will also be provided.
POST /users/{userId}/transfers/received
- Register a sent transfer
Registers a completed outgoing transfer from a user at your organization to another entity. Usually this is called after the
/withdrawaladdresses
endpoint that pre-screens a counterparty before allowing the transfer to proceed.
POST /users/{userId}/transfers/sent
- Optional: Register deposit addresses In the future, KYT will be able to detect deposits (but not withdrawals) based on the deposit address. While this endpoint is not currently required for monitoring or integration, you can implement it now for use with upcoming functionality.
POST /users/{userId}/depositaddresses
Transactions must be associated with a User ID for user risk score calculation.
Prepare for launch 2
Internal systems only
While you can work entirely in the Chainalysis environment (see above), this implementation pulls Chainalysis’s data into your own system where you can incorporate KYT into your automated payments workflow.
Integrating Chainalysis’s data into your internal system helps you to get a more comprehensive picture of your risk activity and automate actions on transfers based on Chainalysis data. However, you will be missing out on functionality and convenience by not working within the KYT UI.
This implementation requires registering /transfers/sent
and /transfers/received
(described above), as well as /withdrawaladdresses
.
- Pre-screen a withdrawal address Before allowing a transfer out of your organization, you can check the risk rating of the entity (cluster) associated with the address where the intended funds are going. After receiving a risk rating and information on the potential counterparty, you can determine whether to allow the withdrawal transfer to proceed.
For example, if you want to stop the withdrawal of funds to a sanction or terrorist financing entity, you would use this request.
POST /users/{userId}/withdrawaladdresses
Lift off
KYT UI & Internal systems
This setup is more robust than the previous implementations, as it encompasses both the KYT UI and your internal systems. You will be able to take action on transfers within your system and also have the full functionality of the UI, helping to provide continuity in the data you review between systems.
However, it does not take advantage of all the KYT features, such as alerts.
This implementation requires registering /transfers/sent
, /transfers/received
, and /withdrawaladdresses
(described above).
With this implementation you can:
- Create a URL link from your internal systems to the KYT UI.
- Set flags based on Chainalysis data. For example, a 'high risk' response from
transfers/received
puts risky deposits up for review by your compliance team. - Pull Chainalysis data into your payment review system. For example, you can include a transfer's risk rate in your payments review queue.
Cruising altitude
KYT UI & Internal systems
This implementation pulls Chainalysis data into both the KYT UI and your internal systems with an additional API endpoint, /alerts
.
Cruising Altitude brings the most powerful data that KYT offers - alerts - into your internal system. You can perform the actions mentioned above (setting flags, pulling the risk score into payments queues, etc) with the benefit that the data you are using is the most robust. This implementation can also help you match the alerts workflows that your compliance team is likely doing in the UI.
This implementation requires registering /transfers/sent
, /transfers/received
, /withdrawaladdresses
(described above), as well as /alerts
.
- Get alerts
Retrieves the details of all of your alerts in KYT and pulls those alerts into your own system. Alerts allow you to identify risky transfers on your platform.
GET /alerts
To the moon
KYT UI & Internal systems
This implementation pulls Chainalysis data into both the KYT UI and your internal systems. It builds on the implementations above with an additional endpoint, /users
. GET /users
provides you with a user risk score for each of your users.
Alerts and user risk score are two powerful analytic metrics that are provided by the KYT API for assessing your risk activity. User risk scores allow you to perform user-level automated review in your internal system on top of the transfer-level review from above.
This implementation requires registering /transfers/sent
, /transfers/received
, /withdrawaladdresses
, /alerts
(described above), as well as /users
.
- Get users Retrieves details, including the user risk score (as LOW, MEDIUM, HIGH, or SEVERE), on all registered users in your system. You can use the user risk score to manually review or hold transfers made by high-risk users.
GET /users
Compliance workflows
Below are example compliance workflows using KYT’s most powerful risk assessment features to help you formulate policies and procedures based on the information provided by the KYT and Reactor UIs, and the KYT API.
The workflows below focus on three KYT features that help you prioritize risk activity: alerts, user risk score, and counterparty screen:
- Alerts in KYT are generated whenever a transfer involves a risky counterparty and/or crosses a value threshold. A single transfer can trigger multiple alerts.
- The user risk score helps you to identify high-risk users in your organization.
- Deposit and withdrawal requests registered with Chainalysis returns a counterparty risk rating as highRisk/unknown/lowRisk. If highRisk or lowRisk (i.e. if the counterparty is known), the Category (darknet market, exchange, etc.) and Name (Hydra Marketplace, Kraken.com) will also be returned.
We suggest using alerts as a notification and starting point for review and interacting with user risk profiles. The KYT Dashboard in the UI gives you a high-level overview of your risk for managerial and organizational reporting/monitoring, while alerts is where an analyst will spend most of their time.
Deposit workflow
This is a suggested workflow for funds that are received by a user at your organization. Note that in most cases, you cannot stop an incoming transfer from occurring. However, KYT helps you to detect the risk associated with the transfer and take appropriate compliance action. For example, if a user receives funds directly from a cluster categorized as child abuse material, you can decide how you want to react (e.g. freeze funds, investigate, and likely file a SAR).
1. Register the transfer When funds arrive at a user address, POST the received transfer.
2. Check alerts and/or user risk score
You can check alerts:
- Via the UI: search for the transaction ID in the search bar at the top of the KYT UI. If an alert was raised on the transfer, it will appear in the transfer details panel.
- Via the API: by using the
GET /alerts
. Alert levels are returned as SEVERE, HIGH, MEDIUM, and LOW.
We suggest checking alerts first to assess risky transfer activity and then moving to review all alerts at the user level by clicking the unique URL for the user. You can also check the user’s risk score to see if the user is high risk.
You can check user risk score:
- Via the UI: search for the user’s ID in the search bar at the top of the KYT UI. The risk score is found on the user information page as Severe, High, Medium, or Low.
- Via the API: by using the
riskScore
property inGET /users/{userid}
.
3. Take action
Take action on the received funds. For example, you can hold the transfer and submit it to be manually reviewed, immediately freeze the user’s funds, or deem the transfer non-risky.
Chainalysis continually monitors for risk on each registered transfer.
Withdrawal workflow
This is a suggested workflow for funds that are being sent by a user at your organization. Unlike deposits, you can stop a risky withdrawal from occurring if at the time of the withdrawal, the withdrawal address has been identified as risky. For example, if a user requests a withdrawal towards a sanctioned address, you can decide to block this request.
1. Check Withdrawal Address
The withdrawal prescreen is used in real-time to pre-screen for counterparty risk. When a user requests a withdrawal, register the withdrawal address by making a POST request to the /withdrawaladdresses
endpoint. For known counterparties, a risk rating of high risk or low risk will be provided, as well as the counterparty’s name.
Note that it is common to withdraw to a previously unknown address, so the majority of addresses you check may have an unknown rating.
2. Take action
After performing the withdrawal address counterparty screen, take action on the pending transfer. You may choose to block a transfer that is high risk, or flag it for further review.
3. Register the transfer
Always be sure to register an approved withdrawal as a sent transfer upon completion so that your team can access it in the compliance dashboard and KYT can monitor the transfer. Note that KYT updates risk for transfers on an ongoing basis, but does not update withdrawal addresses. The latter will always return the rating when the address was first screened.
As with deposits, Chainalysis monitors sent transfers for you over time.
4. Check alerts
Look at the user information page in the UI for the user's associated withdrawal addresses and alerts. Alerts that have a sent direction mean that the withdrawal was approved.
Example compliance actions
Here are some examples of compliance actions taken by our customers when following the workflows above:
“If we notice a withdrawal request towards terrorism financing, we will show the user the withdrawal request is processing and call our Financial Intelligence Unit hotline to ask if they want us to block the transaction (and likely tip off the user) or allow it to occur (for ongoing monitoring).”
“If we detect a direct deposit from a darknet market, we will silently freeze the account. Typically we will file a SAR report, offboard the user, and allow the user to withdraw their funds via a fiat conversion to their bank account.”
“If we detect a pattern of risky (in)direct darknet market transactions, we will freeze the account. Typically we will file a SAR report, offboard the user, and allow the user to withdraw their cryptocurrency.”
“Transfers to mixing services are prohibited on our platform. If we see a withdrawal request to a mixer, we flag the transfer for review and ask the user to explain the purpose of the withdrawal.”
Internal systems
Note that instead of performing these checks manually, you can automate the process by building a response within your own internal systems. See the suggested implementations above for more information. The benefits of automating compliance include:
- Prevent successful money laundering.
- Proactively reduce risky activity from occurring.
- Nudge users toward correct behavior.
- More efficient and meaningful time usage of your compliance team.
Navigating asset tiers
What information do I need to send?
The asset's tier determines which request body properties are required. For mature and emerging assets, KYT populates transfer information with blockchain and pricing data.
For pre-growth assets, the additional information (e.g., transferTimestamp
or assetAmount
) is optional, but we suggest supplying it for maximal value as KYT uses it to generate alerts.
In subsequent GET requests, KYT will return only the information supplied in the initial POST registration request.
Once a network graduates to the emerging or mature tiers, KYT will backfill the transfer with blockchain and pricing data.
For more information about KYT's functionality for each tier, see Asset coverage.
Mature and emerging
To register transfers on mature or emerging networks, you need to supply at minimum the following request body properties:
network
asset
transferReference
Pre-growth
To register transfers from pre-growth networks, you need to supply at minimum the following request body properties:
network
asset
transferReference
To maximize KYT value, we recommend you also supply the following request body properties:
transferTimestamp
assetAmount
outputAddress
inputAddresses
assetPrice
assetDenomination
network
and asset
properties
The network
property informs KYT which blockchain network the transaction occurred on. For example, values could be Ethereum
, Avalanche
, Bitcoin
, Lightning
, etc.
The asset
property informs KYT of which asset or metatoken was used in the transfer. For example, if Ether was transacted, you would supply ETH
as the value. If the ERC-20 Aave was transacted, you would supply AAVE
as the value.
The combination of network
and asset
allows KYT to distinguish between assets that operate on two or more networks, such as the stablecoin USDT. USDT exists on multiple networks, including Ethereum, Tron, Avalanche, etc. Another example is bitcoin, which exists on both the Bitcoin and Lightning networks.
To register a transfer of USDT on the Avalanche network, you would supply "Network": "Avalanche", "Asset": "USDT"
. However, to register a transfer of USDT on the Ethereum network, you would supply "Network": "Ethereum", "Asset": "USDT"
.
For lists of the networks Chainalysis currently supports, see Mature and emerging networks and Pre-growth networks.
transferReference
property
The transferReference
property enables Chainalysis to locate the transfer on the blockchain. Because blockchain networks use different record-keeping models, you must supply different values for the property depending on the record-keeping model. In our network tiers, we indicate which format is used.
UTXO model
For networks using a UTXO model, you must reference a transaction hash and corresponding output address or transaction hash index:
{transaction_hash}:{output_address}
, or{transaction_hash}:{output_index}
As an example, lets say you want to register a transfer with the transaction hash 9022c2d59d1bed792b6449e956b2fe26b44b1043bbc1662d92ecd29526d7f34e
and an output address of 18SuMh4AFgTSQRvwFzdYGieHtgKDveHtc
, which is in the 6th place in the transaction.
To register the transfer with the output address, your property would look like: "transferReference": "9022c2d59d1bed792b6449e956b2fe26b44b1043bbc1662d92ecd29526d7f34e:18SuMh4AFgTSQRvwFzdYGieHtgKDveHtc"
To register the transfer with the output index, your property would look like: "transferReference": "9022c2d59d1bed792b6449e956b2fe26b44b1043bbc1662d92ecd29526d7f34e:5"
.
Note that the output address is the destination address for the funds within the transaction. For received transfers, the output address is an address you control. For sent transfers, the output address is an external address.
Account/balance model
For networks using an account model (e.g., Ethereum and EVM-compatible), you must reference a transaction hash and corresponding output address: {transaction_hash}:{output_address}
.
To register a simple Ether transfer, your property might look like: "transferReference": "0xe823c9b7895f9c47985c80e4611272f8194403e885c9cc603422cd609d738098:0x3d21a92285bf17cbdde5f77531b8b58ac400288a"
.
For funds sent to a smart contract, use the smart contract's address as the output address. If receiving funds from a smart contract, use the end-user's destination address as the output address.
In transactions where an output address is used multiple times (e.g., certain interactions with a smart contracts), KYT will register the first output address where the amount is greater than 0.
Other models
Some assets require a unique transferReference
value that does not fit into the above schemas.
Monero
For deposits, use {transaction_hash}:{transaction_index}:{receiving_address}:{payment_ID}
.
For withdrawals, use {transaction_hash}:{transaction_index}:{withdrawal_address}:{payment_ID}
.
Lightning Network
For the Lightning Network, you must supply a combination of the payment hash and recipient node key. Your property should look like {payment_hash}:{node_key}
.
Just a transaction hash
Some networks require just a transaction hash and no output address or output index. We indicate these networks in the transferReference
column of the network tables.
Tokens with nonunique symbols
In rare instances, two metatokens on a single network may share the same asset symbol. Since KYT uses the transferReference
property to identify the transaction, nonunique asset symbols do not affect KYTs ability to monitor the transaction. In the extremely rare scenario that two metatokens shared an asset symbol and sent funds to the same outputAddress
in the same transaction, KYT will register the first transaction seen. If this is the incorrect transaction, please contact Chainalysis Customer Support.
Registering Lightning Network transactions and withdrawals
You can use the KYT API to register Lightning Network (LN) transfers and withdrawal attempts. Registering LN transactions is similar to registering any other transaction, but with a few modifications to the values of a handful of request body properties.
This article describes how to register transactions using the POST /v2/users/{userId}/transfers
and /v2/users/{userId}/withdrawal-attempts
endpoints, but you can also use the v1 received transfers, sent transfers, and withdrawal pre-screening endpoints by applying the principles outlined in this article to the values of the v1 request body properties.
Updated request properties
Since Lightning Network transactions occur “off-chain”, Chainalysis requires you to treat certain request properties slightly differently than in typical “on-chain” transactions. The following properties are required and use slightly different values:
network
asset
transferReference
transferTimestamp
assetAmount
network
and asset
Be sure to use Lightning
as the value for the network
property and BTC
as the value for the asset
property. Failing to specify Lightning
may result in a failed request.
For example, the property value pair should look like the following: "network": "Lightning", "asset": "BTC"
.
transferReference
To uniquely identify a Lightning Network transaction, you must supply a combination of the payment hash and node key the funds are being sent to as the value for the transferReference
property. Separate the payment hash and node key with a colon (payment-hash:node-key
) and supply that string as the value for the property.
As an example, the property value pair should look similar to the following but supplied with your own payment hash and the recipient node key: "transferReference": "2db4d5579a0e198bd5ce8ffe83ecd0de94acde98cde94125337e2419ebb4cc50:02a0c9089ace681ef4e6ae5310b028d9c2a09187bfbc616da6251e3d08801851b8"
.
transferTimestamp
For Lightning Network transactions, transferTimestamp
is a required property. Chainalysis uses the timestamp to (1) identify the USD value for any transferred Bitcoin and (2) generate time-based behavioral alerts.
Be sure to send your timestamp in the UTC ISO 8601 format. For example, the property value pair should look similar to the following: "transferTimestamp": "2021-11-25T11:48:21.000000"
.
assetAmount
assetAmount
is also a required property and should be denominated in Bitcoin.
The following is an example of the property value pair: "assetAmount": ".00588"
.
Unrequired request properties
You are not required to send the remaining request properties such as outputAddress
, assetPrice
, and assetDenomination
, but you will not receive an error response if you do send them. In the scenario that you do send them, you will not receive any values that you sent in the POST request in subsequent GET requests.
Since the Lightning Network handles transactions off-chain, there is no standard wallet address to use as a value for the outputAddress
property. Instead, use the Node Key as the value for this property, similarly to the transferReference
property.
Putting it all together
You can register transactions or withdrawal attempts with either the v1 or v2 endpoints; both provide the same functionality. See the right-most column for request examples of each endpoint.
The following is an example to register a sent LN transfer using the
v2/users/{userId}/transfers
endpoint:
curl -X POST 'https://api.chainalysis.com/api/kyt/v2/users/new_user_01/transfers' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Content-type: application/json' \
--data '{
"network": "Lightning",
"asset": "BTC",
"transferReference": "2db4d5579a0e198bd5ce8ffe83ecd0de94acde98cde94125337e2419ebb4cc50:02a0c9089ace681ef4e6ae5310b028d9c2a09187bfbc616da6251e3d08801851b8",
"direction": "sent",
"transferTimestamp": "2021-11-25T11:48:21.000000",
"assetAmount": ".00588"
}'
import requests
headers = {
'Token': '{YOUR_API_KEY}'
}
transfer_data = {
'network': 'Lightning',
'asset': 'BTC',
'transferReference': '2db4d5579a0e198bd5ce8ffe83ecd0de94acde98cde94125337e2419ebb4cc50:02a0c9089ace681ef4e6ae5310b028d9c2a09187bfbc616da6251e3d08801851b8',
'direction': 'sent',
'transferTimestamp': '2021-11-25T11:48:21.000000',
'assetAmount': '.00588',
}
response = requests.post('https://api.chainalysis.com/api/kyt/v2/users/new_user_01/transfers', headers=headers, json=transfer_data)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v2/users/new_user_01/transfers', {
method: 'POST',
headers: {
'Token': '{YOUR_API_KEY}',
'Content-type': 'application/json'
},
body: JSON.stringify({
'network': 'Lightning',
'asset': 'BTC',
'transferReference': '2db4d5579a0e198bd5ce8ffe83ecd0de94acde98cde94125337e2419ebb4cc50:02a0c9089ace681ef4e6ae5310b028d9c2a09187bfbc616da6251e3d08801851b8',
'direction': 'sent',
'transferTimestamp': '2021-11-25T11:48:21.000000',
'assetAmount': '.00588'
})
});
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v2/users/new_user_01/transfers");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("POST");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Content-type", "application/json");
httpConn.setDoOutput(true);
OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream());
writer.write("{\n \"network\": \"Lightning\",\n \"asset\": \"BTC\",\n \"transferReference\": \"2db4d5579a0e198bd5ce8ffe83ecd0de94acde98cde94125337e2419ebb4cc50:02a0c9089ace681ef4e6ae5310b028d9c2a09187bfbc616da6251e3d08801851b8\",\n \"direction\": \"sent\",\n \"transferTimestamp\": \"2021-11-25T11:48:21.000000\",\n \"assetAmount\": \".00588\"\n}");
writer.flush();
writer.close();
httpConn.getOutputStream().close();
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
The following is an example to register the same transaction as above, but with the
v1/users/{userId}/transfers/sent
endpoint:
curl -X POST 'https://api.chainalysis.com/api/kyt/v1/users/new_user_01/transfers/sent' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Content-type: application/json' \
--data-raw '[
{
"network": "Lightning",
"asset": "BTC",
"transferReference": "2db4d5579a0e198bd5ce8ffe83ecd0de94acde98cde94125337e2419ebb4cc50:02a0c9089ace681ef4e6ae5310b028d9c2a09187bfbc616da6251e3d08801851b8",
"transferTimestamp": "2021-11-25T11:48:21.000000",
"assetAmount": ".00588"
}
]'
import requests
headers = {
'Token': '{YOUR_API_KEY}'
}
transfer_data = [
{
'network': 'Lightning',
'asset': 'BTC',
'transferReference': '2db4d5579a0e198bd5ce8ffe83ecd0de94acde98cde94125337e2419ebb4cc50:02a0c9089ace681ef4e6ae5310b028d9c2a09187bfbc616da6251e3d08801851b8',
'transferTimestamp': '2021-11-25T11:48:21.000000',
'assetAmount': '.00588',
},
]
response = requests.post('https://api.chainalysis.com/api/kyt/v1/users/new_user_01/transfers/sent', headers=headers, json=transfer_data)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v1/users/new_user_01/transfers/sent', {
method: 'POST',
headers: {
'Token': '{YOUR_API_KEY}',
'Content-type': 'application/json'
},
body: JSON.stringify([
{
'network': 'Lightning',
'asset': 'BTC',
'transferReference': '2db4d5579a0e198bd5ce8ffe83ecd0de94acde98cde94125337e2419ebb4cc50:02a0c9089ace681ef4e6ae5310b028d9c2a09187bfbc616da6251e3d08801851b8',
'transferTimestamp': '2021-11-25T11:48:21.000000',
'assetAmount': '.00588'
}
])
});
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v1/users/new_user_01/transfers/sent");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("POST");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Content-type", "application/json");
httpConn.setDoOutput(true);
OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream());
writer.write("[\n {\n \"network\": \"Lightning\",\n \"asset\": \"BTC\",\n \"transferReference\": \"2db4d5579a0e198bd5ce8ffe83ecd0de94acde98cde94125337e2419ebb4cc50:02a0c9089ace681ef4e6ae5310b028d9c2a09187bfbc616da6251e3d08801851b8\",\n \"transferTimestamp\": \"2021-11-25T11:48:21.000000\",\n \"assetAmount\": \".00588\"\n }\n]");
writer.flush();
writer.close();
httpConn.getOutputStream().close();
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
Withdrawal attempts and withdrawal pre-screening
You can use either the POST /v2/users/{userId}/withdrawal-attempts
or POST /v1/users/{userId}/withdrawaladdresses
endpoint to assess the risk of a Node Key a user is attempting to withdraw funds to.
When registering with either endpoint, be sure to use Lightning
as the value for the network property, BTC
as the value for the asset property, and the Node Key as the value for the address
property. To learn more about using these endpoints, see Register a withdrawal attempt (v2) and Register withdrawal addresses (v1).
V2 endpoints
Registration
These endpoints register transfers and withdrawal attempts.
POST /v2/users/{userId}/transfers
POST /v2/users/{userId}/withdrawal-attempts
Register a transfer
ENDPOINT
POST
/v2/users/{userId}/transfers
The following is an example request to create a user and register a transfer of a mature or emerging asset:
curl -X POST 'https://api.chainalysis.com/api/kyt/v2/users/new_user_01/transfers' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Content-type: application/json' \
--data '{
"network": "Bitcoin",
"asset": "BTC",
"transferReference": "2d9bfc3a47c2c9cfd0170198782979ed327442e5ed1c8a752bced24d490347d4:1H7aVb2RZiBmdbnzazQgVj2hWR3eEZPg6v",
"direction": "received"
}'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
}
transfer_data = {
'network': 'Bitcoin',
'asset': 'BTC',
'transferReference': '2d9bfc3a47c2c9cfd0170198782979ed327442e5ed1c8a752bced24d490347d4:1H7aVb2RZiBmdbnzazQgVj2hWR3eEZPg6v',
'direction': 'received',
}
response = requests.post('https://api.chainalysis.com/api/kyt/v2/users/new_user_01/transfers', headers=headers, json=transfer_data)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v2/users/new_user_01/transfers', {
method: 'POST',
headers: {
'Token': '{YOUR_API_KEY}',
'Content-type': 'application/json'
},
body: JSON.stringify({
'network': 'Bitcoin',
'asset': 'BTC',
'transferReference': '2d9bfc3a47c2c9cfd0170198782979ed327442e5ed1c8a752bced24d490347d4:1H7aVb2RZiBmdbnzazQgVj2hWR3eEZPg6v',
'direction': 'received'
})
});
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v2/users/new_user_01/transfers");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("POST");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Content-type", "application/json");
httpConn.setDoOutput(true);
OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream());
writer.write("{\n \"network\": \"Bitcoin\",\n \"asset\": \"BTC\",\n \"transferReference\": \"2d9bfc3a47c2c9cfd0170198782979ed327442e5ed1c8a752bced24d490347d4:1H7aVb2RZiBmdbnzazQgVj2hWR3eEZPg6v\",\n \"direction\": \"received\"\n}");
writer.flush();
writer.close();
httpConn.getOutputStream().close();
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
The following is an example request to create a user and register a transfer of a pre-growth asset:
curl -X POST 'https://api.chainalysis.com/api/kyt/v2/users/new_user_02/transfers' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Content-type: application/json' \
--data '{
"network": "Chia",
"asset": "XCH",
"transferReference":"0x3419e2be63a8404826a49ab250b007f27447333019079d083c2b322c4cbe3cd4:xch19ukfhgdjx9w5zw5jkhcrf7sr9qkvhgtk0lv6u7c5m9ptj60dt4tscqkmna",
"direction":"sent",
"transferTimestamp": "2022-03-10T20:37:32+00:00",
"assetAmount": "1.75",
"outputAddress": "xch19ukfhgdjx9w5zw5jkhcrf7sr9qkvhgtk0lv6u7c5m9ptj60dt4tscqkmna",
"inputAddresses": ["xch1vjgqx900fwejxgn8667h6pykn544fkxwwqtszv0dmd7ngar52eyq8gr9zd"],
"assetPrice": "69.09",
"assetDenomination": "USD"
}'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
}
transfer_data = {
'network': 'Chia',
'asset': 'XCH',
'transferReference': '0x3419e2be63a8404826a49ab250b007f27447333019079d083c2b322c4cbe3cd4:xch19ukfhgdjx9w5zw5jkhcrf7sr9qkvhgtk0lv6u7c5m9ptj60dt4tscqkmna',
'direction': 'sent',
'transferTimestamp': '2022-03-10T20:37:32+00:00',
'assetAmount': '1.75',
'outputAddress': 'xch19ukfhgdjx9w5zw5jkhcrf7sr9qkvhgtk0lv6u7c5m9ptj60dt4tscqkmna',
'inputAddresses': [
'xch1vjgqx900fwejxgn8667h6pykn544fkxwwqtszv0dmd7ngar52eyq8gr9zd',
],
'assetPrice': '69.09',
'assetDenomination': 'USD',
}
response = requests.post('https://api.chainalysis.com/api/kyt/v2/users/new_user_02/transfers', headers=headers, json=transfer_data)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v2/users/new_user_02/transfers', {
method: 'POST',
headers: {
'Token': '{YOUR_API_KEY}',
'Content-type': 'application/json'
},
body: JSON.stringify({
'network': 'Chia',
'asset': 'XCH',
'transferReference': '0x3419e2be63a8404826a49ab250b007f27447333019079d083c2b322c4cbe3cd4:xch19ukfhgdjx9w5zw5jkhcrf7sr9qkvhgtk0lv6u7c5m9ptj60dt4tscqkmna',
'direction': 'sent',
'transferTimestamp': '2022-03-10T20:37:32+00:00',
'assetAmount': '1.75',
'outputAddress': 'xch19ukfhgdjx9w5zw5jkhcrf7sr9qkvhgtk0lv6u7c5m9ptj60dt4tscqkmna',
'inputAddresses': [
'xch1vjgqx900fwejxgn8667h6pykn544fkxwwqtszv0dmd7ngar52eyq8gr9zd'
],
'assetPrice': '69.09',
'assetDenomination': 'USD'
})
});
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v2/users/new_user_02/transfers");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("POST");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Content-type", "application/json");
httpConn.setDoOutput(true);
OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream());
writer.write("{\n \"network\": \"Chia\",\n \"asset\": \"XCH\",\n \"transferReference\":\"0x3419e2be63a8404826a49ab250b007f27447333019079d083c2b322c4cbe3cd4:xch19ukfhgdjx9w5zw5jkhcrf7sr9qkvhgtk0lv6u7c5m9ptj60dt4tscqkmna\",\n \"direction\":\"sent\",\n \"transferTimestamp\": \"2022-03-10T20:37:32+00:00\",\n \"assetAmount\": \"1.75\",\n \"outputAddress\": \"xch19ukfhgdjx9w5zw5jkhcrf7sr9qkvhgtk0lv6u7c5m9ptj60dt4tscqkmna\",\n \"inputAddresses\": [\"xch1vjgqx900fwejxgn8667h6pykn544fkxwwqtszv0dmd7ngar52eyq8gr9zd\"],\n \"assetPrice\": \"69.09\",\n \"assetDenomination\": \"USD\"\n}");
writer.flush();
writer.close();
httpConn.getOutputStream().close();
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
This endpoint registers a transfer. Once you make a request for a transfer, KYT stores it and begins processing.
For transfers that are valid and KYT can process, the transfer should process within 30 seconds.
This endpoint returns the below response:
202
: Indicates KYT has accepted your transfer and will process your request.
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
userId |
String | Yes | A unique string that identifies your user. If creating a transfer for an existing user, use their existing userId . |
Request body schema
Property | Type | Required | Description |
---|---|---|---|
network |
String | Yes | The network this transfer occurred on. See our supported networks list for the approrpiate values. |
asset |
String | Yes | The cryptocurrency or token used in this transfer. The value must be the asset's symbol, e.g., BTC for Bitcoin, ETH for Ether, UNI for Uniswap etc. |
transferReference |
String | Yes | A combination of the transaction hash and output address or index of the transaction, separated with a colon. For example, {transaction_hash}:{output_address} or {transaction_hash}:{output_index} . Learn more about the transferReference property here. |
direction |
String | Yes | This value defines whether the transfer is sent or received . This value is case insensitive. |
transferTimestamp |
String | No | The timestamp when the transfer occurred, in the UTC ISO 8601 format. The timestamp should correspond to the timestamp of the block that included the transaction. |
assetAmount |
Number | No | The amount of cryptocurrency funds used in this transfer. |
outputAddress |
String | No | The destination address for funds within the transaction. |
inputAddresses |
Array | No | A list of input addresses of the transfer. Chainalysis uses this property to match network identifications. Note: This property is available only when registering transactions of pre-growth assets. |
assetPrice |
Number | No | The USD price of the asset at the time of the transfer. |
assetDenomination |
String | No | The denomination of the assetPrice property. Available only as USD . |
Response schema
The following is an example JSON response body from the Bitcoin request above:
{
"updatedAt": null,
"asset": "BTC",
"network": "BITCOIN",
"transferReference": "2d9bfc3a47c2c9cfd0170198782979ed327442e5ed1c8a752bced24d490347d4:1H7aVb2RZiBmdbnzazQgVj2hWR3eEZPg6v",
"tx": null,
"idx": null,
"usdAmount": null,
"assetAmount": null,
"timestamp": null,
"outputAddress": null,
"externalId": "fc8e053e-8833-344d-b025-40559eafd16f"
}
A successful request will return the following JSON response and properties:
Property | Type | Description |
---|---|---|
updatedAt |
String or null |
The timestamp when the transfer was last updated, in the UTC ISO 8601 format. Note: After your initial POST request, this value will be null until KYT processes your transfer. Poll the summary endpoint until updatedAt returns a value. |
asset |
String | An echo back of the cryptocurrency asset you defined in the request body. This is the asset's symbol, e.g., BTC for Bitcoin, ETH for Ether, etc. |
network |
String | An echo back of the blockchain network you defined in the request body. |
transferReference |
String | An echo back of the transfer reference you defined in the request body. |
tx |
String or null |
The transaction hash of the transfer. Note: After your initial POST request, this value will be null until KYT processes your transfer and you call a subsequent GET request. |
idx |
Integer or null |
The transfer’s index. Note: After your initial POST request, this value will be null until KYT processes your transfer and you call a subsequent GET request. |
usdAmount |
Number or null |
The US Dollar amount of funds used in this transfer. Note: After your initial POST request, this value will be null until KYT processes your transfer and you call a subsequent GET request. |
assetAmount |
Number or null |
The amount of cryptocurrency funds used in this transfer. Note: After your initial POST request, this value will be null until KYT processes your transfer and you call a subsequent GET request. |
timestamp |
String or null |
The timestamp when the transfer occurred, in the UTC ISO 8601 format. Note: After your initial POST request, this value will be null until KYT processes your transfer and you call a subsequent GET request. |
outputAddress |
String or null |
The destination address for funds within the transaction. Note: After your initial POST request, this value will be null until KYT processes your transfer and you call a subsequent GET request. |
externalId |
String | A Chainalysis-generated identifier of this transfer. |
Register a withdrawal attempt
ENDPOINT
POST
/v2/users/{userId}/withdrawal-attempts
The following is an example request to register a withdrawal attempt:
curl -X POST 'https://api.chainalysis.com/api/kyt/v2/users/user0003/withdrawal-attempts' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Content-type: application/json' \
--data '{
"network": "Cardano",
"asset": "ADA",
"address": "1EM4e8eu2S2RQrbS8C6aYnunWpkAwQ8GtG",
"attemptIdentifier": "attempt1",
"assetAmount": 5.0,
"assetPrice": 1000.0,
"assetDenomination": "USD",
"attemptTimestamp": "2020-12-09T17:25:40.008307"
}'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
}
withdrawal_attempt_data = {
'network': 'Cardano',
'asset': 'ADA',
'address': '1EM4e8eu2S2RQrbS8C6aYnunWpkAwQ8GtG',
'attemptIdentifier': 'attempt1',
'assetAmount': 5,
'assetPrice': 1000,
'assetDenomination': 'USD',
'attemptTimestamp': '2020-12-09T17:25:40.008307',
}
response = requests.post('https://api.chainalysis.com/api/kyt/v2/users/user0003/withdrawal-attempts', headers=headers, json=withdrawal_attempt_data)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v2/users/user0003/withdrawal-attempts', {
method: 'POST',
headers: {
'Token': '{YOUR_API_KEY}',
'Content-type': 'application/json'
},
body: JSON.stringify({
'network': 'Cardano',
'asset': 'ADA',
'address': '1EM4e8eu2S2RQrbS8C6aYnunWpkAwQ8GtG',
'attemptIdentifier': 'attempt1',
'assetAmount': 5,
'assetPrice': 1000,
'assetDenomination': 'USD',
'attemptTimestamp': '2020-12-09T17:25:40.008307'
})
});
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v2/users/user0003/withdrawal-attempts");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("POST");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Content-type", "application/json");
httpConn.setDoOutput(true);
OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream());
writer.write("{\n \"network\": \"Cardano\",\n \"asset\": \"ADA\", \n \"address\": \"1EM4e8eu2S2RQrbS8C6aYnunWpkAwQ8GtG\", \n \"attemptIdentifier\": \"attempt1\", \n \"assetAmount\": 5.0, \n \"assetPrice\": 1000.0, \n \"assetDenomination\": \"USD\", \n \"attemptTimestamp\": \"2020-12-09T17:25:40.008307\"\n}");
writer.flush();
writer.close();
httpConn.getOutputStream().close();
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
This endpoint registers a withdrawal attempt. Once you make a request for a withdrawal attempt, KYT stores it and begins processing.
For a valid address, KYT will process the withdrawal attempt immediately after receiving the POST request.
This endpoint returns the below response:
202
: Indicates KYT has accepted your withdrawal attempt and will process your request.
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
userId |
String | Yes | A unique string that identifies your user. If creating a withdrawal attempt for an existing user, use their existing userId . |
Request body schema
Property | Type | Required | Description |
---|---|---|---|
network |
String | Yes | The blockchain network this withdrawal attempt occurred on. See our supported networks list for the approrpiate values. |
asset |
String | Yes | The cryptocurrency or token used in this withdrawal attempt. The value must be the asset's symbol, e.g., BTC for Bitcoin, ETH for Ether, UNI for Uniswap etc. |
address |
String | Yes | The cryptocurrency address of the wallet to which the withdrawal attempt is being made. |
attemptIdentifier |
String | Yes | A unique string that identifies this withdrawal attempt. Keys can be any valid string but must be unique for every distinct withdrawal attempt. |
assetAmount |
Number | Yes | The amount of cryptocurrency funds used in this withdrawal attempt. |
assetPrice |
Number | No | The USD price of the asset at the time of the withdrawal attempt. Note: If you supply an assetPrice you must also supply the assetDenomination . |
assetDenomination |
String | No | The denomination of the assetPrice property. Available only as USD . Note: If you supply the assetDenomination you must also supply an assetPrice . |
attemptTimestamp |
String | Yes | The timestamp when the withdrawal attempt occurred, in the UTC ISO 8601 format. |
Response schema
The following is an example JSON response body from the above request:
{
"asset": "ADA",
"network": "CARDANO",
"address": "1EM4e8eu2S2RQrbS8C6aYnunWpkAwQ8GtG",
"attemptIdentifier": "attempt1",
"assetAmount": 5.0,
"usdAmount": null,
"updatedAt": "2022-04-27T20:21:43.803+00:00",
"externalId": "e27adb25-7344-3ae3-9c80-6b4879a85826"
}
A successful request will return the following JSON response and properties:
Property | Type | Description |
---|---|---|
asset |
String | An echo back of the asset you defined in the request body. This is the asset's symbol, e.g., BTC for Bitcoin, ETH for Ether, etc. |
network |
String | An echo back of the blockchain network you defined in the request body. |
address |
String | An echo back of the address you defined in the request body. |
attemptIdentifier |
String | An echo back of the identifier you created in the request body. |
assetAmount |
Number | An echo back of the asset amount defined in the request body. |
usdAmount |
Number or null |
The US Dollar amount of funds used in this transfer. Note: After your initial POST request, this value will be null until KYT processes your transfer and you call a subsequent GET request. |
updatedAt |
String or null |
The timestamp when the withdrawal attempt was last updated, in the UTC ISO 8601 format. Note: After your initial POST request, this value may be null until KYT processes your withdrawal attempt. If null , poll the summary endpoint until updatedAt returns a value. |
externalId |
String | A Chainalysis-generated identifier of the withdrawal attempt. |
Transfers
These endpoints return a transfer's summary, direct exposure, alerts, and any available network identifications.
GET /v2/transfers/{externalId}
GET /v2/transfers/{externalId}/exposures
GET /v2/transfers/{externalId}/alerts
GET /v2/transfers/{externalId}/network-identifications
Get a summary
ENDPOINT
GET
/v2/transfers/{externalId}
The following is an example request to retrieve a summary for a registered transfer:
curl -X GET 'https://api.chainalysis.com/api/kyt/v2/transfers/393905a7-bb96-394b-9e20-3645298c1079' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
response = requests.get('https://api.chainalysis.com/api/kyt/v2/transfers/393905a7-bb96-394b-9e20-3645298c1079', headers=headers)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v2/transfers/393905a7-bb96-394b-9e20-3645298c1079', {
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json'
}
});
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v2/transfers/393905a7-bb96-394b-9e20-3645298c1079");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
This endpoint returns the summary of the transfer you registered in your POST /transfers
request.
Note: You must register your transfer using the Transfer Registration endpoint before you implement the below.
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
externalId |
String | Yes | The Chainalysis-generated identifier of this transfer. This is returned in the response body of the POST /transfers request. |
Request body schema
This call has no accompanying request body.
Response schema
The following is an example JSON response body from the above request:
{
"updatedAt": "2022-03-16T16:17:19.613215",
"asset": "BTC",
"network": "BITCOIN",
"transferReference": "9f318afbad2a183f97750bc51a75b582ad8f9e9cbfb50401148857ca27cde10c:17A16QmavnUfCW11DAApiJxp7ARnxN5pGX",
"tx": "9f318afbad2a183f97750bc51a75b582ad8f9e9cbfb50401148857ca27cde10c",
"idx": 2,
"usdAmount": 722091.75,
"assetAmount": 11.35113454,
"timestamp": "2021-11-16T18:33:33.000+00:00",
"outputAddress": "17A16QmavnUfCW11DAApiJxp7ARnxN5pGX",
"externalId": "154f96d1-4fa8-32fb-9db8-c8922b862476"
}
Tip: If the updatedAt
property returns null
, KYT has not finished processing the transfer and other properties will also return null
. We suggest you poll this GET request until the property populates.
A successful request will return the following JSON response and properties for a given externalId
:
Property | Type | Description |
---|---|---|
updatedAt |
String or null |
The timestamp of when the transfer was last updated, in the UTC ISO 8601 format. If null , the transfer has not yet processed, or couldn’t process as it has not appeared in the blockchain. |
asset |
String or null |
The asset used in the transfer. |
network |
String or null |
The blockchain network the transfer occurred on. |
transferReference |
String or null |
The transferReference you defined in the POST call's request body. |
tx |
String or null |
The transaction hash of the transfer. |
idx |
Integer or null |
The transfer's index. If null , either KYT has not finished processing the transfer or the transaction does not contain indexed transfers. |
usdAmount |
Number or null |
The US Dollar amount of funds used in this transfer. If null , either KYT has not finished processing the transfer or the information was not sent to Chainalysis in the GET request. |
assetAmount |
Number or null |
The amount of cryptocurrency funds used in this transfer. If null , either KYT has not finished processing the transfer or the information was not sent to Chainalysis in the GET request. |
timestamp |
String or null |
The blockchain timestamp when the transfer occurred, in the UTC ISO 8601 format. If null , either KYT has not finished processing the transfer or the information was not sent to Chainalysis in the GET request. |
outputAddress |
String or null |
The destination address for funds within the transaction. If null , either KYT has not finished processing the transfer or the information was not sent to Chainalysis in the GET request. |
externalId |
String | The Chainalysis-generated identifier of this transfer. This is returned in the response body of the POST /transfers request. |
Get direct exposure
ENDPOINT
GET
/v2/transfers/{externalId}/exposures
The following is an example request to retrieve direct exposure from a registered
BTC
transfer:
curl -X GET 'https://api.chainalysis.com/api/kyt/v2/transfers/ae5e6b88-ad59-3688-ac2d-2b5a251e799e/exposures' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
response = requests.get('https://api.chainalysis.com/api/kyt/v2/transfers/ae5e6b88-ad59-3688-ac2d-2b5a251e799e/exposures', headers=headers)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v2/transfers/ae5e6b88-ad59-3688-ac2d-2b5a251e799e/exposures', {
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json'
}
});
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v2/transfers/ae5e6b88-ad59-3688-ac2d-2b5a251e799e/exposures");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
This endpoint returns any available direct exposure information for a registered transfer. This endpoint only returns direct exposure for transfers of mature and emerging assets. If the transfer was of a pre-growth asset, the endpoint returns null
values.
You should call this endpoint after KYT has processed your transfer. To verify whether KYT has processed your transfer, call the Transfers summary endpoint, then check whether the updatedAt
response body property contains a value. If updatedAt
returns null
, KYT has not yet processed the transfer.
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
externalId |
String | Yes | The Chainalysis-generated identifier of this transfer. This is returned in the response body of the POST /transfers request. |
Request body schema
This call has no accompanying request body.
Response schema
The following is an example JSON response body from the above request:
{
"direct": {
"name": "BitPay.com",
"category": "merchant services"
}
}
A successful request will return the following JSON response and properties for a given externalId
:
Property | Type | Description |
---|---|---|
direct |
Object or null |
Contains information of a given cluster's direct exposure. If null , the entity has neither a name and category or Chainalysis has identified neither the name and category. |
direct.name |
String | The name of a given cluster's counterparty as identified by Chainalysis. If null , the entity has no name or Chainalysis has not identified the entity. |
direct.category |
String | The category of a given cluster's counterparty as identified by Chainalysis. If null , the entity has no category or Chainalysis has not identified the entity's category. |
Get alerts
ENDPOINT
GET
/v2/transfers/{externalId}/alerts
The following is an example request to retrieve alert information of a registered transfer:
curl -X GET 'https://api.chainalysis.com/api/kyt/v2/transfers/393905a7-bb96-394b-9e20-3645298c1079/alerts' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
response = requests.get('https://api.chainalysis.com/api/kyt/v2/transfers/393905a7-bb96-394b-9e20-3645298c1079/alerts', headers=headers)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v2/transfers/393905a7-bb96-394b-9e20-3645298c1079/alerts', {
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json'
}
});
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v2/transfers/393905a7-bb96-394b-9e20-3645298c1079/alerts");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
This endpoint returns a transfer's alerts, if available.
You will receive a successful response after KYT has processed your transfer.
To ensure a successful response, call the Transfer Summary endpoint to view if the updatedAt
property in your response body is populated before examining your transfer's alerts.
Note: Alerts for transfers generate according to your organization's alert rules. Learn more about how to define these rules in the KYT UI here.
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
externalId |
String | Yes | The Chainalysis-generated identifier of this transfer. This is returned in the response body of the POST /transfers request. |
Request body schema
This call has no accompanying request body.
Response schema
The following is an example JSON response body from the above request:
{
"alerts": [
{
"alertLevel": "HIGH",
"category": "custom address",
"service": "Play Royal",
"externalId": "906ff226-8b64-11eb-8e52-7b35a3dc1742",
"alertAmount": 5000.00,
"exposureType": "DIRECT"
}
]
}
A successful request will return the following JSON response and properties for a given externalId
:
Property | Type | Description |
---|---|---|
alerts |
Array | Contains alert information for a given transfer. If no alerts have been generated, the array will be empty. |
alerts.alertLevel |
String | Defines the alert's risk as SEVERE , HIGH , MEDIUM , or LOW . |
alerts.category |
String or null |
The entity category of the service as identified by Chainalysis. If null returns, Chainalysis has not yet identified the counterparty's entity category. |
alerts.service |
String or null |
The name of the service as defined by Chainalysis. If null returns, Chainalysis has not yet identified the service's name. |
alerts.externalId |
String | A unique identify of the alert. |
alerts.alertAmount |
Number | The USD amount that caused the alert to trigger. |
alerts.exposureType |
String | Defines the exposure direction as DIRECT or INDIRECT . |
Get network identifications
ENDPOINT
GET
/v2/transfers/{externalId}/network-identifications
The following is an example request to retrieve the count of network identifications from a registered transfer:
curl -X GET 'https://api.chainalysis.com/api/kyt/v2/transfers/94d72448-6963-3536-9996-1637f6756f0b/network-identifications' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
response = requests.get('https://api.chainalysis.com/api/kyt/v2/transfers/94d72448-6963-3536-9996-1637f6756f0b/network-identifications', headers=headers)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v2/transfers/94d72448-6963-3536-9996-1637f6756f0b/network-identifications', {
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json'
}
});
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v2/transfers/94d72448-6963-3536-9996-1637f6756f0b/network-identifications");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
This endpoint returns any available network identifications for a registered transfer of a pre-growth asset. If the transfer was of a mature or emerging asset, the endpoint will return a 400 error.
You should call this endpoint after KYT has processed your transfer. To verify whether KYT has processed your transfer, call the Transfers summary endpoint, then check whether the updatedAt
response body property contains a value. If updatedAt
returns null
, KYT has not yet processed the transfer.
Learn more about Network Identifications here.
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
externalId |
String | Yes | The Chainalysis-generated identifier of this transfer. This is returned in the response body of the POST /transfers request. |
Request body schema
This call has no accompanying request body.
Response schema
The following is an example JSON response body from the above request:
{
"count": 1,
"networkIdentificationOrgs": [
{
"name": "XYZ Cryptocurrency Exchange"
}
]
}
A successful request will return the following JSON response and properties for a given externalId
:
Property | Type | Description |
---|---|---|
count |
Number | The number of other organizations in the Chainalysis's data network that registered the transfer in the opposing direction. For example, if you registered the transfer in the SENT direction, you will receive the count of registered transfers in the RECEIVED direction. |
networkIdentificationOrg |
Array | Contains the names of the organizations that registered the transfer in Chainalysis's data network. If there are no network identifications, the array will be empty. |
networkIdentificationOrg.name |
String | The name of the organization that registered the transfer in Chainalysis's data network. |
Withdrawal attempts
These endpoints return a withdrawal attempt's summary, direct exposure, alerts, and any available network identifications.
GET /v2/withdrawal-attempts/{externalId}
GET /v2/withdrawal-attempts/{externalId}/exposures
GET /v2/withdrawal-attempts/{externalId}/alerts
GET /v2/withdrawal-attempts/{externalId}/high-risk-addresses
GET /v2/withdrawal-attempts/{externalId}/network-identifications
Get a summary
ENDPOINT
GET
/v2/withdrawal-attempts/{externalId}
The following is an example request to retrieve a summary for a withdrawal attempt:
curl -X GET 'https://api.chainalysis.com/api/kyt/v2/withdrawal-attempts/e27adb25-7344-3ae3-9c80-6b4879a85826' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
response = requests.get('https://api.chainalysis.com/api/kyt/v2/withdrawal-attempts/e27adb25-7344-3ae3-9c80-6b4879a85826', headers=headers)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v2/withdrawal-attempts/e27adb25-7344-3ae3-9c80-6b4879a85826', {
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json'
}
});
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v2/withdrawal-attempts/e27adb25-7344-3ae3-9c80-6b4879a85826");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
This endpoint returns the summary of the withdrawal attempt you registered in your POST /withdrawal-attempts
request.
Note: You must register your withdrawal attempt using the Withdrawal Attempt Registration endpoint before you implement the below.
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
externalId |
String | Yes | The Chainalysis-generated identifier of this withdrawal attempt. This is returned in the response body of the POST /withdrawal-attempts request. |
Request body schema
This call has no accompanying request body.
Response schema
The following is an example JSON response body from the above request:
{
"asset": "BTC",
"network": "BITCOIN",
"address": "1EM4e8eu2S2RQrbS8C6aYnunWpkAwQ8GtG",
"attemptIdentifier": "attempt11",
"assetAmount": 5.0,
"usdAmount": 5000.00,
"updatedAt": "2021-03-23T15:21:10.835+00:00",
"externalId": "e27adb25-7344-3ae3-9c80-6b4879a85826"
}
Tip: If the updatedAt
property returns null
, KYT has not finished processing the withdrawal attempt and other properties will also return null
. We suggest you poll this GET request until the property populates.
A successful request will return the following JSON response and properties for a given externalId
:
Property | Type | Description |
---|---|---|
asset |
String or null |
The asset used in the withdrawal attempt. |
network |
String or null |
The blockchain network the withdrawal attempt occurred on. |
address |
String or null |
The address where the withdrawal attempt is being made. |
attemptIdentifier |
String or null |
An echoback of the attemptIdentifier you created in the POST request. |
assetAmount |
Number or null |
The amount of cryptocurrency funds used in this withdrawal attempt. |
usdAmount |
Number or null |
The amount of US Dollars used in this withdrawal attempt. |
updatedAt |
String or null |
The timestamp of when the withdrawal attempt was last processed, in the UTC ISO 8601 format. If null , the withdrawal attempt has not yet processed. |
externalId |
String | A Chainalysis-generated identifier of this withdrawal attempt. This is returned in the response body of the POST request. |
Get direct exposure
ENDPOINT
GET
/v2/withdrawal-attempts/{externalId}/exposures
The following is an example request to retrieve direct exposure information from a registered
BTC
withdrawal attempt:
curl -X GET 'https://api.chainalysis.com/api/kyt/v2/withdrawal-attempts/e27adb25-7344-3ae3-9c80-6b4879a85826/exposures' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
response = requests.get('https://api.chainalysis.com/api/kyt/v2/withdrawal-attempts/e27adb25-7344-3ae3-9c80-6b4879a85826/exposures', headers=headers)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v2/withdrawal-attempts/e27adb25-7344-3ae3-9c80-6b4879a85826/exposures', {
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json'
}
});
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v2/withdrawal-attempts/e27adb25-7344-3ae3-9c80-6b4879a85826/exposures");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
This endpoint returns any available direct exposure information for a withdrawal attempt. This endpoint only returns direct exposure for withdrawal attempts of mature and emerging assets. If the withdrawal attempt was of a pre-growth asset, the endpoint returns null
values.
You should call this endpoint after KYT has processed your withdrawal attempt. To verify whether KYT has processed your withdrawal attempt, call the Withdrawal attempts summary endpoint, then check whether the updatedAt
response body property contains a value. If updatedAt
returns null
, KYT has not yet processed the withdrawal attempt.
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
externalId |
String | Yes | The Chainalysis-generated identifier of this withdrawal attempt. This is returned in the response body of the POST /withdrawal-attempts request. |
Request body schema
This call has no accompanying request body.
Response schema
The following is an example JSON response body from the above request:
{
"direct": {
"name": null,
"category": null
}
}
A successful request will return the following JSON response and properties for a given externalId
:
Property | Type | Description |
---|---|---|
direct |
Object | Contains information of a given cluster's direct exposure. |
direct.name |
String | The name of a given cluster's counterparty as identified by Chainalysis. If null , the entity has no name or Chainalysis has not identified the entity. |
direct.category |
String | The category of a given cluster's counterparty as identified by Chainalysis. If null , the entity has no category or Chainalysis has not identified the entity's category. |
Get alerts
ENDPOINT
GET
/v2/withdrawal-attempts/{externalId}/alerts
The following is an example request to retrieve alert information from a registered withdrawal attempt:
curl -X GET 'https://api.chainalysis.com/api/kyt/v2/withdrawal-attempts/eb67cbdc-8bea-11eb-83d5-378cec3052c2/alerts' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
response = requests.get('https://api.chainalysis.com/api/kyt/v2/withdrawal-attempts/eb67cbdc-8bea-11eb-83d5-378cec3052c2/alerts', headers=headers)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v2/withdrawal-attempts/eb67cbdc-8bea-11eb-83d5-378cec3052c2/alerts', {
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json'
}
});
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v2/withdrawal-attempts/eb67cbdc-8bea-11eb-83d5-378cec3052c2/alerts");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
This endpoint returns a withdrawal attempt's alerts, if available.
You will receive a successful response after KYT has processed your withdrawal attempt.
To ensure a successful response, call the Withdrawal Attempt Summary endpoint to view if the updatedAt
property in your response body is populated before examining your withdrawal attempt's alerts.
Note: Alerts for withdrawal attempts generate according to your organization's alert rules. Learn more about how to define these rules in the KYT UI here.
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
externalId |
String | Yes | The Chainalysis-generated identifier of this withdrawal attempt. This is returned in the response body of the POST /withdrawal-attempts request. |
Request body schema
This call has no accompanying request body.
Response schema
The following is an example JSON response body from the above request:
{
"alerts": [
{
"alertAmount": 5000.0,
"alertLevel": "HIGH",
"category": "custom address",
"exposureType": "DIRECT",
"externalId": "eb67cbdc-8bea-11eb-83d5-378cec3052c2",
"service": "Play Royal"
}
]
}
A successful request will return the following JSON response and properties for a given externalId
:
Property | Type | Description |
---|---|---|
alerts |
Array | Contains alert information for a given withdrawal attempt. |
alerts.alertAmount |
Number | The USD amount that caused the alert to trigger. |
alerts.alertLevel |
String | Defines the alert's risk as SEVERE , HIGH , MEDIUM , or LOW . |
alerts.category |
String or null |
The entity category of the service as identified by Chainalysis. If null returns, Chainalysis has not yet identified the counterparty's entity category. |
alerts.exposureType |
String | Defines the exposure direction as DIRECT or INDIRECT . |
alerts.externalId |
String | A unique identifier of the alert. |
alerts.service |
String or null |
The name of the service as defined by Chainalysis. If null returns, Chainalysis has not yet identified the service's name. |
Get address identifications
ENDPOINT
GET
/v2/withdrawal-attempts/{externalId}/high-risk-addresses
The following is an example request to retrieve custom address information from a registered withdrawal attempt:
curl -X GET 'https://api.chainalysis.com/api/kyt/v2/withdrawal-attempts/eb67cbdc-8bea-11eb-83d5-378cec3052c2/high-risk-addresses' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
response = requests.get('https://api.chainalysis.com/api/kyt/v2/withdrawal-attempts/eb67cbdc-8bea-11eb-83d5-378cec3052c2/high-risk-addresses', headers=headers)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v2/withdrawal-attempts/eb67cbdc-8bea-11eb-83d5-378cec3052c2/high-risk-addresses', {
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json'
}
});
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v2/withdrawal-attempts/eb67cbdc-8bea-11eb-83d5-378cec3052c2/high-risk-addresses");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
This endpoint returns address identification for the withdrawal attempt, either a user’s upload of custom addresses or Chainalysis identified addresses.
You will receive a successful response after KYT has processed your withdrawal attempt.
To ensure a successful response, call the Withdrawal Attempt Summary endpoint to view if the updatedAt
property in your response body is populated before examining your withdrawal attempt's high risk address information.
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
externalId |
String | Yes | The Chainalysis-generated identifier of this withdrawal attempt. This is returned in the response body of the POST /withdrawal-attempts request. |
Request body schema
This call has no accompanying request body.
Response schema
The following is an example JSON response body for a withdrawal attempt with a Chainalysis Identification:
{
"chainalysisIdentifications": [
{
"addressName": "SANCTIONS: OFAC SDN: Xiaobing Yan",
"categoryName": "sanctions",
"description": "This specific address 12QtD5BFwRsdNsAZY76UVE1xyCGNTojH9h within the cluster has been identified as belonging to an individual on OFAC's SDN list. YAN, Xiaobing (Chinese Traditional: 顏曉兵; Chinese Simplified: 颜晓兵) (a.k.a. \"YAN, Steven\"; a.k.a. \"ZHOU, William\"), Wuhan, Hubei, China (Chinese Simplified: 武汉市, 湖北省, China; Chinese Traditional: 武漢市, 湖北省, China); DOB 25 Mar 1977; POB Wuhan City, Hubei, China; citizen China; Gender Male; Chinese Commercial Code 7346 2556 0365; Citizen's Card Number 421002197703250019 (China) (individual) [SDNTK]."
}
],
"customAddresses": []
}
The following is an example JSON response body for a withdrawal attempt with custom address information:
{
"chainalysisIdentifications": [],
"customAddresses": [
{
"addressName": "Play Royal",
"categoryName": "gambling",
"description": "This address belongs to playroyal.com"
}
]
}
A successful request will return the following JSON response and properties for a given externalId
:
Property | Type | Description |
---|---|---|
chainalysisIdentifications |
Array | A Chainalysis Identified Address, representing an address-level identification that is part of a larger cluster. The array will be empty if there are no Chainalysis Identifications. |
chainalysisIdentifications.addressName |
String | The name designated to the Chainalysis Identification. |
chainalysisIdentifications.categoryName |
String | The category of the Chainalysis Identification. |
chainalysisIdentifications.description |
String | The OSINT description of the Chainalysis Identification. |
customAddresses |
Array | Contains custom address information that matches the addresses in the withdrawal attempt. The array will be empty if there are no Custom Addresses. |
customAddresses.addressName |
String | The name of the address the custom address was registered under. |
customAddresses.categoryName |
String | The category of the custom address as identified by Chainalysis. |
customAddresses.description |
String | The description of the custom address. |
Get network identifications
ENDPOINT
GET
/v2/withdrawal-attempts/{externalId}/network-identifications
The following is an example request to retrieve the count of network identifications from a registered withdrawal attempt:
curl -X GET 'https://api.chainalysis.com/api/kyt/v2/withdrawal-attempts/159cc56a-ae87-3aab-a2fc-2d4f9f42885f/network-identifications' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
response = requests.get('https://api.chainalysis.com/api/kyt/v2/withdrawal-attempts/159cc56a-ae87-3aab-a2fc-2d4f9f42885f/network-identifications', headers=headers)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v2/withdrawal-attempts/159cc56a-ae87-3aab-a2fc-2d4f9f42885f/network-identifications', {
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json'
}
});
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v2/withdrawal-attempts/159cc56a-ae87-3aab-a2fc-2d4f9f42885f/network-identifications");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
This endpoint returns any available network identifications for a registered withdrawal attempt of a pre-growth asset. If the withdrawal attempt was of a mature or emerging asset, the endpoint will return a 400 error.
You should call this endpoint after KYT has processed your withdrawal attempt. To verify whether KYT has processed your withdrawal attempt, call the Withdrawal attempts summary endpoint, then check whether the updatedAt
response body property contains a value. If updatedAt
returns null
, KYT has not yet processed the withdrawal attempt.
Learn more about Network Identifications here.
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
externalId |
String | Yes | The Chainalysis-generated identifier of this withdrawal attempt. This is returned in the response body of the POST /withdrawal-attempts request. |
Request body schema
This call has no accompanying request body.
Response schema
The following is an example JSON response body from the above request:
{
"count": 1,
"networkIdentificationOrgs": [
{
"name": "XYZ Cryptocurrency Exchange"
}
]
}
A successful request will return the following JSON response and properties for a given externalId
:
Property | Type | Description |
---|---|---|
count |
Integer | The number of other organizations in Chainalysis's data network that registered the withdrawal attempt. |
networkIdentificationOrgs |
Array | Contains the names of the organizations that registered the withdrawal attempt in Chainalysis's data network. |
networkIdentificationOrgs.name |
String | The name of the organization that registered the withdrawal attempt in Chainalysis's data network. |
V1 endpoints
Received transfers
These endpoints either register or retrieve received transfers.
POST /v1/users/{userId}/transfers/received
GET /v1/users/{userId}/transfers/received
With them, you can perform the following actions:
- Register incoming transfers (deposits) to a user at your organization.
- Screen deposits for illicit (high risk) counterparties, and allow for programmatic alerts and action.
- Retrieve all received transfers used to calculate an individual users risk score.
For a list of networks that KYT currently supports, see Supported networks and assets.
Register a received transfer
ENDPOINT
POST
/v1/users/{userId}/transfers/received
The following is an example request to register a received Bitcoin transfer for user
005
using an output address in thetransferReference
.
curl -X POST 'https://api.chainalysis.com/api/kyt/v1/users/005/transfers/received' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '[
{
"network": "Bitcoin",
"asset": "BTC",
"transferReference": "43ed2f877b437153c78f8a2fd09b9852c65d53734590388e2698824e812a464f:12aEpHFWiogdNV1Bp6EizQwwXuV91LAn87"
}
]'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
received_transfer_data = [
{
'network': 'Bitcoin',
'asset': 'BTC',
'transferReference': '43ed2f877b437153c78f8a2fd09b9852c65d53734590388e2698824e812a464f:12aEpHFWiogdNV1Bp6EizQwwXuV91LAn87',
},
]
response = requests.post('https://api.chainalysis.com/api/kyt/v1/users/005/transfers/received', headers=headers, json=received_transfer_data)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v1/users/005/transfers/received', {
method: 'POST',
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify([
{
'network': 'Bitcoin',
'asset': 'BTC',
'transferReference': '43ed2f877b437153c78f8a2fd09b9852c65d53734590388e2698824e812a464f:12aEpHFWiogdNV1Bp6EizQwwXuV91LAn87'
}
])
});
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v1/users/005/transfers/received");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("POST");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
httpConn.setRequestProperty("Content-Type", "application/json");
httpConn.setDoOutput(true);
OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream());
writer.write("[\n {\n \t\"network\": \"Bitcoin\",\n \"asset\": \"BTC\",\n \t\"transferReference\": \"43ed2f877b437153c78f8a2fd09b9852c65d53734590388e2698824e812a464f:12aEpHFWiogdNV1Bp6EizQwwXuV91LAn87\"\n }\n]");
writer.flush();
writer.close();
httpConn.getOutputStream().close();
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
The following is an example request to register a received Bitcoin transfer for user
005
using an output index in thetransferReference
.
curl -X POST 'https://api.chainalysis.com/api/kyt/v1/users/005/transfers/received' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '[
{
"network": "Bitcoin",
"asset": "BTC",
"transferReference": "43ed2f877b437153c78f8a2fd09b9852c65d53734590388e2698824e812a464f:1"
}
]'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
received_transfer_data = [
{
'network': 'Bitcoin',
'asset': 'BTC',
'transferReference': '43ed2f877b437153c78f8a2fd09b9852c65d53734590388e2698824e812a464f:1',
},
]
response = requests.post('https://api.chainalysis.com/api/kyt/v1/users/005/transfers/received', headers=headers, json=received_transfer_data)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v1/users/005/transfers/received', {
method: 'POST',
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify([
{
'network': 'Bitcoin',
'asset': 'BTC',
'transferReference': '43ed2f877b437153c78f8a2fd09b9852c65d53734590388e2698824e812a464f:1'
}
])
});
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v1/users/005/transfers/received");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("POST");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
httpConn.setRequestProperty("Content-Type", "application/json");
httpConn.setDoOutput(true);
OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream());
writer.write("[\n {\n \t\"network\": \"Bitcoin\",\n \"asset\": \"BTC\",\n \t\"transferReference\": \"43ed2f877b437153c78f8a2fd09b9852c65d53734590388e2698824e812a464f:1\"\n }\n]");
writer.flush();
writer.close();
httpConn.getOutputStream().close();
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
userId |
String | Yes | A unique string that identifies your user associated with this transfer. If creating a transfer for an existing user, use their existing userId . |
Query parameters
Parameter | Type | Required | Description |
---|---|---|---|
bulkImport |
Boolean | No | Determines whether to register transfers in bulk. You can submit either true or false . If set to true , no real-time response is returned and the request executes faster. We suggest using this parameter if you do not require the real-time response. There is a limit of 100 transfers when bulkImport is false or unspecified and 1000 transfers when bulkImport is true. |
Request body schema
Property | Type | Required | Description |
---|---|---|---|
network |
String | Yes | The blockchain network this transfer occurred on. See our supported networks list for the approrpiate values. |
asset |
String | Yes | The cryptocurrency or token used in this transfer. The value must be the asset's symbol, e.g., BTC for Bitcoin, ETH for Ether, UNI for Uniswap etc. |
transferReference |
String | Yes | A combination of the transaction hash and output address or index of the transaction, separated with a colon. For example, {transaction_hash}:{output_address} or {transaction_hash}:{output_index} . Learn more about the transferReference property here. |
transferTimestamp |
String | No | The blockchain timestamp when the transfer occurred, in the UTC and ISO 8601 format. Note: Available only for pre-growth assets. |
assetAmount |
Number | No | The amount of cryptocurrency funds used in this transfer. Note: Available only for pre-growth assets. |
Response schema
The following is an example JSON response body from the above requests:
[
{
"transferReference": "2d9bfc3a47c2c9cfd0170198782979ed327442e5ed1c8a752bced24d490347d4:1H7aVb2RZiBmdbnzazQgVj2hWR3eEZPg6v",
"asset": "BTC",
"cluster": {
"name": "Coinbase.com",
"category": "exchange"
},
"rating": "lowRisk"
}
]
A successful request will return a 200 response, indicating KYT successfully registered and will process your transfer.
If the asset used in the transfer is from a mature network, the response will display a populated array of the below properties. If the asset used in the transfer is from an emerging or pre-growth network, the response will display an empty array. To get more information about these transfers, call the GET /v1/users/{userId}/transfers/received
endpoint.
Property | Type | Description |
---|---|---|
transferReference |
String | An echo back of the transfer reference you defined in the request body. |
asset |
String | An echo back of the asset type you defined in the request body. This is the asset's symbol, e.g., BTC for Bitcoin, ETH for Ether, etc. |
cluster |
Object or null |
The identification of a group of addresses estimated by Chainalysis to be controlled by a single entity. This value will be null if the cluster has not yet been identified. |
cluster.name |
String | The name of a named Cluster. |
cluster.category |
String | The category the named Cluster belongs to, ie. Exchange. |
rating |
String | The risk rating of the transfer, either highRisk , lowRisk , or unknown . The rating corresponds to the entity's category. Learn more about entities here and highRisk categories here . unknown means Chainalysis has not yet identified the entity or the entity is Unnamed. |
Get a received transfer
ENDPOINT
GET
/v1/users/{userId}/transfers/received
The following is an example request to retrieve all received transfers for user
BTC_01
curl -X GET 'https://api.chainalysis.com/api/kyt/v1/users/BTC_01/transfers/received' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
response = requests.get('https://api.chainalysis.com/api/kyt/v1/users/BTC_01/transfers/received', headers=headers)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v1/users/BTC_01/transfers/received', {
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json'
}
});
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v1/users/BTC_01/transfers/received");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
This endpoint retrieves all registered received transfers for a given userId
. Note that the response also returns received transfers registered with the POST /v2/users/{userId}/transfers
endpoint.
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
userId |
String | Yes | A unique string of the user associated with this transfer. |
Query parameters
Parameter | Type | Required | Description |
---|---|---|---|
limit |
Integer | No | A limit on the number of objects returned. |
offset |
Integer | No | The position of the first object returned in the response. The default is 0, which starts the page at the first result. |
Request body schema
This call has no accompanying request body.
Response schema
The following is an example JSON response body from the above request:
{
"limit": 100,
"offset": 0,
"total": 1,
"data": [
{
"asset": "BTC",
"transferReference": "2d9bfc3a47c2c9cfd0170198782979ed327442e5ed1c8a752bced24d490347d4:1H7aVb2RZiBmdbnzazQgVj2hWR3eEZPg6v",
"amount": "4",
"amountUSD": "561.64",
"timestamp": "2021-07-07T18:29:14Z",
"rating": "lowRisk",
"cluster": {
"name": "Coinbase.com",
"category": "exchange"
}
}
]
}
A successful request will return the following JSON response and properties for a given userId
.
Property | Type | Description |
---|---|---|
limit |
Integer | An echo back of the limit query parameter. |
offset |
Integer | An echo back of the offset query parameter. |
total |
Integer | The total number of transfer objects returned. |
data |
Array | An array containing transfer data. |
data.asset |
String | The asset associated with the Transaction associated with the user. |
data.transferReference |
String | The transferReference you defined in the POST call's request body. |
data.amount |
Number | The amount of cryptocurrency funds used in this transfer. |
data.amountUSD |
Number | The US Dollar amount of funds used in this transfer. |
data.timestamp |
String | The blockchain timestamp when the transfer occurred, in the UTC and ISO 8601 format. |
data.rating |
String | The risk rating of the transfer, either highRisk , lowRisk , or unknown . The rating corresponds to the entity's category. Learn more about entities here and highRisk categories here . unknown means Chainalysis has not yet identified the entity or the entity is Unnamed. |
data.cluster |
Object or null |
The identification of a group of addresses estimated by Chainalysis to be controlled by a single entity. This value will be null if the cluster has not yet been identified. |
data.cluster.name |
String | The name of a named Cluster. |
data.cluster.category |
String | The category the named Cluster belongs to, e.g., an exchange. |
Sent transfers
These endpoints either register or retrieve sent transfers.
POST /v1/users/{userId}/transfers/sent
GET /v1/users/{userId}/transfers/sent
With them, you can:
- Register outgoing transfers (withdrawals) from users at your organization
- Call directly after the WithdrawalAddress endpoint for transfers executed after pre-screening
- Retrieve all sent transfers used in the calculation for a users risk score
For a list of networks that KYT currently supports, see Supported networks and assets.
Register a sent transfer
ENDPOINT
POST
/v1/users/{userId}/transfers/sent
The following is an example request to register a sent Bitcoin transfer for the user
006
using an output address in thetransferReference
.
curl -X POST 'https://api.chainalysis.com/api/kyt/v1/users/006/transfers/sent' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '[
{
"network": "Bitcoin",
"asset": "BTC",
"transferReference": "7c2238d4de91472061d66f918bda68541f33bd84ce994bcf191cd1315fa41118:16HgQB937BRDk3PyS9nSUuHE2P6CbjNuAe"
}
]'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
sent_transfer_data = [
{
'network': 'Bitcoin',
'asset': 'BTC',
'transferReference': '7c2238d4de91472061d66f918bda68541f33bd84ce994bcf191cd1315fa41118:16HgQB937BRDk3PyS9nSUuHE2P6CbjNuAe',
},
]
response = requests.post('https://api.chainalysis.com/api/kyt/v1/users/006/transfers/sent', headers=headers, json=sent_transfer_data)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v1/users/006/transfers/sent', {
method: 'POST',
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify([
{
'network': 'Bitcoin',
'asset': 'BTC',
'transferReference': '7c2238d4de91472061d66f918bda68541f33bd84ce994bcf191cd1315fa41118:16HgQB937BRDk3PyS9nSUuHE2P6CbjNuAe'
}
])
});
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v1/users/006/transfers/sent");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("POST");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
httpConn.setRequestProperty("Content-Type", "application/json");
httpConn.setDoOutput(true);
OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream());
writer.write("[\n {\n \t\"network\": \"Bitcoin\",\n \"asset\": \"BTC\",\n \t\"transferReference\": \"7c2238d4de91472061d66f918bda68541f33bd84ce994bcf191cd1315fa41118:16HgQB937BRDk3PyS9nSUuHE2P6CbjNuAe\"\n }\n]");
writer.flush();
writer.close();
httpConn.getOutputStream().close();
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
The following is an example request to register a sent Bitcoin transfer for the user
006
using an output index in thetransferReference
.
curl -X POST 'https://api.chainalysis.com/api/kyt/v1/users/006/transfers/sent' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '[
{
"network": "Bitcoin",
"asset": "BTC",
"transferReference": "7c2238d4de91472061d66f918bda68541f33bd84ce994bcf191cd1315fa41118:0"
}
]'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
sent_transfer_data = [
{
'network': 'Bitcoin',
'asset': 'BTC',
'transferReference': '7c2238d4de91472061d66f918bda68541f33bd84ce994bcf191cd1315fa41118:0',
},
]
response = requests.post('https://api.chainalysis.com/api/kyt/v1/users/006/transfers/sent', headers=headers, json=sent_transfer_data)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v1/users/006/transfers/sent', {
method: 'POST',
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify([
{
'network': 'Bitcoin',
'asset': 'BTC',
'transferReference': '7c2238d4de91472061d66f918bda68541f33bd84ce994bcf191cd1315fa41118:0'
}
])
});
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v1/users/006/transfers/sent");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("POST");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
httpConn.setRequestProperty("Content-Type", "application/json");
httpConn.setDoOutput(true);
OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream());
writer.write("[\n {\n \t\"network\": \"Bitcoin\",\n \"asset\": \"BTC\",\n \t\"transferReference\": \"7c2238d4de91472061d66f918bda68541f33bd84ce994bcf191cd1315fa41118:0\"\n }\n]");
writer.flush();
writer.close();
httpConn.getOutputStream().close();
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
userId |
String | Yes | A unique string that identifies your user associated with this transfer. If creating a transfer for an existing user, use their existing userId . |
Request body schema
Property | Type | Required | Description |
---|---|---|---|
network |
String | Yes | The network this transfer occurred on. See our supported networks list for the approrpiate values. |
asset |
String | Yes | The cryptocurrency or token used in this transfer. The value must be the asset's symbol, e.g., BTC for Bitcoin, ETH for Ether, UNI for Uniswap etc. |
transferReference |
String | Yes | A combination of the transaction hash and output address or index of the transaction, separated with a colon. For example, {transaction_hash}:{output_address} or {transaction_hash}:{output_index} . Learn more about the transferReference property here. |
transferTimestamp |
String | No | The blockchain timestamp when the transfer occurred, in the UTC ISO 8601 format. Note: Available only for pre-growth assets. |
assetAmount |
Number | No | The amount of cryptocurrency funds used in this transfer. Note: Available only for pre-growth assets. |
Response schema
The above requests return an empty JSON response.
The response will be an empty zero byte response, indicated by a response header Content-Length: 0
.
Get a sent transfer
ENDPOINT
GET
/v1/users/{userId}/transfers/sent
The following is an example request for retrieving all sent transfers for user
BTC_01
.
curl -X GET 'https://api.chainalysis.com/api/kyt/v1/users/BTC_01/transfers/sent' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
response = requests.get('https://api.chainalysis.com/api/kyt/v1/users/BTC_01/transfers/sent', headers=headers)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v1/users/BTC_01/transfers/sent', {
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json'
}
});
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v1/users/BTC_01/transfers/sent");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
This endpoint retrieves all registered sent transfers for a given userId
. Note that the response also returns sent transfers registered with the POST /v2/users/{userId}/transfers
endpoint.
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
userId |
String | Yes | A unique string of the user associated with this transfer. |
Query parameters
Parameter | Type | Required | Description |
---|---|---|---|
limit |
Integer | No | A limit on the number of objects returned. If unspecified, the default is 100. |
offset |
Integer | No | The position of the first object returned in the response. The default is 0, which starts the page at the first result. |
Request body schema
This call has no accompanying request body.
Response schema
The following is an example JSON response body from the above request:
{
"limit": 100,
"offset": 0,
"total": 1,
"data": [
{
"asset": "ETH",
"transferReference": "0x41af697d34230e084ff5fb74fab30fc878a14567ac8f97573ba98c401adc4d97:0x30FF461f7f077E6E18CE0670D352c0c54CA3D102",
"amount": 6.31049917,
"amountUSD": 1195.71,
"timestamp": "2019-09-16T09:36:09Z",
"cluster": {
"name": "Kraken.com",
"category": "exchange"
}
}
]
}
A successful request will return the following properties in JSON format for a given userId
.
Property | Type | Description |
---|---|---|
limit |
Integer | An echo back of the limit query parameter. |
offset |
Integer | An echo back of the offset query parameter. |
total |
Integer | The total number of transfer objects returned. |
data |
Array | An array containing transfer data. |
data.asset |
String | The asset associated with the Transaction associated with the user. |
data.transferReference |
String | The <transaction_hash:output_address OR index> combination associated with the transaction. |
data.amount |
Number | The amount of cryptocurrency funds used in this transfer. |
data.amountUSD |
Number | The US Dollar amount of funds used in this transfer. |
data.timestamp |
String | The timestamp when the transfer occurred, in the UTC and ISO 8601 format. |
data.cluster |
Object or null |
The identification of a group of addresses estimated by Chainalysis to be controlled by a single entity. This value will be null if the cluster has not yet been identified. |
data.cluster.name |
String | The name of a named Cluster. |
data.cluster.category |
String | The category the named Cluster belongs to, ie. Exchange. |
Withdrawal pre-screening
These endpoints allow you to register, retrieve, or delete a withdrawal address.
POST /v1/users/{userId}/withdrawaladdresses
GET /v1/users/{userId}/withdrawaladdresses
DELETE /v1/users/{userId}/withdrawaladdresses/{asset}/{address}
With them, you can:
- Pre-screen in real-time for counterparty risk within your withdrawal workflow.
- Receive a risk rating for known counterparties.
- Determine to either proceed with a transfers/sent API call to register the transaction for a given users profile.
Note that KYT updates risk for transfers on a continuous basis, but does not update withdrawal addresses on a continuous basis. The /withdrawaladdresses
endpoints return the cluster information that was available when the API was called. For continuous monitoring, you should register any withdrawals as a sent transfer.
For a list of networks that KYT supports, see Supported networks and assets.
Register withdrawal addresses
ENDPOINT
POST
/v1/users/{userId}/withdrawaladdresses
This endpoint registers a withdrawal addresses and returns a risk rating for a single User ID.
The following is an example request to register a Bitcoin withdrawal address of
1MCPcGbnrv4Uv9HcrjoQwzJaEFGrwzE6Pk
for user0001
:
curl -X POST 'https://api.chainalysis.com/api/kyt/v1/users/0001/withdrawaladdresses' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '[
{
"network": "Bitcoin",
"asset": "BTC",
"address": "1MCPcGbnrv4Uv9HcrjoQwzJaEFGrwzE6Pk"
}
]'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
register_withdrawal_data = [
{
'network': 'Bitcoin',
'asset': 'BTC',
'address': '1MCPcGbnrv4Uv9HcrjoQwzJaEFGrwzE6Pk',
},
]
response = requests.post('https://api.chainalysis.com/api/kyt/v1/users/0001/withdrawaladdresses', headers=headers, json=register_withdrawal_data)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v1/users/0001/withdrawaladdresses', {
method: 'POST',
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify([
{
'network': 'Bitcoin',
'asset': 'BTC',
'address': '1MCPcGbnrv4Uv9HcrjoQwzJaEFGrwzE6Pk'
}
])
});
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v1/users/0001/withdrawaladdresses");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("POST");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
httpConn.setRequestProperty("Content-Type", "application/json");
httpConn.setDoOutput(true);
OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream());
writer.write("[\n {\n \"network\": \"Bitcoin\",\n \"asset\": \"BTC\",\n \"address\": \"1MCPcGbnrv4Uv9HcrjoQwzJaEFGrwzE6Pk\"\n }\n]");
writer.flush();
writer.close();
httpConn.getOutputStream().close();
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
userId |
String | Yes | The userId to associate with the withdrawal address. |
Query parameters
Parameter | Type | Required | Description |
---|---|---|---|
bulkImport |
Boolean | No | Determines whether to register withdrawal addresses in bulk. You can submit either true or false . If set to true , no real-time response is returned and the request executes faster. We suggest using this parameter if you do not require the real-time response. There is a limit of 100 items when bulkImport is false or not specified and 1000 items when bulkImport is true . |
If using bulkImport
, it should be passed in as a parameter in the URL, not sent in the request body:
https://api.chainalysis.com/api/kyt/v1/users/BTC_01/withdrawaladdresses?bulkImport=true
Request body schema
Property | Type | Required | Description |
---|---|---|---|
network |
String | Yes | The blockchain network this withdrawal occurred on. See our supported networks list for the approrpiate values. |
asset |
String | Yes | The asset to screen. The value must be the asset's symbol, i.e., BTC for bitcoin, ETH for Ether, UNI for Uniswap. |
address |
String | Yes | The asset's address used to determine counterparty risk. |
Response schema
The following is an example JSON response body from the above request:
[
{
"network": "Bitcoin",
"asset": "BTC",
"address": "1MCPcGbnrv4Uv9HcrjoQwzJaEFGrwzE6Pk",
"cluster": {
"name": "Huobi.com",
"category": "exchange"
},
"rating": "lowRisk",
"customAddress": null,
"chainalysisIdentification": null
}
]
The following is an example JSON response body for an unknown recipient on an emerging network:
[
{
"network": "Ethereum",
"asset": "USDC",
"address": "0x7de5aba7de728950c92c57d08e20d4077161f12f",
"cluster": null,
"rating": "unknown",
"customAddress": null,
"chainalysisIdentification": null
}
]
The following is an example JSON response body for a Custom Address on a pre-growth network:
[
{
"network": "Verge",
"asset": "XVG",
"address": "DEa5gMxbiDb96WfTN25nwzjzfjYHzHMsWi",
"cluster": null,
"rating": "unknown",
"customAddress": {
"addressName": "Scam Address & User",
"description": "This address belongs scamming user John Doe",
"categoryName": "scam"
},
"chainalysisIdentification": null
}
]
The following is an example JSON response body for a Chainalysis Identification on a mature network:
[
{
"network": "Litecoin",
"asset": "LTC",
"address": "LaizKtS5DUhPuP1nTQcc83MS7HwK6vk85z",
"cluster": {
"name": "Gatecoin.com",
"category": "exchange"
},
"rating": "highRisk",
"customAddress": null,
"chainalysisIdentification": {
"addressName": "SANCTIONS: OFAC SDN Guanghua Zheng 2020-07-17 LaizKtS5DUhPuP1nTQcc83MS7HwK6vk85z",
"description": "This specific address LaizKtS5DUhPuP1nTQcc83MS7HwK6vk85z within the cluster has been identified as belonging to an individual on OFAC's SDN list. \n\nZHENG, Guanghua (Chinese Traditional: 鄭广華; Chinese Simplified: 郑广华); DOB 04 Nov 1955; POB Shanghai, China; nationality China; citizen China; Email Address zhenguanghua1955@outlook.com; alt. Email Address zhenguanghua1955@gmail.com; Gender Male; Passport E51809923 (China) issued 25 May 2015 expires 24 May 2025; Identification Number 310108195511041616 (China); Chinese Commercial Code 6774 1639 5478 (individual) [SDNTK].\n\nhttps://home.treasury.gov/news/press-releases/sm1063",
"categoryName": "sanctions"
}
}
]
A successful request will return the following JSON response for known recipients.
Property | Type | Description |
---|---|---|
network |
String | The blockchain network this withdrawal occurred on. |
asset |
String | The asset associated with the withdrawal address. The value is the asset or token's symbol, e.g. BTC , ETH , UNI , etc. |
address |
String | The asset's withdrawal address. |
cluster |
Object or null |
The identification of a group of addresses estimated by Chainalysis to be controlled by a single entity. This value will be null if the cluster has not yet been identified. |
cluster.name |
String | The name of the named cluster. |
cluster.category |
String | The category of the named cluster. |
rating |
String | The risk rating of the known recipient address. |
customAddress |
Object or null |
An address you've uploaded through the KYT UI that allows you to receive alerts on activity. This value will be null if not applicable. |
customAddress.addressName |
String | The name you've given to the Custom Address in the CSV file. |
customAddress.description |
String | The description you've provided for the Custom Address in the CSV file. |
customAddress.categoryName |
String | The category name you provided for the Custom Address in the CSV file. |
chainalysisIdentification |
Object or null |
A Chainalysis Identified Address, representing an address-level identification that is part of a larger cluster. This value will be null if not applicable. |
chainalysisIdentification.addressName |
String | The name designated to the Chainalysis Identification. |
chainalysisIdentification.description |
String | The OSINT from the Chainalysis Identification. |
chainalysisIdentification.categoryName |
String | The category of the Chainalysis Identification. |
Note that the responses are not mutually exclusive. For example, you can have a known or unknown recipient and a Custom Address and/or a Chainalysis Identification.
Retrieve withdrawal addresses
ENPOINT
GET
/v1/users/{userId}/withdrawaladdresses
This endpoint retrieves all withdrawal addresses and risk ratings for a single User ID.
The following is an example request to list all withdrawal addresses for user
0001
:
curl -X GET 'https://api.chainalysis.com/api/kyt/v1/users/0001/withdrawaladdresses' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
response = requests.get('https://api.chainalysis.com/api/kyt/v1/users/0001/withdrawaladdresses', headers=headers)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v1/users/0001/withdrawaladdresses', {
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json'
}
});
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v1/users/0001/withdrawaladdresses");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
userId |
String | Yes | The userId to retrieve withdrawal addresses for. |
Query parameters
Parameter | Type | Required | Description |
---|---|---|---|
limit |
Integer | No | A limit on the number of objects returned. |
offset |
Integer | No | The position of the first object returned in the response. The default is 0, which starts the page at the first result. |
Request body schema
This call has no accompanying request body.
Response schema
The following is an example JSON response body for the above request:
{
"total": 100,
"limit": 3,
"offset": 0,
"data": [
{
"network": "Bitcoin",
"asset": "BTC",
"address": "1MCPcGbnrv4Uv9HcrjoQwzJaEFGrwzE6Pk",
"cluster": {
"name": "Huobi",
"category": "Exchange"
},
"customAddress": null,
"chainalysisIdentification": null,
"rating": "lowRisk",
},
{
"network": "Ethereum",
"asset": "USDC",
"address": "0x7de5aba7de728950c92c57d08e20d4077161f12f",
"cluster": null,
"rating": "unknown",
"customAddress": null,
"chainalysisIdentification": null
},
{
"network": "Verge",
"asset": "XVG",
"address": "DEa5gMxbiDb96WfTN25nwzjzfjYHzHMsWi",
"cluster": null,
"rating": "unknown",
"customAddress": {
"addressName": "Scam Address & User",
"description": "This address belongs scamming user John Doe",
"categoryName": "scam"
},
"chainalysisIdentification": null
}
]
}
A successful request will return the following JSON response for a known recipient.
Property | Type | Description |
---|---|---|
limit |
Integer | The limit of the data set. |
offset |
Integer | The offset of the data set. |
total |
Integer | The total number of results in the data set. |
data |
Array | An array containing address information. |
data.network |
String | The blockchain network the withdrawal occurred on. |
data.asset |
String | The asset associated with the withdrawal address. |
data.address |
String | The asset's withdrawal address. |
data.cluster |
Object or null |
The identification of a group of addresses estimated by Chainalysis to be controlled by a single entity. This value will be null if the cluster has not yet been identified. |
data.cluster.name |
String | The name of the named cluster. |
data.cluster.category |
String | The category of the named cluster, i.e., Exchange. |
data.customAddress |
Object or null |
An address you've uploaded through the KYT UI that allows you to receive alerts on activity. This value will be null if not applicable. |
data.customAddress.addressName |
String | The name you've given to the Custom Address in the CSV file. |
data.customAddress.description |
String | The description you've provided for the Custom Address in the CSV file. |
data.customAddress.categoryName |
String | The category name you provided for the Custom Address in the CSV file. |
data.chainalysisIdentification |
Object or null |
A Chainalysis Identified Address, representing an address-level identification that is part of a larger cluster. This value will be null if not applicable. |
data.chainalysisIdentification.addressName |
String | The name designated to the Chainalysis Identification. |
data.chainalysisIdentification.description |
String | The OSINT from the Chainalysis Identification. |
data.chainalysisIdentification.categoryName |
String | The category of the Chainalysis Identification. |
data.rating |
String | The risk rating of the known recipient address. |
Delete withdrawal addresses
ENPOINT
DELETE
/v1/users/{userId}/withdrawaladdresses/{asset}/{address}
This endpoint deletes a specific withdrawal address for a given user.
The following is an example request to delete a Bitcoin withdrawal address of
1MCPcGbnrv4Uv9HcrjoQwzJaEFGrwzE6Pk
for user0001
:
curl -X DELETE 'https://api.chainalysis.com/api/kyt/v1/users/0001/withdrawaladdresses/BTC/1MCPcGbnrv4Uv9HcrjoQwzJaEFGrwzE6Pk' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
response = requests.delete('https://api.chainalysis.com/api/kyt/v1/users/0001/withdrawaladdresses/BTC/1MCPcGbnrv4Uv9HcrjoQwzJaEFGrwzE6Pk', headers=headers)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v1/users/0001/withdrawaladdresses/BTC/1MCPcGbnrv4Uv9HcrjoQwzJaEFGrwzE6Pk', {
method: 'DELETE',
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json'
}
});
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v1/users/0001/withdrawaladdresses/BTC/1MCPcGbnrv4Uv9HcrjoQwzJaEFGrwzE6Pk");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("DELETE");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
userId |
String | Yes | The userId to retrieve withdrawal addresses for. |
asset |
String | Yes | The asset name associated with the withdrawal address . The value must be the asset or token's symbol, e.g., BTC for Bitcoin, ETH for Ether, UNI for Uniswap. |
address |
String | Yes | The asset's withdrawal address associated with the userId requested. |
Response schema
The above request returns an empty JSON response body.
The call has an empty Response Body. ie. a zero byte response, indicated by a response header Content-Length: 0
.
Deposit addresses
These endpoints allow you to register, retrieve, or delete a cryptocurrency address for a given user that receives funds into your organization.
POST /v1/users/{userId}/depositaddresses
GET /v1/users/{userId}/depositaddresses
DELETE /v1/users/{userId}/depositaddresses/{asset}/{address}
These endpoints are not currently required for integration. For ongoing monitoring, you should register transfers with the transfers/sent and transfers/received endpoints.
Note: In the future, KYT will be able to detect deposits (but not withdrawals) based on the deposit address. While this endpoint is not currently required for monitoring or integration, you can implement it now for use with this upcoming functionality.
The list of assets that KYT supports appears below. Note that this endpoint only accepts the assets from this list. To send data on additional assets (including assets that KYT may support in the future), use the transfers/sent and transfers/received endpoints.
Register deposit addresses
ENDPOINT
POST
/v1/users/{userId}/depositaddresses
The following is an example request to register a Bitcoin deposit address of
1MCPcGbnrv4Uv9HcrjoQwzJaEFGrwzE6Pk
for userBTC_01
:
curl -X POST 'https://api.chainalysis.com/api/kyt/v1/users/BTC_01/depositaddresses' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '[{"asset": "BTC", "address": "1MCPcGbnrv4Uv9HcrjoQwzJaEFGrwzE6Pk"}]'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
register_deposit_data = [
{
'asset': 'BTC',
'address': '1MCPcGbnrv4Uv9HcrjoQwzJaEFGrwzE6Pk',
},
]
response = requests.post('https://api.chainalysis.com/api/kyt/v1/users/BTC_01/depositaddresses', headers=headers, json=register_deposit_data)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v1/users/BTC_01/depositaddresses', {
method: 'POST',
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify([
{
'asset': 'BTC',
'address': '1MCPcGbnrv4Uv9HcrjoQwzJaEFGrwzE6Pk'
}
])
});
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v1/users/BTC_01/depositaddresses");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("POST");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
httpConn.setRequestProperty("Content-Type", "application/json");
httpConn.setDoOutput(true);
OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream());
writer.write("[{\"asset\": \"BTC\", \"address\": \"1MCPcGbnrv4Uv9HcrjoQwzJaEFGrwzE6Pk\"}]");
writer.flush();
writer.close();
httpConn.getOutputStream().close();
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
userId |
String | Yes | The userId to associate with the deposit address. |
Request body schema
Property | Type | Required | Description |
---|---|---|---|
asset |
String | Yes | The asset to associate with the deposit address. |
address |
String | Yes | The deposit address to be associated with the userId and asset . |
Response body
The above request returns an empty JSON response.
The response will be empty, i.e. a zero byte response, indicated by a response header Content-Length: 0
.
Retrieve deposit addresses
ENDPOINT
GET
/v1/users/{userId}/depositaddresses
Retrieves all deposit addresses for a single User ID.
The following is an example request to list all deposit addresses for user
BTC_01
:
curl -X GET 'https://api.chainalysis.com/api/kyt/v1/users/BTC_01/depositaddresses' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
response = requests.get('https://api.chainalysis.com/api/kyt/v1/users/BTC_01/depositaddresses', headers=headers)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v1/users/BTC_01/depositaddresses', {
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json'
}
});
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v1/users/BTC_01/depositaddresses");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
userId |
String | Yes | The userId from which you wish to retrieve the deposit addresses. |
Query parameters
Parameter | Type | Required | Description |
---|---|---|---|
limit |
Integer | No | A limit on the number of objects returned. |
offset |
Integer | No | The position of the first object returned in the response. The default is 0, which starts the page at the first result. |
Request body
This call has no accompanying request body.
Response body
The following is an example JSON response body from the above request:
{
"total": 1000,
"limit": 0,
"offset": 0,
"data": [
{
"asset": "BTC",
"address": "1MCPcGbnrv4Uv9HcrjoQwzJaEFGrwzE6Pk"
},
{...}
]
}
A successful request will return the following JSON response and properties for a given userId
.
Property | Type | Description |
---|---|---|
total |
Integer | The total number of results in the data set. |
limit |
Integer | The limit of the data set. |
offset |
Integer | The offset of the data set. |
data |
Array | An array that contains the deposit address information |
data.asset |
String | The asset associated with the deposit address for the user. |
data.address |
String | The deposit address associated with the user. |
Delete deposit addresses
ENDPOINT
DELETE
/v1/users/{userId}/depositaddresses/{asset}/{address}
The following is an example request to delete an associated Bitcoin deposit address of
1MCPcGbnrv4Uv9HcrjoQwzJaEFGrwzE6Pk
for userBTC_01
:
curl -X DELETE 'https://api.chainalysis.com/api/kyt/v1/users/BTC_01/depositaddresses/BTC/1MCPcGbnrv4Uv9HcrjoQwzJaEFGrwzE6Pk' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
response = requests.delete('https://api.chainalysis.com/api/kyt/v1/users/BTC_01/depositaddresses/BTC/1MCPcGbnrv4Uv9HcrjoQwzJaEFGrwzE6Pk', headers=headers)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v1/users/BTC_01/depositaddresses/BTC/1MCPcGbnrv4Uv9HcrjoQwzJaEFGrwzE6Pk', {
method: 'DELETE',
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json'
}
});
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v1/users/BTC_01/depositaddresses/BTC/1MCPcGbnrv4Uv9HcrjoQwzJaEFGrwzE6Pk");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("DELETE");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
This endpoint deletes the associated deposit address for a single User ID.
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
userId |
String | Yes | The userId associated with the deposit address. |
asset |
String | Yes | The asset associated with the deposit address. |
address |
String | Yes | The deposit address to be deleted. |
Request body
This call has no accompanying request body.
Response body
The above request returns an empty JSON response body.
The response will be empty, ie. a zero byte response, indicated by a response header Content-Length: 0
.
Users
These endpoints allow you to retrieve either all your users or a single user, as well as rename a user.
GET /v1/users/
GET /v1/users/{userId}
POST /v1/users/rename
Users are aggregations of transfers, tagged by User ID. Users in KYT should map 1:1 to users on your platform. Analyzing risk at a user level allows you to compare all the historical transfers made by a user to their current transfer.
The Users endpoint generates risk score reports for all users submitted to Chainalysis KYT. The endpoint supports both a summary format of all users and detailed reporting on specific users when passed a corresponding user ID. You are also able to rename User IDs via the API.
In determining User IDs, it is important to keep the following information in mind:
- You should use the same User ID across all supported asset types. This allows KYT to assess the user risk based on his or her combined activity.
- We encourage you to use an ID which relates to your internal system, as it helps your compliance team quickly find and monitor the users.
- Important: the User ID should not contain personally identifiable information.
Get all users
ENDPOINT
GET
/v1/users/
This endpoint retrieves and lists all User IDs in your KYT system.
The following is an example request to list all User IDs:
curl -X GET 'https://api.chainalysis.com/api/kyt/v1/users/' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
response = requests.get('https://api.chainalysis.com/api/kyt/v1/users/', headers=headers)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v1/users/', {
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json'
}
});
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v1/users/");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
Query parameters
Parameter | Type | Required | Description |
---|---|---|---|
limit |
Integer | No | Places a limit on the number of returned user objects. |
offset |
Integer | No | The position of the first user object returned in the response. The default is 0, which starts the page at the first result. |
Response schema
The following is an example JSON response body from the above request:
{
"limit": 100,
"offset": 0,
"total": 127,
"data": [
{
"userId": "new_user_01",
"score": "green",
"lastActivity": "2021-01-06T13:49:34.013Z",
"scoreUpdatedDate": "2021-07-29T21:20:18.008030Z",
"riskScore": "LOW"
},
{...}
]
}
The response body will be a brief summary of the list of User IDs in the Chainalysis KYT system.
score
is based on the legacy user risk model, while riskScore
reflects the current model that was implemented in late May 2020. We recommend using the riskScore
property going forward. Find more information about the user risk model here (login required).
A successful request will return the following JSON
response and properties for all users.
Property | Type | Description |
---|---|---|
total |
Integer | The total number of user objects returned. |
limit |
Integer | An echo back of the limit query parameter. |
offset |
Integer | An echo back of the offset query parameter. |
data |
Array | An array containing user data. |
data.userId |
String | The User ID of the user. |
data.score |
String or null |
The score of the User ID as green , amber , or red . Based on the legacy user risk model. |
data.lastActivity |
String or null |
The timestamp of the User ID's last tracked activity, in the UTC ISO 8601 format. |
data.scoreUpdatedDate |
String or null |
The timestamp when the score was last calculated, in the UTC ISO 8601 format. This field will update whenever activity occurs that could affect the user risk score, including: a new transfer is registered, a new alert is generated, or an alert status is changed. |
data.riskScore |
String or null |
The overall score of the User ID as LOW , MEDIUM , HIGH , or SEVERE . Based on the new user risk model. We recommend using this property going forward. |
Note: If a user is created in KYT but has no associated transfers or activity, score
, lastActivity
, scoreUpdatedDate
, and riskScore
will return with null
values.
Get a single user by {userId}
ENDPOINT
GET
/v1/users/{userId}
This endpoint retrieves detailed information on a single user in the Chainalysis KYT system.
The following is an example request to list a single User ID,
BTC_01
:
curl -X GET 'https://api.chainalysis.com/api/kyt/v1/users/new_user_01' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
response = requests.get('https://api.chainalysis.com/api/kyt/v1/users/new_user_01', headers=headers)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v1/users/new_user_01', {
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json'
}
});
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v1/users/new_user_01");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
userId |
String | Yes | The unique KYT identifier of the user. |
Response schema
The following is an example JSON response body from the above request:
{
"userId": "new_user_01",
"score": "green",
"lastActivity": "2021-01-06T13:49:34.013Z",
"scoreUpdatedDate": "2021-07-29T21:20:18.008030Z",
"riskScore": "LOW",
"creationDate": "2017-09-23T11:44:51.00Z",
"exposureDetails": [
{
"cluster": {
"name": "Coinbase.com",
"category": "exchange"
},
"sentIndirectExposure": 25,
"sentDirectExposure": 0,
"receivedIndirectExposure": 25,
"receivedDirectExposure": 0
}
]
}
A successful request will return the following JSON
response and properties for all users.
Property | Type | Description |
---|---|---|
userId |
String | The User ID of the user. |
score |
String or null |
The score of the User ID as green , amber , or red . Based on the legacy user risk model. |
lastActivity |
String or null |
The timestamp of the user's last tracked activity, in the UTC ISO 8601 format. |
scoreUpdatedDate |
String or null |
The timestamp when the score was last calculated, in the UTC ISO 8601 format. This field will update whenever activity occurs that could affect the user risk score, including: a new transfer is registered, a new alert is generated, or an alert status is changed. |
riskScore |
String or null |
The overall score of the User ID as LOW , MEDIUM , HIGH , or SEVERE . Based on the new user risk model. We recommend using this property going forward. |
creationDate |
String or null |
The timestamp when the user was created in the KYT system, in the UTC ISO 8601 format. |
exposureDetails |
Array | An array of details about the user's exposure to risk. |
exposureDetails.cluster |
Object | The identification of a group of addresses estimated by Chainalysis to be controlled by a single entity. |
exposureDetails.cluster.name |
String | The name of the named cluster. |
exposureDetails.cluster.category |
String | The category the named cluster belongs to. |
exposureDetails.sentIndirectExposure |
Number | Total Sent Indirect Exposure for the user in USD. |
exposureDetails.sentDirectExposure |
Number | Total Sent Direct Exposure for the user in USD. |
exposureDetails.receivedIndirectExposure |
Number | Total Received Indirect Exposure for the user in USD. |
exposureDetails.receivedIndirectExposure |
Number | Total Sent Indirect Exposure for the user in USD. |
Note: If a user is created in KYT but has no associated transfers or activity, score
, lastActivity
, scoreUpdatedDate
, and riskScore
will return with null
values and the exposureDetails
array will be empty.
score
is based on the legacy user risk model, while riskScore
reflects the current model that was implemented in late May 2020. We recommend using the riskScore
property going forward. Find more information about the user risk model here (login required).
Rename users
ENDPOINT
POST
/v1/users/rename
This endpoint renames a userId
in the KYT system. The request supports up to 1000 renames at a time.
The following is an example request to rename a User ID from
test_1
touser_1
andtest_2
touser_2
:
curl -X POST 'https://api.chainalysis.com/api/kyt/v1/users/rename' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json'
--header 'Content-Type: application/json' \
--data '[
{
"from": "test_1",
"to": "user_1"
},
{
"from": "test_2",
"to": "user_2"
}
]'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
user_data = [
{
'from': 'test_1',
'to': 'user_1'
},
{
'from': 'test_2',
'to': 'user_2'
},
]
response = requests.post('https://api.chainalysis.com/api/kyt/v1/users/rename', headers=headers, json=user_data)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v1/users/rename', {
method: 'POST',
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json'
},
body: JSON.stringify([
{
"from": "test_1",
"to": "user_1"
},
{
"from": "test_2",
"to": "user_2"
}
])
});
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v1/users/rename");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("POST");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Content-type", "application/json");
httpConn.setDoOutput(true);
OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream());
writer.write("[\n {\n \"from\": \"test_1\",\n \"to\": \"user_1\"\n },\n {\n \"from\": \"test_2\",\n \"to\": \"user_2\"\n }\n]");
writer.flush();
writer.close();
httpConn.getOutputStream().close();
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
The above request returns an empty
JSON
response.
Request body schema
Property | Type | Description |
---|---|---|
from |
String | The userId that you wish to rename. |
to |
String | The new userId that you wish to use. This must be a new userId and not currently exist in the KYT system. Using an existing userId will return a 400 error. |
Response schema
The endpoint returns an empty JSON response.
Alerts
These endpoints allow you to retrieve alerts, assign an alert, update an alert status or comment, and retrieve an alert's history.
GET /v1/alerts/
POST /v1/alerts/{alertIdentifier}/assignment
POST /v1/alerts/{alertIdentifier}/statuses
GET /v1/alerts/{alertIdentifier}/activity
Alerts in KYT are raised for risky transfers on your platform. A single transfer can trigger multiple alerts. Alert levels include Severe, High, Medium, and Low, and are based on factors such as category, service/entity, direct versus indirect exposure, direction, and amount.
Get all alerts
This endpoint retrieves information on all alerts that have been raised within your organization. By default, only trasfer alerts will be returned. To return behavioral alerts or withdrawal attempts, use the alertType
query parameter.
ENDPOINT
GET
/v1/alerts/
The following is an example request to retrieve all the alerts in your KYT system:
curl -X GET 'https://api.chainalysis.com/api/kyt/v1/alerts/' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
response = requests.get('https://api.chainalysis.com/api/kyt/v1/alerts/', headers=headers)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v1/alerts/', {
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json'
}
});
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v1/alerts/");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
The following is an example request to retrieve all Bitcoin alerts with a high alert level:
curl -X GET 'https://api.chainalysis.com/api/kyt/v1/alerts/?asset=BTC&level=HIGH' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
params = {
'asset': 'BTC',
'level': 'HIGH',
}
response = requests.get('https://api.chainalysis.com/api/kyt/v1/alerts/', params=params, headers=headers)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v1/alerts/?asset=BTC&level=HIGH', {
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json'
}
});
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v1/alerts/?asset=BTC&level=HIGH");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
The following is an example request to retrieve all Bitcoin alerts with a severe alert level for a particular user:
curl -X GET 'https://api.chainalysis.com/api/kyt/v1/alerts/?level=SEVERE&userId=User017&asset=BTC' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
params = {
'level': 'SEVERE',
'userId': 'User017',
'asset': 'BTC',
}
response = requests.get('https://api.chainalysis.com/api/kyt/v1/alerts/', params=params, headers=headers)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v1/alerts/?level=SEVERE&userId=User017&asset=BTC', {
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json'
}
});
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v1/alerts/?level=SEVERE&userId=User017&asset=BTC");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
Query parameters
Parameter | Type | Required | Description |
---|---|---|---|
asset |
String | No | Returns all alerts for a specific asset. |
createdAt_gte |
String | No | Returns all alerts created greater than or equal to a specific date and time. Must be in the UTC ISO 8601 format. |
createdAt_lte |
String | No | Returns all alerts created less than or equal to a specific date and time. Must be in the UTC ISO 8601 format. |
alertStatusCreatedAt_gte |
String | No | Returns all alert statuses and comments greater than or equal to a specific date and time. Must be in the UTC ISO 8601 format. |
alertStatusCreatedAt_lte |
String | No | Returns all alert statuses and comments less than or equal to a specific date and time. Must be in the UTC ISO 8601 format. |
level |
String | No | Filters alerts by alert level as SEVERE , HIGH , MEDIUM , and LOW (values must be in caps). |
transferReference |
String | No | Returns all alerts associated with a specific transfer reference. See Transfer Reference Formats sorted by timestamp , createdAt , level , and alertAmountUsd . For example, createdAt sorts alerts by the date the alert was created. See below for more information about the sort parameter. |
userId |
String | No | Returns all alerts for a specific user. |
limit |
Integer | No | Places a limit on the number of returned alert objects. |
offset |
Integer | No | Indicates the position of the first alert object returned in the response. The default is 0, which starts the page at the first result. |
sort |
String | No | Used to sort the order which alerts are returned in the response. You can sort by timestamp , level , alertAmountUsd , alertStatusCreatedAt , assignedTo , assignedBy , and assignedAt . Your parameter must include a direction specifier of either desc or asc preceded by a URL-encoded space character. For example: sort=timestamp%20desc . |
alertType |
String | No | Filters the the type of alerts returned as TRANSFER ,BEHAVIORAL , or WITHDRAWAL . |
Using the createdAt_gte
and createdAt_lte
parameters
You can use createdAt_gte
and createdAt_lte
to specify a date range. You can also keep track of your newest alerts by querying for all alerts after a given time (createdAt_gte
), store the time you made the query, then query it again later for all alerts since the initial query.
Using the alertStatuscreatedAt_gte
and alertStatuscreatedAt_lte
parameters
You can use these parameters to specify a date range for the alert status and periodically retrieve alerts with recent status changes or comments. You can keep track of alerts with recent status and comment updates by storing the timestamp of when you made the query, then using it in subsequent queries.
Using the sort
parameter
The sort
parameter determines the order that the alerts are returned. You can sort by timestamp
, createdAt
, level
, alertAmountUsd
, alertStatusCreatedAt
, and the assignedTo
, assignedBy
, and assignedAt
parameters. For example, to return all alerts sorted by amount from highest to lowest value (descending), use: ?sort=alertAmountUsd%20desc
.
Get all alerts from a single transfer
To return all alerts on a single transfer, it is recommended to use the userId
, asset
, and transferReference
parameters all at once. In rare circumstances, the same transferReference
can exist for multiple assets or multiple users.
Request body schema
This call has no accompanying request body.
Response schema
The following is an example JSON response body of a transfer alert:
{
"total": 1000,
"limit": 0,
"offset": 0,
"data": [
{
"alertAmountUsd": 132.50,
"transactionHash": "b765440e872ab6e2521694d10465415bda4adf8ed7fc2fdafb6d39bd17c5fddf",
"exposureType": "DIRECT",
"alertStatus": "Flagged",
"transferReportedAt": "2017-01-05T14:23:00.397Z",
"alertIdentifier": "a6a5d0f8-9753-11e9-a517-ebce3e967522",
"transferReference": "b765440e872ab6e2521694d10465415bda4adf8ed7fc2fdafb6d39bd17c5fddf:1",
"alertStatusCreatedBy": null,
"valid": true,
"transferCountWindow": null,
"ruleAsset": null,
"direction": "SENT",
"timestamp": "2011-06-18T08:22:21Z",
"period": null,
"windowSize": null,
"transferredValuePercentage": 100.0,
"level": "HIGH",
"service": "Silk Road Marketplace",
"alertStatusCreatedAt": "2020-01-14T13:57:58.713226Z",
"userId": "mtgox_ghosts",
"transferCount": null,
"createdAt": "2019-06-17T17:39:41.550575Z",
"alertType": "TRANSFER",
"transferOutputAddress": "1DP38CC2kf6ewUDaaVd9nBfuAD8SP15g2T",
"validity": "VALID",
"category": "darknet market",
"transactionIndex": 1,
"asset": "BTC",
"rule": ">$100 sent directly to darknet market",
"minThreshold": 100,
"maxThreshold": null
},
{...}
]
}
The following is an example JSON response body of a behavioral alert:
"total": 1000,
"limit": 0,
"offset": 0,
"data": [
{
"alertAmountUsd": 82271.52,
"transactionHash": null,
"exposureType": "DIRECT",
"alertStatus": "Unreviewed",
"transferReportedAt": null,
"alertIdentifier": "12b76b58-4706-11ec-ab82-133bbbe0a113",
"transferReference": null,
"alertStatusCreatedBy": null,
"valid": true,
"transferCountWindow": "[10,)",
"ruleAsset": "AAVE",
"direction": "RECEIVED",
"timestamp": null,
"period": "[\"2021-02-04 00:00:00+00\",\"2021-02-05 00:00:00+00\")",
"windowSize": "1 day",
"transferredValuePercentage": null,
"level": "SEVERE",
"service": null,
"alertStatusCreatedAt": "2021-11-16T17:53:15.096Z",
"userId": "Example User 1",
"transferCount": 11,
"createdAt": "2021-11-16T17:53:15.096Z",
"alertType": "BEHAVIORAL",
"transferOutputAddress": null,
"validity": "VALID",
"category": null,
"transactionIndex": null,
"asset": null,
"rule": "$0-$50000 received",
"minThreshold": 0,
"maxThreshold": 50000
},
{...}
]
}
The response is sorted by alertStatusCreatedAt
by default. Note that when an alert is created, the alertStatus
is set to Unreviewed
and the alertStatusCreatedAt
is set to the time the alert was created.
A successful request will return the following JSON response and properties for all alerts:
Property | Type | Description |
---|---|---|
total |
Integer | The total number of alert objects returned. |
limit |
Integer | An echo back of the limit query parameter. |
offset |
Integer | An echo back of the offset query parameter. |
data |
Array | An array containing alert data. |
data.alertAmountUsd |
Number | The amount of the transfer that triggered the alert in USD. |
data.transactionHash |
String or null |
The transaction hash of the transfer that triggered the alert. Will be null for behavioral alerts. |
data.exposureType |
String | The type of exposure of the transfer that triggered the alert as DIRECT or INDIRECT . |
data.alertStatus |
String | The status of the alert as Unreviewed , In Review , Flagged , No Review , or Dismissed . |
data.transferReportedAt |
String null |
The timestamp when the transfer was registered in KYT, in the UTC ISO 8601 format. Will be null for behavioral alerts. |
data.alertIdentifier |
String | The alert ID. |
data.transferReference |
String or null |
The transaction hash and index or output address of the transfer that triggered the alert. Will be null for behavioral alerts. |
data.alertStatusCreatedBy |
String or null |
The username of the person in your organization that changed the alert status. This will be null if the alert's status has not yet been updated. |
data.valid |
Boolean | Indicates whether the alert is valid. |
data.transferCountWindow |
String or null |
The range that the transfer count must fall between for KYT to generate the behavioral alert. The leftmost integer represents the lower bound, and the rightmost integer represents the upper bound. The value uses interval notation to indicate whether the endpoints are inclusive. To learn more, see the interval notation definition. Will be null for transfer alerts. |
data.ruleAsset |
String or null |
The asset that the behavioral alert rule is tracking. Will be null if the behavioral alert rule is tracking behavior of any asset or for transfer alerts. |
data.direction |
String | The direction of the transfer that triggered the alert as SENT or RECEIVED . |
data.timestamp |
String or null |
The time when the transfer on the blockchain that caused the alert, in the UTC ISO 8601 format. Will be null for behavioral alerts. |
data.period |
String or null |
The time window KYT monitored to generate the behavioral alert. The leftmost timestamp represents the start time, and the rightmost timestamp represents the end time. The timestamps are in the UTC ISO 8601 format. The value uses interval notation to indicate whether the endpoints are inclusive. To learn more, see the interval notation definition. Will be null for transfer alerts. |
data.windowSize |
String or null |
The length of the behavioral alert window, either 1 day , 1 week , or 1 month . Will be null for transfer alerts. |
data.transferredValuePercentage |
Number or null |
The measure of the alerted amount as a percentage of the total transfer amount (the alerted amount divided by the total amount of the transfer in USD). Will be null for behavioral alerts. |
data.level |
String | The level of the alert as SEVERE , HIGH , MEDIUM , or LOW . |
data.service |
String or null |
The name of the counterparty in the transfer that triggered the alert. Note that the service will be null for indirect exposure alerts, as you can have multiple indirect counterparties contributing to one alert. |
data.alertStatusCreatedAt |
String | The timestamp when the alert status was last changed, in the UTC ISO 8601 format. |
data.userId |
String | The User ID of the user. |
data.transferCount |
Integer or null |
The number of transfers that occurred during the period . Will be null for transfer alerts. |
data.createdAt |
String | The timestamp when the alert was created, in the UTC ISO 8601 format. |
data.alertType |
String | The type of alert, either TRANSFER or BEHAVIORAL . |
data.transferOutputAddress |
String or null |
The destination address for funds within the transaction. Will be null for behavioral alerts. |
data.validity |
String | The status of the alert as either VALID , INVALID , or REVALID . |
data.category |
String or null |
The entity category the alert rule is tracking. Will be null if the rule is behavioral and tracking for any category. |
data.transactionIndex |
Integer or null |
The 0-indexed number of the transfer in the transaction that caused the alert. Will be null for behavioral alerts. |
data.asset |
String or null |
The asset used in the transfer. Will be null for behavioral alerts. |
data.rule |
String | An explanation of why the transfer or behavior caused an alert. |
data.minThreshold |
Integer | The minimum amount in USD of the alert rule. |
data.maxThreshold |
Integer or null |
The maximum amount in USD of the alert rule. This value will be null if a maxThreshold is not set for the rule. |
Assign an alert
This endpoint assigns an alert to a member of your organization.
ENDPOINT
POST
/v1/alerts/{alertIdentifier}/assignment
The following is an example request to assign someone to an alert:
curl -X POST 'https://api.chainalysis.com/api/kyt/v1/alerts/{alertIdentifier}/assignment' \
--header 'Token: <API_KEY' \
--header 'Content-Type: application/json' \
--data '{"alertAssignee":"user.email@example.com"}'
import requests
headers = {
'Token': '<API_KEY',
}
assign_alert_data = {
'alertAssignee': 'user.email@example.com',
}
response = requests.post('https://api.chainalysis.com/api/kyt/v1/alerts/{alertIdentifier}/assignment', headers=headers, json=assign_alert_data)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v1/alerts/{alertIdentifier}/assignment', {
method: 'POST',
headers: {
'Token': '<API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
'alertAssignee': 'user.email@example.com'
})
});
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v1/alerts/{alertIdentifier}/assignment");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("POST");
httpConn.setRequestProperty("Token", "<API_KEY");
httpConn.setRequestProperty("Content-Type", "application/json");
httpConn.setDoOutput(true);
OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream());
writer.write("{\"alertAssignee\":\"user.email@example.com\"}");
writer.flush();
writer.close();
httpConn.getOutputStream().close();
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
alertIdentifier |
String | Yes | A unique identifier that references the alert. |
Request body schema
Property | Type | Required | Description |
---|---|---|---|
alertAssignee |
String | Yes | The email address of the user you wish to assign the alert to. Note: to unassign an alert, return this value with null . |
Response schema
The following is an example JSON response body from the above request:
{
"alertIdentifier": "bdc1f806-f5ad-11eb-8540-2b716172c24d",
"assignedBy": "admin.email@example.com",
"assignedTo": "user.email@example.com",
"alertAssignedAt": "2021-08-11T21:21:59.806Z"
}
A successful request will return the following JSON response and properties for the given alert:
Property | Type | Description |
---|---|---|
alertIdentifier |
String | A unique identifier that references the alert. |
assignedBy |
String | The email address of the user who assigned the alert. |
assignedTo |
String | The email address of the user assigned to the alert. |
alertAssignedAt |
String | The timestamp when the alert was assigned, in the UTC IS0 8601 format. |
Post alert statuses and comments
This endpoint sends and changes an alert status and comment you provide for a given alert.
ENDPOINT
POST
/v1/alerts/{alertIdentifier}/statuses
The following is an example request to send an alert status and comment in the KYT system:
curl -X POST 'https://api.chainalysis.com/api/kyt/v1/alerts/1a895b44-2a78-11eb-978c-5bb1dd49843e/statuses' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '{
"status":"No Review",
"comment":"Test comment"
}'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
alert_data = {
'status': 'No Review',
'comment': 'Test comment',
}
response = requests.post('https://api.chainalysis.com/api/kyt/v1/alerts/1a895b44-2a78-11eb-978c-5bb1dd49843e/statuses', headers=headers, json=alert_data)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v1/alerts/1a895b44-2a78-11eb-978c-5bb1dd49843e/statuses', {
method: 'POST',
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
'status': 'No Review',
'comment': 'Test comment'
})
});
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v1/alerts/1a895b44-2a78-11eb-978c-5bb1dd49843e/statuses");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("POST");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
httpConn.setRequestProperty("Content-Type", "application/json");
httpConn.setDoOutput(true);
OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream());
writer.write("{\n \"status\":\"No Review\",\n \"comment\":\"Test comment\"\n}");
writer.flush();
writer.close();
httpConn.getOutputStream().close();
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
alertIdentifier |
String | Yes | A unique identifier that references the alert. |
Request body schema
Property | Type | Required | Description |
---|---|---|---|
status |
String | Yes | Define the alert status, whether Unreviewed , In Review , Flagged , No Review , or Dismissed . |
comment |
String | No | Include the comment you want to specify for the given alert. |
Response schema
The following is an example JSON response body from the above request:
{
"alertIdentifier": "1a895b44-2a78-11eb-978c-5bb1dd49843e",
"alertStatus": "No Review",
"alertComment": "Test comment",
"alertStatusCreatedBy": "user@example.com",
"alertStatusCreatedAt": "2021-05-24T19:36:08.593571Z"
}
A successful request will return the following JSON response and properties for the given alert:
Property | Type | Description |
---|---|---|
alertIdentifier |
String | A unique identifier that references the alert. |
alertStatus |
String | The alert status you submitted in your request body whether Unreviewed , In Review , Flagged , No Review , or Dismissed . |
alertComment |
String or null |
The comment you submitted in your request body. This value will be null if no comment was left. |
alertStatusCreatedBy |
String | The email address of the KYT account that changed the alert status and/or added a comment. |
alertStatusCreatedAt |
String | The timestamp when the the alert status was created, in the UTC ISO 8601 format. |
Get alert statuses and comments
This endpoint retrieves an alert's comment and status history.
ENDPOINT
GET
/v1/alerts/{alertIdentifier}/activity
The following is an example request to retrieve an alert's comment and status history:
curl -X GET 'https://api.chainalysis.com/api/kyt/v1/alerts/17124a04-cadf-11eb-9921-0bdd1e279318/activity' \
--header 'Token: {YOUR_API_KEY}'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
}
response = requests.get('https://api.chainalysis.com/api/kyt/v1/alerts/17124a04-cadf-11eb-9921-0bdd1e279318/activity', headers=headers)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v1/alerts/17124a04-cadf-11eb-9921-0bdd1e279318/activity', {
headers: {
'Token': '{YOUR_API_KEY}'
}
});
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v1/alerts/17124a04-cadf-11eb-9921-0bdd1e279318/activity");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
Path parameters
Parameter | Type | Required | Description |
---|---|---|---|
alertIdentifier |
String | Yes | A unique identifier that references the alert. |
Request body schema
This call has no accompanying request body.
Response schema
The following is an example JSON response body from the above request:
{
"alertIdentifier": "17124a04-cadf-11eb-9921-0bdd1e279318",
"statusItems": [
{
"alertStatusIdentifier": "17124a04-cadf-11eb-9921-0bdd1e279318-10857095",
"status": "Unreviewed",
"updatedBy": "user1@yourCrytpoService.com",
"comment": ">$500 received indirectly from scam. Should review ASAP.",
"updated": "2021-07-01T06:42:23.881301Z"
},
{
"alertStatusIdentifier": "17124a04-cadf-11eb-9921-0bdd1e279318-11554113",
"status": "Flagged",
"updatedBy": "user2@yourCrytpoService.com",
"comment": "This is a definitely a scam.",
"updated": "2021-07-21T21:57:33.343724Z"
}
]
}
A successful request will return the following JSON response and properties for the given alert:
Property | Type | Description |
---|---|---|
alertIdentifier |
String | A unique identifier that references the alert. |
statusItems |
Array | An array that contains the alert status items. If the array is empty, the alert status has never been been updated or the alert has not yet been commented upon. |
statusItems.alertStatusIdentifier |
String | A unique identifier that references the alert status. |
statusItems.status |
String | The alert's status at the time of update, either Unreviewed , In Review , Flagged , No Review , or Dismissed . |
statusItems.updatedBy |
String | The email address of the KYT account that updated the alert's status and/or comment. |
statusItems.comment |
String | The alert's comment at the time of update. |
statusItems.updated |
String | The timestamp when the alert's status and/or comment was updated, in the UTC ISO 8601 format. |
Resources
Error handling
KYT uses standard HTTP response codes to indicate the success or failure of an API request.
Response codes in the 2xx
range indicate success. Response codes in the 4xx
range indicate an error due to the information provided, such as a missing or incorrectly formatted parameter or request body property. Response codes in the 5xx
range indicate an internal server error within Chainalysis.
KYT API codes and meaning
Code | Meaning | Description |
---|---|---|
200 | Successful request | The request was successful. |
400 | Bad request | The request was unacceptable. This may refer to a missing or improperly formatted parameter or request body property, non-valid JSON, or the use of testnet blockchain data. The Get network identifications endpoints for /transfers and /withdrawal-attempts will return this error for mature and emerging networks. |
403 | Forbidden | Your API key is invalid. This may be because your API Key is expired or not sent correctly as the value of the Token HTTP header. |
404 | Not found | This may be because you either requested a nonexistent endpoint or referenced a user that does not exist. |
406 | Not acceptable | You requested a response format that the API cannot produce. We currently only support JSON output. |
409 | Conflict | The request has a conflict. This may indicate a conflict of association with another user, such as overlapping addresses. |
500 | Internal server error | (Rare.) This indicates an error with Chainalysis's server. For transfer registration requests, we suggest trying the request again to ensure KYT ingests the transfer. |
503 | Service unavailable error | (Rare.) Chainalysis's server may be unavailable or not ready to handle the request. For transfer registration requests, we suggest trying the request again to ensure KYT ingests the transfer. |
504 | Request timeout | (Rare.) This indicates that our API gateway did not get a response from our application servers within the expected time. |
Error response schema
The following is an example JSON response body from a
400
error:
{
"status": 400,
"message": "The BTC transfer reference a2d9bfc3a47c2c9cfd0170198782979ed327442e5ed1c8a752bced24d490347d4:1H7aVb2RZiBmdbnzazQgVj2hWR3eEZPg6v is not syntactically valid"
}
Property | Type | Description |
---|---|---|
status |
Integer | The error code reported. |
message |
String | A human-readable string that provides further detail about the error. |
Data types
Timestamp
2017-09-23T21:44:51.01Z
2017-09-23T21:44:51.01+00.00
Unless specified otherwise, the KYT API returns all timestamps in the ISO 8601 format and adheres to the UTC time standard. Sometimes UTC is indicated by +00.00
, Z
, or in cases where no timezone information is provided, the timestamp is still UTC. Please ensure your system can parse the ISO 8601 format. Most modern languages and libraries will handle ISO 8601 timestamps without issue.
Whenever submitting data to KYT, please ensure your timestamps adhere to the UTC time standard.
Glossary
The Chainalysis KYT API uses the following terms for all Asset types tracked:
Account
An Account, sometimes known as a Balance, is the method that some cryptocurrencies and tokens use to keep track of transactions in their transaction ledger in the Blockchain. The Account transaction method keeps track of the total currency in the Account in a global state. This transaction method allows for Smart Contracts to be created that keep track of different states to perform different tasks based on the state.
Examples of Account transaction based cryptocurrencies include:
- Ethereum
- Paxos Standard Token
- TrueUSD
- Tether
Address
An Address is a cryptographic hash of a Public and Private key pair that holds value for a given cryptocurrency or token Asset. Bitcoin and other cryptocurrencies that use UTXO based transactions use what is called an "Address". Ethereum and other currencies that use Account based transactions use what is known as an "Account".
Output Address
Output addresses are the destination addresses of where cryptocurrency is sent. A correct output address depends on whether the transfer is RECEIVED
or SENT
.
For RECEIVED
transfers, the output address is internal to your service (the address where your service received funds).
For SENT
transfers, the output address is external to your service (the address where your service sent funds).
Deposit Address
Deposit addresses are addresses that you manage on behalf of your users, where they can deposit value to your service. A deposit address is always associated with exactly one user, and should never be reused for another user, but a user can have multiple deposit addresses. Deposit addresses can be registered even before they have received value.
Withdrawal Address
Withdrawal addresses are foreign Bitcoin addresses outside your service, to which the user intends to send value. Multiple users might send value to the same withdrawal addresses.
Withdrawal addresses should be registered as early as possible for best results, for instance right when the address is pasted into a withdrawal form. When registering a withdrawal address a real-time rating of the address as a recipient of value is returned. This allows you to take action on suspicious behavior immediately.
Asset
An Asset in KYT is the cryptocurrency or token being tracked (Bitcoin, Tether, etc).
A current list of supported assets by the KYT API can retrieved using the assets endpoint. You can also send data for additional assets and as soon as KYT fully supports the coin, the system will automatically backfill any data you've sent. A list of all assets you can send appears below.
Cluster
A cluster is a collection of cryptocurrency addresses that Chainalysis has identified to be controlled by one entity.
Interval notation
The use of parentheses and brackets to indicate whether endpoints are inclusive. Parentheses indicate the endpoints are not inclusive, brackets indicate the endpoints are inclusive, and a mixed set indicates one of the endpoints is inclusive. For example:
- (20, 40) means greater than 20 and less than 40.
- [20, 40] means greater than or equal to 20 and less than or equal to 40.
- (20, 40] means greater than 20 and less than or equal to 40.
- [20, 40) means greater than or equal to 20 and less than 40.
To learn even more, see Wikipedia.
KYT
KYT is the abbreviation for Know Your Transaction.
Transfer
A Transfer is the part of a transaction that transfers value from one address to another address. For some asset types like Ethereum each transaction is one transfer, but for asset types like Bitcoin a transaction can contain multiple transfers.
Received Transfer
Received transfers are the value transfers that your service receives on behalf of a user into their deposit address. Received transfers are registered and processed according to the same rules as sent transfers.
Sent Transfer
Sent transfers are the value that your service sends on behalf of a user when the user makes a withdrawal from your service. Regardless of asset type, the transfer will be part of a transaction. A transfer can be registered as soon as its transaction has been created, and even before it has been broadcast to a blockchain. Once a transfer has been registered, KYT will track it.
For some asset types, the transfer will have to “settle” before it is processed by KYT; in Bitcoin we will wait until the transaction is 5 blocks deep to make sure the risk score reflects stable data. Registered transfers that remain unsettled for too long will automatically discard the registered transfer after a timeout of several days.
User IDs
All user activity is recorded in the KYT API under a user ID. The API allows you to use any unique user ID within the following constraints:
- The
userID
must be between 1 and 200 characters. - Allowed characters are
-_:a-zA-Z0-9
. - You should use the same user ID for a user across all supported asset types, as this allows KYT to assess the user risk based on his or her combined activity.
- You are encouraged to use an ID which you can relate to your internal system, as this allows your compliance team to quickly find and monitor the users in KYT.
UTXO
UTXO is the abbreviation for Unspent Transaction Output. UTXO is the method that some cryptocurrencies use to keep track of transactions in their transaction ledger in the Blockchain. UTXO is the amount of unspent cryptocurrency that can be spent in new transactions.
Examples of UXTO transaction based cryptocurrencies include:
- Bitcoin
- Bitcoin Cash
- Litecoin
Supported networks and assets
The following are network lists for each asset tier. For an overview of KYT’s functionality for each tier, see Asset coverage.
Mature and emerging networks
The following is a list of mature and emerging networks. We provide the Network's name, the native asset symbol, resources for metatoken symbols, and the transferReference
property.
Network | Native Asset Symbol | Metatokens supported | transferReference format |
---|---|---|---|
Algorand | ALGO | Metatokens on the Algorand network are supported as pre-growth assets: https://algoexplorer.io/assets | {transaction_hash}:{output_address} |
Arbitrum | All tokens: arbiscan.io/tokens | {transaction_hash}:{output_address} |
|
Avalanche | AVAX | All tokens: avascan.info/marketcap | {transaction_hash}:{output_address} |
Binance_Smart_Chain | BNB | All tokens: bscscan.com/tokens | {transaction_hash}:{output_address} |
Bitcoin | BTC | {transaction_hash}:{output_address} OR {transaction_hash}:{output_index} |
|
Bitcoin_Cash | BCH | {transaction_hash}:{output_address} OR {transaction_hash}:{output_index} |
|
Bitcoin_Satoshi_Vision | BSV | {transaction_hash}:{output_address} |
|
Celo | CELO | All tokens: explorer.celo.org/tokens | {transaction_hash}:{output_address} |
Cronos | CRO | All tokens: cronoscan.com/tokens | {transaction_hash}:{output_address} |
Dash | DASH | {transaction_hash}:{output_address} OR {transaction_hash}:{output_index} |
|
Dogecoin | DOGE | {transaction_hash}:{output_address} |
|
EOS | EOS | {transaction_hash}:{output_address} OR {transaction_hash}:{output_index} |
|
Ethereum | ETH | All tokens | {transaction_hash}:{output_address} |
Ethereum_Classic | ETC | {transaction_hash}:{output_address} |
|
Fantom | FTM | All tokens: ftmscan.com/tokens | {transaction_hash}:{output_address} |
Lightning | {payment_hash}:{recipient_node_key} |
||
Litecoin | LTC | {transaction_hash}:{output_address} OR {transaction_hash}:{output_index} |
|
Omni | OMNI | USDT | {transaction_hash} |
Optimism | All tokens: optimistic.etherscan.io/tokens | {transaction_hash}:{output_address} |
|
Polygon | MATIC | All tokens: polygonscan.com/tokens | {transaction_hash}:{output_address} |
Solana | SOL | USDC, USDT, PAI, SAFU, SRM, OXY, WSOL and NDRG | {transaction_hash}:{output_address} |
Tron | TRX | All TRC-20 tokens: tronscan.org/#/tokens/list | {transaction_hash}:{output_address} |
XRP | XRP | {transaction_hash}:{output_address} |
|
Zcash | ZEC | {transaction_hash}:{output_address} OR {transaction_hash}:{output_index} |
For metatoken symbols (ERC-20s, TRC-20s, etc.), we suggest the resources in the table above, as well as the following external lists:
- tokenlists.org (for ERC-20s)
- coinmarketcap.com (provides some API functionality to retrieve asset symbols)
- coingecko.com
Pre-growth networks
The following is a list of pre-growth networks. We provide the network's name and the transferReference
format. If a network is listed here, we also support that network's metatokens. If a network isn't listed here that you would like Chainalysis to support, please contact your CSM.
Network | transferReference format |
---|---|
Aeternity | {transaction_hash}:{output_address} |
Algorand Note: Algorand's native asset, ALGO, is supported as a mature asset. Metatokens on the Algorand network are supported as pre-growth assets. |
{transaction_hash}:{output_address} |
Ardor | {transaction_hash}:{output_address} |
Ark | {transaction_hash}:{output_address} |
Beam | {transaction_hash}:{output_address} |
Bitcoin_Diamond | {transaction_hash}:{output_address} |
Bitcoin_Gold | {transaction_hash}:{output_address} |
Bitshares | {transaction_hash}:{output_address} |
Binance_Chain | {transaction_hash}:{output_address} |
Blackcoin | {transaction_hash}:{output_address} |
Bytom | {transaction_hash}:{output_address} |
Cardano | {transaction_hash}:{output_address} |
Chia | {transaction_hash}:{output_address} |
Concordium | {transaction_hash}:{output_address} |
Consensus | {transaction_hash}:{output_address} |
Cosmos | {transaction_hash} |
Counterparty | {transaction_hash}:{output_address} |
Decred | {transaction_hash}:{output_address} |
Deso | {transaction_hash}:{output_address} |
Diamond | {transaction_hash}:{output_address} |
Diem | {transaction_hash}:{output_address} |
Digibyte | {transaction_hash}:{output_address} |
Dreamcoin | {transaction_hash}:{output_address} |
Ecash | {transaction_hash}:{output_address} |
Elrond | {transaction_hash}:{output_address} |
Ergo | {transaction_hash}:{output_address} |
EUNO | {transaction_hash}:{output_address} |
Factom | {transaction_hash}:{output_address} |
Filecoin | {transaction_hash}:{output_address} |
Flare | {transaction_hash}:{output_address} |
Flow | {transaction_hash}:{output_address} |
Folmcoin | {transaction_hash}:{output_address} |
Force | {transaction_hash}:{output_address} |
Gochain | {transaction_hash}:{output_address} |
Gulden | {transaction_hash}:{output_address} |
Gxchain | {transaction_hash}:{output_address} |
Harmony | {transaction_hash}:{output_address} |
Hdac | {transaction_hash}:{output_address} |
Hedera | {transaction_hash}:{output_address} |
Helium | {transaction_hash}:{output_address} |
Hive | {transaction_hash}:{output_address} |
Horizen | {transaction_hash}:{output_address} |
HPB | {transaction_hash}:{output_address} |
Icon | {transaction_hash}:{output_address} |
Iconic | {transaction_hash}:{output_address} |
Internet_Computer | {transaction_hash}:{output_address} |
Iost | {transaction_hash}:{output_address} |
Iota | {transaction_hash}:{output_address} |
Kava | {transaction_hash}:{output_address} |
Klaytn | {transaction_hash}:{output_address} |
Komodo | {transaction_hash}:{output_address} |
Kusama | {transaction_hash}:{output_address} |
Link | {transaction_hash}:{output_address} |
Liquid | {transaction_hash}:{output_address} |
Lisk | {transaction_hash}:{output_address} |
LTO_Network | {transaction_hash}:{output_address} |
Metadium | {transaction_hash}:{output_address} |
Milk | {transaction_hash}:{output_address} |
Mina | {transaction_hash}:{output_address} |
Monacoin | {transaction_hash}:{output_address} |
Monero | Deposits: {transaction_hash}:{Transaction_index}:{Receiving_address}:{Payment_id} Withdrawals: {transaction_hash}:{Transaction_index}:{Withdrawal_address}:{Payment_id} |
Moonbeam | {transaction_hash}:{output_address} |
Navcoin | {transaction_hash}:{output_address} |
Near | {transaction_hash}:{output_address} |
Neblio | {transaction_hash}:{output_address} |
Nebulas | {transaction_hash}:{output_address} |
Nem | {transaction_hash}:{output_address} |
Neo | {transaction_hash}:{output_address} |
Nervos | {transaction_hash}:{output_address} |
Oasis | {transaction_hash}:{output_address} |
Okexchain | {transaction_hash}:{output_address} |
Ontology | {transaction_hash}:{output_address} |
Palette | {transaction_hash}:{output_address} |
Payprotocol | {transaction_hash}:{output_address} |
Peercoin | {transaction_hash}:{output_address} |
Pivx | {transaction_hash}:{output_address} |
Polkadot | {transaction_hash}:{output_address} |
Proton | {transaction_hash}:{output_address} |
Qtum | {transaction_hash}:{output_address} |
Radix | {transaction_hash}:{output_address} |
Ravencoin | {transaction_hash}:{output_address} |
Reddcoin | {transaction_hash}:{output_address} |
Secret | {transaction_hash}:{output_address} |
Sia | {transaction_hash}:{output_address} |
Simple_Ledger_Protocol | {transaction_hash}:{output_address} |
smartBCH | {transaction_hash}:{output_address} |
Social_Send | {transaction_hash}:{output_address} |
Sologenic | {transaction_hash}:{output_address} |
Stacks | {transaction_hash}:{output_address} |
Stellar | {transaction_hash} |
Straits | {transaction_hash}:{output_address} |
Supercoin | {transaction_hash}:{output_address} |
Symbol | {transaction_hash}:{output_address} |
Syscoin | {transaction_hash}:{output_address} |
Tachyon | {transaction_hash}:{output_address} |
Terra | {transaction_hash}:{output_address} |
Tezos | {transaction_hash}:{output_address} |
Theta | {transaction_hash}:{output_address} |
Thorchain | {transaction_hash}:{output_address} |
V_Systems | {transaction_hash}:{output_address} |
VeChain | {transaction_hash}:{output_address} |
Verge | {transaction_hash}:{output_address} |
Voucher_Coin | {transaction_hash}:{output_address} |
Wanchain | {transaction_hash}:{output_address} |
Waves | {transaction_hash} |
Wax | {transaction_hash}:{output_address} |
Waykichain | {transaction_hash}:{output_address} |
Woo_Network | {transaction_hash}:{output_address} |
XinFin | {transaction_hash}:{output_address} |
For asset and metatoken symbols, we suggest the following resources:
- coinmarketcap.com (provides some API functionality to retrieve asset symbols)
- coingecko.com
Deprecated endpoints
The following section contains historical information for deprecated endpoints.
Get assets
The following is an example request for listing all assets:
curl -X GET 'https://api.chainalysis.com/api/kyt/v1/assets' \
--header 'Token: {YOUR_API_KEY}' \
--header 'Accept: application/json'
import requests
headers = {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json',
}
response = requests.get('https://api.chainalysis.com/api/kyt/v1/assets', headers=headers)
import fetch from 'node-fetch';
fetch('https://api.chainalysis.com/api/kyt/v1/assets', {
headers: {
'Token': '{YOUR_API_KEY}',
'Accept': 'application/json'
}
});
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://api.chainalysis.com/api/kyt/v1/assets");
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Token", "{YOUR_API_KEY}");
httpConn.setRequestProperty("Accept", "application/json");
InputStream responseStream = httpConn.getResponseCode() / 100 == 2
? httpConn.getInputStream()
: httpConn.getErrorStream();
Scanner s = new Scanner(responseStream).useDelimiter("\\A");
String response = s.hasNext() ? s.next() : "";
System.out.println(response);
}
}
The above request returns JSON structured as below. Note that the "data" array would typically display the data for all assets but has been truncated for this example.
[
{
"symbol": "AUDX",
"name": "Etoro AUD"
},
{
"symbol": "BCH",
"name": "Bitcoin Cash"
},
{
"symbol": "BNB",
"name": "Binance Coin"
},
{
"symbol": "BTC",
"name": "Bitcoin"
},
{...}
]
GET https://api.chainalysis.com/api/kyt/v1/assets
Returns a list of cryptocurrencies supported in the KYT API and the corresponding symbol.