Public API
Run MAIVE, WAIVE, WLS, and RTMA models from your own scripts and pipelines.
The API is free and anonymous: no accounts, no API keys, no sign-up. It is the same compute this web app uses, given a documented JSON contract. Abuse is bounded by server-side concurrency caps and edge rate limits rather than by identity.
Base URL
https://api.maive.euContent type is application/json in both directions. The machine-readable contract is the OpenAPI 3 spec, which is the source of truth; the usage guide has the same examples in narrative form.
Endpoints
| Endpoint | Kind | Description |
|---|---|---|
POST /v1/run-model | Sync | Run MAIVE, WAIVE, or WLS and get the result in the response. |
POST /v1/run-rtma | Sync | Run RTMA and get the result in the response. |
POST /v1/runs | Async | Queue a run; returns a jobId immediately. |
GET /v1/runs/{jobId} | Async | Poll a run; returns the result once it has succeeded. |
GET /v1/health | Meta | Health check. |
Synchronous or asynchronous
Async is the recommended default.
Synchronous endpoints return the result in the same response, which is the simplest thing to call, but they are subject to the edge's roughly 100 second proxy cap. That is comfortable for typical runs (about 15 to 60 seconds including a cold start) and unsuitable for anything larger.
The asynchronous path (POST /v1/runs then poll GET /v1/runs/{jobId} every 2 to 5 seconds) holds no long-lived connection, so it has no failure mode tied to run duration. Use it by default, especially for batch or CI usage. Batch status for several runs at once: GET /v1/runs?ids=a,b,c (up to 100 ids, statuses only).
Data
Send data as an array of row objects. Columns are resolved by canonical key name when present (matched case-insensitively); otherwise the first 3 or 4 keys are read positionally in the order effect, se, n_obs, study_id.
| Field | Type | Required for | Notes |
|---|---|---|---|
effect | number | MAIVE-family, RTMA | Estimated effect size. |
se | number | MAIVE-family, RTMA | Standard error; must be greater than 0. |
n_obs | integer | MAIVE-family | Number of observations; must be a positive integer. |
study_id | string | Optional | Enables study clustering. If present, rows must be at least unique studies plus 3. |
The MAIVE family (/v1/run-model) needs 3 or 4 columns and at least 4 rows. RTMA (/v1/run-rtma) needs 2 columns (effect, se); rows with a missing or non-positive se are silently dropped. These rules mirror the app's own validation page and run server-side, so you get a structured 400 instead of a raw R error.
Parameters
Every parameter is optional; unset ones fall back to the defaults below.
A minimal valid request is just {"data": [...]}:
{
"data": [
{"effect": 0.42, "se": 0.11, "n_obs": 120},
{"effect": 0.31, "se": 0.06, "n_obs": 90},
{"effect": 0.55, "se": 0.20, "n_obs": 45},
{"effect": 0.12, "se": 0.04, "n_obs": 200}
]
}MAIVE, WAIVE, and WLS
| Parameter | Values | Default |
|---|---|---|
modelType | MAIVE | WAIVE | WLS | MAIVE |
maiveMethod | PET | PEESE | PET-PEESE | EK | PET-PEESE |
weight | equal_weights | standard_weights | adjusted_weights | study_weights | equal_weights |
standardErrorTreatment | not_clustered | clustered | clustered_cr2 | bootstrap | clustered_cr2 |
includeStudyDummies | boolean | false |
includeStudyClustering | boolean | false |
computeAndersonRubin | boolean | false |
useLogFirstStage | boolean | false |
winsorize | number (percent, 0 disables) | 0 |
shouldUseInstrumenting | boolean | derived: false for WLS, true otherwise |
RTMA
| Parameter | Values | Default |
|---|---|---|
favorPositive | boolean | true |
alphaSelect | number | 0.05 |
ciLevel | number | 0.95 |
winsorize | number (percent, 0 disables) | 0 |
seed | integer (sampler seed, echoed in the response) | 2025 |
Plots (funnelPlot, zScorePlot, and their width and height companions) are excluded by default: each is a base64 PNG of roughly 50KB that most callers do not need. Add ?include=plot to any run or poll request to embed them.
RTMA convergence diagnostics
Whether an RTMA result means anything at all.
Every RTMA response carries a diagnostics object. Check it before using the numbers beside it: mu and tau are modes from an optimisation that runs separately from the sampler, so they can fail on their own while the credible intervals stay sound. A null means the value could not be read off the fit, which is not the same as the fit being healthy.
| Field | Type | Notes |
|---|---|---|
diagnostics.optimConverged | boolean | null | Whether the optimisation that produces the reported modes converged. It runs separately from the sampler, so false means mu and tau are meaningless while muCI and tauCI, being posterior quantiles, stay valid. |
diagnostics.rHat | { mu, tau }: number | null | Gelman-Rubin statistic per parameter. Above 1.01 the chains have not converged on the same distribution. |
diagnostics.nEff | { mu, tau }: number | null | Effective posterior sample size per parameter. The sampler runs four chains, so below roughly 400 the summaries rest on few independent draws. |
diagnostics.divergences | integer | null | Divergent transitions. Any at all mean part of the posterior went unexplored, so the intervals can be biased even at a healthy rHat. |
The response has no standard error for mu on purpose. The se column that phacking reports internally is the Monte Carlo error of the posterior mean, not the posterior standard deviation, and the two differ by more than an order of magnitude on ordinary data. Use muCI for uncertainty.
Example: synchronous run
curl -s https://api.maive.eu/v1/run-model \
-H 'Content-Type: application/json' \
-d '{
"data": [
{"effect": 0.42, "se": 0.11, "n_obs": 120},
{"effect": 0.31, "se": 0.06, "n_obs": 90},
{"effect": 0.55, "se": 0.20, "n_obs": 45},
{"effect": 0.12, "se": 0.04, "n_obs": 200}
]
}'Example: asynchronous run
Submit, poll until terminal, then read the result.
# 1. Submit
job=$(curl -s https://api.maive.eu/v1/runs \
-H 'Content-Type: application/json' \
-d '{
"modelType": "MAIVE",
"data": [
{"effect": 0.42, "se": 0.11, "n_obs": 120},
{"effect": 0.31, "se": 0.06, "n_obs": 90},
{"effect": 0.55, "se": 0.20, "n_obs": 45},
{"effect": 0.12, "se": 0.04, "n_obs": 200}
]
}' | jq -r '.jobId')
echo "jobId: $job"
# 2. Poll until terminal
while true; do
status=$(curl -s "https://api.maive.eu/v1/runs/$job" | tee /tmp/run.json | jq -r '.status')
echo "status: $status"
[[ "$status" == "succeeded" || "$status" == "failed" || "$status" == "timedout" ]] && break
sleep 3
done
# 3. Read the result
jq '.result' /tmp/run.jsonErrors and rate limits
Every error shares one envelope:
{
"error": {
"code": "validation_error",
"message": "Data must have 3 or 4 columns; found 6."
}
}| Code | Status | Meaning |
|---|---|---|
validation_error | 400 | The request body failed validation. |
not_found | 404 | Unknown or expired jobId. |
method_not_allowed | 405 | The HTTP method is not supported on this route. |
payload_too_large | 413 | The dataset is too large to queue (roughly 900KB of JSON). Use a synchronous endpoint instead. |
rate_limited | 429 | Edge rate limit or the backend concurrency cap was hit. |
internal_error | 500 | An unexpected server-side error occurred. |
not_configured | 503 | This deployment has no async runs infrastructure configured. |
Two guardrails apply: a hard concurrency cap on the compute backend, and per-IP rate limiting at the edge. Both are tuned from observed load, so treat any 429 as "retry with backoff" rather than a hard quota.
jobId and privacy
- A
jobIdis an opaque bearer token, not an account-scoped identifier. Anyone holding it can read that run's status and result, so treat it like a share link. - Runs and their results expire after 48 hours; polling after that returns
404 not_found. - Synchronous runs are stateless. Asynchronous runs persist the parameters and the result for up to 48 hours; the input dataset itself is not persisted beyond the transient queue message.
- Do not submit confidential or personally identifiable data. There is no data-classification or redaction layer; treat every request the way you would treat a request to any other unauthenticated public web service.
Citation
If you use this API in published or reported work, please cite the paper behind the model you ran. RTMA is Mathur's method; the other models are MAIVE's.
How to Cite This App
Please cite the method you used, and the MAIVE app that runs it, when using this tool for your research.
MAIVE, WAIVE, and WLS models
Irsova, Z., Bom, P.R.D., Havranek, T., & Rachinger, H. (2025). Spurious precision in meta-analysis of observational research. Nature Communications, 16, 8454. https://doi.org/10.1038/s41467-025-63261-0
RTMA model
Mathur, M. B. (2024). P-hacking in meta-analyses: A formalization and new meta-analytic methods. Research Synthesis Methods, 15(3), 483-499. https://doi.org/10.1002/jrsm.1701
RTMA software
Mathur, M., & Braginsky, M. (2023). phacking: Sensitivity Analysis for p-Hacking in Meta-Analyses. R package version 0.2.1. https://doi.org/10.32614/CRAN.package.phacking