ESCIMate API
Programmatic access to the statistical consistency checker. Version 0.3.5.
Base URLs
| Environment | URL |
|---|---|
| Production | https://escimate.app |
| Local development | http://127.0.0.1:9422 |
Cold-start warning: the production server (Render free tier) sleeps after 15 minutes of inactivity. The first request after idle may take ~76 seconds. Probe /health first to wake the server.
No authentication required. No API keys. No rate limiting at the API level — but Plumber is single-threaded, so concurrent requests are queued.
Contents
1. Quick Start
Health check:
curl https://escimate.app/healthAnalyze a PDF file:
curl -X POST https://escimate.app/api/v1/process \
-F "file=@paper.pdf"Analyze pasted text:
curl -X POST https://escimate.app/api/v1/process-text \
-H "Content-Type: application/json" \
-d '{"text": "t(28) = 2.21, p = .035, d = 0.80"}'Analyze text with custom options:
curl -X POST https://escimate.app/api/v1/process-text \
-H "Content-Type: application/json" \
-d '{
"text": "F(1, 58) = 7.23, p = .009, eta2 = .11",
"options": {
"alpha": 0.05,
"one_tailed": false,
"cross_type_action": "NOTE",
"stats": ["t", "F", "r", "chisq"]
}
}'Generate an HTML report (file or text):
curl -X POST "https://escimate.app/api/v1/report?style=beginner" \
-F "file=@paper.pdf" \
-o report.html
curl -X POST https://escimate.app/api/v1/report-text \
-H "Content-Type: application/json" \
-d '{"text": "t(28) = 2.21, p = .035, d = 0.80", "style": "expert"}' \
-o report.htmlCompare with statcheck:
curl -X POST https://escimate.app/api/v1/compare \
-F "file=@paper.pdf"
curl -X POST https://escimate.app/api/v1/compare-text \
-H "Content-Type: application/json" \
-d '{"text": "t(28) = 2.21, p = .035, d = 0.80"}'Python example:
import requests
# From text
r = requests.post(
"https://escimate.app/api/v1/process-text",
json={"text": "t(28) = 2.21, p = .035, d = 0.80",
"options": {"alpha": 0.05}},
timeout=120,
)
data = r.json()
for row in data["results"]:
print(row["test_type"], row["status"], row.get("delta_effect"))
# From file
with open("paper.pdf", "rb") as f:
r = requests.post(
"https://escimate.app/api/v1/process",
files={"file": f},
data={"options": '{"alpha": 0.01}'},
timeout=300,
)
print(r.json()["summary"])JavaScript / Node example:
// From text
const res = await fetch("https://escimate.app/api/v1/process-text", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
text: "t(28) = 2.21, p = .035, d = 0.80",
options: { alpha: 0.05 },
}),
})
const data = await res.json()
console.log(data.summary)
// From file (browser)
const fd = new FormData()
fd.append("file", fileInput.files[0])
fd.append("options", JSON.stringify({ alpha: 0.05 }))
await fetch("https://escimate.app/api/v1/process", { method: "POST", body: fd })2. Endpoints
/healthReturns server health status. No auth. Useful for cold-start detection.
{"status":"healthy","time":"2026-04-27 12:00:00","version":"0.3.5"}/api/v1/processAnalyze an uploaded file. Accepts PDF, HTML, DOCX, TXT.
Content-Type: multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
| file | file | yes | .pdf, .html, .docx, .txt |
| options | JSON string | no | See Configuration Options |
/api/v1/process-textAnalyze raw text.
Content-Type: application/json
| Field | Type | Required | Description |
|---|---|---|---|
| text | string | yes | Non-empty text to analyze |
| options | object | no | See Configuration Options |
/api/v1/reportGenerate a self-contained HTML report from a file. Form field file required.
Query params: style=beginner (default) or style=expert.
Returns text/html.
/api/v1/report-textHTML report from raw text. Body: { "text": "...", "style": "beginner" | "expert" }.
/api/v1/compareSide-by-side comparison with statcheck on a file. Returns merged JSON results.
/api/v1/compare-textstatcheck comparison on raw text. Body: { "text": "..." }.
3. Configuration Options
Pass via the options object (text endpoints) or as a JSON-encoded options form field (file endpoints).
| Option | Type | Default | Description |
|---|---|---|---|
alpha | number | 0.05 | Significance threshold for decision-error detection. |
tol_effect | object | see §8 | Per-type effect size tolerances. Partial overrides merged with defaults. |
cross_type_action | string | "NOTE" | Status for cross-type-only matches. ERROR | WARN | NOTE | SKIP. |
design_ambiguous_action | string | "WARN" | Status for design-ambiguous t-test ERRORs. ERROR | WARN | NOTE. |
unknown_groups_action | string | "WARN" | Status for d/g ERRORs when n1/n2 unknown. ERROR | WARN | NOTE. |
min_confidence | integer | 0 | Filter results below this 0–10 confidence score. |
ci_affects_status | boolean | true | Whether CI bound mismatches downgrade PASS to NOTE. |
one_tailed | boolean | false | Assume one-tailed tests when computing p-values. |
plausibility_filter | boolean | true | Flag implausible effect sizes as extraction suspects. |
stats | string[] | ["t","F","r","chisq","z","U","W","H","regression"] | Which test types to detect. |
assume_equal_ns_when_missing | boolean | true | Assume equal groups when n1/n2 not reported. |
log | boolean | true | Anonymous usage logging. Set false to suppress. |
Additional R-only parameters (ci_level, paired_r_grid, tol_ci, tol_p, max_text_length, max_stats_per_text, ci_method_phi, ci_method_V, messages) are documented in API.md.
4. Response Format
Process endpoints return:
{
"results": [ /* one object per detected statistic */ ],
"normalized_text": "Full text after Unicode normalization...",
"summary": {
"total": 12,
"pass": 8,
"ok": 2,
"note": 1,
"warn": 0,
"error": 1,
"extraction_suspect_count": 0,
"cross_type_count": 0
},
"effectcheck_version": "0.3.5"
}Each results[] object includes 50+ fields across these groups:
- Identification —
location,raw_text,context_window,test_type,source - Parsed values —
df1,df2,stat_value,N,n1,n2,table_r,table_c - Reported values —
reported_type,effect_reported,ciL_reported,ciU_reported,p_reported,p_symbol,p_valid,N_source - Type-matched comparison —
matched_variant,matched_value,delta_effect,ambiguity_level,all_variants(JSON) - Computed effect sizes —
d_ind,g_ind,dz,dav,drm,eta2,partial_eta2,omega2,cohens_f,phi,V,rank_biserial_r,cliffs_delta,epsilon_squared,kendalls_W,standardized_beta,partial_r, … - P-value & decision error —
p_computed,decision_error,decision_error_reason - CI comparison —
ci_match,ciL_computed,ciU_computed,ci_delta_lower,ci_delta_upper,ci_check_status,ci_method_match,ci_width_ratio,ci_symmetry,ci_level_source - v0.3.5 CI audit —
effect_reported_decimals,ciL_reported_decimals,ciU_reported_decimals,stat_value_decimals,ci_expected,ci_reported,ci_level_mismatch,ci_clipped_to_bound,ci_symmetry_class - Reproducibility —
repro_code,repro_output - Status & metadata —
status,check_type,extraction_suspect,design_ambiguous,design_inferred,variants_tested,uncertainty_level,uncertainty_reasons,assumptions_used,insufficient_data
Field-level documentation also lives in the R package's function help: ?effectcheck::check_text.
5. Status Taxonomy
| Status | Meaning | Criteria |
|---|---|---|
| PASS | Consistent | Effect size delta ≤ tolerance (or APA rounding match). |
| OK | P-value consistent | No effect size to compare; reported p matches recomputed p. Also for 'ns' confirmed. |
| NOTE | Cannot fully verify | Missing data, CI mismatch downgrade, plausibility filter, or cross-type fallback. |
| WARN | Moderate discrepancy | 1× < delta ≤ 5× tolerance, design-ambiguous t-test, or qualifying decision error. |
| ERROR | Large discrepancy | Delta > 5× tolerance, or clear decision error (significance flips). |
| SKIP | Not verifiable | Extraction-only result with nothing to check. |
Modifiers: decision error escalates to ≥ WARN; CI mismatch downgrades PASS → NOTE (when ci_affects_status); plausibility filter downgrades ERROR → NOTE; design-ambiguous t-test ERROR downgrades to design_ambiguous_action.
6. Effect Size Families
| Test type | Reported | Computed variants |
|---|---|---|
| t-test | d | d_ind, d_ind_equalN, d_ind_min, d_ind_max, dz, dav, drm |
| t-test | g | g_ind (Hedges' bias-corrected) |
| t-test | r | r (point-biserial from t) |
| F / ANOVA | eta², partial η², ω², f | eta2, partial_eta2, omega2, cohens_f |
| F / ANOVA | d (2-group) | d (converted from F) |
| r (correlation) | r | r (verified via t-transformation) |
| chi-square | phi, V, OR, h | phi (2×2), V (larger), OR, h |
| z-test | d, r | d = z/√N; r converted from z |
| Mann-Whitney U | rank_biserial_r, cliffs_delta | rank_biserial_r, cliffs_delta |
| Wilcoxon W | rank_biserial_r | rank_biserial_r |
| Kruskal-Wallis H | epsilon², Kendall's W | epsilon_squared, kendalls_W |
| Regression | β, partial_r | standardized_beta (from t and df), partial_r |
7. R Package API
Install from CRAN (recommended) or GitHub:
# Stable release from CRAN
install.packages("effectcheck")
# Or development version from GitHub
# install.packages("remotes")
remotes::install_github("giladfeldman/escicheck")Core functions:
library(effectcheck)
# From text
res <- check_text("t(28) = 2.21, p = .035, d = 0.80")
print(res); summary(res)
# Single file
res <- check_file("paper.pdf", alpha = 0.01)
# Whole directory (recursive)
res <- check_dir("manuscripts/", stats = c("t", "F"))
# HTML report
generate_report(res, out = "report.html", style = "beginner")
# statcheck side-by-side
cmp <- compare_with_statcheck("t(28) = 2.21, p = .035, d = 0.80")statcheck-compatible wrappers also exported: checkPDF(), checkHTML(), checkPDFdir(), checkHTMLdir(), checkDOCXdir().
8. Tolerance Defaults
| Effect size | Tolerance | Effect size | Tolerance |
|---|---|---|---|
| d, g, dz, dav, drm | 0.02 | r, partial_r | 0.005 |
| phi, V, h, cohens_f | 0.02 | eta2, etap2, omega2 | 0.01 |
| beta, R2 | 0.01 | f2 | 0.02 |
| rank_biserial_r, cliffs_delta | 0.02 | epsilon_squared, kendalls_W | 0.01 |
| OR, RR, IRR | 0.5 | tol_ci / tol_p | 0.02 / 0.001 |
Plausibility bounds: |d|, |g|, |cohens_f|, |beta| > 10; |r|, |phi|, |V|, |R2|, |eta2|, |omega2|, |rank_biserial_r|, |cliffs_delta|, |epsilon_squared|, |kendalls_W| > 1; |OR|, |RR|, |IRR| > 100; |h| > 3.15. Any effect size delta > 1.0 is flagged as a likely extraction error.
9. Error Handling
| Code | Meaning | When |
|---|---|---|
| 200 | Success | Request processed successfully. |
| 400 | Bad Request | Empty file or missing/empty text field. |
| 500 | Internal Server Error | Corrupt file, invalid format, or R error. |
JSON endpoints return { "error": "message" }. HTML report endpoints return an HTML error page.
CORS allows: localhost:2249/3000, 127.0.0.1:2249/3000, escimate.app, www.escimate.app, *.vercel.app. Non-browser requests (no Origin) get *.
10. Limits & Performance
| Limit | Default | Notes |
|---|---|---|
| max_text_length | 10,000,000 chars (~10 MB) | Per request |
| max_stats_per_text | 10,000 | Per request |
| Upload size | 100 MB | plumber.max_post_body |
| Cold start | ~76 s | Render free tier sleeps after 15 min idle |
| Warm latency | ~12 s | Typical PDF, single user |
| Concurrency | 1 (queued) | Plumber single-threaded; 3 concurrent ≈ 22/35/47 s |
No rate limiting at the API level. Be considerate — this is a free academic service. For heavy batch use, run the R package or self-host with the Docker image (see GitHub).
Run it yourself
The effectcheck R package is open-source on GitHub and CRAN — the same engine that powers this API. For local or batch use without going through the hosted API, install the R package and call check_text() / check_file() / check_dir() directly. The hosted web service (frontend, deployment glue) is not open-source.