Library / SDK
DataDog/datadogpy avatar
DataDog/datadogpy

datadogpy: the Python client for Datadog events, metrics and DogStatsD

The Datadog Python library

673 stars329 forksPythonNOASSERTION

At a glance

What is it?
datadogpy is Datadog's own Python library, wrapping the HTTP API for events and metrics and speaking DogStatsD over UDP or a Unix socket. It is a thin client with a global initialize() call and a configurable environment, which makes it easy to adopt and easy to outgrow.
Who is it for?
datadogpy fits a Python service that already sends data to Datadog and needs event submission, metric types and DogStatsD tagging without hand-rolling HTTP calls. It is the wrong choice for a team that wants a stateless, dependency-injected client or a project that does not use Datadog at all, since the global initialize() call and the Datadog-specific types are the design rather than an accident.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A client library, and a deliberately small one

datadogpy is described in the README as a collection of tools suitable for inclusion in existing Python projects or for the development of standalone scripts, providing an abstraction on top of two interfaces: Datadog's raw HTTP interface and the Agent's DogStatsD metrics aggregation server. The stated purpose is to interact with Datadog and efficiently report events and metrics.

That framing is worth taking seriously because it says what the library is not. It is not a monitoring agent, it is not a dashboard tool, and it does not scrape anything. It sends what your application already knows about.

The audience is therefore a Python engineer whose service is already instrumented, or who wants to instrument it, and who would rather call a typed function than construct an HTTP request and hand-assemble a metric payload. The library also ships a command line shell, with the dog, dogwrap, dogshell and dogshellwrap entry points registered in the package metadata, for quick manual submissions.

The package is on PyPI as datadog, with the README pointing to a separate, generated library called datadog-api-client-python for covering all the Datadog HTTP API endpoints. The division of labour is sensible: datadogpy handles the common cases and the DogStatsD protocol, and the generated client covers the long tail of endpoints.

The default branch is master, the last push was on 2026-10-20, and the repository is not archived, so this is an actively worked-on client rather than a frozen one.

Events and metrics go through one global initialize call

The API surface in the README is short. You import initialize and api, pass an options dictionary holding your API key and app key, and call initialize with it.

The environment variable route is the alternative. The README says that instead of using initialize with the options parameters, you can set DATADOG_API_KEY and DATADOG_APP_KEY in the context of your application, and that if those are not set the library falls back to Datadog's APM environment variable prefixes, DD_API_KEY and DD_APP_KEY:

python
from datadog import initialize, api

# Assuming you've set `DD_API_KEY` and `DD_APP_KEY` in your env,
# initialize() will pick it up automatically
initialize()

This two-name fallback is the kind of detail that saves an afternoon. The APM-prefixed variables are the ones an APM-instrumented service already has, so in a container that runs the Datadog agent the library often works with no configuration at all.

The cost of this design is the global state. The api module is configured by a call that mutates process-wide state, which means tests that need different credentials have to reset it, and two clients with different keys in one process are awkward. For a single-tenant service that is fine. For a library you ship to third parties, it is a constraint.

For metric types, the README links the dedicated documentation rather than repeating the API, and the list covers count, gauge, set, histogram, timer and distribution.

DogStatsD over UDP or a Unix socket, with a precedence rule

The metrics path is the DogStatsD client, and it needs the Agent to be running and available. The README shows the UDP instantiation with an explicit host and port:

python
from datadog import initialize, statsd

options = {
    "statsd_host": "127.0.0.1",
    "statsd_port": 8125,
}

initialize(**options)

The Unix socket variant swaps those two options for a single statsd_socket_path entry holding the path to the socket.

Both also have an environment variable form, DD_DOGSTATSD_URL, with a udp:// or unix:// prefix, and both come with the same rule stated in the README: manually supplying a host, port or socket path takes precedence over the environment variable. That precedence is worth internalising, because in a container where the environment sets the socket path and an application config file also sets a host and port, the config file wins and the connection may go somewhere unexpected.

Two operational notes from the README. The Agent must be running for DogStatsD metrics to arrive, which means the library is not a standalone metrics system. And in development you can disable statsd metric collection using DD_DOGSTATSD_DISABLE=True, or any non-empty value.

One thing the documentation does not cover in the visible text is the behaviour when the Agent is not running. Given the client speaks UDP to a local port, a client library that fails loudly on a missing listener would be surprising, and silence is the more likely behaviour, but the README does not state it and it is worth checking against the metrics documentation before relying on it.

Origin detection is a tag, and the constant_tags list is yours to protect

Origin detection is a mechanism for working out which pod a DogStatsD packet came from, so the pod's tags can be added to the metric tag list. The implementation is a small thing with a sharp edge.

The client attaches an internal tag called entity_id, whose value is the content of the DD_ENTITY_ID environment variable, which is the pod's UID. The Datadog Agent then uses that tag to add container tags to the metrics. In a Kubernetes manifest you set it by pointing the environment variable at the pod's metadata:

yaml
env:
  - name: DD_ENTITY_ID
    valueFrom:
      fieldRef:
        fieldPath: metadata.uid

The sharp edge is in the warning the README attaches to it. Because the Agent uses this tag to add container tags to the metrics, overwriting that global tag would lose them, and the README says to make sure you only append to the constant_tags list.

In practice this means: if you configure constant_tags, append your own tags to the list rather than assigning a new list to it. Assigning constant_tags = ["my.tag"] discards entity_id and the container tags the Agent would have added, and the metrics still arrive, just without the per-pod attribution. The failure is silent, which is exactly the kind of bug that costs a day.

