RESTOpenAPI 3.0 spec · Python + Node SDKs · v0.4.2

Plagiarism checker API.

Drop-in REST endpoint for plagiarism + AI detection. OpenAPI 3.0 spec, Python and Node SDKs, Apache 2.0 reference implementation. From your first curl to webhook-driven volume — same engine that runs at noplag.com.

REST · OpenAPI 3.0Python + Node SDKsApache 2.0 reference impl
THE FIRST CALL

From zero to a similarity score in one curl.

POST text, get scored intervals back. No SDK required, no compile step, no proprietary wire format.

REQUEST · bashCopy
$ curl https://engine.noplag.app/v1/checks \    -H "Authorization: Bearer $NOPLAG_KEY" \    -H "Content-Type: application/json" \    -H "Idempotency-Key: $(uuidgen)" \    -d '{        "text": "The rain in Spain falls...",        "corpora": ["web", "academic"],        "ai_detection": true,        "webhook_url": "https://you.example/hook"    }'
RESPONSE · 200 OK · application/jsonCopy
{  "check_id": "chk_01HX9R2Y4...",  "status": "complete",  "similarity_pct": 23.4,  "ai_score": 0.08,  "engine_commit": "v0.4.2",  "sources": [    { "url": "https://en.wikipedia...",      "matched_chars": 142,      "similarity_pct": 100.0 }  ]}
THE ENDPOINTS

Four endpoints. One contract. Versioned in the URL.

Same OpenAPI 3.0 spec lives in the open-source repo and on engine.noplag.app. SDKs regenerate from it on every tagged release.

POST/v1/checksSubmit a checkSynchronous up to 8s; webhook callback above that. Idempotency-Key required.
GET/v1/checks/{id}Fetch a checkReturns the latest report state. Stream intervals available via Accept: text/event-stream.
GET/v1/sources/{id}Inspect a sourceResolves a matched source by ID — full URL, snapshot date, fetched-from cache vs live.
POST/v1/foldersManage foldersCreate / list / move checks. Scopes corpus matching to per-folder private documents.
POST/v1/webhooksRegister a webhookHMAC-signed deliveries; exponential retry over 24h; replay any delivery from the dashboard.
Full reference at api./docsopenapi.yaml
AUTH + RATE LIMITS

Bearer tokens. Idempotency keys. Honest rate-limit headers.

Nothing exotic. Standard HTTP idioms — we follow them so your existing retry middleware works.

AUTHENTICATION

Bearer tokens. One header, no signing dance.

1.
Create a keyDashboard → Settings → API. Keys are scoped (read / write / admin) and rotatable in two clicks.
2.
Pass it on every callAuthorization: Bearer nplg_live_…. Test keys (nplg_test_) hit a sandbox that never persists submissions.
3.
Add Idempotency-Key on POSTsAny UUID works. The same key + body returns the cached response for 24h, so retries are free of double-charges and duplicate checks.
Authorization: Bearer nplg_live_a82f...Idempotency-Key: 0e8f9c44-...
RATE LIMITS

Per-key, per-minute. Headers tell you exactly where you are.

TIERREQ/MINMONTHLYBURST
Free10100 / mo20
Pro601,000 / mo120
Premium30010,000 / mo600
EnterpriseCustomNegotiatedCustom
X-RateLimit-Limit: 60X-RateLimit-Remaining: 41X-RateLimit-Reset: 1747920480Retry-After: 19  (only on 429)
THE SDKs

Two SDKs. Generated from the same OpenAPI 3.0 spec.

Python + Node ship on every tagged engine release. Other languages (Go, Ruby, Java) generate from openapi.yaml — community PRs welcome.

PYTHONpip install noplag
from noplag import Noplagclient = Noplag(api_key="nplg_live_…")report = client.checks.create(    text="...",    corpora=["web", "academic"],    ai_detection=True,)print(report.similarity_pct)
NODEnpm i @noplag/sdk
import Noplag from "@noplag/sdk";const client = new Noplag({    apiKey: process.env.NOPLAG_KEY,});const report = await client.checks.create({    text: "...",    corpora: ["web", "academic"],    aiDetection: true,});
<200msp95 latency
99.95%uptime SLA
3regions (US/EU/AP)
OAPI 3spec format
Apache 2.0reference impl
WEBHOOKS

Long-running checks call you back. With retries that don't lose your mind.

01

Four event types. JSON body. HMAC-SHA256 signature in the header.

check.completedReport ready · embeds the full report JSON
check.failedPermanent failure · includes error code + retry advice
check.progressIntermediate progress · sources resolved so far
folder.sharedFolder access granted to a teammate or external auditor
02

Exponential retries over 24 hours. Replayable from the dashboard.

Retry curve5s, 30s, 2m, 10m, 1h, 6h, 24h — then dead-letter
Timeout10s connect, 30s total per attempt
Success criteriaHTTP 2xx response within window; we follow redirects
Replay UIPick any delivery from the dashboard and re-fire to a different URL
03

Check the signature. Reject anything that doesn't compute.

