Skip to main content

dataverse-api

uv Ruff codecov

The dataverse-api package is an abstraction layer developed for allowing simple interaction with Microsoft Dataverse Web API.

Table of Contents

Description

The main goal of this project was to enable some use-cases against Dataverse that I wanted to explore for a work assignment, while getting some experience in programming and testing out different ways of setting up the codebase.

The functionality I have built into this Dataverse wrapper constitutes the functionality I have wanted to explore myself.

Most important is to enable creating, upserting, updating and deleting rows of data into Dataverse tables using common data structures, and implementing choices on how these requests are to be formed. For example, when creating new rows, the user can choose between individual POST requests per row, combining data into batch requests against the $batch endpoint, or even to use the CreateMultiple Dataverse action.

The framework is written in Python 3.11, seeing as this runtime is available in the current release of Azure Functions.

Getting started

Usage is fairly simple - authentication must be handled by the user. The DataverseClient simply accepts an already authorized requests.Session with which to handle API requests.

I suggest using msal and msal-requests-auth for authenticating the Session. The examples below include this way of implementing auth:

import os

from msal import ConfidentialClientApplication
from msal_requests_auth.auth import ClientCredentialAuth
from requests import Session

from dataverse_api import DataverseClient

# Prepare Auth
app_reg = ConfidentialClientApplication(
    client_id=os.getenv("app_id"),
    client_credential=os.getenv("client_secret"),
    authority=os.getenv("authority_url"),
)
environment_url = os.getenv("environment_url")
auth = ClientCredentialAuth(client=app_reg, scopes=[environment_url + "/.default"])

# Prepare Session
session = Session()
session.auth = auth

# Instantiate DataverseClient
client = DataverseClient(session=session, environment_url=environment_url)

# Instantiate interface to Entity
entity = client.entity(logical_name="organization")

# Read data!
entity.read(select=["name"])

Logging

The package uses Python's standard logging module with loggers under the dataverse_api namespace, such as dataverse_api.entity and dataverse_api.utils.batching. It installs only a NullHandler: it does not configure the root logger, set logging levels, or write to the console by default. Log records propagate to handlers configured by your application.

For a standalone application, configure logging once at startup:

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(levelname)s %(name)s: %(message)s",
)
logging.getLogger("dataverse_api").setLevel(logging.DEBUG)

This enables detailed logging for this package without enabling debug logging for other libraries. If your application or framework already configures logging, use its existing handlers instead of calling basicConfig again; ensure their levels and filters allow the records you want to see.

To reduce package output to errors, use logging.getLogger("dataverse_api").setLevel(logging.ERROR). Individual module loggers can also be configured independently. API request failures that are returned as response objects are logged at ERROR; potentially unsupported alternate-key types are logged at WARNING.

Development environment

uv is used for managing dependencies. To develop dataverse-api, follow the below steps to set up your local environment:

  1. Install uv if you haven't already.

  2. Clone repository:

$ git clone git@github.com:MarcusRisanger/dataverse-api.git
  1. Move into the newly created local repository:
$ cd dataverse-api
  1. Create virtual environment and install dependencies:
$ uv venv --python 3.11
$ uv sync

Code requirements

All code must pass ruff style checks to be merged. It is recommended to install pre-commit hooks to ensure this locally before commiting code:

$ uv tool install pre-commit

Each public method, class and module should have docstrings. Docstrings are written in the Numpy style.

Testing

To run tests on multiple Python versions, we use tox:

$ uv tool install tox --with tox-uv

When tox is installed, simply run:

$ tox run

To Do:

  • Documentation ..
  • Metadata
    • Choice
    • Multichoice
    • Money
  • Entity implementation
    • Illegal file extensions
    • Upload file
    • Upload image
    • Upload large file
    • Add / remove relationships
    • Add relationships (single/collection valued) as attr on entity
    • Add columns as attr on entity
  • Add Tests:
    • Add column
    • Remove column
    • Add alternate key
    • Remove alternate key

Usage

DataverseClient

Initialize DataverseClient

For now, I've coded the framework around the requests library, for good and bad! In the future, I will consider generalizing further, letting the user pass an authenticated requests handler of choice to the framework by specifying a Protocol class instead.

To instantiate, pass a requests.Session together with a Dataverse environment URL to the DataverseClient constructor:

session = Session()
session.auth = auth
environment_url = os.getenv("dataverse_url")

client = DataverseClient(session=session, environment_url=environment_url)

Create new Entity