The mechanism is also why the DD_ENTITY_ID value is the pod UID rather than a hostname. A hostname is not guaranteed to be unique across a cluster lifetime, whereas a UID is.

Supporting Python 2.7 shapes the packaging, and the docs still show setup.py

The package metadata is where the project's real constraints show. The requires-python field reads >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*, and the classifiers list Python 2.7 and 3.7 through 3.9, with CPython and PyPy.

That tells you two things. The library still installs on Python 2.7, which for a client library is a deliberate choice to keep working in older environments, and the classifier list has not been updated for 3.10 and later even though the install constraint does allow them.

The dependency list shows the same vintage. It needs requests>=2.6.0, plus typing for Python below 3.5 and configparser below 5 for Python below 3.0, with the setup.py file carrying comments explaining that both are included so a single wheel build serves every supported version.

The build itself has moved to hatchling, with the version read from datadog/version.py through the tool.hatch.version path, while setup.py remains in the repository and the README still documents python setup.py install as the source install route. Both can be true, but it means the packaging metadata is described in two places, and the pyproject build requirements ask for hatchling>=0.15.0.

There is a separate dogshell implementation and a Rakefile.rb, plus a LICENSE-3rdparty.csv that tracks third party licences separately from the main LICENSE file. GitHub cannot classify the project's licence, so the metadata shows it as a custom one, while the pyproject metadata states BSD-3-Clause and the setup.py header says all files are licensed under BSD-3-Clause unless stated otherwise. The three sources do not line up neatly, and a legal review should read the LICENSE file itself rather than a badge.

Where datadogpy stops and the generated client takes over

The README is explicit about the boundary. To support all the Datadog HTTP APIs, a generated library is available separately as datadog-api-client-python, and the full list of supported endpoints with working code examples is in the Datadog API documentation and in the datadogpy documentation.

So the division is by coverage. datadogpy is the hand-written, ergonomic layer for the things applications do constantly: post an event, submit a metric, set a log, check a monitor. The generated client is the complete, auto-generated surface for the hundreds of remaining endpoints.

That is a sound approach and it has a cost. The hand-written layer is the one with a stable, pleasant API, and it is the one that will keep working across Datadog API changes. The generated client covers more, but it is regenerated, so its surface moves with the API and your code against it moves too.

The other realistic alternative is not a library at all. Sending a DogStatsD metric over UDP is a few bytes of formatted text, and posting an event is an HTTP request. Plenty of teams write those directly and take on the payload formatting, the tagging conventions and the origin detection rules themselves. The trade is that you lose the helper types, the environment fallback chain and the constant_tags handling, and you own the correctness of the metric format.

The most common wrong reason to use a client is to get a nice call signature. If the only requirement is submitting a counter, the overhead of a global initialize and a stateful module is more than the problem deserves.

Maintenance, testing and how to read the changelog

The release history shows steady, frequent work. Version 0.53.0 was published on 2026-07-24, 0.54.0 on 2026-09-22, and 0.55.0 on 2026-09-30, and the last push to the repository was on 2026-10-20 with the repository not archived. The versions are still below 1.0, which is a useful signal about the API stability the maintainers are willing to promise.

The project runs two separate Azure Pipelines builds, with badges for unit tests and integration tests on the master branch, and a Read the Docs documentation status badge. The split between unit and integration pipelines matters for a client like this, because the integration suite is where the library talks to a real Datadog endpoint and where the network behaviour is actually exercised.

The repository carries the usual supporting files for a project of this size. There is a CHANGELOG.md that the README points to for changes, a CONTRIBUTING.md, a DEVELOPMENT.md, a RELEASING.md describing the release process, a SUPPORT.md, a tox.ini, a mypy.ini, a.pylintrc, a.pre-commit-config.yaml and a doc directory.

The typing story is worth a note given the mypy configuration and the py.typed package data. The library ships a py.typed marker, which means type checkers will use its annotations, and it carries a mypy configuration of its own, so the client is typed for consumers rather than only internally.

When you pin a version, the CHANGELOG is the document to read, especially because the 0.x line gives no semver guarantee that a breaking change will come with a major bump.

Editorial conclusion

datadogpy fits a Python service that already sends data to Datadog and needs event submission, metric types and DogStatsD tagging without hand-rolling HTTP calls. It is the wrong choice for a team that wants a stateless, dependency-injected client or a project that does not use Datadog at all, since the global initialize() call and the Datadog-specific types are the design rather than an accident. Before adopting it, confirm your Python version is inside the supported range, decide whether the global api module fits your test setup, and check the CHANGELOG for the version you are pinning.

Frequently asked questions

How do I install datadogpy?

The README gives pip install datadog for a standard install, and python setup.py install for installing from source.

Which environment variables does datadogpy read for API credentials?

It reads DATADOG_API_KEY and DATADOG_APP_KEY, and if those are not set it falls back to the APM prefixes DD_API_KEY and DD_APP_KEY.

How do I send metrics with datadogpy over a Unix socket?

Pass a statsd_socket_path option to initialize, for example the path in the options dictionary, or set DD_DOGSTATSD_URL with a unix:// prefix, and a manually supplied socket path takes precedence over the environment variable.

What is DD_ENTITY_ID used for in datadogpy?

It holds the pod UID and the client attaches it as the internal entity_id tag so the Datadog Agent can add container tags to the metrics, which is why you should append to constant_tags rather than replace the list.

Does datadogpy cover every Datadog API endpoint?

No, the README points to the separately generated datadog-api-client-python library for full coverage of the Datadog HTTP APIs.

Official sources

  1. DataDog/datadogpy on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/datadog-datadogpy.svg)](https://hysenlabs.com/projects/datadog-datadogpy)

Was this useful?