Authentication
Every request must include the Authorization: Bearer YOUR_API_KEY header. Create and manage keys in your xdcscan.io account under Account → API Keys.
Authentication errors return 401 Unauthorized with { "message": "Invalid API key" }.
GET /account/api/v1/user/info HTTP/1.1
Host: xdcscan.io
Authorization: Bearer YOUR_API_KEY
# 401 response when the key is missing or invalid:
{ "message": "Invalid API key" }GET /account/api/v1/user/info HTTP/1.1
Host: xdcscan.io
Authorization: Bearer YOUR_API_KEY
# 401 response when the key is missing or invalid:
{ "message": "Invalid API key" }Response envelope
Every JSON response is wrapped in a top-level data field. For list endpoints, data carries both the array and a pagination object.
# Single resource:
{ "data": { "...": "..." } }
# List resource:
{ "data": { "items": [ ... ], "pagination": { ... } } }# Single resource:
{ "data": { "...": "..." } }
# List resource:
{ "data": { "items": [ ... ], "pagination": { ... } } }Errors
The API uses standard HTTP status codes. All error responses share the same JSON shape.
| HTTP | Code string | Description |
|---|---|---|
| 400 | BadRequest | The request body or query parameters are invalid. Check message for details. |
| 401 | Unauthorized | The API key is missing, expired, or invalid. |
| 404 | NotFound | The resource does not exist or is not owned by the authenticated account. |
| 429 | TooManyRequests | Request rate exceeded. Wait before retrying. |
| 500 | InternalServerError | Unexpected server error. Retry with exponential backoff. |
# Error response schema:
{ "message": "string" }# Error response schema:
{ "message": "string" }API Versioning
Account API endpoints are prefixed with a version number (/account/api/v1/). The public REST API at /api/v2 is versioned independently — an upgrade to one does not affect the other.
| Version | Status | Endpoints |
|---|---|---|
| v1 | Stable | API keys, user info, watchlists, tags, custom ABIs |
Future breaking changes are introduced as a new version prefix; prior versions remain available for a deprecation period announced on the OpenScanAI GitHub organization.
Pagination
List endpoints such as GET /user/watchlist use offset-based pagination via skip and limit. The response includes a pagination object with total, skip, limit, and hasMore so you can drive a paging loop without guessing when to stop.
| Parameter | Type | Default | Minimum | Description |
|---|---|---|---|---|
| skip | integer | 0 | 0 | Number of records to skip (offset) |
| limit | integer | 100 | 1 | Maximum records to return per page |
# Stream every page sequentially:
SKIP=0
LIMIT=100
while :; do
curl -s "https://xdcscan.io/account/api/v1/user/watchlist?skip=$SKIP&limit=$LIMIT" \
-H "Authorization: Bearer $OPENSCAN_API_KEY" > page.json
jq -e '.data.pagination.hasMore' page.json > /dev/null || break
SKIP=$((SKIP + LIMIT))
done# Stream every page sequentially:
SKIP=0
LIMIT=100
while :; do
curl -s "https://xdcscan.io/account/api/v1/user/watchlist?skip=$SKIP&limit=$LIMIT" \
-H "Authorization: Bearer $OPENSCAN_API_KEY" > page.json
jq -e '.data.pagination.hasMore' page.json > /dev/null || break
SKIP=$((SKIP + LIMIT))
done GET /user/info
Retrieve the authenticated account's profile. The fastest way to verify a key works.
| Parameter | Description |
|---|---|
Authorization header string
required
| Bearer YOUR_API_KEY |
Response 200 OK
| Field | Description |
|---|---|
data.name string | Display name |
data.email string | Account email address |
data.nickname string | Public nickname shown in the explorer |
data.registered_at string | ISO-8601 registration time |
curl https://xdcscan.io/account/api/v1/user/info \
-H "Authorization: Bearer $OPENSCAN_API_KEY"curl https://xdcscan.io/account/api/v1/user/info \
-H "Authorization: Bearer $OPENSCAN_API_KEY" GET /user/api-keys
List API keys for the authenticated account. Key secrets are never returned after creation.
| Parameter | Description |
|---|---|
Authorization header string
required
| Bearer YOUR_API_KEY |
Response 200 OK
| Field | Description |
|---|---|
data[].id string (uuid) | API key ID — used to revoke the key |
data[].name string | Label given at creation |
data[].created_at string | ISO-8601 creation time |
data[].last_used_at string | ISO-8601 time of last authenticated request, or null |
curl https://xdcscan.io/account/api/v1/user/api-keys \
-H "Authorization: Bearer $OPENSCAN_API_KEY"curl https://xdcscan.io/account/api/v1/user/api-keys \
-H "Authorization: Bearer $OPENSCAN_API_KEY" POST /user/api-keys
Create a new API key. The secret is returned exactly once — store it securely.
| Parameter | Description |
|---|---|
Authorization header string
required
| Bearer YOUR_API_KEY (or a session token) |
name body string
required
| Label for the key, e.g. the service that will use it |
Response 201 Created
| Field | Description |
|---|---|
data.id string (uuid) | API key ID |
data.name string | Label given at creation |
data.api_key string | The key secret — shown only in this response |
The same key works on the public REST API v2 and raises your rate limit there. See API Keys & Rate Limits.
Create one key per service so usage attribution and revocation stay simple.
curl -X POST https://xdcscan.io/account/api/v1/user/api-keys \
-H "Authorization: Bearer $OPENSCAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "production-backend" }'curl -X POST https://xdcscan.io/account/api/v1/user/api-keys \
-H "Authorization: Bearer $OPENSCAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "production-backend" }' DELETE /user/api-keys/{id}
Revoke an API key immediately. Requests authenticated with it start returning 401 at once.
| Parameter | Description |
|---|---|
Authorization header string
required
| Bearer YOUR_API_KEY |
id path string (uuid)
required
| API key ID to revoke |
Response 200 OK
curl -X DELETE "https://xdcscan.io/account/api/v1/user/api-keys/9f2b1c4e-7a3d-4c21-9e8f-1a2b3c4d5e6f" \
-H "Authorization: Bearer $OPENSCAN_API_KEY"curl -X DELETE "https://xdcscan.io/account/api/v1/user/api-keys/9f2b1c4e-7a3d-4c21-9e8f-1a2b3c4d5e6f" \
-H "Authorization: Bearer $OPENSCAN_API_KEY" GET /user/watchlist
List watched addresses for the authenticated account, with pagination.
| Parameter | Description |
|---|---|
Authorization header string
required
| Bearer YOUR_API_KEY |
skip query integer | Offset. Default 0 |
limit query integer | Max records. Default 100 |
Response 200 OK
| Field | Description |
|---|---|
data.items[] array | One entry per watched address |
data.items[].id string | Watchlist entry ID — used to update or remove the entry |
data.items[].address_hash string | Watched address |
data.items[].name string | Label shown in notifications |
data.items[].notification_settings object | Per-transfer-type incoming/outgoing toggles |
data.pagination.total number | Total watched addresses for the account |
data.pagination.hasMore boolean | True when more pages are available |
curl "https://xdcscan.io/account/api/v1/user/watchlist?skip=0&limit=100" \
-H "Authorization: Bearer $OPENSCAN_API_KEY"curl "https://xdcscan.io/account/api/v1/user/watchlist?skip=0&limit=100" \
-H "Authorization: Bearer $OPENSCAN_API_KEY" POST /user/watchlist
Add an address to the watchlist and choose which events trigger notifications.
| Parameter | Description |
|---|---|
Authorization header string
required
| Bearer YOUR_API_KEY |
address_hash body string
required
| Address to watch (0x-prefixed) |
name body string
required
| Label shown in notifications and the UI |
notification_settings body object | Per-type toggles: native, ERC-20, ERC-721, ERC-1155, each with incoming/outgoing booleans |
notification_methods body object | Delivery channels: email and/or webhook |
Response 201 Created
| Field | Description |
|---|---|
data.id string | Watchlist entry ID |
data.address_hash string | Watched address |
Watching the same address twice updates the existing entry instead of creating a duplicate.
Webhook URLs are configured account-wide under Account → Notifications — see the watchlist notifications section below.
curl -X POST https://xdcscan.io/account/api/v1/user/watchlist \
-H "Authorization: Bearer $OPENSCAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"address_hash": "0x1234567890abcdef1234567890abcdef12345678",
"name": "Treasury wallet",
"notification_settings": {
"native": { "incoming": true, "outgoing": true },
"ERC-20": { "incoming": true, "outgoing": false }
},
"notification_methods": { "email": true, "webhook": true }
}'curl -X POST https://xdcscan.io/account/api/v1/user/watchlist \
-H "Authorization: Bearer $OPENSCAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"address_hash": "0x1234567890abcdef1234567890abcdef12345678",
"name": "Treasury wallet",
"notification_settings": {
"native": { "incoming": true, "outgoing": true },
"ERC-20": { "incoming": true, "outgoing": false }
},
"notification_methods": { "email": true, "webhook": true }
}' DELETE /user/watchlist/{id}
Remove an address from the watchlist. Notifications for it stop immediately.
| Parameter | Description |
|---|---|
Authorization header string
required
| Bearer YOUR_API_KEY |
id path string
required
| Watchlist entry ID to remove |
Response 200 OK
curl -X DELETE "https://xdcscan.io/account/api/v1/user/watchlist/42" \
-H "Authorization: Bearer $OPENSCAN_API_KEY"curl -X DELETE "https://xdcscan.io/account/api/v1/user/watchlist/42" \
-H "Authorization: Bearer $OPENSCAN_API_KEY"Watchlist notifications
Watchlist notifications
Watched addresses generate notifications when matching transactions are indexed — typically within seconds of block confirmation. Delivery is by email, webhook, or both.
Webhooks: configure the endpoint URL in Account → Notifications. The endpoint must accept JSON POST bodies over HTTPS and answer 2xx within 10 seconds; failed deliveries are retried with backoff.
Payload: each delivery is a watchlist.transaction event with the watchlist entry and the full transaction object (hash, from, to, value, block, timestamp).
Signatures: every delivery carries an X-OpenScan-Signature header — an HMAC-SHA256 of the raw body keyed with the secret shown when you create the webhook. Always verify it before processing.
At-least-once delivery: retries mean your endpoint may see the same event twice. Deduplicate on transaction.hash.# Example webhook payload:
{
"event": "watchlist.transaction",
"watchlist_entry": {
"address_hash": "0x1234567890abcdef1234567890abcdef12345678",
"name": "Treasury wallet"
},
"transaction": {
"hash": "0x9a8b7c6d...",
"from": "0x0000000000000000000000000000000000000001",
"to": "0x1234567890abcdef1234567890abcdef12345678",
"value": "1500000000000000000000",
"block": 89000123,
"timestamp": "2026-08-30T12:34:56Z"
}
}# Example webhook payload:
{
"event": "watchlist.transaction",
"watchlist_entry": {
"address_hash": "0x1234567890abcdef1234567890abcdef12345678",
"name": "Treasury wallet"
},
"transaction": {
"hash": "0x9a8b7c6d...",
"from": "0x0000000000000000000000000000000000000001",
"to": "0x1234567890abcdef1234567890abcdef12345678",
"value": "1500000000000000000000",
"block": 89000123,
"timestamp": "2026-08-30T12:34:56Z"
}
} PATCH /user/watchlist/{id}
Update a watchlist entry's label, notification settings, or delivery channels.
| Parameter | Description |
|---|---|
Authorization header string
required
| Bearer YOUR_API_KEY |
id path string
required
| Watchlist entry ID |
name body string | New label |
notification_settings body object | Replacement per-type toggles |
notification_methods body object | Replacement delivery channels |
Response 200 OK
Omitted fields keep their current values — send only what changes.
curl -X PATCH "https://xdcscan.io/account/api/v1/user/watchlist/42" \
-H "Authorization: Bearer $OPENSCAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"notification_settings": {
"native": { "incoming": true, "outgoing": false }
}
}'curl -X PATCH "https://xdcscan.io/account/api/v1/user/watchlist/42" \
-H "Authorization: Bearer $OPENSCAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"notification_settings": {
"native": { "incoming": true, "outgoing": false }
}
}'Tags & custom ABIs
GET /user/custom-abis
List custom ABIs uploaded by the authenticated account.
| Parameter | Description |
|---|---|
Authorization header string
required
| Bearer YOUR_API_KEY |
Response 200 OK
| Field | Description |
|---|---|
data[].id string | Custom ABI record ID |
data[].contract_address_hash string | Contract the ABI applies to |
data[].name string | Label given at upload |
data[].abi array | The ABI JSON |
curl https://xdcscan.io/account/api/v1/user/custom-abis \
-H "Authorization: Bearer $OPENSCAN_API_KEY"curl https://xdcscan.io/account/api/v1/user/custom-abis \
-H "Authorization: Bearer $OPENSCAN_API_KEY" POST /user/custom-abis
Upload a custom ABI for a contract. The explorer uses it to decode methods and events for your account.
| Parameter | Description |
|---|---|
Authorization header string
required
| Bearer YOUR_API_KEY |
contract_address_hash body string
required
| Contract address (0x-prefixed) |
name body string
required
| Label for the ABI |
abi body array | string
required
| ABI JSON — as an array or a stringified array |
Response 201 Created
| Field | Description |
|---|---|
data.id string | Custom ABI record ID |
Uploading an ABI for a contract that already has one replaces it.
Publicly verified contracts do not need a custom ABI — their verified ABI takes precedence. See the smart-contracts endpoints in the REST API reference.
curl -X POST https://xdcscan.io/account/api/v1/user/custom-abis \
-H "Authorization: Bearer $OPENSCAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contract_address_hash": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
"name": "MyContract",
"abi": "[{"type":"function","name":"balanceOf","inputs":[{"name":"account","type":"address"}],"outputs":[{"name":"","type":"uint256"}],"stateMutability":"view"}]"
}'curl -X POST https://xdcscan.io/account/api/v1/user/custom-abis \
-H "Authorization: Bearer $OPENSCAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contract_address_hash": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
"name": "MyContract",
"abi": "[{"type":"function","name":"balanceOf","inputs":[{"name":"account","type":"address"}],"outputs":[{"name":"","type":"uint256"}],"stateMutability":"view"}]"
}'See also
- Getting Started — full workflow with examples
- Quickstart — watch your first address in five API calls
- REST API v2 — public chain data: blocks, transactions, tokens, contracts
- xdcscan.io — the live OpenScan.AI explorer for XDC Network
- GitHub: OpenScanAI — source and issues