Channels are a way for you to send metrics, logs and images to the Neptune UI. They are a mechanism that allows you to:

  • monitor and debug your experiment’s execution by observing channels during runtime;
  • build interactive charts from numeric channels to observe trends and fluctuations;
  • compare experiments, selecting the most promising one.

A channel is a series of pairs (x, y), where x is always a floating point number, and y can be a floating point number, text or an image with name and description. When sending new values to a channel, remember that x should be strictly increasing and y has to be of the same type within a single channel.

channels screenshot

Sending Values to Channels

Sending something to a channel, requires creating the channel first. You can do it explicitly, by calling create_channel method or let Neptune create it implicitly with the channel_send method. Both methods return a handle to the created channel, which you can use to send values (see send).

Regardless of which method you choose to send values, you can always omit the x parameter, in which case Neptune will automatically assign it for you.

Let’s send a hello world text to a channel named my_channel:

ctx.channel_send("my_channel", "hello world")

Or, equivalently:

ctx.channel_send("my_channel", 1.0, "hello world")

When Neptune creates a channel implicitly, it infers its type based on the argument. In this instance, my_channel is of type neptune.TEXT.

If you prefer to have more control over the channel type, you can pass its type upon creation (using the create_channel method):

my_channel_handle = ctx.create_channel(

my_channel_handle.send(2.0, "world")

Channel Types

Neptune supports three types of data as channel values. The following channel types can be used when creating a channel.

Channel’s type Value type
neptune.ChannelType.NUMERIC float
neptune.ChannelType.TEXT str/unicode
neptune.ChannelType.IMAGE neptune.Image

Below are 3 examples of sending basic values of each type.

ctx.channel_send("my_text_channel", "hello world")
ctx.channel_send("my_numeric_channel", 3.14)
ctx.channel_send("my_image_channel", neptune.Image(
  name="image’s name",
  description="this image depicts a cat",

The last example requires a bit more explanation. neptune.Image constructor takes str/unicode as name of the image, str/unicode as description of the image and PIL.Image as image data. Name and description will be displayed in the Neptune UI.


The maximum supported size of the image is 512x512 px.

Sending Values Printed to the Console

Sometimes the simplest solution is the most useful. If your code prints some logs to the stdout or stderr, you can easily convert those logs into channels by running the experiment with additional command line arguments.

Let’s take a look at a minimal example:


import neptune
from random import random

ctx = neptune.Context()

while True:
  print('loss {}'.format(random()))
  print('acc {}'.format(random()))

Command to run:

neptune send --log-channel loss --log-channel acc:accuracy

Neptune will create two numeric channels: loss and accuracy based on the output of your experiment.

In Detail

Under the hood, this feature works by parsing stdout and stderr for prefixes defined with the --log-channel (or just -l) parameter. When a prefix is found, the value following the prefix is converted to floating point number and sent to Neptune via an implicitly created channel.

The channels are named after their respective prefixes, by default, but can be renamed using the prefix:desired_channel_name syntax.

Example: OpenCV Images

The short snippet below shows how to send OpenCV images to Neptune.

import cv2
import numpy as np
import PIL
import neptune

ctx = neptune.Context()

# The following assumes that example_image.jpg is available
image = cv2.imread('/input/example_image.jpg', 0)

neptune_image = neptune.Image(
name="OpenCV image",
description="One and only opencv image",

ctx.channel_send("bluebox", neptune_image)