Skip to content

Neptune legacy API#

The legacy client is deprecated

With the 1.0 release of the Neptune client library, we're ending active maintenance and support for the Neptune legacy API.

From 1.0 onward, you must import the legacy API with the following statement:

import neptune.legacy as neptune

We strongly encourage you to migrate to the new client at your earliest convenience.

Legacy docs#

Neptune legacy client documentation

Migrating to the new API#

This guide walks you through migrating your legacy code to the new Python API.


import neptune.legacy as neptune


import neptune

run = neptune.init_run(project="my-workspace/my-project", tags=["resnet"])


The names of the environment variables have not changed.

Instead of specifying the project name or API token in the code, you can provide them by setting the NEPTUNE_PROJECT and NEPTUNE_API_TOKEN environment variables.

Hierarchical structure#

Runs can be viewed as nested dictionary-like structures that you can define in your code. This lets you organize your metadata in a way that is most convenient for you.

The hierarchical structure that you apply to your metadata is reflected when you view the metadata in the app.

The structure of a run consists of fields that are organized into namespaces.

The path of a field is a combination of the namespaces and its name: if you store a value (such as a float, 0.8) in a field named momentum under a namespace params, the full path of the field is params/momentum.

You can organize any type of metadata this way – such as images, parameters, metrics, scores, model checkpoints, and CSV files.

Let's look at the following code:

import neptune

run = neptune.init_run(project="my_workspace/my_project")

run["about/JIRA"] = "NPT-952"

run["parameters/batch_size"] = 5
run["parameters/algorithm"] = "ConvNet"

for epoch in range(100):
    acc_value = ...
    loss_value = ...


The resulting structure of the run will be:

    "JIRA": String
    "batch_size": Float
    "algorithm": String
    "accuracy": FloatSeries
    "loss": FloatSeries
"trained_model": File

Batch assign#

You can assign values to multiple fields in batch by using a dictionary.

With the legacy API, you had to pass parameters when creating an experiment and it was not possible to change them afterward. In addition, nested dictionaries were not fully supported.

import neptune.legacy as neptune

PARAMS = {"epoch_nr": 100, "lr": 0.005, "use_nesterov": True}


With the new neptune API, it's up to you when and where you want to specify parameters. You can also update them later:

import neptune

PARAMS = {"epoch_nr": 100, "lr": 0.005, "use_nesterov": True}

run = neptune.init_run()
run["my_params"] = PARAMS

# You can also specify parameters one by one
run["my_params/batch_size"] = 64

# Update lr value
run["my_params/lr"] = 0.007

The artificial distinction between parameters and properties is also gone. You can log and access them in a single, unified way.

Interacting with files (artifacts)#

You are no longer bound to store files only in the artifacts folder.

Whether it's a model checkpoint, custom interactive visualization, or audio file, you can track it in the same hierarchical structure with the rest of the metadata:

