Skip to main content

Random Numbers by Statistical Distribution

MOCK DATA

Seeded draws from normal, log-normal, exponential, Poisson, binomial, Pareto and Zipf distributions, with empirical and theoretical statistics.

Generated test data for fixtures and development

Base URL
/api/distributions
Capabilities
2 routes Seedable
Last updated
July 29, 2026

GET /api/distributions

Each record is one independent draw: value, the theoretical CDF at that value in percentile (null for the discrete distributions), the sampler branch in method, and index so that repeated discrete draws stay distinct records.

Live requestRuns against the public API
No key required
GET/api/distributions?dist=normal

Request parameters

Which distribution to draw from. Each one reads its own parameters; anything belonging to another distribution is echoed back with a warning and ignored.

dist=uniform: lower bound, inclusive. Defaults to 0. Must be ≤ max, and min=max is allowed (with a warning) — every draw is then that exact value. Rounded draws are clamped up to the smallest value with `precision` decimals that is still ≥ min.

dist=uniform: upper bound. Defaults to 1. Draws are rounded to `precision` and clamped down to the largest value with that many decimals that is still ≤ max, so a value can land exactly on the bound but never past it.

Location. dist=normal: the mean itself. dist=lognormal: the mean of the underlying normal, so exp(mean) is the MEDIAN of the draw, not its mean. Defaults to 0.

Scale for dist=normal and dist=lognormal; must be > 0 (zero or negative is a 400). For lognormal it is the standard deviation of the underlying normal. Defaults to 1.

dist=exponential: the rate λ; must be > 0. The theoretical mean is 1/rate. Defaults to 1.

dist=poisson: the mean number of events per interval; must be > 0. Required — Poisson has no canonical default. At or below 30 the sampler is Knuth's exact method, above it a normal approximation.

dist=binomial: the number of Bernoulli trials n (1–10000). Required. At or below 30 the sampler sums real trials, above it uses a normal approximation.

dist=binomial: the success probability p of one trial, between 0 and 1 inclusive (anything outside is a 400). Required.