Every delivery carries X-Noplag-Signature: t=<unix>,v1=<hex>. Compute HMAC-SHA256 over the timestamp + body with your webhook secret and constant-time compare.

h = hmac.new(secret,    f"{t}.{body}".encode(),    hashlib.sha256).hexdigest()assert hmac.compare_digest(h, v1)
DEV PRINCIPLES

We follow HTTP. That's the whole API design document.

Idempotent on POST. Versioned in the URL. Errors that say what to do next. Headers that tell you exactly where you are in the rate-limit window. Pagination by cursor, not offset. Timestamps in UTC ISO 8601. We don't have a custom HATEOAS dialect, we don't return 200 OK on errors, and we don't make you implement OAuth for a server-to-server call. The API design document is RFC 9110 and the OpenAPI 3.0 spec — there isn't a separate “Noplag Way” you have to learn.

Read the design notes
DESIGN CONSTRAINTS
IDEMPSame Idempotency-Key + body within 24h returns the cached response. Retry on network failure with the same key — no double-charge, no duplicate check.
VERMajor version in the URL (/v1, /v2). Breaking changes mean a new version path — old version supported 12 months past announcement.
ERRRFC 9457 problem-details JSON for every 4xx/5xx. type, title, detail, instance, plus a noplag.retry_strategy hint.
PAGCursor-based on every list endpoint. ?limit=100&cursor=… returns next_cursor in the body. No offset surprises at page 47 when new rows arrive mid-iteration.
OBSResponse includes X-Engine-Commit, X-Request-Id, and Server-Timing per stage. Replay any check by ID; the report links to the exact engine release that produced it.
OPENReference implementation Apache 2.0 at github.com/NoplagLabs/noplag-engine. Self-host the call if your data can't leave your network — same API surface.
FAQ

The questions our integration engineers actually ask.

What's the call latency for a typical document?
p50 around 600ms for a 500-word check against the indexed corpus; p95 ~1.8s. Add ~2s when Layer-W (live web verification) is enabled — that round-trips through Google + Brave. Long documents (over 8s expected work) return a check_id immediately and call back via webhook.
How does pricing meter against the API?
One billed call per /v1/checks submission, regardless of document size up to the per-plan word ceiling (1,500 words Pro, 50,000 Premium, custom Enterprise). Idempotent retries with the same Idempotency-Key + body within 24h don't double-count. /v1/checks/{id} reads and SDK auxiliary calls are free.
Can I run the API self-hosted?
Yes — github.com/NoplagLabs/noplag-engine is the same Apache-2.0 reference implementation that runs at engine.noplag.app. docker compose up gets you a local /v1/checks endpoint. The cloud tier adds the managed corpus + the Google/Brave API keys; bring your own for full self-host.
Is there a rate-limit retry header I can trust?
On 429 we return Retry-After in seconds. The X-RateLimit-* headers are returned on every response — you can pre-throttle off X-RateLimit-Remaining rather than waiting for a 429. Both are accurate, both are stable across deploys.
What's the SDK release cadence?
Python and Node SDKs auto-regenerate from openapi.yaml on every engine tag. Major engine releases (v0.x → v0.y) get same-day SDK versions. Patch fixes ship same-week. Both SDKs follow semver; the underlying API surface only breaks on /v1 → /v2.
What happens when the corpus changes mid-month?
Every check stamps X-Engine-Commit and a corpus snapshot timestamp. Re-running an older check against the latest corpus is one query parameter (?corpus_snapshot=…). The original report is preserved with its original snapshot — we never silently change a previously-returned similarity score.
How do I test without burning my monthly quota?
Test keys (nplg_test_…) hit a sandbox: full API surface, never persists submissions, never counts against your monthly quota or rate limits. Webhooks fire from the sandbox as well, so end-to-end integration tests are free.
What's the right way to do bulk checks?
Submit in parallel up to your rate-limit ceiling — there's no batch endpoint by design (a 500-doc batch that fails on doc 312 is worse than 500 independent calls). For high-volume pipelines, pair the API with webhooks: submit, get check_id back immediately, do the next 200 submissions, handle results out-of-order as they arrive.
Can I scope a check to only my own folders?
Yes — corpora: [] with folder_id in the request body matches only against user-private folders. Combine with corpora: ["academic"] to do “only academic + my folders”. Per-call scoping; no separate billing tier needed.
What changes if I want EU residency or an on-prem setup?
Enterprise tier provisions a dedicated EU-residency endpoint (api.eu.noplag.com) with the same OpenAPI surface, so personal data stays in the EEA. Or self-host the reference implementation entirely inside your own VPC — same engine, same /v1 contract, nothing leaving your network. (We're relaunching as an open-core company; formal attestations like SOC 2 are on the roadmap, not yet in hand — the auditable engine is what we offer in the meantime.)

Get an API key. Make your first call.

Free trial includes 100 API calls. Pro tier $23/mo for 1,000. Apache 2.0 reference implementation if you'd rather keep the call inside your network.

Plagiarism Checker API — REST, OpenAPI 3.0, SDKs — Noplag