# Concepts

# Data model

Prometheus fundamentally stores all data as [_time
series_](http://en.wikipedia.org/wiki/Time_series): streams of timestamped
values belonging to the same metric and the same set of labeled dimensions.
Besides stored time series, Prometheus may generate temporary derived time series
as the result of queries.

## Metric names and labels

Every time series is uniquely identified by its metric name and optional key-value pairs called labels.

***Metric names:***

* Metric names SHOULD specify the general feature of a system that is measured (e.g. `http_requests_total` - the total number of HTTP requests received).
* Metric names MAY use any UTF-8 characters.
* Metric names SHOULD match the regex `[a-zA-Z_:][a-zA-Z0-9_:]*` for the best experience and compatibility (see the warning below). Metric names outside of that set will require quoting e.g. when used in PromQL (see the [UTF-8 guide](../guides/utf8.md#querying)).

NOTE: Colons (':') are reserved for user-defined recording rules. They SHOULD NOT be used by exporters or direct instrumentation.

***Metric labels:***

Labels let you capture different instances of the same metric name. For example: all HTTP requests that used the method `POST` to the `/api/tracks` handler. We refer to this as Prometheus's "dimensional data model". The query language allows filtering and aggregation based on these dimensions. The change of any label's value, including adding or removing labels, will create a new time series.

* Label names MAY use any UTF-8 characters.
* Label names beginning with `__` (two underscores) MUST be reserved for internal Prometheus use.
* Label names SHOULD match the regex `[a-zA-Z_][a-zA-Z0-9_]*` for the best experience and compatibility (see the warning below). Label names outside of that regex will require quoting e.g. when used in PromQL (see the [UTF-8 guide](../guides/utf8.md#querying)).
* Label values MAY contain any UTF-8 characters.
* Labels with an empty label value are considered equivalent to labels that do not exist.

WARNING: The [UTF-8](../guides/utf8.md) support for metric and label names was added relatively recently in Prometheus v3.0.0. It might take time for the wider ecosystem (downstream PromQL compatible projects and vendors, tooling, third-party instrumentation, collectors, etc.) to adopt new quoting mechanisms, relaxed validation etc. For the best compatibility it's recommended to stick to the recommended ("SHOULD") character set.

INFO: See also the [best practices for naming metrics and labels](/docs/practices/naming/).

## Samples

Samples form the actual time series data. Each sample consists of:

* a float64 or [native histogram](https://prometheus.io/docs/specs/native_histograms/) value
* a millisecond-precision timestamp

## Notation

Given a metric name and a set of labels, time series are frequently identified
using this notation:

    <metric name>{<label name>="<label value>", ...}

For example, a time series with the metric name `api_http_requests_total` and
the labels `method="POST"` and `handler="/messages"` could be written like
this:

    api_http_requests_total{method="POST", handler="/messages"}

This is the same notation that [OpenTSDB](http://opentsdb.net/) uses.

Names with UTF-8 characters outside the recommended set must be quoted, using
this notation:

    {"<metric name>", <label name>="<label value>", ...}

Since metric name are internally represented as a label pair
with a special label name (`__name__="<metric name>"`) one could also use the following notation:

    {__name__="<metric name>", <label name>="<label value>", ...}

# Jobs and instances

In Prometheus terms, an endpoint you can scrape is called an _instance_,
usually corresponding to a single process. A collection of instances with the same purpose, a process replicated for scalability or reliability for example, is called a _job_.

For example, an API server job with four replicated instances:

   * job: `api-server`
      * instance 1: `1.2.3.4:5670`
      * instance 2: `1.2.3.4:5671`
      * instance 3: `5.6.7.8:5670`
      * instance 4: `5.6.7.8:5671`

## Automatically generated labels and time series

When Prometheus scrapes a target, it attaches some labels automatically to the
scraped time series which serve to identify the scraped target:

* `job`: The configured job name that the target belongs to.
* `instance`: The `<host>:<port>` part of the target's URL that was scraped.

If either of these labels are already present in the scraped data, the behavior
depends on the `honor_labels` configuration option. See the
[scrape configuration documentation](/docs/prometheus/latest/configuration/configuration/#scrape_config)
for more information.

For each instance scrape, Prometheus stores a [sample](/docs/introduction/glossary#sample) in
the following time series:

* `up{job="<job-name>", instance="<instance-id>"}`: `1` if the instance is
   healthy, i.e. reachable, or `0` if the scrape failed.
* `scrape_duration_seconds{job="<job-name>", instance="<instance-id>"}`:
   duration of the scrape.
* `scrape_samples_post_metric_relabeling{job="<job-name>", instance="<instance-id>"}`:
   the number of samples remaining after metric relabeling was applied.
* `scrape_samples_scraped{job="<job-name>", instance="<instance-id>"}`:
   the number of samples the target exposed.
* `scrape_series_added{job="<job-name>", instance="<instance-id>"}`:
   the approximate number of new series in this scrape. *New in v2.10*

The `up` time series is useful for instance availability monitoring.

With the [`extra-scrape-metrics` feature flag](/docs/prometheus/latest/feature_flags/#extra-scrape-metrics) several additional metrics are available:

* `scrape_timeout_seconds{job="<job-name>", instance="<instance-id>"}`: The configured `scrape_timeout` for a target.
* `scrape_sample_limit{job="<job-name>", instance="<instance-id>"}`: The configured `sample_limit` for a target. Returns zero if there is no limit configured.
* `scrape_body_size_bytes{job="<job-name>", instance="<instance-id>"}`: The uncompressed size of the most recent scrape response, if successful. Scrapes failing because `body_size_limit` is exceeded report -1, other scrape failures report 0.

# Metric types

The Prometheus instrumentation libraries offer four core metric types. With the
exception of native histograms, these are currently only differentiated in the
API of instrumentation libraries and in the exposition protocols.
The Prometheus server does not yet make
use of the type information and flattens all types except native histograms
into untyped time series of floating point values. Native histograms, however,
are ingested as time series of special composite histogram samples. In the
future, Prometheus might handle other metric types as [composite
types](/blog/2026/02/14/modernizing-prometheus-composite-samples/), too. There
is also ongoing work to persist the type information of the simple float
samples.

## Counter

A _counter_ is a cumulative metric that represents a single [monotonically
increasing counter](https://en.wikipedia.org/wiki/Monotonic_function) whose
value can only increase or be reset to zero on restart. For example, you can
use a counter to represent the number of requests served, tasks completed, or
errors.

Do not use a counter to expose a value that can decrease. For example, do not
use a counter for the number of currently running processes; instead use a gauge.

Instrumentation library usage documentation for counters:

   * [Go](http://godoc.org/github.com/prometheus/client_golang/prometheus#Counter)
   * [Java](https://prometheus.github.io/client_java/getting-started/metric-types/#counter)
   * [Python](https://prometheus.github.io/client_python/instrumenting/counter/)
   * [Ruby](https://github.com/prometheus/client_ruby#counter)
   * [.Net](https://github.com/prometheus-net/prometheus-net#counters)
   * [Rust](https://docs.rs/prometheus-client/latest/prometheus_client/metrics/counter/index.html)

## Gauge

A _gauge_ is a metric that represents a single numerical value that can
arbitrarily go up and down.

Gauges are typically used for measured values like temperatures or current
memory usage, but also "counts" that can go up and down, like the number of
concurrent requests.

Instrumentation library usage documentation for gauges:

   * [Go](http://godoc.org/github.com/prometheus/client_golang/prometheus#Gauge)
   * [Java](https://prometheus.github.io/client_java/getting-started/metric-types/#gauge)
   * [Python](https://prometheus.github.io/client_python/instrumenting/gauge/)
   * [Ruby](https://github.com/prometheus/client_ruby#gauge)
   * [.Net](https://github.com/prometheus-net/prometheus-net#gauges)
   * [Rust](https://docs.rs/prometheus-client/latest/prometheus_client/metrics/gauge/index.html)

## Histogram

A _histogram_ records observations (usually things like request durations or
response sizes) by counting them in configurable buckets. It also provides a sum
of all observed values. As such, a histogram is essentially a bucketed counter.
However, a histogram can also represent the current state of a distribution, in
which case it is called a _gauge histogram_. In contrast to the usual
counter-like histograms, gauge histograms are rarely directly exposed by
instrumented programs and are thus not (yet) usable in instrumentation
libraries, but they are represented in newer versions of the protobuf
exposition format and in [OpenMetrics](https://openmetrics.io/). They are also
created regularly by PromQL expressions. For example, the outcome of applying
the `rate` function to a counter histogram is a gauge histogram, in the same
way as the outcome of applying the `rate` function to a counter is a gauge.

Histograms exists in two fundamentally different versions: The more recent
_native histograms_ and the older _classic histograms_.

A native histogram is exposed and ingested as composite samples, where each
sample represents the count and sum of observations together with a dynamic set
of buckets.

A classic histogram, however, consists of multiple time series of simple float
samples. A classic histogram with a base metric name of `<basename>` results in
the following time series:

  * cumulative counters for the observation buckets, exposed as
    `<basename>_bucket{le="<upper inclusive bound>"}`
  * the **total sum** of all observed values, exposed as `<basename>_sum`
  * the **count** of events that have been observed, exposed as
    `<basename>_count` (identical to `<basename>_bucket{le="+Inf"}` above)

Native histograms are generally much more efficient than classic histograms,
allow much higher resolution, do not require explicit configuration of bucket
boundaries during instrumentation, and provide atomicity when transferred over
the network (e.g. via the Prometheus remote write procol, where classic
histograms suffer from possible partial transfer because their constituent time
series are transferred independently). Their bucketing schema ensures that they
are always aggregatable with each other, even if the resolution might have
changed, while classic histograms with different bucket boundaries are not
generally aggregatable. If the instrumentation library you are using supports
native histograms (currently this is the case for Go and Java), you should
probably [prefer native histograms over classic
histograms](/docs/practices/histograms).

If you are stuck with classic histograms for whatever reason, there is a way to
get at least some of the benefits of native histograms: You can configure
Prometheus to ingest classic histograms into a special form of native
histograms, called Native Histograms with Custom Bucket boundaries (NHCB).
NHCBs are stored as the same composite samples as usual native histograms,
providing increased efficiency and atomic network transfers, similar to regular
native histgorms. However, the buckets of NHCBs still have the same layout as
in their classic counterparts, statically configured during instrumentation,
with the same limited resolution and range and the same problems of
aggregatability upon changing the bucket boundaries.

Use the [`histogram_quantile()`
function](/docs/prometheus/latest/querying/functions/#histogram_quantile) to
calculate quantiles from histograms or even aggregations of histograms. It
works for both classic and native histograms, using a slightly different
syntax. Histograms are also suitable to calculate an [Apdex
score](http://en.wikipedia.org/wiki/Apdex).

You can operate directly on the buckets of a classic histogram, as they are
represented as individual series (called `<basename>_bucket{le="<upper
inclusive bound>"}` as described above). Remember, however, that these buckets
are [cumulative](https://en.wikipedia.org/wiki/Histogram#Cumulative_histogram),
i.e. every bucket counts all observations less than or equal to the upper
boundary provided as a label. With native histograms, you can look at
observations within given boundaries with the [`histogram_fraction()`
function](/docs/prometheus/latest/querying/functions/#histogram_fraction) (to
calculate fractions of observations) and the [trim operators]() (to filter for
the desired band of observations).

See [histograms and summaries](/docs/practices/histograms) for details of
histogram usage and differences to [summaries](#summary).

NOTE: Beginning with Prometheus v3.0, the values of the `le` label of classic
histograms are normalized during ingestion to follow the format of
[OpenMetrics Canonical Numbers](https://github.com/prometheus/OpenMetrics/blob/main/specification/OpenMetrics.md#considerations-canonical-numbers).

Instrumentation library usage documentation for histograms:

   * [Go](http://godoc.org/github.com/prometheus/client_golang/prometheus#Histogram)
   * [Java](https://prometheus.github.io/client_java/getting-started/metric-types/#histogram)
   * [Python](https://prometheus.github.io/client_python/instrumenting/histogram/)
   * [Ruby](https://github.com/prometheus/client_ruby#histogram)
   * [.Net](https://github.com/prometheus-net/prometheus-net#histogram)
   * [Rust](https://docs.rs/prometheus-client/latest/prometheus_client/metrics/histogram/index.html)

## Summary

Similar to a _histogram_, a _summary_ samples observations (usually things like
request durations and response sizes). While it also provides a total count of
observations and a sum of all observed values, it calculates configurable
quantiles over a sliding time window.

A summary with a base metric name of `<basename>` exposes multiple time series
during a scrape:

  * streaming **φ-quantiles** (0 ≤ φ ≤ 1) of observed events, exposed as `<basename>{quantile="<φ>"}`
  * the **total sum** of all observed values, exposed as `<basename>_sum`
  * the **count** of events that have been observed, exposed as `<basename>_count`

See [histograms and summaries](/docs/practices/histograms) for
detailed explanations of φ-quantiles, summary usage, and differences
to [histograms](#histogram).

NOTE: Beginning with Prometheus v3.0, the values of the `quantile` label are normalized during
ingestion to follow the format of [OpenMetrics Canonical Numbers](https://github.com/prometheus/OpenMetrics/blob/main/specification/OpenMetrics.md#considerations-canonical-numbers).

Instrumentation library usage documentation for summaries:

   * [Go](http://godoc.org/github.com/prometheus/client_golang/prometheus#Summary)
   * [Java](https://prometheus.github.io/client_java/getting-started/metric-types/#summary)
   * [Python](https://prometheus.github.io/client_python/instrumenting/summary/)
   * [Ruby](https://github.com/prometheus/client_ruby#summary)
   * [.Net](https://github.com/prometheus-net/prometheus-net#summary)