Shape; must be > 0. dist=pareto: the tail index — required, and log5/log4 ≈ 1.161 is the classic 80/20 shape. dist=zipf: the rank exponent, defaulting to 1 (Zipf's law).

dist=pareto: the scale — the smallest value the distribution can take; must be > 0. Defaults to 1. No draw is ever returned below it: after rounding, a value is clamped up to the smallest number with `precision` decimals that is still ≥ xmin.

dist=zipf: the number of ranks N (1–100000). Required. Draws are whole ranks 1..N with P(k) ∝ k^−alpha.

Decimal places for the continuous distributions (uniform, normal, lognormal, exponential, pareto). Defaults to 4. Rounding happens last and is clamped back inside the support; a grid too coarse for the distribution's interquartile range is warned about, and one that leaves no valid uniform value is a 400. Ignored — with a warning — by Poisson, binomial and Zipf, whose draws are whole numbers.

Advanced response options7 options

How many records to generate (1–100).

Deterministic output: the same seed always returns the same records. Omit for random (the used seed is echoed in meta.seed).

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

Return all fields except these (comma-separated).

Response format: json envelope, ndjson (one record per line) or csv.

Pretty-print the JSON response.

Drop the envelope: return the raw array/object without data/meta wrapper.

Response

Example parameters are ready. Send the request to inspect the live response.

Route reference

Code samples Ready-to-copy requests in 4 languages
Choose a code sample language
curl "https://randomapi.dev/api/distributions?dist=normal"
const res = await fetch("https://randomapi.dev/api/distributions?dist=normal");
const { data, meta } = await res.json();
import requests

data = requests.get("https://randomapi.dev/api/distributions?dist=normal").json()["data"]
$json = json_decode(file_get_contents(
  "https://randomapi.dev/api/distributions?dist=normal"
), true);
$data = $json["data"];
Parameters Route-specific request inputs 13
dist enum required

Which distribution to draw from. Each one reads its own parameters; anything belonging to another distribution is echoed back with a warning and ignored.

allowed: uniform | normal | lognormal | exponential | poisson | binomial | pareto | zipf
example: dist=normal
min float

dist=uniform: lower bound, inclusive. Defaults to 0. Must be ≤ max, and min=max is allowed (with a warning) — every draw is then that exact value. Rounded draws are clamped up to the smallest value with `precision` decimals that is still ≥ min.

allowed: -1000000000 – 1000000000
example: min=0
max float

dist=uniform: upper bound. Defaults to 1. Draws are rounded to `precision` and clamped down to the largest value with that many decimals that is still ≤ max, so a value can land exactly on the bound but never past it.

allowed: -1000000000 – 1000000000
example: max=10
mean float

Location. dist=normal: the mean itself. dist=lognormal: the mean of the underlying normal, so exp(mean) is the MEDIAN of the draw, not its mean. Defaults to 0.

allowed: -1000000000 – 1000000000
example: mean=100
stddev float

Scale for dist=normal and dist=lognormal; must be > 0 (zero or negative is a 400). For lognormal it is the standard deviation of the underlying normal. Defaults to 1.

allowed: ≤ 1000000000
example: stddev=15
rate float

dist=exponential: the rate λ; must be > 0. The theoretical mean is 1/rate. Defaults to 1.

allowed: ≤ 1000000000
example: rate=0.5
lambda float

dist=poisson: the mean number of events per interval; must be > 0. Required — Poisson has no canonical default. At or below 30 the sampler is Knuth's exact method, above it a normal approximation.

allowed: ≤ 1000000000
example: lambda=4
trials int

dist=binomial: the number of Bernoulli trials n (1–10000). Required. At or below 30 the sampler sums real trials, above it uses a normal approximation.

allowed: 1 – 10000
example: trials=20
probability float

dist=binomial: the success probability p of one trial, between 0 and 1 inclusive (anything outside is a 400). Required.

example: probability=0.3
alpha float

Shape; must be > 0. dist=pareto: the tail index — required, and log5/log4 ≈ 1.161 is the classic 80/20 shape. dist=zipf: the rank exponent, defaulting to 1 (Zipf's law).

allowed: ≤ 1000
example: alpha=1.5
xmin float

dist=pareto: the scale — the smallest value the distribution can take; must be > 0. Defaults to 1. No draw is ever returned below it: after rounding, a value is clamped up to the smallest number with `precision` decimals that is still ≥ xmin.

allowed: ≤ 1000000000
example: xmin=1
support int

dist=zipf: the number of ranks N (1–100000). Required. Draws are whole ranks 1..N with P(k) ∝ k^−alpha.

allowed: 1 – 100000
example: support=1000
precision int

Decimal places for the continuous distributions (uniform, normal, lognormal, exponential, pareto). Defaults to 4. Rounding happens last and is clamped back inside the support; a grid too coarse for the distribution's interquartile range is warned about, and one that leaves no valid uniform value is a 400. Ignored — with a warning — by Poisson, binomial and Zipf, whose draws are whole numbers.

allowed: 0 – 10
example: precision=2
Universal parameters Shared response and formatting options 7
count int

How many records to generate (1–100).

default: 10
allowed: 1 – 100
example: count=3
seed int

Deterministic output: the same seed always returns the same records. Omit for random (the used seed is echoed in meta.seed).

example: seed=42
fields list

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

example: fields=index,value
exclude list

Return all fields except these (comma-separated).

example: exclude=method
format enum

Response format: json envelope, ndjson (one record per line) or csv.

default: json
allowed: json | ndjson | csv
example: format=csv
pretty boolean

Pretty-print the JSON response.

default: false
example: pretty=true
unwrap boolean

Drop the envelope: return the raw array/object without data/meta wrapper.

default: false
example: unwrap=true
Response schema Fields returned in each record 4
index integer

0-based record position. It exists so repeated discrete draws remain distinct records without any jitter being added to `value`.

example: 0

value number

The draw. A decimal rounded to `precision` for uniform, normal, lognormal, exponential and pareto; a whole number for poisson, binomial and zipf.

example: 106.9337

percentile float nullable

Theoretical CDF evaluated at the returned `value`, 0–1, rounded to 6 decimals. Null for poisson, binomial and zipf, whose CDF is a step function.

example: 0.678048

method string

The sampler branch actually used: inverse-cdf, box-muller, knuth, bernoulli-sum or normal-approximation.

example: box-muller

Documented examples

Build-generated requests and complete responses
9
Ten standard normal draws
GET /api/distributions?dist=normal
{
  "data": [
    {
      "index": 0,
      "value": -0.503,
      "percentile": 0.307482,
      "method": "box-muller"
    },
    {
      "index": 1,
      "value": 0.2839,
      "percentile": 0.611756,
      "method": "box-muller"
    },
    {
      "index": 2,
      "value": 1.4461,
      "percentile": 0.925925,
      "method": "box-muller"
    },
    {
      "index": 3,
      "value": -0.4989,
      "percentile": 0.308925,
      "method": "box-muller"
    },
    {
      "index": 4,
      "value": -0.412,
      "percentile": 0.34017,
      "method": "box-muller"
    },
    {
      "index": 5,
      "value": -0.1553,
      "percentile": 0.438292,
      "method": "box-muller"
    },
    {
      "index": 6,
    …
    "generatedAt": "2026-07-29T08:13:22.000Z"
  }
}
Uniform decimals across a bounded range
GET /api/distributions?dist=uniform&min=0&max=100&precision=2&count=10&seed=4
{
  "data": [
    {
      "index": 0,
      "value": 42.77,
      "percentile": 0.4277,
      "method": "inverse-cdf"
    },
    {
      "index": 1,
      "value": 73.35,
      "percentile": 0.7335,
      "method": "inverse-cdf"
    },
    {
      "index": 2,
      "value": 17.12,
      "percentile": 0.1712,
      "method": "inverse-cdf"
    },
    {
      "index": 3,
      "value": 49.08,
      "percentile": 0.4908,
      "method": "inverse-cdf"
    },
    {
      "index": 4,
      "value": 89.08,
      "percentile": 0.8908,
      "method": "inverse-cdf"
    },
    {
      "index": 5,
      "value": 15.59,
      "percentile": 0.1559,
      "method": "inverse-cdf"
    },
    {
      "index": 6,
    …
    "generatedAt": "2026-07-29T08:13:22.000Z"
  }
}
IQ-shaped normal values
GET /api/distributions?dist=normal&mean=100&stddev=15&count=20&seed=42
{
  "data": [
    {
      "index": 0,
      "value": 92.4545,
      "percentile": 0.30747,
      "method": "box-muller"
    },
    {
      "index": 1,
      "value": 104.2583,
      "percentile": 0.611751,
      "method": "box-muller"
    },
    {
      "index": 2,
      "value": 121.6914,
      "percentile": 0.925924,
      "method": "box-muller"
    },
    {
      "index": 3,
      "value": 92.5158,
      "percentile": 0.308908,
      "method": "box-muller"
    },
    {
      "index": 4,
      "value": 93.8202,
      "percentile": 0.340175,
      "method": "box-muller"
    },
    {
      "index": 5,
      "value": 97.6712,
      "percentile": 0.438311,
      "method": "box-muller"
    },
    {
      "index": 6,
    …
    "generatedAt": "2026-07-29T08:13:22.000Z"
  }
}
Log-normal latencies in milliseconds
GET /api/distributions?dist=lognormal&mean=4.6&stddev=0.55&precision=1&count=20&seed=7
{
  "data": [
    {
      "index": 0,
      "value": 164.3,
      "percentile": 0.819161,
      "method": "box-muller"
    },
    {
      "index": 1,
      "value": 55,
      "percentile": 0.140612,
      "method": "box-muller"
    },
    {
      "index": 2,
      "value": 105.3,
      "percentile": 0.541136,
      "method": "box-muller"
    },
    {
      "index": 3,
      "value": 71.7,
      "percentile": 0.275764,
      "method": "box-muller"
    },
    {
      "index": 4,
      "value": 76.6,
      "percentile": 0.317294,
      "method": "box-muller"
    },
    {
      "index": 5,
      "value": 142.1,
      "percentile": 0.741585,
      "method": "box-muller"
    },
    {
      "index": 6,
    …
    "generatedAt": "2026-07-29T08:13:22.000Z"
  }
}
Poisson arrivals per minute
GET /api/distributions?dist=poisson&lambda=4&count=20&seed=1
{
  "data": [
    {
      "index": 0,
      "value": 3,
      "percentile": null,
      "method": "knuth"
    },
    {
      "index": 1,
      "value": 2,
      "percentile": null,
      "method": "knuth"
    },
    {
      "index": 2,
      "value": 3,
      "percentile": null,
      "method": "knuth"
    },
    {
      "index": 3,
      "value": 3,
      "percentile": null,
      "method": "knuth"
    },
    {
      "index": 4,
      "value": 2,
      "percentile": null,
      "method": "knuth"
    },
    {
      "index": 5,
      "value": 5,
      "percentile": null,
      "method": "knuth"
    },
    {
      "index": 6,
    …
    "generatedAt": "2026-07-29T08:13:22.000Z"
  }
}
Binomial conversions out of 200 visitors
GET /api/distributions?dist=binomial&trials=200&probability=0.12&count=10&seed=3
{
  "data": [
    {
      "index": 0,
      "value": 25,
      "percentile": null,
      "method": "normal-approximation"
    },
    {
      "index": 1,
      "value": 37,
      "percentile": null,
      "method": "normal-approximation"
    },
    {
      "index": 2,
      "value": 24,
      "percentile": null,
      "method": "normal-approximation"
    },
    {
      "index": 3,
      "value": 25,
      "percentile": null,
      "method": "normal-approximation"
    },
    {
      "index": 4,
      "value": 18,
      "percentile": null,
      "method": "normal-approximation"
    },
    {
      "index": 5,
      "value": 25,
      "percentile": null,
      "method": "normal-approximation"
    },
    {
      "index": 6,
    …
    "generatedAt": "2026-07-29T08:13:22.000Z"
  }
}
Pareto file sizes with an 80/20 tail
GET /api/distributions?dist=pareto&alpha=1.161&xmin=1024&precision=0&count=10&seed=5
{
  "data": [
    {
      "index": 0,
      "value": 1730,
      "percentile": 0.456015,
      "method": "inverse-cdf"
    },
    {
      "index": 1,
      "value": 1650,
      "percentile": 0.425276,
      "method": "inverse-cdf"
    },
    {
      "index": 2,
      "value": 5421,
      "percentile": 0.855558,
      "method": "inverse-cdf"
    },
    {
      "index": 3,
      "value": 1079,
      "percentile": 0.058933,
      "method": "inverse-cdf"
    },
    {
      "index": 4,
      "value": 1580,
      "percentile": 0.39561,
      "method": "inverse-cdf"
    },
    {
      "index": 5,
      "value": 10173,
      "percentile": 0.930448,
      "method": "inverse-cdf"
    },
    {
      "index": 6,
    …
    "generatedAt": "2026-07-29T08:13:22.000Z"
  }
}
Zipf-ranked picks from 1000 ranks
GET /api/distributions?dist=zipf&support=1000&count=15&seed=9
{
  "data": [
    {
      "index": 0,
      "value": 6,
      "percentile": null,
      "method": "inverse-cdf"
    },
    {
      "index": 1,
      "value": 10,
      "percentile": null,
      "method": "inverse-cdf"
    },
    {
      "index": 2,
      "value": 13,
      "percentile": null,
      "method": "inverse-cdf"
    },
    {
      "index": 3,
      "value": 2,
      "percentile": null,
      "method": "inverse-cdf"
    },
    {
      "index": 4,
      "value": 193,
      "percentile": null,
      "method": "inverse-cdf"
    },
    {
      "index": 5,
      "value": 33,
      "percentile": null,
      "method": "inverse-cdf"
    },
    {
      "index": 6,
    …
    "generatedAt": "2026-07-29T08:13:22.000Z"
  }
}
Just the values, newline-delimited
GET /api/distributions?dist=exponential&rate=0.5&fields=value&format=ndjson&count=25
{"value":1.2993}
{"value":0.0807}
{"value":3.8896}
{"value":0.3353}
{"value":3.5879}
{"value":0.0335}
{"value":0.1736}
{"value":3.5873}
{"value":1.2138}
{"value":6.7377}
{"value":8.597}
{"value":0.3965}
{"value":0.1631}
{"value":1.5526}
{"value":2.7816}
{"value":3.3317}
{"value":0.5679}
{"value":1.8913}
{"value":1.261}
{"value":1.3326}
{"value":3.1711}
{"value":0.017}
{"value":0.0658}
{"value":2.3421}
{"value":1.756}

GET /api/distributions/summary

Draws size values from the chosen distribution and reports the sample's mean, variance, standard deviation, extremes and quantiles alongside the closed-form moments. Moments that do not exist are null with an explanation in theoretical.note.

Live requestRuns against the public API
No key required
GET/api/distributions/summary?dist=normal&mean=100&stddev=15

Request parameters

Which distribution to draw from. Each one reads its own parameters; anything belonging to another distribution is echoed back with a warning and ignored.

How many values to draw before computing the statistics (1–10000). Larger samples sit closer to the theoretical moments on average — the remaining gap is sampling error.

Determinism anchor for the sample. Any integer is accepted and reduced modulo 2³² exactly as the root route's `seed` is, so the sample is exactly the records the root route returns for that seed — `seed=-1` and `seed=4294967295` are the same sample on both routes.

dist=uniform: lower bound, inclusive. Defaults to 0. Must be ≤ max, and min=max is allowed (with a warning) — every draw is then that exact value. Rounded draws are clamped up to the smallest value with `precision` decimals that is still ≥ min.

dist=uniform: upper bound. Defaults to 1. Draws are rounded to `precision` and clamped down to the largest value with that many decimals that is still ≤ max, so a value can land exactly on the bound but never past it.

Location. dist=normal: the mean itself. dist=lognormal: the mean of the underlying normal, so exp(mean) is the MEDIAN of the draw, not its mean. Defaults to 0.

Scale for dist=normal and dist=lognormal; must be > 0 (zero or negative is a 400). For lognormal it is the standard deviation of the underlying normal. Defaults to 1.

dist=exponential: the rate λ; must be > 0. The theoretical mean is 1/rate. Defaults to 1.

dist=poisson: the mean number of events per interval; must be > 0. Required — Poisson has no canonical default. At or below 30 the sampler is Knuth's exact method, above it a normal approximation.

dist=binomial: the number of Bernoulli trials n (1–10000). Required. At or below 30 the sampler sums real trials, above it uses a normal approximation.

dist=binomial: the success probability p of one trial, between 0 and 1 inclusive (anything outside is a 400). Required.

Shape; must be > 0. dist=pareto: the tail index — required, and log5/log4 ≈ 1.161 is the classic 80/20 shape. dist=zipf: the rank exponent, defaulting to 1 (Zipf's law).

dist=pareto: the scale — the smallest value the distribution can take; must be > 0. Defaults to 1. No draw is ever returned below it: after rounding, a value is clamped up to the smallest number with `precision` decimals that is still ≥ xmin.

dist=zipf: the number of ranks N (1–100000). Required. Draws are whole ranks 1..N with P(k) ∝ k^−alpha.

Decimal places for the continuous distributions (uniform, normal, lognormal, exponential, pareto). Defaults to 4. Rounding happens last and is clamped back inside the support; a grid too coarse for the distribution's interquartile range is warned about, and one that leaves no valid uniform value is a 400. Ignored — with a warning — by Poisson, binomial and Zipf, whose draws are whole numbers.

Advanced response options4 options

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

Return all fields except these (comma-separated).

Pretty-print the JSON response.

Drop the envelope: return the raw array/object without data/meta wrapper.

Response

Example parameters are ready. Send the request to inspect the live response.

Route reference

Code samples Ready-to-copy requests in 4 languages
Choose a code sample language
curl "https://randomapi.dev/api/distributions/summary?dist=normal&mean=100&stddev=15"
const res = await fetch("https://randomapi.dev/api/distributions/summary?dist=normal&mean=100&stddev=15");
const { data, meta } = await res.json();
import requests

data = requests.get("https://randomapi.dev/api/distributions/summary?dist=normal&mean=100&stddev=15").json()["data"]
$json = json_decode(file_get_contents(
  "https://randomapi.dev/api/distributions/summary?dist=normal&mean=100&stddev=15"
), true);
$data = $json["data"];
Parameters Route-specific request inputs 15
dist enum required

Which distribution to draw from. Each one reads its own parameters; anything belonging to another distribution is echoed back with a warning and ignored.

allowed: uniform | normal | lognormal | exponential | poisson | binomial | pareto | zipf
example: dist=normal
size int

How many values to draw before computing the statistics (1–10000). Larger samples sit closer to the theoretical moments on average — the remaining gap is sampling error.

default: 1000
allowed: 1 – 10000
example: size=5000
seed int

Determinism anchor for the sample. Any integer is accepted and reduced modulo 2³² exactly as the root route's `seed` is, so the sample is exactly the records the root route returns for that seed — `seed=-1` and `seed=4294967295` are the same sample on both routes.

default: 0
example: seed=42
min float

dist=uniform: lower bound, inclusive. Defaults to 0. Must be ≤ max, and min=max is allowed (with a warning) — every draw is then that exact value. Rounded draws are clamped up to the smallest value with `precision` decimals that is still ≥ min.

allowed: -1000000000 – 1000000000
example: min=0
max float

dist=uniform: upper bound. Defaults to 1. Draws are rounded to `precision` and clamped down to the largest value with that many decimals that is still ≤ max, so a value can land exactly on the bound but never past it.

allowed: -1000000000 – 1000000000
example: max=10
mean float

Location. dist=normal: the mean itself. dist=lognormal: the mean of the underlying normal, so exp(mean) is the MEDIAN of the draw, not its mean. Defaults to 0.

allowed: -1000000000 – 1000000000
example: mean=100
stddev float

Scale for dist=normal and dist=lognormal; must be > 0 (zero or negative is a 400). For lognormal it is the standard deviation of the underlying normal. Defaults to 1.

allowed: ≤ 1000000000
example: stddev=15
rate float

dist=exponential: the rate λ; must be > 0. The theoretical mean is 1/rate. Defaults to 1.

allowed: ≤ 1000000000
example: rate=0.5
lambda float

dist=poisson: the mean number of events per interval; must be > 0. Required — Poisson has no canonical default. At or below 30 the sampler is Knuth's exact method, above it a normal approximation.

allowed: ≤ 1000000000
example: lambda=4
trials int

dist=binomial: the number of Bernoulli trials n (1–10000). Required. At or below 30 the sampler sums real trials, above it uses a normal approximation.

allowed: 1 – 10000
example: trials=20
probability float

dist=binomial: the success probability p of one trial, between 0 and 1 inclusive (anything outside is a 400). Required.

example: probability=0.3
alpha float

Shape; must be > 0. dist=pareto: the tail index — required, and log5/log4 ≈ 1.161 is the classic 80/20 shape. dist=zipf: the rank exponent, defaulting to 1 (Zipf's law).

allowed: ≤ 1000
example: alpha=1.5
xmin float

dist=pareto: the scale — the smallest value the distribution can take; must be > 0. Defaults to 1. No draw is ever returned below it: after rounding, a value is clamped up to the smallest number with `precision` decimals that is still ≥ xmin.

allowed: ≤ 1000000000
example: xmin=1
support int

dist=zipf: the number of ranks N (1–100000). Required. Draws are whole ranks 1..N with P(k) ∝ k^−alpha.

allowed: 1 – 100000
example: support=1000
precision int

Decimal places for the continuous distributions (uniform, normal, lognormal, exponential, pareto). Defaults to 4. Rounding happens last and is clamped back inside the support; a grid too coarse for the distribution's interquartile range is warned about, and one that leaves no valid uniform value is a 400. Ignored — with a warning — by Poisson, binomial and Zipf, whose draws are whole numbers.

allowed: 0 – 10
example: precision=2
Universal parameters Shared response and formatting options 4
fields list

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

example: fields=dist,size
exclude list

Return all fields except these (comma-separated).

example: exclude=seed
pretty boolean

Pretty-print the JSON response.

default: false
example: pretty=true
unwrap boolean

Drop the envelope: return the raw array/object without data/meta wrapper.

default: false
example: unwrap=true
Response schema Fields returned in each record 7
dist string

The distribution that was sampled.

example: normal

size integer

Number of values drawn.

example: 1000

parameters object

The resolved distribution parameters that were used, defaults included (plus `precision` for the continuous distributions).

example: {"mean":100,"stddev":15,"precision":4}

empirical object

Statistics of the drawn sample: `mean`, population `variance`, `stddev`, `min`, `max`, `median`, `p25`, `p75`, `p95`. Quantiles are nearest-rank, so each is an actual draw.

example: {"mean":99.9571,"variance":220.5983,"stddev":14.8525,"min":52.7431,"max":148.9902,"median":99.9128,"p25":89.7712,"p75":110.0264,"p95":124.3187}

theoretical object

Closed-form `mean` and `variance` of the distribution itself. Either is null when the moment does not exist, or cannot be represented as a double, and `note` then explains which.

example: {"mean":100,"variance":225,"note":null}

method string

The sampler branch used for every value in the sample.

example: box-muller

seed integer

The seed the sample was drawn from.

example: 42

Documented examples

Build-generated requests and complete responses
5
Normal sample statistics
GET /api/distributions/summary?dist=normal&mean=100&stddev=15
{
  "data": {
    "dist": "normal",
    "size": 1000,
    "parameters": {
      "mean": 100,
      "stddev": 15,
      "precision": 4
    },
    "empirical": {
      "mean": 99.523127,
      "variance": 210.609656,
      "stddev": 14.512397,
      "min": 50.4711,
      "max": 146.4477,
      "median": 100.0259,
      "p25": 89.9118,
      "p75": 109.1211,
      "p95": 122.3621
    },
    "theoretical": {
      "mean": 100,
      "variance": 225,
      "note": null
    },
    "method": "box-muller",
    "seed": 0
  },
  "meta": {
    "endpoint": "distributions",
    "route": "/summary",
    "params": {
      "dist": "normal",
      "size": 1000,
      "seed": 0,
      "mean": 100,
      "stddev": 15,
      "precision": 4
    },
    "generatedAt": "2026-07-29T08:13:22.000Z"
  }
}
Poisson: empirical against theoretical
GET /api/distributions/summary?dist=poisson&lambda=4&size=5000&seed=42
{
  "data": {
    "dist": "poisson",
    "size": 5000,
    "parameters": {
      "lambda": 4
    },
    "empirical": {
      "mean": 3.9702,
      "variance": 3.813312,
      "stddev": 1.95277,
      "min": 0,
      "max": 12,
      "median": 4,
      "p25": 3,
      "p75": 5,
      "p95": 7
    },
    "theoretical": {
      "mean": 4,
      "variance": 4,
      "note": null
    },
    "method": "knuth",
    "seed": 42
  },
  "meta": {
    "endpoint": "distributions",
    "route": "/summary",
    "params": {
      "dist": "poisson",
      "size": 5000,
      "seed": 42,
      "lambda": 4
    },
    "generatedAt": "2026-07-29T08:13:22.000Z"
  }
}
Pareto whose theoretical mean is infinite
GET /api/distributions/summary?dist=pareto&alpha=0.8&size=2000
{
  "data": {
    "dist": "pareto",
    "size": 2000,
    "parameters": {
      "alpha": 0.8,
      "xmin": 1,
      "precision": 4
    },
    "empirical": {
      "mean": 21.284961,
      "variance": 32352.089235,
      "stddev": 179.866865,
      "min": 1.0003,
      "max": 4103.8254,
      "median": 2.4197,
      "p25": 1.4569,
      "p75": 5.5733,
      "p95": 37.1668
    },
    "theoretical": {
      "mean": null,
      "variance": null,
      "note": "A Pareto distribution has an infinite mean for alpha ≤ 1 and an infinite variance for alpha ≤ 2, so both are reported as null instead of a number the distribution does not have."
    },
    "method": "inverse-cdf",
    "seed": 0
  },
  "meta": {
    "endpoint": "distributions",
    "route": "/summary",
    "params": {
      "dist": "pareto",
      "size": 2000,
      "seed": 0,
      "alpha": 0.8,
      "xmin": 1,
      "precision": 4
    },
    "generatedAt": "2026-07-29T08:13:22.000Z"
  }
}
Exponential service times
GET /api/distributions/summary?dist=exponential&rate=0.25&size=10000&seed=7
{
  "data": {
    "dist": "exponential",
    "size": 10000,
    "parameters": {
      "rate": 0.25,
      "precision": 4
    },
    "empirical": {
      "mean": 3.979804,
      "variance": 15.903349,
      "stddev": 3.9879,
      "min": 0.0002,
      "max": 36.5569,
      "median": 2.7852,
      "p25": 1.1515,
      "p75": 5.4894,
      "p95": 11.7879
    },
    "theoretical": {
      "mean": 4,
      "variance": 16,
      "note": null
    },
    "method": "inverse-cdf",
    "seed": 7
  },
  "meta": {
    "endpoint": "distributions",
    "route": "/summary",
    "params": {
      "dist": "exponential",
      "size": 10000,
      "seed": 7,
      "rate": 0.25,
      "precision": 4
    },
    "generatedAt": "2026-07-29T08:13:22.000Z"
  }
}
Zipf over 500 ranks
GET /api/distributions/summary?dist=zipf&support=500&alpha=1.2&size=5000&seed=3
{
  "data": {
    "dist": "zipf",
    "size": 5000,
    "parameters": {
      "alpha": 1.2,
      "support": 500
    },
    "empirical": {
      "mean": 43.9554,
      "variance": 7800.444211,
      "stddev": 88.320123,
      "min": 1,
      "max": 500,
      "median": 6,
      "p25": 2,
      "p75": 36,
      "p95": 259
    },
    "theoretical": {
      "mean": 43.321384,
      "variance": 7799.173693,
      "note": null
    },
    "method": "inverse-cdf",
    "seed": 3
  },
  "meta": {
    "endpoint": "distributions",
    "route": "/summary",
    "params": {
      "dist": "zipf",
      "size": 5000,
      "seed": 3,
      "alpha": 1.2,
      "support": 500
    },
    "generatedAt": "2026-07-29T08:13:22.000Z"
  }
}

About this API

Coverage & behavior

Draws seeded pseudo-random numbers from eight named distributions, so fixtures, load-test inputs and charts have a realistic shape instead of the flat noise a uniform generator gives you. Every response names the exact sampler branch it used in method, and /summary puts a sample's empirical statistics next to the closed-form theoretical ones.

Parameters per distribution. A parameter with a canonical textbook default is optional; the rest must be supplied, and omitting one is a 400 naming both the parameter and the distribution. Every resolved value — defaults included — is echoed in meta.params.

dist parameters defaults returned draw
uniform min, max 0, 1 decimal in [min, max]
normal mean, stddev 0, 1 decimal of either sign
lognormal mean, stddev 0, 1 decimal ≥ 0 — the draw itself is positive, but rounding can bring a tiny one down to 0
exponential rate 1 decimal ≥ 0
poisson lambda required whole number ≥ 0
binomial trials, probability both required whole number in [0, trials]
pareto alpha, xmin alpha required, xmin 1 decimal ≥ xmin
zipf alpha, support alpha 1, support required whole rank in [1, support]

Every continuous draw is rounded to precision last, so that column describes the value you actually receive, not just the mathematics behind it. See Rounding below.

The algorithms, all standard and all named.

  • uniform — inverse CDF min + u·(max − min). method: "inverse-cdf".
  • normal — the Box–Muller transform: mean + stddev·√(−2·ln u₁)·cos(2π u₂). The transform produces two independent deviates; this endpoint always keeps the cosine one and discards the sine, so a record depends only on its own seed stream and never on the record before it. Because the uniform stream has 2⁻³² resolution the deviate is truncated at |z| ≈ 6.6604 — a tail worth about 3 in 100 billion draws, and the reason no draw can come back as Infinity. method: "box-muller".
  • lognormalexp() of that same normal deviate, which is why exp(mean) is the median of the draw and exp(mean + stddev²/2) its mean. method: "box-muller".
  • exponential — inverse CDF −ln(1 − u)/rate. u = 0 maps to exactly 0, the left end of the support, and the generator's 2⁻³² resolution caps one draw at ln(2³²)/rate ≈ 22.18/rate — there is no code path that can return Infinity. Draws smaller than half a rounding step also come back as 0, which is inside the support. method: "inverse-cdf".
  • poissonKnuth's product method (multiply uniforms until the product drops below e^−λ), which is exact, for lambda ≤ 30. Above 30 the sampler switches to a rounded normal approximation round(λ + √λ·z) floored at 0 and reports method: "normal-approximation" instead of "knuth". 30 is both the conventional point where that approximation becomes good and the point where Knuth's ~λ+1 draws per value get expensive.
  • binomial — the sum of n Bernoulli trials (exact) for trials ≤ 30, the same cost cutoff: an exact sum at trials=10000 and size=10000 would be 100 million draws in one request. Above 30 the sampler switches to a rounded normal approximation round(np + √(np(1−p))·z) clamped to [0, n] and reports method: "normal-approximation" instead of "bernoulli-sum". If that approximation runs while n·p or n·(1−p) is below 10 — the textbook validity rule — the response adds a warning saying so, because that is where the approximated shape stops being trustworthy.
  • pareto — inverse CDF xmin/(1 − u)^(1/alpha). alpha = log 5 / log 4 ≈ 1.161 is the classic 80/20 shape. method: "inverse-cdf".
  • zipf — the exact inverse CDF of the truncated Zipf law P(k) = k^−alpha / H(N, alpha) over ranks 1..support, from a cumulative table built once per request (not once per record) and searched in O(log N) per draw. method: "inverse-cdf".

Rounding: precision is applied last, and never at the cost of the support. The five continuous distributions round their draw to precision decimals (default 4) as the final step, so the rounding grid — a step of 10^−precision — is part of the answer, not cosmetic:

  • The documented bounds always hold. Rounding can step a draw past a bound that is not itself on the grid, so a rounded value is clamped to the tightest bound that is both inside the support and on the grid: ceil(min) / floor(max) for uniform, ceil(xmin) for pareto. A pareto draw is therefore never returned below xmin, and a value never carries more decimals than you asked for. A normal is unbounded and needs no clamp; lognormal and exponential are bounded below by 0, which sits on every grid — which is why a very small lognormal draw can legitimately be returned as 0 even though the distribution itself is strictly positive.
  • A grid coarser than the distribution gets a warning. If fewer than four distinct grid points fit inside the interquartile range — precision=0 on a uniform over [0.3, 0.7], rate=1e9 at the default precision — the values stop being a usable sample of that shape. The response says so in meta.warnings, naming the step and the interquartile range. Nothing is silently flattened.
  • An empty grid is a 400. ?dist=uniform&min=0.3&max=0.7&precision=0 has no value with zero decimals anywhere inside its bounds, so it is refused rather than answered with something outside them.

Why every record has an index. A generated list has to return count distinct records, and discrete draws repeat by nature — ten Poisson draws at lambda=2 will contain duplicates, as they should. index (the 0-based record position) makes the records distinct without touching the numbers: no jitter is ever added to value, because that would quietly corrupt the very distribution you asked for. If you only want the numbers, project it away with ?fields=value.

percentile is the theoretical CDF evaluated at the value that was actually returned — the rounded one, so you can recompute it yourself — clamped to [0, 1] and rounded to 6 decimals. The normal and log-normal CDFs use the erf approximation published as Abramowitz & Stegun formula 7.1.26, whose maximum absolute error is 1.5e-7; six decimals is as far as that is worth reporting. Poisson, binomial and Zipf return percentile: null: their CDF is a step function, so "the percentile of this draw" is not well defined.

/summary draws size values (default 1000) and reports them as statistics. Because record i uses exactly the seed derivation the root route uses, the sample is the first size records the root route returns for the same seed and parameters. empirical.variance is the population variance (divided by size, not size − 1); median, p25, p75 and p95 use the nearest-rank method — the ⌈p·size⌉-th smallest draw — so each is a real value from the sample, not an interpolation. Unlike the root route, /summary defaults to seed=0 rather than a random seed, so it is reproducible out of the box.

Honest limits.

  • These are deterministic draws from a seeded mulberry32 stream. They are statistically fine for fixtures, load-shape modelling and tests, and they are not cryptographically secure — never use them for keys, tokens, lotteries, gambling or anything else security-sensitive.
  • Empirical statistics of a finite sample will not equal the theoretical ones. That gap is sampling error, not a bug; raise size and it shrinks.
  • precision rounds the returned values but not the theoretical moments, which are the closed forms of the unrounded distribution. At a sensible precision the difference is far below sampling error; at a coarse one the warning above tells you the two are no longer describing the same thing.
  • Theoretical moments that do not exist come back as null with an explanation in theoretical.note — a Pareto with alpha ≤ 1 has an infinite mean, and with alpha ≤ 2 an infinite variance. The same happens to a moment that exists but is too large for a double (a log-normal with a very large stddev), and note says which of the two it was. Never a fabricated number.
  • Parameter combinations whose largest possible draw would leave IEEE-754 double range (a tiny rate, a tiny Pareto alpha, an enormous log-normal mean) are refused with a 400 that shows the computed bound, so no response ever contains Infinity or an unexplained null.

Use it for

  • Generate latency fixtures with a realistic long tail instead of uniform noise
  • Model arrivals per interval with Poisson draws or conversions out of N visitors with binomial draws
  • Check a histogram, quantile or streaming-statistics implementation against known theoretical moments
  • Seed charts and dashboards with a believable distribution shape from a fixed seed

Frequently asked questions

How do I generate normal distribution random numbers?

Call /api/distributions?dist=normal&mean=100&stddev=15. Each draw comes from a Box–Muller transform of the seeded stream, and every record reports the theoretical CDF at its own value in percentile.

Which distributions can I draw from?

Eight: uniform, normal, lognormal, exponential, Poisson, binomial, Pareto and Zipf. Every record names the exact sampler branch used in its method field.

Is the Poisson generator exact?

For lambda up to 30 it uses Knuth's product method, which is exact. Above 30 it switches to a rounded normal approximation and says so with method: "normal-approximation" — the switch is never hidden.

Why are all my values coming back as 0 or the same number?

precision (default 4) rounds every continuous draw, so a distribution narrower than that grid collapses onto a few points. The response warns whenever fewer than four grid steps fit in the interquartile range — raise precision and the shape comes back.

How do I check the shape of the data?

/api/distributions/summary returns a sample's empirical mean, variance, min, max, median and p25/p75/p95 next to the theoretical moments. For the same seed and parameters its sample is exactly what the root route returns.

Can I use these numbers for a lottery or key generation?

No. They come from a deterministic seeded mulberry32 stream — reproducible by design and not cryptographically secure. Use them for fixtures and load-test shapes only.

Standards & references