Legacy New
neptune.log_artifact("model_viz.png") run[model/viz"].upload("model_viz.png")
neptune.log_artifact("") run[trained_model"].upload("")
neptune.download_artifact("") run["trained_model"].download()


In the legacy API, artifacts mimicked a file system. What you uploaded was saved under the same name, with the extension, etc.

The idea behind the new Python API is more database-like: We have a field (with a path) and under it, we store some content - Float, String, series of String, or File. In this model, the extension is part of the content.

Example: If under the path "model/last" you upload a .pt file, the file will be displayed as "" in the app.

When it's unambiguous, we implicitly convert an object to type File and there is no need for explicit conversion.

Example: For Matplotlib charts, you can do


instead of



We've integrated neptune-contrib file-related functionalities into the core library.

The conversion methods are available as File factory methods:

Interactive charts (Altair, Bokeh, Plotly, Matplotlib)#

Legacy New
from neptunecontrib.api import log_chart from neptune.types import File
log_chart("int_chart", chart) run["int_chart"].upload(File.as_html(chart))

pandas DataFrame#

Legacy New
from neptunecontrib.api import log_table from neptune.types import File
log_table("pred_df", df) run["pred_df"].upload(File.as_html(df))

Audio and video#

We've expanded the range of files that are natively supported in the Neptune app, so for audio and video files you no longer need to use conversion methods:

Legacy New

from neptunecontrib.api import log_audio



from neptunecontrib.api import log_video



Pickled objects#

Legacy New
from neptunecontrib.api import log_pickle from neptune.types import File
log_pickle("model.pkl", model) run["model"].upload(File.as_pickle(model))

HTML strings#

Legacy New
from neptunecontrib.api import log_html from neptune.types import File
log_html("custom_viz", html_string) run["custom_viz"].upload(File.from_content(model), extension="html")

Scores and metrics#

Logging metrics is quite similar, except that you can now organize them in a hierarchical structure:

Legacy API New API
neptune.log_metric("acc", 0.97) run["acc"].append(0.97)
neptune.log_metric("train_acc", 0.97) run["train/acc"].append(0.97)
neptune.log_metric("loss", 0.8) run["key"].append()

To log scores you don't need to use Series fields anymore as you can track single values anytime, anywhere:

Legacy API New API
neptune.log_metric("final_accuracy", 0.8) run["final_accuracy"] = 0.8

Text and image series#

Similar changes need to be applied for text and image series:

Legacy API New API
neptune.log_text("train_log", custom_log_msg) run["train/log"].append(custom_log_msg)
neptune.log_image("misclassified", filepath) run["misclassified"].append(File(filepath))
neptune.log_image("pred_dist", hist_chart) run["pred_dist"].append(hist_chart)

To add a single image that you want to view with the rest of the metrics you no longer need to use Series fields. As you control whether they are grouped in the same namespace you can upload it as a single File field.

Legacy API New API
neptune.log_image("train_hist", hist_chart) run["train/hist"].upload(hist_chart)


We've redesigned how our integrations work:

  • They're more tightly integrated with our base library and the API is more unified.
  • You can update the version of each integration separately in case of dependency conflicts.
  • There's a smaller number of dependencies if you're not using all integrations.

There is no longer a neptune-contrib library for the new Python API – instead, each integration has two parts:

  • Boilerplate code for ease of use in the main library:

    import neptune.integrations.framework_name

  • The actual integration that can be installed separately:

    pip install neptune-framework-name

    or as an extra together with neptune:

    pip install "neptune[framework-name]"

Existing integrations from neptune-contrib are still fully functional. You can use them both with projects using the previous structure and the new structure. However, integrations from neptune-contrib use the legacy Python API, while the new integrations are rewritten to fully leverage the new Python API for better metadata organization.


For detailed integration guides, see the Integrations tab.

Let's look at how this works like in the case of the Keras integration:


Legacy New
pip install neptune-contrib

pip install "neptune[tensorflow-keras]"


pip install neptune-tensorflow-keras


import neptune.legacy as neptune


from neptunecontrib.monitoring.keras import NeptuneMonitor
import neptune

run = neptune.init_run()

from neptune.integrations.tensorflow_keras import NeptuneCallback


Interaction with tags is quite similar: tags are stored as a StringSet field under the sys/tags field.

import neptune.legacy as neptune


neptune.create_experiment(params=PARAMS, tags=["maskRCNN"])

neptune.append_tags("finetune", "keras")
import neptune

run = neptune.init_run(tags=["maskRCNN"])

run["sys/tags"].add(["finetune", "keras"])

Logging to a project with legacy runs#

If you have a project with a set of runs (previously Experiments) that were created using the Legacy API and you would like to add runs using the new API, there are a few things to remember.

Metadata structure#

Each type of metadata from the previous version (parameters, properties, logs, artifacts, and monitoring) are mapped into a separate fixed namespace. If you want to be able to compare those, you need to make sure that the field path is backward compatible:

Legacy New
neptune.create_experiment(params=params_dict) run["parameters"] = params_dict
neptune.set_property("SEED", 2458) run["properties/SEED"] = str(2458)
neptune.log_metric("acc", 0.98) run["logs/acc"].append(0.98)
neptune.log_artifact("", "") run["artifacts/model"].upload("")

Data types#

Neptune is type-sensitive. For example, a column in the experiments table represents a field with a specific path and type. In particular, FloatSeries and Float are two different types and Neptune by default doesn't know how to compare them.

With the Legacy API, it wasn't possible to set Float fields outside of the create_experiment() function, and a common practice was to store those as FloatSeries with one entry through the use of log_metric() function. To achieve backward compatibility, you need to create a FloatSeries with the new API as well, by using the append() method.

Properties set through Legacy API are always String fields. To achieve backward compatibility, use assignment with explicit casting to str to ensure that the resulting field will be a string.