๐ JSON-RPC
Code samples
POST /api/v1/btc/jsonrpc
curl -X POST 'https://settla.net/api/v1/btc/jsonrpc' \
-H 'X-API-Key: $SETTLA_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "1.0",
"method": "getblockchaininfo",
"params": [],
"id": 1
}'
Proxies a Bitcoin JSON-RPC 1.0 request to the configured BTC node and returns the node's response.
Only read-only chain queries and broadcasting of an already signed transaction are accepted: decoderawtransaction, estimatesmartfee, getbestblockhash, getblock, getblockchaininfo, getblockcount, getblockhash, getblockheader, getblockstats, getdifficulty, getmempoolinfo, getrawtransaction, gettxout, sendrawtransaction, testmempoolaccept. Any other method โ in particular wallet methods โ is rejected with -32601 and never reaches the node.
Batches are supported and are validated as a whole: a single disallowed method rejects the entire batch. A batch is limited to 20 calls. The forwarded body is re-encoded from the parsed request, so duplicate JSON keys cannot smuggle a different method to the node.
The response is the node's own JSON-RPC envelope โ {result, error, id} โ not the flattened shape the REST endpoints return. Branch on error, not on the status code: a node-level failure comes back as HTTP 500 but still carries a usable {result: null, error: {code, message}, id} body, and in a batch the per-call errors sit inside a 200. params is required even when the method takes none: send [].
This is the escape hatch for everything the REST endpoints do not cover โ estimatesmartfee before building a transaction, gettxout to check whether an output is still unspent, testmempoolaccept to dry-run a broadcast, getblockheader to read a block header without pulling megabytes of transactions.
Body parameter โ Chain state โ height, difficulty, sync progress
{
"jsonrpc": "1.0",
"method": "getblockchaininfo",
"params": [],
"id": 1
}
Body parameter โ Fee estimate for confirmation within 6 blocks
{
"jsonrpc": "1.0",
"method": "estimatesmartfee",
"params": [
6
],
"id": 1
}
Body parameter โ Is output 0 of this transaction still spendable?
{
"jsonrpc": "1.0",
"method": "gettxout",
"params": [
"5fc111d07140a808ba08ac28f61fd436efbfe9d84f5437abe980b42c8d9f0a5e",
0
],
"id": 1
}
Body parameter โ Batch โ two calls in one round trip
[
{
"jsonrpc": "1.0",
"method": "getblockcount",
"params": [],
"id": 1
},
{
"jsonrpc": "1.0",
"method": "getblockhash",
"params": [
965000
],
"id": 2
}
]
๐ ๏ธ Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| body | body | object | true | none |
Example responses
200 Response โ getblockchaininfo (trimmed)
{
"result": {
"chain": "main",
"blocks": 965135,
"headers": 965135,
"bestblockhash": "00000000000000000001f61ff38b20267df62cd2264085bbe23997d9b36a2c69",
"bits": "17023cc1",
"target": "000000000000000000023cc10000000000000000000000000000000000000000",
"difficulty": 125807076547197.5,
"time": 1788332427,
"mediantime": 1788329121,
"verificationprogress": 1,
"initialblockdownload": false,
"chainwork": "00000000000000000000000000000000000000014426abd5a51cc72b122956f0",
"size_on_disk": 872577596875,
"pruned": false,
"warnings": []
},
"error": null,
"id": 1
}
200 Response โ estimatesmartfee โ feerate is BTC per kvB, not sat/vB
{
"result": {
"feerate": 0.00001013,
"blocks": 6
},
"error": null,
"id": 1
}
200 Response โ gettxout โ a
resultofnullmeans the output is already spent or never existed
{
"result": {
"bestblock": "00000000000000000000612c6c03a9b1465947557f7d45a82aeca20156902ff8",
"confirmations": 137,
"value": 0.012,
"scriptPubKey": {
"asm": "0 e93523416d7137dad7b63813ca2a72010bc94d3b",
"desc": "addr(bc1qay6jxstdwyma44ak8qfu52njqy9ujnfm37hllg)#dxsdzh08",
"hex": "0014e93523416d7137dad7b63813ca2a72010bc94d3b",
"address": "bc1qay6jxstdwyma44ak8qfu52njqy9ujnfm37hllg",
"type": "witness_v0_keyhash"
},
"coinbase": false
},
"error": null,
"id": 1
}
200 Response โ Batch response โ match by
id, do not rely on position
[
{
"result": 965136,
"error": null,
"id": 1
},
{
"result": "00000000000000000001595977e6000ce56129f5c9b4073e31ccc30b90b97da9",
"error": null,
"id": 2
}
]
400 Response โ Body is not valid JSON
{
"error": {
"code": -32700,
"message": "Parse error: request body is not valid JSON"
}
}
400 Response โ No
methodfield
{
"error": {
"code": -32600,
"message": "Invalid Request: \"method\" must be a string"
}
}
400 Response โ Batch is an empty array
{
"error": {
"code": -32600,
"message": "Invalid Request: empty batch"
}
}
403 Response
{
"error": {
"code": -32601,
"message": "Method \"getbalance\" is not available through this endpoint"
},
"id": 1
}
500 Response
{
"result": null,
"error": {
"code": -22,
"message": "TX decode failed: 0100000000010000 Make sure the tx has at least one input."
},
"id": 1
}
502 Response
{
"error": {
"code": -32603,
"message": "Failed to connect to BTC node"
},
"id": 1
}
๐ Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 200 | OK | Node answered. For a single call the body is one {result, error, id} object; for a batch it is an array of them, one per request, in the order sent. | Inline |
| 400 | Bad Request | Malformed request, rejected before the node was contacted: body is not valid JSON (-32700), method missing or not a string, batch empty or over 20 calls (-32600). | Inline |
| 403 | 403 | Method is not on the allowlist. Every wallet method lands here โ getbalance, sendtoaddress, listunspent, dumpprivkey and the rest. The request never reaches the node. | Inline |
| 500 | Internal Server Error | The node itself rejected the call โ bad parameters, undecodable transaction, unknown hash. The body is a normal JSON-RPC envelope and the node's own error code is in error.code; this is not necessarily an outage. Also returned when the BTC node URL or credentials are not configured. | Inline |
| 502 | Bad Gateway | Could not reach the BTC node at all โ connection refused or timed out. | Inline |
๐ Response Schema
Status Code 400
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| ยป result | objectยฆnull | false | none | Always null when the node produced the error. Absent on proxy-level rejections. |
| ยป error | object | false | none | none |
| ยปยป code | integer | false | none | -32700 parse error, -32600 invalid request, -32601 method not allowed, -32603 proxy could not reach the node. Any other value is Bitcoin Core's own code passed through (-22 decode failed, -5 not found, -8 bad parameter, -26 transaction rejected). |
| ยปยป message | string | false | none | none |
| ยป id | object | false | none | Echoed back from the rejected request when it carried an id, so client libraries can match the error to the call. |
Status Code 403
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| ยป result | objectยฆnull | false | none | Always null when the node produced the error. Absent on proxy-level rejections. |
| ยป error | object | false | none | none |
| ยปยป code | integer | false | none | -32700 parse error, -32600 invalid request, -32601 method not allowed, -32603 proxy could not reach the node. Any other value is Bitcoin Core's own code passed through (-22 decode failed, -5 not found, -8 bad parameter, -26 transaction rejected). |
| ยปยป message | string | false | none | none |
| ยป id | object | false | none | Echoed back from the rejected request when it carried an id, so client libraries can match the error to the call. |
Status Code 500
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| ยป result | objectยฆnull | false | none | Always null when the node produced the error. Absent on proxy-level rejections. |
| ยป error | object | false | none | none |
| ยปยป code | integer | false | none | -32700 parse error, -32600 invalid request, -32601 method not allowed, -32603 proxy could not reach the node. Any other value is Bitcoin Core's own code passed through (-22 decode failed, -5 not found, -8 bad parameter, -26 transaction rejected). |
| ยปยป message | string | false | none | none |
| ยป id | object | false | none | Echoed back from the rejected request when it carried an id, so client libraries can match the error to the call. |
Status Code 502
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| ยป result | objectยฆnull | false | none | Always null when the node produced the error. Absent on proxy-level rejections. |
| ยป error | object | false | none | none |
| ยปยป code | integer | false | none | -32700 parse error, -32600 invalid request, -32601 method not allowed, -32603 proxy could not reach the node. Any other value is Bitcoin Core's own code passed through (-22 decode failed, -5 not found, -8 bad parameter, -26 transaction rejected). |
| ยปยป message | string | false | none | none |
| ยป id | object | false | none | Echoed back from the rejected request when it carried an id, so client libraries can match the error to the call. |