CLNRest is a lightweight Python-based core lightning plugin that transforms RPC calls into a REST service. By generating REST API endpoints, it enables the execution of Core Lightning's RPC methods behind the scenes and provides responses in JSON format.

A complete documentation for the REST interface is available at REST API REFERENCE.



REST API REFERENCE can also be tested with your own server.

By default, the base URL is set to connect with the Blockstream-hosted regtest node.

However, it can be configured to connect to your own cln node as described below:

  • Select {protocol}://{ip}:{port}/ from Base URL dropdown on the right section of the page.

  • Click on the right side of the dropdown and configure protocol, ip and port values according to your setup.

  • The ip should be configured with your system's public IP address.

  • Default rest-host is but this testing will require it to be

Note: This setup is for testing only. It is highly recommended to test with non-mainnet (regtest/testnet) setup only.


Install required packages with pip install json5 flask flask_restx gunicorn pyln-client flask-socketio gevent gevent-websocket or pip install -r requirements.txt.

Note: if you have the older c-lightning-rest plugin, you can use disable-plugin to avoid any conflict with this one. Of course, you could use this one instead!


If rest-port is not specified, the plugin will disable itself.

  • --rest-port: Sets the REST server port to listen to (3010 is common)
  • --rest-protocol: Specifies the REST server protocol. Default is HTTPS.
  • --rest-host: Defines the REST server host. Default is
  • --rest-certs: Defines the path for HTTPS cert & key. Default path is same as RPC file path to utilize gRPC's client certificate. If it is missing at the configured location, new identity will be generated.
  • --rest-csp: Creates a whitelist of trusted content sources that can run on a webpage and helps mitigate the risk of attacks.
    Default CSP is set as default-src 'self'; font-src 'self'; img-src 'self' data:; frame-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline';.
    Example CSP: rest-csp=default-src 'self'; font-src 'self'; img-src 'self'; frame-src 'self'; style-src 'self'; script-src 'self';.
  • --rest-cors-origins: Define multiple origins which are allowed to share resources on web pages to a domain different from the one that served the web page. Default is * which allows all origins. Example to define multiple origins:


With the default configurations, the Swagger user interface will be available at The POST method requires rune header for authorization.

  • A new rune can be created via createrune or the list of existing runes can be retrieved with listrunes command.

Note: in version v23.08, a parameter Nodeid was required to be the id of the node we're talking to (see id (pubkey) received from getinfo ). You can still send this for backwards compatiblity, but it is completely ignored.


Example curl command for POST will also require a rune header like below:
curl -k -X POST '' -H 'Rune: <node-rune>'

With -k or --insecure option curl proceeds with the connection even if the SSL certificate cannot be verified.
This option should be used only when testing with self signed certificate.

Websocket Server

Websocket server is available at clnrest queues up notifications received for a second then broadcasts them to listeners.

Websocket client examples


import socketio
import requests

http_session = requests.Session()
http_session.verify = True
    "rune": "your-generated-rune"
sio = socketio.Client(http_session=http_session)

def connect():
    print("Client Connected")

def disconnect():
    print(f"Server connection closed.\nCheck CLN logs for errors if unexpected")

def message(data):
    print(f"Message from server: {data}")

def error(err):
    print(f"Error from server: {err}")




const io = require('');

const socket = io.connect('', {extraHeaders: {rune: "your-generated-rune"}});

socket.on('connect', function() {
  console.log('Client Connected');

socket.on('disconnect', function(reason) {
  console.log('Server connection closed: ', reason, '\nCheck CLN logs for errors if unexpected');

socket.on('message', function(data) {
  console.log('Message from server: ', data);

socket.on('error', function(err) {
  console.error('Error from server: ', err);


<!DOCTYPE html>
    <title>Socket.IO Client Example</title>
    <script src=""></script>
    <h1>Socket.IO Client Example</h1>
    <div id="status">Not connected</div>
    <h3>Send Message:</h3>
    <input type="text" id="messageInput" placeholder="Type your message here">
    <button onclick="sendMessage()">Send</button>
    <h3>Received Messages:</h3>
    <div id="messages"></div>
        const statusElement = document.getElementById('status');
        const messagesElement = document.getElementById('messages');

        const socket = io('', {extraHeaders: {rune: "your-generated-rune"}});

        socket.on('connect', () => {
            statusElement.textContent = 'Client Connected';

        socket.on('disconnect', (reason) => {
            statusElement.textContent = 'Server connection closed: ' + reason + '\n Check CLN logs for errors if unexpected';

        socket.on('message', (data) => {
            const item = document.createElement('li');
            item.textContent = JSON.stringify(data);
            console.log('Message from server: ', data);

        socket.on('error', (err) => {
            const item = document.createElement('li');
            item.textContent = JSON.stringify(err);
            console.error('Error from server: ', err);

        function sendMessage() {
            const message = messageInput.value;
            if (message) {
                socket.emit('message', message);
                messageInput.value = '';