It is possible to create a new Entity using the DataverseClient. This requires a full EntityMetadata definition according to Dataverse standards. You can make this yourself and follow the MetadataDumper protocol, or use the provided define_entity function.

The define_label function makes it simple to generate Label metadata with correct LocalizedLabels in its payload.

In the example below, the optional return_representation argument has been set to True to receive the full Entity metadata definition as created by Dataverse as part of the server response. The response can be parsed by EntityMetadata classmethod to get a full fledged object for editing.

from dataverse_api.metadata.attributes import StringAttributeMetadata
from dataverse_api.metadata.entity import EntityMetadata, define_entity
from dataverse_api.utils.labels import define_label

new_entity = define_entity(
    schema_name="new_name",
    attributes=[
        StringAttributeMetadata(
            schema_name="new_primary_col",
            is_primary_name=True,
            description=define_label("Primary column for Entity."),
            display_name=define_label("Autonumber Column"),
            auto_number_format="{SEQNUM:6}-#-{RANDSTRING:3}",
        )
    ],
    description=define_label("Entity Created by Client"),
    display_name=define_label("Programmatically Created Table"),
)

resp = client.create_entity(new_entity, return_representation=True)
entity_meta = EntityMetadata.model_validate_dataverse(resp.json())

Update existing Entity

You can update an existing Entity definition easily by retrieving the Entity metadata definition, and reupload an adjusted version.

Below is a simple example. Note that this method also supports return_representation in the same manner as the DataverseClient.create_entity() method, if you want to return the edited Entity metadata as persisted in Dataverse.

metadata = client.get_entity_definition("new_name")
metadata.display_name.localized_labels[0].label = "Overridden Display Name"

client.update_entity(metadata)

DataverseEntity

Initialize interface with Entity

To initializes an interface with a specific Dataverse Entity, use the DataverseClient.entity() method. It returns a DataverseEntity object that allows interaction with this specific entity.

foo = client.entity(logical_name="foo")

As of now, only LogicalName is supported for instantiating a new DataverseEntity object.

Read

The DataverseEntity.read() method has been furnished with the necessary arguments to do querying as specified in the Microsoft Dataverse documentation.

A simple example:

data = foo.read(select=["name", "address"], filter="salary gt 10000", top=5, order_by="salary desc")

Create

To create rows, you can use a DataFrame supported by narwhals or a simple construct like a list of dicts, where each dict contains the data for a single row.

Below is an example of creating rows in the Entity by passing a dataframe and specifying that the creation method should be the CreateMultiple web API Action. The return_created argument can be set to True if you need the IDs as reference.

foo.create(data=df, mode="multiple", return_created=True)

Note that the different modes provide different content when return_created is set to True - the script simply sets a Prefer header to include created data in the server response.

For now the user may choose how to handle this based on the list of requests.Response objects that will be returned by the method.

Upsert

Upserting data into Dataverse is simple. If you are just updating existing data you may have the URI (Primary Attribute ID) in your data. You can then omit the alternate_key argument.

foo.upsert(data=df, alternate_key="my_key", mode="batch")

Delete

TBD

Add and remove Attributes

TBD

Add and remove Alternate Keys

TBD

Metadata

Release files for dataverse-api 1.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dataverse-api 1.5.0
File Size Uploaded Provenance Actions
dataverse_api-1.5.0.tar.gz 159.6 kB With provenance Details for dataverse_api-1.5.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for dataverse-api 1.5.0
File Interpreter ABI Platform Actions
dataverse_api-1.5.0-py3-none-any.whl Python 3 none any Details for dataverse_api-1.5.0-py3-none-any.whl

Total release size: 192.1 kB

Release files / dataverse_api-1.5.0.tar.gz

Download URL dataverse_api-1.5.0.tar.gz
Size 159.6 kB
Tags Source
SHA-256 checksum
How to use checksums
6d8e7aea30f32eb545ace487516e0d3b54a84b5d64db81ce88ec6f6b52ec655b
BLAKE2b-256 checksum
How to use checksums
c9a327ae3bd6f1245208d24ac001580af5871330171e580b5f42a9fdebd9dd01
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

Release files / dataverse_api-1.5.0-py3-none-any.whl

Download URL dataverse_api-1.5.0-py3-none-any.whl
Size 32.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d254cd8f325d700f953bfb4f4bf0d507702b40402a8358d28d0c35a34f07b63c
BLAKE2b-256 checksum
How to use checksums
d379f8f1ec70e5fcf1424b751e85edb01b7565b34b820d5f1ae5e6f2b8c9badd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

Security for dataverse-api 1.5.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for dataverse_api-1.5.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.5.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page